Логотоп
              TkGate

TKGate 2.1: Digital Circuit Editor and Simulator

© 1987-2015 by Jeffery P. Hansen
© 2015-2018 by Andrey V. Skvortsov

This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.


TkGate is a graphical editor and  event-driven simulator for digital circuits with a tcl/tk-based interface.  Supported circuit elements include basic gates (AND, OR, etc.), tri-state gates, adders, multipliers, registers, memories and mos transistors.  Hierarchical design is also supported with support for user defined modules.  Save files are based on the Verilog netlist format.
 

Contents:

  1. Introduction to TkGate
  2. Using the Editor
  3. Working with Modules
  4. HDL Modules
  5. Using the Simulator with TkGate
  6. Gate Microcode and Assembly Compiler (gmac)
  7. Making Virtual Peripheral Devices (VPDs)
  8. Setting TkGate Options
  9. TkGate Library Components
Appendices
  1. List of Built-In Gate Types
  2. List of System Tasks
  3. List of Simulator Error Messages
  4. Using the Verga Simulator in Stand-Alone Mode
  5. Sample Gmac Input File

2. Using the Editor

This chapter will introduce the basic concepts needed to create a graphical circuit in TkGate.

2.1 Understanding the TKGate Interface

TkGate Main Window


Figure 2.1: TkGate Main Window
The main components of the TKGate edit window consists of a menu bar and tool bar at the top, the module, net and port lists on the left, a status bar on the bottom, and the main editing area in the center. The main edit window contains major mode tabs to switch between "Edit" mode for editing your circuit, "Interface" mode for editing the interface of any modules you define, and "Simulate" mode for simulating your circuit. Scroll bars can be used to scroll the circuit, the list of modules or the list of nets. 

2.1.1 Main Menu

The TkGate menu bar appears at the top edge of the main window and is arranged by category. The menus that are available can change when you enter different modes (e.g., editing versus simulation).

2.1.2 Pop-up Menus

Many of the features described in this manual can also be accessed using the context sensitive pop-up menus. The pop-up menus are obtained by pressing the right mouse button, and the menu items that appear depend on what was selected. The menu that appears, depends on where you clicked and what the current mode is.

2.1.3 Toolbar

Frequently used commands can be accessed through the TkGate toolbar. These include commands for opening, saving and printing circuit files, changing the editing tool, opening and closing modules and controlling the simulator. The toolbars that are displayed change dynamically with mode. You can also configure which toolbars you want to use on the Toolbars tab of the Options dialog box. A complete list of the toolbar buttons are given in the List of Toolbar Buttons.

2.1.4 Keyboard Shortcuts

Many of the commands that can be accessed through the menu or toolbar can also be accessed through keyboard shortcuts. Commands for creating gates are typical single character letters. When possible, the letter is the first character of that gate type (e.g., "a" to create an "AND" gate and "r" to create a "register"). By default, file handling commands use Emacs-style shortcuts (e.g., "ctrl-c ctrl-s" to save, "ctrl-x ctrl-f" to load a file, etc.) If you prefer, you can choose to use Windows style shortcuts through the Interface tab on the options dialog box.

2.1.5 Module List and Module Hierarchy

The modules defined in the current circuit can be shown as in an alphabetical list view (press modlist tool to select), or in a hierarchy view (press modtree tool to select). The list view simply displays all of the modules in alphabetical order. The hierarchy view displays the module hierarchy starting at the top-level module. Under each module is the list of modules used by that module. There may also be additional hierarchies with the loaded library parts and any unused modules. Modules and the module hierarchy are described in greater detail in Section 3.3 Module Hierarchy.

2.1.6 Net and Port Lists

Below the module list in the lower-left corner of the main window is the list of nets or ports in the current module. A symbol precedes each net name depending on the type and whether the net is 1-bit or multi-bit as shown in the table below:

1-Bit
Symbol
Multi-Bit
Symbol
Description
Wire with
                a hidden name Wire with
                a hidden name2 Wire with a hidden name.
Wire with a
                visible name Wire with
                a visible name2 Wire with a visible name.
register
                variable register
                variable2 Register variable (HDL modules only).
Wire with
                a probe Wire with
                a probe2 Wire with a probe attached to it (in simulation mode).
Module
                input port Module
                input port2 Module input port.
Module
                output port Module
                output port2 Module output port.
Module
                bidirectional port Module
                bidirectional port2 Module bidirectional port.

The "Ports" list can be used to list the modules nets that are defined as part of the interface for the current module. These are nets that the user is expected to define in the module definition.

2.1.7 Status Bar

Status Bar

Figure 2.2: Status Bar
The status bar on the bottom indicates the file that is being edited, and the current module from that file that is displayed in the circuit window.  A "*" after the file name indicates that the buffer has been modified since the last time the file was saved.  Below the file and module name is a message bar for informational messages from TKGate.  These messages include confirmation for files that are loaded or saved, or information on the currently selected gate or wire.

2.1.8 Major Mode Tabs

Major Mode Tabs

Figure 2.3: Major Mode Tabs
The main edit window has three tabs to indicate and select the major mode in TkGate. "Edit" mode is used for creating and editing your circuit. The majority of your time using TkGate is likely to be in this mode. The "Interface" tab is used to design the interface for any modules that you define. You can also use this mode to view the interface of a module without creating a module of that type. The "Simulate" tab is used to perform simulations. When you press this tab, TkGate will send your circuit to the simulator, check for errors, and if your circuit is error free, start the simulator. By default, the simulator will be "paused" and you will need to press the sim go
      tool button.

The icon in the upper right corner of the interface can also help show you what mode you are in. The possible icons are:

Symbol Description
gatelogo Edit/Interface Mode - The TkGate logo is shown when TkGate is in the "Edit" or "Interface" modes.
simlogo Paused Simulation - A stopped AND dragon shows that TkGate is simulation mode, and the simulation is paused.
runsim Running Simulation - A running AND dragon shows that TkGate is in simulation mode, and the simulation is running.

2.1.9 Balloon Help

Balloon Help

Figure 2.4: Balloon Help
Balloon help is available for many of the elements in the interface, typically on the prompt text. To use balloon help, simply move the mouse cursor over an interface element. After about one second, an informational help message will be displayed as long as the mouse cursor has not moved. For example, moving the mouse cursor over the "Nets" label on the main window produces a balloon help message as shown below: Balloon help is also available in most of the dialog boxes.  Position the cursor over the text in a dialog box to view a more detailed description.

2.2 Loading and Saving Circuits

Open File Dialog Box

Figure 2.5: Open File Dialog Box
Opening and saving circuits as well as circuit printing operations are done through the "File" menu option on the main window.  To load a file into TKGate, you can either specify the file name on the command line when you start, select "Open..." from the "File" menu or use the file open button button on the toolbar. You can also use the keyboard shortcut Ctl-X Ctl-F. When opening a new file, all existing modules will be flushed from the buffer, and the modules in the new file will be loaded. TkGate save files have the extension ".v" and are in a special annotated Verilog format. Files saved from TkGate contain special comments with information such as circuit elements and wire positions. Also, gates with no explicit counter part in Verilog such as switches and clock generators are saved as annotation comments in the save file.

You can also load non-TkGate generated Verilog files. These files will load as a set of HDL modules. When saving from a file that was loaded as a non-TkGate Verilog file, the saved file will contain TkGate annotations.

When you open or save a circuit, you will see a dialog box such as the one shown in the figure on the right. Enter a file name at the prompt and press the "Open" (or "Save") button, or double click on a file name.  Only the appropriate type of files will be displayed.  You can change directories by double clicking on a folder to open it, or by using the button in the upper right corner to move up to the parent folder.

2.3 Loading Libraries

library manager


Figure 2.6: Library Manager
Libraries are loaded and unloaded through the "Library Manager". Select "Library Manager" from the "File" menu or select file lib
      tool from the toolbar. This will open the library manager as shown below: To load a library, select it and press the "Load" button. To unload a library, select it and press the "Unload" button. When you have finished selecting which libraries you wish to have loaded, close the dialog box by pressing "OK". If you press "Cancel", all of your changes to the set of loaded libraries will be ignored.

The set of libraries that are displayed in the library are found by searching the TkGate library path. The library path can be set through the library options dialog and can include user-defined libraries.

When you save your design after loading libraries, TkGate will store the names of libraries you have loaded in the save file. Then when you load your design again, TkGate will automatically load the necessary libraries.

2.4 Basic Editing Tools

The basic editing tools are selected through the "Mode" toolbar. While a mode is active, the mouse cursor will change to reflect the active mode. The available editing tools are shown in the table below.

Button Cursor Shortcut Description

mov_curs.gif
ptr.gif
F1 The Move/Connect tool is indicated by the arrow cursor.  Use this tool for most editing operations including creating gates, connecting wires, moving gates and wires, opening module instances, opening property boxes of gates and wires, and deleting circuit elements (with the "delete" key).

mov_curs.gif
solder.gif
(F1) The Connect tool is indicated by the soldering iron cursor.  To use this tool, first select the move/connect tool and then press and hold the left mouse button on a wire endpoint.  The cursor will then change to the connect tool as long as you are holding down the mouse button.

cut_curs.gif
cutters.gif
F2 The Wire Cutter tool is indicated by the wire cutter cursor.  Use this tool to cut wires by clicking on the wire at the point you want to cut it.  If you cut a wire with an unconnected end, the wire segments between the cut point and the unconnected end will be deleted automatically.

inv_curs.gif
invaddel.gif
F3 The Inverter tool is indicated by the inverter bubble grabber cursors.  Use this tool to add or remove inverter bubbles on the inputs and outputs of gates.  Click the cursor near an input or output to converter a non-inverting port to an inverting port or vice-versa.

size_curs.gif
wsize.gif
F4 The Wire Size/Type tool is indicated by the ribbon cable cursor. Use this mode to change the bit width of a wire or the type of wire. The desired bit width and wire type should be selected in the selector boxes next to the size_curs.gif button on the Mode toolbar. Any wires that are clicked on will have their size and type changed to the selected size/type. The wire type affects how the wire's value will be set when multiple gates drive a wire. You can also change wire bit widths and types using the Wire Properties dialog box.

2.4.1 Creating Gates

There are three main ways to create gates in TkGate: using the menu bar, using pop-up menus, and using keyboard shortcuts. Once you have mastered the keyboard shortcuts, that is generally the preferred way to create gates. These three methods are described in the following sections.
Creating a Gate Using the Menu Bar
create1.gif
1) Left click to select the point.
create2.gif
2) Select gate type from menu bar.
create3.gif
3) Gate is created at location set by point.
Figure 2.7: Creating a Gate
1) To create a gate such as a NAND gate, first select the Move/Connect tool , and then press the left mouse button at the position you wish to create it. While you are holding the mouse button down, the cursor will change to an outline arrow to indicate the current orientation. In the case shown at the left, the current orientation is 0 degrees indicating the standard orientation.

When you release the button the arrow will disappear and the mark symbol will remain. The mark indicates the center point for the new circuit element. Clicking the left mouse button at another location will move the mark, and selecting a gate or wire will clear the mark.

2) Select the gate type by opening the "Make" menu. The circuit elements are organized into seven basic categories:

There are also two additional non-circuit element objects that can be created. A "Comment" is an arbitrary block of text that can be used to add documentation to your circuit, and a "Frame" is a labeled box that you can place around a portion of your circuit.

3) After selecting the menu option or typing the shortcut, the circuit element will be created and displayed in bold to indicate that it is selected. While the gate is still selected, you can press the toolbar button or press the Tab key to rotate the gate to the desired orientation.

Creating a Gate Using the Pop-up Menu


Right click to use pop-up menus to create a gate.
Figure 2.8: Creating a Gate with Pop-up Menu
To create a gate using the pop-up menu, right click on the position at which you wish to create a gate. This will both create the position mark and bring up the pop-up menu. Then select "Make", and then maneuver through the gate category to select the appropriate gate type.
Creating a Gate Using Keyboard Shortcuts

To create a gate using keyboard shortcuts, first set a mark in the same way as when creating a gate using the menu bar (Section 2.5.1). Then simply type the shortcut for the gate you wish to create. Most of the gate types have single character commands to create them. For example, you can type "a" to create an AND gate, and "A" to create a NAND gate. A list of the gate types and their keyboard shortcuts are shown in Section 2.16.

2.4.2 Connecting Wires

Port-to-Port Connections

Step 1: select endpoint

Step 2: drag wire end

Step 3: bring wire end
close to other terminal

Step 4: release and connect

Figure 2.9: Port-to-Port Connections
Port-to-port connections are used to connect the end points of two wires. Before making the connections, make sure you have selected the "Move/Connect" tool from the "Tool" menu. Then, click and hold the left mouse button on the endpoint of one of the ends you wish to connect. The mouse cursor will change to a soldering iron to indicate that connect mode is active as shown in Step 1.

Next, drag the mouse toward the other endpoint you wish to connect to as shown in Step 2, TKGate will automatically introduce any necessary bends and maintain connections with only horizontal and vertical segments. Bring the end close the the wire you wish to connect to as shown in Step 3. When you release the mouse button, the wires will connect as shown in Step 4.

Port-to-Joint Connections

Step 1: select endpoint

Step 2: drag wire end

Step 3: bring wire end
close to other wire
and release

Step 4: continue making
connections as necessary

Figure 2.10: Port-to-Joint Connections
Port-to-joint connections are used to connect a wire end point to the center or corner of another wire, or to an existing wire joint. These connections are made essentially the same way as port-to-port connections. First, as in with port-to-port connections, make sure you have selected the "Move/Connect" tool from the "Tool" menu. Then, select a free endpoint to get the soldering iron (Step 1). Drag the wire near the wire you want to connect to (Step 2) and release the wire to connect to the nearby wire segment (or corner) and create a solder joint (Step 3). You can continue the process to connect as many ports to a net as necessary (Step 4).
Creating Bit Taps


Figure 2.11: Bit Tap
When you attempt to do a port-to-joint connection with wires of different sizes, a "bit tap" will be created if you are connecting a wire with smaller bit size to one with a larger bit size. A bit tap element allows you to select one or more bits off of a bigger wire. To set the desired bit, you should double click on the tap to open the Gate Properties box, select the "Details" tab and enter the desired bit or bits. Ranges are indicated as a pair of bit numbers (high number first) separated by a colon (":") character. For example, to select the low four bits you would use the range "3:0". In the bit tap shown here, bit 4 of the register output is used as an input to the AND gate.

Bit taps can only be used to "read" values off of a larger wire. To individually drive bits on a larger wire you should use the "Concat" device. This device can support data flow in either direction.

Creating New Joints


Figure 2.12:
Wire Tap
You can also create joints by right clicking on a wire and selecting "Add Wire Segments". This will create a new unattached wire segment to the wire on which you clicked as shown here.

2.4.3 Moving Gates and Wires

Moving Single Gates
movgat1.gif
Step 1: select gate
movgat2.gif
Step 2: drag gate

Figure 2.13: Moving Single Gates
To move a single gate, press and hold the left mouse button on the gate you wish to move. The selected gate will be shown in bold as shown in Step 1. While holding the mouse left mouse button down drag the gate to the new position as shown in Step 2. Any wires connected to the gate will move along with it. For very fine adjustments, you can also move the selected gate using the arrow keys.
Moving Groups of Gates

Step 1: start selection

Step 2: drag selection box

Step 3: grab selection

Step 4: drag selection

Figure 2.14: Moving Groups of Gates
To move a group of gates, first select the Move/Connect tool and press and hold the left mouse button at some point near, but not directly on the group you wish to move. The "X" mark should appear just as if you were going to create a new gate (Step 1). Next, while holding the left mouse button drag the cursor to enclose a group of gates in the selection box (Step 2). The box will appear after moving the cursor a few millimeters. Release the mouse button to select the circuit elements enclosed in the box (Step 3). Then, click and hold the mouse cursor on one of the selected elements (Step 4). You can then move the entire group to a new position.

The gates in a group selection will remain selected until you click on a gate not in the selection, or click on an empty part of the canvas. If you do this operation while holding the control key, you can add gates to the selection rather than create a new selection.

2.4.4 Cut/Paste and Deleting Gates

The selected gate or gates can be deleted by pressing the delete key, or by right clicking and selecting the "Delete" option. The selection may also be cut or copied to the cut buffer by right clicking and selecting "Cut" or "Copy". The original names of the gates and wires in the cut buffer will be remembered and used when pasting if possible, but when pasting multiple copies from the cut buffer, it may be necessary for TkGate to rename those wires and gates.

2.4.5 Rotating Gates

You can rotate single gates, or groups of gates by selecting the single gate or group and using the and toolbar buttons to rotate counter-clockwise or clockwise, respectively. You can also use the Tab or Shift-Tab keys. When rotating a selection, it can not have any wires connecting a selected element to a non-selected element.

You can also set the default rotation by clicking on the button on the toolbar. The inverter symbol on the button will turn to show the current rotation. Any gates that you create will have the default rotation shown on this button.

2.4.6 Wire Cutter Tool


Before Cut

After Cut

Figure 2.15: Cutting Wires
To cut a wire, select the tool from the toolbar to get the wire cutters tool. Click on a wire to cut the wire at the selected point. If there are any loose ends of a wire that is cut, TkGate will automatically delete the loose ends. It will also delete any joints that are no longer needed as the result of a cut. You can also use the wire cutter tool on the edge of a module to "cut off" a port if the module does not have a protected interface.

2.4.7 Inverter Add/Remove Tool


Before Add

After Add

Figure 2.16: Inverting Inputs/Outputs
You can use the tool to add or remove inverters from a gate input or output. Select the tool and click on an input or output port of a gate. If there is currently no inverter on that port, one will be added. If there is already an inverter on that port, the inverter will be removed.

2.4.8 Wire Bit-Size and Type Tool



Figure 2.17: Wire Type Selector
You can use the tool to change the bit-size and type of one or more wires. Use the "Bits" and "Type" selectors next to the button to set the desired bit size and type. Any wires you click on with this cursor will be set to the size and type displayed. The set of possible wire types and their interpretation are described in the section on writing Text Verilog Modules. In most cases you should use the "wire" wire type. The wire size and type can also be set by double clicking on a wire to open the Wire Properties dialog box.

2.4.9 Zoom-In/Zoom-Out

You can use the and buttons to zoom in and zoom out. There are four zoom levels.

2.5 Gate Properties

Gate parameters can be edited by double clicking on a gate, or by selecting the gate and then invoking the "Properties..." command from the "Gate" menu.  The property dialog box is contains up to four pages which can be selected through the tabs at the top. The "General" page contains basic properties that are common to all gate types, the "Port" page allows editing of ports on the gate, the optional "Details" page contains additional properties that are special to particular gate types, and the optional "Delay" page allows editing of delay parameters of gates.

2.5.1 General Properties




Figure 2.18: General Properties
The properties which can be edited on the "General" page shown to the right are:

2.5.2 Port Properties




Figure 2.19: Port Properties
The "Ports" page contains a list of all the ports on the selected gate. To edit ports on a gate, double click on the port you wish to modify, or select a port and hit the "Edit..." button. You can edit the fields that are modifiable in-line in the dialog box. You can change the signal name or the bit width, but the port name and type may not be editable for all element types. You can also use the "Add.." and "Delete" buttons to add new ports or delete ports for some element types.

The fields in the port list are:

2.5.3 Detail Properties




Figure 2.20: Detail Properties
The "Details" page of the Gate Parameters dialog box is used to set properties that are specific to a gate type. Not all gates have a "Details" page. The example shown here is for a MUX. There are two possible choices for the ordering of the inputs: left-to-right with the lowest numbered input on the left, and right-to-left with the lowest numbered input on the right. There are also two possible choices for which side the MUX selector is placed on.

Other types of gates which take detailed parameters include modules (which require a function name), memories (for which a memory initialization file can be specified), switches (for which an initial value can be specified) and clocks (for which the clock parameters can be specified).

2.5.3 Parameter Properties




Figure 2.21: Parameter Properties
Modules defined as Verilog text can have "parameters" in addition to ports. Parameters are constant values that are used by the module. To change the value of a parameter, double click in the "Value" column and enter the new value for that parameter. See the section on writing text Verilog modules for more information on how to create modules with parameters.

2.5.4 Delay Properties




Figure 2.22: Delay Properties
Delay for gates can be set either by specifying a standard technology or by customizing the delay for each individual gate. To use a standard technology, select "Standard Delay" and select a technology from the selector button. You can choose one of the built-in technologies "default" or "unit" that comes with TkGate, or add additional technology definitions (see Gate Delay Files for details). The "default" technology is roughly based on CMOS delays, and the "unit" technology simply sets single epoch delays for everything. Note that the delay values shown here can be affected by the bit-width of the ports, the number of ports and the existence/absence of inverters on the ports of gates. In some cases, adding an inverter to a port on a gate can decrease the delay value (e.g., adding an inverter to change an AND into a NAND).

To set the delay values for an individual gate, select "Custom Delay" and enter values for each of the delay parameters in epochs. Delays values must be integers greater than or equal to one, and less than 4096.

2.6 Wire Properties




Figure 2.23: Wire Properties
Wire properties can be edited by either double clicking on a wire, by double clicking on a net name from the "Nets" list, or by selecting a net from the "Nets" list and then invoking the "Properties..." command from the "Gate" menu.

The net properties are:


2.7 Wire Name and Bit-Size Labels

One way to display the label of a wire is to uncheck the "Hide" option in the
Wire Properties dialog box. Another easier way is to right click on the wire and select "Show Label". This will show the label of the net at the point at which you clicked. A wire may be labeled on one or all segments. You can also click on a wire label and drag it to a new position on the net. When you let go of the label, it will be attached to the closest point on the closest segment. It will also be attached on the same side on which you drop it. You can remove a label from a segment by dragging it far from the wire and dropping it, or by explicitly right clicking and selecting "Hide Label". The "Hide Label" function will hide only the label on the selected segment.

Right clicking and selecting "Hide All Labels" will make all of the labels on a net invisible, but TkGate will remember where they were placed. The next time you select "Show Label", or uncheck "Hide" in the Wire Properties dialog box, all of the hidden labels will become visible again. If you instead select "Clear All Labels", the labels will be hidden, and the original locations forgotten.

You can turn display of the bit width of a multi-bit wire on and off by right clicking and selecting "Show Bit Width" or "Hide Bit Width". The display can be controlled for each segment, but the size is always displayed in the middle of the segment and can not be moved.

2.8 Searching for Gates and Wires




Figure 2.24: Search Dialog Box
You can search for wires and gates anywhere in your circuit by using the "Find..." option from the "Edit" menu, or by pressing on the toolbar. Enter text into the entry area to search for gates, nets or text in circuit comments. The search results will be updated as you type showing the list of objects that match your search criteria. You can choose to locate objects with names that contain, begin with, end with or exactly match the name you type. You can also choose to ignore case in the match, or limit the search to only gates, nets, comment text, or any combination thereof.



Figure 2.25: Cross-hairs
on Target Device
When you wish to go to an item in the search list, double click on it. TkGate will open the module containing that item, and place cross-hairs on it to show you which item you selected. The search dialog box will remain open until you press "Close" so that you can visit multiple objects until you find the one you are looking for. Press the "Find" button to go to the next object in the list without double clicking.

2.9 HTML in Comments




Figure 2.26: Comment Editor
TkGate supports a small number of HTML markup elements in comments. The markup commands can be used to change font face, font size, or add hyperlinks to other documents or locations in a circuit. Comments are created by clicking the mouse to set a mark, then selecting "Comment" from the "Gate" menu (or using the "C" shortcut).

When you first create a comment, the properties box for the comment will appear and allow you to enter the text of the comment. You can also double click on an existing comment to edit its text. Hold down the Shift key while clicking to avoid following any hyperlinks in the comment.

When you are done typing the comment, click on the "OK" button to close the comment properties box and update the text of the comment as it is displayed on the screen. Currently, unlike a true HTML document, newlines in comments are not wrapped and are displayed as typed.

The supported markup commands are:

Element Description

<value-of name="name"> Replace with value of special TkGate constant.
<a href="url"></a> Insert a hyperlink or link to a Verilog file.
<b></b> Make the enclosed text appear in a bold font.
<i></i> Make the enclosed text appear in an italic font.
<h1></h1> Make the enclosed text appear in Heading 1 font.
<h2></h2> Make the enclosed text appear in Heading 2 font.
<h3></h3> Make the enclosed text appear in Heading 3 font.
<tt></tt> Make the enclosed text appear in fixed-width typewriter (Courier) font.
<small></small> Make the enclosed text appear in a font one step smaller than the current font.
<big></big> Make the enclosed text appear in a font one step bigger than the current font.
<font></font> Set the font characteristics of the enclosed text.
&??; Replaced with a character entity.

2.9.1 Accessing Internal Constants (<value-of>)

This is a special TkGate-specific markup command. It is replaced with the value of the specified variable. The primary purpose of this command is for use on the TkGate start-up page. The possible variable names are "tkgate-version" for the current version of TkGate, "tkgate-homepage" for the current home page of TkGate, "tkgate-mailcontact" for the e-mail address of the TkGate contact person, "tkgate-copyright" for the copyright notice, and "tkgate-release-date" for the release date of this version of TkGate.

2.9.2 Hyperlinks (<a>)

This command inserts a hyperlink into a comment. Different actions are performed depending on the protocol of the link. For "http:" and "mail:" protocol links, TkGate will open the browser or e-mail client that has been registered in the users TkGate options. Use the HTML Tab in the Options dialog box to set your browser and email client.

If the protocol is "file:", then action depends on the extension on the file. If the extension is ".v", then TkGate will load the specified file as if it had been opened through the "File Open..." menu. If the extension is ".html", then TkGate will load the file in a browser, just as with "http:" protocol links. For all other extensions, TkGate will load the file in a text viewer.

Files specified with the "file:" protocol can use one of the following special codes to reference specific TkGate directories:

Code Directory

@T TkGate tutorial directory.
@E TkGate examples directory.
@H TkGate home directory.
@C Directory of currently open circuit.

If a link contains the '#' character, the text after it is treated as a label. A label can reference a gate, wire or module. When the first character is '/', the rest of the label is assumed to reference a module. TkGate will open that module when the link is followed. When the first character is not '/', then the label is assumed to reference a gate or wire instance. A full path specifier must be used. That is a wire "w4" in a module with instance name "m1" (not function name) would be specified with the link "#m1.w4". When instance links are opened, TkGate will go to the containing module and place "cross-hairs" on the item referenced by the label. In the special case where the entire link starts with a '#', these actions are performed in the current circuit.

One additional, TkGate-specific protocol is "action:". Links with the "action:" protocol can be used to invoke TkGate commands. For example "action:newFile" will cause TkGate to open a new file just as if the user had selected "File New..." from the menu. The following actions are supported with the "action:" protocol:

Action Description

newFile Create a new file.
loadFile Load an existing file.
loadLibrary Open the library manager.
saveFile Save the current file.
saveAsFile Save the current file to a new name.
editOptions Open the options dialog box.
startSimulator Start the simulator./td>
blockNew Create a new module.
blockDelete Delete a module.
blockCopy Copy a module.
blockSetRoot Set the root module.
blockRename Rename a module.n
blockClaim Claim a module.
zoomIn Zoom in.
zoomOut Zoom out.

2.9.3 Fonts

Example:

The comment text:

   <font color=toupe family=times size=4>This is a test</font>

Will be rendered as:

  This is a test

There are several command for changing the font size, face and color. The most general of these is the <font> command. TkGate supports the "size", "color" and "face" attributes of this command. The "size" is an integer between 1 and 7 where 1 results in the smallest supported font, and 7 the largest. The "color" attributes specifies the color that should be used for the text. Any color from the X11 color name database may be used. The "face" specifies the font family to use. The supported font families are "courier", "helvetica", "times" and "symbol".

2.9.4 Character entities

Character entities are sequences of characters that are replaced with special symbols. Most of the common character entities available in HTML are supported. For example "&lt;" is replaced with "<", and "&ge;" is replaced with "≥". Greek characters can also be generated. For example, "&alpha;" is replaced with "α".

2.10 Printing a Schematic




Figure 2.27: Print Output Page
To print a circuit, choose the "Print..." option from the "File" menu, or press the button on the toolbar. You can choose to print directly to a printer using a specified print command, or you can print to a file. When printing a single-page document to a file, you can also choose to save the circuit in encapsulated postscript. This will suppress printing of the page frame and title.

The print dialog box is divided into two pages. The first page selects the "Output" of the command and has the following options:





Figure 2.28: Print Content Page
The second page of the print dialog box selects the "Content" to be printed and has the following options:


2.11 Circuit Properties




Figure 2.29: General Circuit Properties
Circuits have several global properties which can be set by selecting "Circuit Properties..." from the "Module" menu.  This will cause the dialog box shown at the right to appear.

The circuit properties are organized into two pages. A "General" page listing several miscellaneous properties, and a "Simulation" page listing properties that affect simulation of the circuit. The properties on the "General" page are:





Figure 2.30: Simulation Circuit Properties
The "Simulation" page of the circuit properties includes the following options:


3. Working with Modules

While in theory you could create your entire design as a single flat collection of primitive gates, when a design gets too large it becomes too difficult to manage this way. In order to manage the complexity of a large design, we must use hierarchical design practices. As with most other tools, TkGate uses modules to manage this complexity. Modules serve a similar purpose as functions do in a C program. They allow the design to be broken into manageable chunks as well as allow for reuse.

3.1 Module Implementations versus Module Instances

TkGate distinguishes between "Modules Implementations" and "Module Instances." A module implementation is analogous to the definition of a C function. It specifies what the module does, and what its interface is. This is divided into an internal definition which is created in an editor window in the same way as any other circuit, and an interface which specified the ports and properties of those ports (analogous to the function prototype or parameter list in a C function).

A module instance is analogous to an invocation of a function in a C program. It is generally displayed as a box with input and output wire attached to it. Each instance of a module operates independent of the other instances. When realized as a circuit, each instance would take up chip area, and be comprised of its own physical wires and components. When simulating, TkGate maintains separate state for the values of wires in each instance and the values of any registers or memories.

3.2 Creating Module Instances


1) Select a module from the module list.


2) Drag to the insertion location.


3) Release mouse button to create.

Figure 3.1: Creating a Module Instance
The easiest way to create a module instance is to drag the module name from the module list. To do this, first 1) select a module from the module list. In this example, we have selected the "REGS" module. Next, 2) drag the module name onto the canvas. The module name will get dragged onto the canvas. Finally, 3) release the mouse button to create an instance of the module. After you have created the module instance, you can select it and drag it with the mouse to position it exactly where you want it.




Figure 3.2: Module Function Selection
Another way to create a module instance is to right click and select "Make → Module → Module Instance" from the pop-up menu, or use the "B" keyboard shortcut. When you do this, the "Details" page of the module properties box will appear to allow you to select which module you wish to create an instance of. Select a name from the list box labeled "Function", or press "New..." to create an instance of a new type of module that you intend to define later. After you have selected the type of module, press "OK" to create the module instance. If you press "Cancel", the operation will be aborted and no module will be created.

When you create a module instance, that instance will have a unique instance name, distinct from the name of the module type or function. The instance name allows you to distinguish between different instances of a module. For example, if you create two REGS modules, one might have an instance name of "g1" and one might have an instance name of "g2". Normally, TkGate automatically assigns the instance name, but you can change it on the "General" tab of the Gate Properties Box dialog box. Note that this dialog box displays and modifies properties of the module instance, not the underlying module implementation.

3.3 Module Hierarchy

Your design in TkGate is comprised of a hierarchy of module instances. There is one module that is designated the top-level module. By default, this module is called "main", but you can rename it to any module you wish. You can also designate any module in your design to be the top-level module. Module instances that are directly included in the top-level module form the next level in the hierarchy, and modules instances included in those 2nd layer modules form the next layer and so on.

3.3.1 Module Hierarchy View



Figure 3.3: Module
Hierarchy View

TkGate's module hierarchy view displays the module hierarchy of your design. Be sure you have selected to see the hierarchical view. When you are in edit mode, the hierarchy is shown in terms of module implementations. That is, at each node in the tree, the children are the names of the modules which are used as instances at least once in the module.

Consider the example shown to the right. The top-level module "main" is indicated by the symbol next to it. In this example, "main" uses one or more module instances of the "EUNIT", "IUNIT" and "MEMORY" modules. The "REG16" module uses one or more instances of the "REG4" and "ZREG4" modules, but the symbol next to "REG4" tells us that there is no definition for that module. The module "ZREG4" shown in red with an underline indicates that that module is the current module in the editor. The symbol next to "EUNIT" underneath "ZREG4" indicates a conflict. ZREG4 includes an instance of "EUNIT", but "ZREG4" itself is contained in an "EUNIT" (it is in "REG16" which is in "EUNIT"). Circuits containing conflicts can not be simulated, all conflicts must be resolved before you can simulate.

Each module in the module list or hierarchy view is preceded by a symbol showing its type. The possible types are:

Symbol Description
The top-level module.
A normal net-list module.
A text HDL module.
Collection of module libraries.
A module library
A module defined in a library.
Collection of modules defined but not used in the circuit.
Reference to an undefined module in hierarchical list.
Conflict/recursive definition in hierarchical list.

HDL and net-list modules may also contain a "lock" on them (for example ). The lock indicates that editing of that module has been disabled in the module options dialog box.

3.3.2 Navigating to Modules

Double click on a module in the module hierarchy or module list view to open that module in the editor. You can also right click on a module instance in the main editing window and select " Open". When you use " Open" to open modules, TkGate will remember the modules you have opened on a stack. The modules on the stack will be displayed in red, with an underline under the module currently displayed in the editor. To close a module, and return to the parent module on the stack, right click in a blank area on the canvas and select " Close".

3.4 Creating and Manipulating Modules

3.4.1 Creating Modules




Figure 3.4: Module Creation
To create a new module, press the button on the toolbar. This will open the dialog box shown in Figure 3.4. After you have opened the dialog box, choose a name for your module and enter it into the "Name" field. Next, choose a module type. A "netlist" module is one that is defined graphically by connecting gates and other modules, and "HDL" module is one that is defined by typing a text description of the module. You can not change the type once the module has been created. In order to change the type, you must delete the module and create a new one of the new type.

There are also several property flags that you can set for your module:


3.4.2 Editing Module Properties




Figure 3.5: Module Properties
You can edit the property flags of a module by right clicking on the module name in the module list and selecting " Properties...". This will bring up a dialog box like the one shown in Figure 3.5. The dialog box will show the name of the module, the name of the file from which that module was loaded, and the type of the module (netlist or HDL). These properties can not be modified. The modifiable attributes of modules are the same as those previously described in Section 3.4.1 Creating Modules.

3.4.3 Deleting, Copying and Renaming Modules




Figure 3.6: Module Deletion
To delete a module implementation, right click on a module in the module list and select " Delete". This will bring up the dialog box shown in Figure 3.6. Press "OK" to delete the module. You can also change the module to be deleted by selecting its name from the drop down list. Any instances of the deleted module used in your design, will not be deleted. If you attempt to simulate, you will get an undefined module error.



Figure 3.7: Copying Modules

To copy a module implementation, right click on the module to be copied from the module list and select " Copy". This will bring up a dialog box like the one shown in Figure 3.7. Make sure the "From" field shows the name of the module you wish to copy. Enter the name of the new module in the "To" field. This will copy both the module definition and the module interface. The new module will appear in the list, or under the "Unused" tree in the hierarchy view.




Figure 3.8: Renaming Modules
To rename a module implementation, right click on the module to be copied from the module list and select " Rename". This will bring up a dialog box like the one shown in Figure 3.8. Make sure the "From" field shows the name of the module you wish to copy. Enter the new name for the module in the "To" field.

3.4.4 Claiming Modules




Figure 3.9: Claiming Modules
Sometimes you may wish to modify a module that is part of a library. But since modules that are part of a library can not be modified, you must "claim" that module before you can make any changes. Claiming the module make that module part of your design, rather than part of the library. This allows you to edit and make changes to the module. When you save your design, your version of the module will be saved.

Since modules that are defined in your design take precedence over library modules, the next time you load your design, the claimed module will continue to be used in lieu of the library version of that module. If you later decide that you would like to revert to the library version, you can delete the module, and reload the library.

To claim a module, right click on the module to be copied from the module list and select " Claim". This will bring up a dialog box like the one shown in Figure 3.9. Make sure the "Name" field shows the name of the module you wish to claim. The symbol next to the module will change to or to indicate that that module is now part of your design.

Only modules that are part of a library can be claimed. If you attempt to open the claim dialog box when there are no library modules in your circuit, you will get an error message.

3.4.5 Setting the Top-Level Module




Figure 3.10: Setting Top-
Level Module
The top-level module is the highest-level module in your hierarchy. It is the module that appears at the top of the hierarchy view and is indicated by the symbol. When simulating your circuit, only modules that are reachable from the top-level module are simulated.

To set the top-level module, right click on the module and select " Set As Root". This will bring up a dialog box like the one shown in Figure 3.10. Make sure the "Name" field shows the name of the correct module. The module hierarchy view will be recomputed with the new module as the top-level module.

3.5 Editing Module Definitions



a) Input Port b) Output Port


c) Inout Port
Figure 3.11: Module Ports
To edit the definition of a netlist module, open the module as described in section 3.3.2 Navigating to Modules. Within the module you can use any of the gates you would use at a higher level including more module instances. Signals as passed between the inside and outside through special circuit elements called ports. There are three types of ports: input, output and inout. These are shown in Figure 3.11a, b and c, respectively. Internal module ports are created the same way as other gates. Right click at the position you wish to create it and select "Module Input", "Module Output" and "Module InOut" from "Module" under the "Make" menu. When you are creating a port, the net properties dialog box will appear to allow you to set the name of the port. Enter a name, and click OK to create it. The wire connected to the port will have the same net name as the port.

The ports you define as part of the internal definition of a module must match the ports that you define on the interface, both in the names used, the type (input, output or inout), and the bit size. If you have already created the interface, then you can click on the "Ports" tab on the lower left-hand side of the main window to see what ports have been created on the interface.

3.6 Editing Module Interfaces




Figure 3.12: Module Interface List
The interface of a module, is its external appearance when it is included by a higher-level module. It includes the number and names of the ports, as well as their position. Each module has a canonical interface which is stored as part of the module definition when you save your design. Individual instances of a module have an interface as well which may or may not match the canonical interface. If you make changes to the interface of a module after you have created one or more instances of it in your circuit, you may need to go back and fix those instances.

While it is possible to edit the interface of a module directly in edit mode then choose "Interface → Set" to make that instance the canonical instance, this is not the recommended way of creating interfaces. The preferred way is to use the interface editor. To use the interface editor, select the "Interface" tab in the main edit window.

If you have the top-level module selected, the interface editor will display a graphical representation for the interfaces of all modules loaded in your design as shown in Figure 3.12. You can edit the interfaces directly on this page, or you can double click on a module, either its graphical form or its name in the module list, to open the single-module interface editor.

There are two basic types of module interfaces: standard interfaces, and symbol interfaces. A standard interface is a rectangular box with ports on the edges of the box. A symbol interface is an arbitrary user-defined symbol created through a bitmap editor. Most of the time, you will likely use standard interfaces, but if you wish to create your own gates, you can create your own symbols for them.

3.6.1 Standard Module Interfaces




Figure 3.13: Module Interface Editor
Figure 3.13 shows the single-module interface editor for a module with a standard interface. At the top of the "Interface Properties" box, is a selector that lets you choose between the "Standard" and "Symbol" interface types. While the module is open, you can switch back and forth between the two types without losing any work you have done on either interface. Below the interface type selector, are the module properties. These are the same properties that can be specified through the module creation and module properties dialog boxes.

Editing Through The Port List
Below the "Interface Properties" box, is the "Port List". The Port List lists all of the ports on the module. For each port, the list includes the name of the port, the type (input, output, or inout), the bit width, and the side of the module box on which the port it attached. To change a value, double click on it. For the port name, you can enter a new port name. Names that begin with a '_' will be displayed with an over-bar to indicate that they are active low signals. For the other fields, a drop down selector will appear allowing you to change the value. Click anywhere outside the open value to close it and make the change effective. Your changes will be reflected in the graphical view of interface. You can also hit the tab key to advance and enter a value for the next field.

To add a new port, you can either double click on the next empty line in the port list, or press the "Add" button. TkGate will attempt to guess an appropriate name for the new port, but you can type in your own name if you prefer. To delete a port, select it and press the "Delete" button.

You can reorder the ports by selecting and dragging them up or down in the port list. Ports that are on the same "Side" will be arranged in the same in both the port list and in the graphical representation of the interface.

Graphical Editing



Figure 3.14: Port Parameters
You can also edit the interface graphically. To change the size of the box, grab a corner or side with the mouse and drag in it or out. To edit the properties of a port, double click on its name in the graphical representation of the interface. The Port Parameters dialog box shown in Figure 3.14 will appear allowing you to change the name, type and bit size of the port. The new values will be updated in the port list after you click "OK".

To add a new port, right click on the edge of the rectangle at the point you wish to add the port and select "Add Input..", "Add Output.." or "Add InOut..". The Port Parameters dialog box will appear in this instance too allowing you to enter the port name and bit size. To delete a port, use the tool and "cut" the wire off the edge of the interface.

To reposition ports, grab them and slide them back and forth on the edge to which they are attached. However, to change the edge to which they are attached, you will need to change the "Side" in the port list.

3.6.2 Automatic Interface Generation




Figure 3.15: Interface Generator
Rather than manual edit the interface of a module, you can automatically generate an interface after you have completed the internal definition of a module. To do this, open the module in the interface editor and press the "Auto Generate..." button. This will bring up the Interface Generator dialog box shown in Figure 3.15. Select the module for which you wish to create an interface, then choose "Port Selection" and "Port Positions" options.

The port selection option tells the interface generator which ports should be included on the generated interface. The possible options are:

The port position option tells the interface generator how the ports on the new interface should be positioned. The possible options are:


3.6.3 Symbolic Module Interfaces




Figure 3.16: Symbol Interface Editor
You can use the built-in bitmap editor to design your own symbols for modules that you create. Open the module in the interface editor and select "Symbol" for the interface type. The symbol editor includes a bit map editor on the top, and a view of your device on the bottom. You can edit the ports in the port list in the same way as for standard modules. Drag the name of any newly created ports onto the bit-map editor to allow you to position it.

You must create an "Unselected" and a "Selected" version of your symbol. The "Unselected" version is the appearance when the symbol is not selected, and the "Selected" version is the appearance when you have selected it in the editor. Normally you can just draw the "Unselected" version, copy and paste it into the "Unselected" version, then press the button on the toolbar to automatically boldify your symbol. However, you may want to use the editor to do some final touch-ups.

There are six symbol editor tools that you can use in designing your symbol:

Tool Description

Bit Tool - Click the left mouse button to set a bit, and the right mouse button to clear a bit. You can also press and hold the mouse button to set/clear multiple bits and you move the mouse.
Line Tool - Click and hold the left mouse button to set one end of the line, and drag the mouse to the other end and release to draw a line. The same action with the right mouse button will clear a line.
Rectangle Tool - Click and hold the left mouse button to set one corner of the rectangle, and drag the mouse to the other corner and release to draw a rectangle. The same action with the right mouse button will clear the rectangle.
Filled Rectangle Tool - Click and hold the left mouse button to set one corner of the filled rectangle, and drag the mouse to the other corner and release to draw a filled rectangle. The same action with the right mouse button will clear the filled rectangle.
Port Tool - Use this tool to select and manipulate ports. Click and drag the port to move it. Use the and buttons to rotate the port clockwise or counter-clockwise.
Area Select Tool - This tool allows you to select a rectangular area, then use the cut, copy and paste tools. There are two types of paste operations. A normal paste overwrites all bits with the contents of the cut buffer. The overlay paste , pastes only the bits that are set in the cut buffer, combining the cut buffer with the existing image. The selection also limits the scope of the bit shifting and rotating operations to the selection.

You can also use the and buttons to rotate the image clockwise or counter-clockwise, or the shift buttons such as to shift the bits left, right, up or down. You can use the area selection tool to limit the scope of these operations to the selected area.

4. HDL Modules




Figure 4.1: HDL Editor Window
Verilog is a widely-used textual language for specifying high-level designs for simulation, synthesis and verification. It is an example of a Hardware Description Language (HDL). It is quite versatile allowing specification from CMOS transistor level, up to high-level algorithmic specifications. TkGate supports a subset of the complete Verilog specification. This chapter assumes that the reader is already familiar with Verilog, and is not intended to be a complete description of Verilog and its features. The reader is directed to one of the many books on Verilog for this purpose. Instead, this chapter is intended to document the features of Verilog that are supported in TkGate's implementation.

The TkGate simulator, called Verga (VERilog simulator for GAte), is a discrete time simulator with time advancing in discrete units called "epochs". All delay must be an integer number of epochs. The Verga simulator is normally used through the TkGate interface, but it can also be run by itself directly on text Verilog files.

4.1 HDL Editor Window

When you open a module that you have designated as an HDL module, the HDL editor window will appear as shown in Figure 4.1. This window is essentially a text editor, and you can edit the Verilog text directly in this window. The editing commands that are available depend on which key-binding style you have selected through the Interface Options dialog box. Additional editor options controlling indentation and colorization can be set through the HDL Options dialog box.

4.1.1 Net List

The nets in your module are shown in the "Nets" list in the lower left corner. The nets list is updated when you open the module, but may not display correctly if there are too many syntax errors in your module.

4.1.2 Cut/Paste



Figure 4.2: Full Adder
You can use the cut and paste tools to cut and paste blocks of text that you select with the mouse. You can also drag the selected block of text with the mouse to a new location if you have that option enabled in the HDL Options dialog box.

It is also possible to paste chunks of gates that you have cut or copied from a graphical module. When you paste such modules into a text HDL module, TkGate will convert that chunk into Verilog netlist format. For example, if you cut or copied the full adder shown in Figure 4.2, pasting it into an HDL module would result in the following Verilog code being generated:

  _GGOR2 #(6) g34 (.I0(w3), .I1(w4), .Z(co));   //: @(216,139) /sn:0 /w:[ 0 0 1 ] /eb:0
  _GGAND2 #(6) g28 (.I0(a), .I1(b), .Z(w4));   //: @(150,141) /sn:0 /w:[ 5 5 1 ] /eb:0
  //: joint g32 (w8) @(172, 65) /w:[ 1 -1 2 4 ]
  //: SWITCH g27 (ci) @(56,26) /sn:0 /w:[ 0 ] /st:0
  _GGAND2 #(6) g31 (.I0(w8), .I1(ci), .Z(w3));   //: @(175,110) /sn:0 /R:3 /w:[ 5 5 1 ] /eb:0
  //: LED g15 (s) @(246,87) /sn:0 /R:2 /w:[ 0 ] /type:0
  //: joint g29 (a) @(117, 62) /w:[ 2 -1 1 4 ]
  //: SWITCH g25 (a) @(56,62) /sn:0 /w:[ 0 ] /st:0
  //: LED g14 (co) @(245,122) /sn:0 /w:[ 0 ] /type:0
  _GGXOR2 #(8) g24 (.I0(w8), .I1(ci), .Z(s));   //: @(213,68) /sn:0 /w:[ 0 3 1 ] /eb:0
  _GGXOR2 #(8) g23 (.I0(a), .I1(b), .Z(w8));   //: @(138,65) /sn:0 /w:[ 3 3 3 ] /eb:0
  //: SWITCH g26 (b) @(56,97) /sn:0 /w:[ 0 ] /st:1
  //: joint g33 (ci) @(177, 70) /w:[ 2 1 -1 4 ]
  //: joint g30 (b) @(111, 97) /w:[ -1 2 1 4 ]
The generated code includes comments (positions starting with "//") that are included as part of TkGate save files indicating the position information for that circuit element. For circuit elements that do not have direct Verilog counter-parts, such as the LEDs and switches, pure comments are generated. In this example, the only actual Verilog code generated is for the five gates. These are represented in terms of TkGate cells with names beginning in "_GG".

4.1.3 Module Navigation

Just as you can open a module in the graphical editor by right clicking on a module and selecting "Open", you can do the same thing with modules in the Verilog text. Right click on the text with the name of the module and select "Open". Right click and select "Close" to close the current module and reopen the next one on the stack. In TkGate, each Verilog module should be opened individually. If you define more than one module, or you change the name in the text description so that it no longer matches the name of the module you have open, TkGate will complain and give you options on how to resolve the problem.

4.2 Verilog Basics

This section gives a very brief overview of Verilog.

4.2.1 Comments

Comments in Verilog follow the same rules as in C. Block comments are enclosed between "/*" and "*/". Line comments begin with "//" and run to the end of the line.

4.2.2 Literals

Verilog literals begin with a letter or a "_" character. The subsequent characters can be letters, digits or "_" characters. TkGate supports escaped literals, but their use is discouraged. Escaped literals begin with a backslash "\" and continue to the next white-space character. "\3+4jk", "\u8[]*9" and "\3342" are examples of escaped literal.

4.2.3 Numbers and Values

Sized Numbers
Verilog numbers can be sized or unsized. Sized numbers have the general form:

    <size>'<base><value>

where <size> is the number of bits, <base> is a single letter indicating the number base, and <value> is the actual value of the number. The possible bases are 'd for decimal, 'h for hexadecimal, 'o for octal and 'b for binary. Examples of sized numbers include:
   8'd42        // The 8-bit decimal number 42
   16'h4fe3     // The 16-bit hexadecimal number 4fe3
   8'b10010011  // The 8-bit binary number 10010011
Unsized Numbers
Unsized numbers can be those that still include the base, or plain numbers with an implied decimal base such as:
   83           // The decimal number 83
   'd42         // The decimal number 42
   'o53         // The octal number 53
   'b11         // The binary number 11
The actual size used to represent unsized numbers is machine dependent, but is guaranteed to be at least 32 bit.
Special digit values
Binary, octal and hexadecimal number may also use a "z" digit to indicate the floating or high impedance state in one or more digits. Similarly an "x" or ? digit can be used to indicate an unknown value. Numbers including a base, may also contain one or more "_" characters to help make the number more readable. The "_" characters are ignore in interpreting the value. Some examples are:
   12'b0101_0111_0010
   16'h7zz3
   1'bx
   8'bx
When the highest digit of a number is x or z, that value is extended to the highest bit in the number. For example 8'bx is equivalent to 8'bxxxxxxxx not 8'b0000000x. If you really mean the later, you should use 8'b0x.
Floating Point Numbers
Verilog specifications can also use floating point numbers. Examples of floating point numbers are "42.0", "0.45" and "3.14". The bit size of floating point numbers is machine dependent, but is at least 32 bits.

Strings
Verilog strings are delimited by the double quote character. The backspace character can be used to quote any double quote characters used in the string. Some examples of string values are:
   "Hello world."
   "Please push \"Enter\" to begin."
   "Exterminate! Exterminate! Exterminate!"
String values are essentially bit vectors with a size equal to eight times the number of characters.

4.3 Data Types

Variables in Verilog can be used to represent registers, or nets connecting components. Each bit in a variables can take on one of six states:

Value Description

0 Logic 0 or false
1 Logic 1 or true
x Unknown state (could be 0, 1 or z)
z Floating state
H High unknown state (could be 1 or z)
L Low unknown state (could be 0 or z)

4.3.1 Net Types

Net data types are those that must be driven to have a value. The difference between the types is primarily in how collisions are handled. Collisions occur when two or more gates attempt to drive the wire to different values. The supported net types are:

Type Description

wire Basic net used to connect components. Collisions result in unknown value.
wand Wired AND net. Collisions result in the AND of the values driven on the net.
wor Wired OR net. Collisions result in the OR of the values driven on the net.
tri Equivalent to "wire", but indicates to reader that tri state values will be used.
tri1 Net with resistive pull up. Takes on 1 value if nothing is driving it.
tri0 Net with resistive pull down. Takes on 0 value if nothing is driving it.
triand Same as wand. Collisions result in the AND of the values driven on the net.
trior Same as wor. Collisions result in the OR of the values driven on the net.
trireg Net with capacitance store. Retains last value written if all drivers are floating.

Nets are declared by specifying the data type followed by a comma separated list of variables, and terminated with a semicolon. Some example net declarations are:
  wire w1;
  wire a,b,c;
  wor p;
  trireg x;

4.3.2 Supply Types

Supply types are used to model ground and supply signals fixed to a 0 or 1 value. The supply types are:

Type Description

supply0 Net fixed at logic 0.
supply1 Net fixed at logic 1.

Examples of supply types are:
   supply1 vdd;
   supply0 gnd;

4.3.3 Register Types

Register types retain their value until they are assigned again. The behave similarly to variables in a C program.

Type Description

reg One bit register variable.
integer General purpose integer variable.
real General purpose floating point variable.
time 64-bit simulation time variable.

Examples of register types are:
   reg r1, r2;
   integer i, j;
   time t;
   real f;

4.3.4 Port Types

Port types are used to declare the type of a port. Variables using one of these types must also appear in the port list of a module.

Type Description

input Module input port.
output Module output port.
inout Module inout (bidirectional) port.

The port types normally act like "wire" nets, but the output variables may be also declared as reg in addition to output. This can either be in two separate declarations such as:
   output z;
   reg z;
or in a combined declaration:
   output reg z;

4.3.5 Event Types

Event variables do not take on a value per se. Instead, they can be used in signaling from one portion of the design to another. They are declared with the event keyword. For example:
  event e;

4.3.6 Parameter Types

You may also use the parameter type to declare constant variables. Constant variables must be assigned from constant value, or an expression involving only constants and other parameter variables. Examples of parameter declarations are:
   parameter delay1 = 9;
   parameter delay2 = 2*delay1 + 7;
   parameter myvalue = 8'h4e;
   parameter mystring = "impudent moose";
Parameters can be used both as values in expressions, and as the value in a delay.

4.3.7 Bit Size Declaration

A bit size declaration can be used with any of the net, supply or port data types. In addition they can be used with the reg data type. The bit size is declared right after the keyword and before the variable. The bit size specifier has the form "[msb:lsb]" where msb is the most significant bit and msb is the least significant bit. Currently TkGate Verilog only supports bit ranges with a 0 least significant bit, and a non-negative most significant bit. Examples include:
   wire [7:0] w1, w2;   // 8-bit wires
   reg [11:0] r1, r2;   // 12-bit registers

4.3.8 Memories

Memories are declared by specifying an address range after the variable name in a reg declaration. For example:
reg [7:0] m[0:1023];
Declares a memory with 1024 eight-bit values. TkGate Verilog only supports memories that start at address 0.

4.3.9 String Variables

You can store strings in reg variables, but you must allocate enough bits to store the string. For example:
   reg [8*11-1:0] s = "hello world";

4.4 Expressions

Verilog uses an infix notation for expressions similar to expressions in C. A wide range of arithmetic, logical, bit-wise and comparison operators are supported. Operator precedence is similar to C, and parenthesis may be used to group expressions. Some example expressions are:
   x*u*(3 + j) + 1
   z + 8'h5
   (q*8'h2) < 8'h5
When the bit sizes of operands in an expression do not match, the bit size of the entire expression is expanded to the bit size of the largest value. For example, when evaluating the expression 16'h3423 + 8'hff, both values are first extended to 16 bits before performing the sum.

4.4.1 Operators

The operators supported in TkGate's implementation of Verilog are shown in the table below. Operators are grouped by precedence from highest to lowest.

Operator Description

{a, b ,...} Concatenation - Concatenates the bits of two or more nets (or expressions) into a single expression. If all of the components are nets, the concatenation may be used as the target of an assignment.
{n{a}} Bit Replications - Concatenates n copies of a together. n must be a constant.

! a Logic NOT - Returns zero if a is zero, one if a is non-zero.
~ a Bit-wise compliment - Reverses all bits in a.
- a Negation - Performs an arithmetic negation of a.
& a Reduction AND - ANDs together all the bits of a and returns the 1-bit result.
| a Reduction OR - ORs together all the bits of a and returns the 1-bit result.
^ a Reduction XOR - XORs together all the bits of a and returns the 1-bit result.
~& a Reduction NAND - NANDs together all the bits of a and returns the 1-bit result.
~| a Reduction NOR - NORs together all the bits of a and returns the 1-bit result.
~^ a Reduction XNOR - XNORs together all the bits of a and returns the 1-bit result.

a * b Multiplication - Returns the product of a and b.
a / b Division - Returns the quotient of a and b.
a % b Remainder/Modulo - Returns the remainder of a/b.

a + b Addition - Returns the sum of a and b.
a - b Subtraction - Returns the difference of a and b.

a >> b Right Shift - Shifts the bits in a to the right by b places.
a << b Left Shift - Shifts the bits in a to the left by b places.
a >>> b Arithmetic Right Shift - Shifts the bits in a to the right by b places arithmetically.
a <<< b Arithmetic Left Shift - Shifts the bits in a to the left by b places arithmetically.

a > b Greater Than - Returns 1 if a is greater than b, otherwise returns 0.
a < b Less Than - Returns 1 if a is less than b, otherwise returns 0.
a >= b Greater Than or Equal - Returns 1 if a is greater than or equal to b, otherwise returns 0.
a <= b Less Than r Equal - Returns 1 if a is less than or equal to b, otherwise returns 0.

a == b Equality - Returns 1 if a and b are equal and 0 if they are not equal. Returns unknown (x) if any bits in a or b are unknown or floating.
a != b Inequality - Returns 0 if a and b are equal and 1 if they are not equal. Returns unknown (x) if any bits in a or b are unknown or floating.
a === b Case Equality - Returns 1 if a and b match exactly including unknown and floating bits. Returns 0 otherwise.
a !== b Case Inequality - Returns 0 if a and b match exactly including unknown and floating bits. Returns 1 otherwise.

a & b Bit-wise AND - Each bit of the result is the AND of the corresponding bits of a and b.
a ~& b Bit-wise NAND - Each bit of the result is the NAND of the corresponding bits of a and b.

a ^ b Bit-wise XOR - Each bit of the result is the XOR of the corresponding bits of a and b.
a ~^ b Bit-wise XNOR - Each bit of the result is the XNOR of the corresponding bits of a and b.

a | b Bit-wise OR - Each bit of the result is the OR of the corresponding bits of a and b.
a ~| b Bit-wise NOR - Each bit of the result is the NOR of the corresponding bits of a and b.

a && b Logical AND - If both a and b have non-zero bits, then return 1, otherwise return 0. However, unknown will be returned if unknown bits in the operands prevent determining the actual result.

a || b Logical OR - If either a and b have non-zero bits, then return 1, otherwise return 0. However, unknown will be returned if unknown bits in the operands prevent determining the actual result.

a ? b : c Conditional Operator - If a is non-zero, then the result is b. If a is zero, then the result is c. If a is unknown or floating, then the result is the bit-wise XNOR of the bits in b and c.

4.4.2 Bit and Memory Addressing

Bits on sized multi-bit variables can be address using the syntax:

     name[bit]

For example, suppose we have the declaration:
   reg [7:0] r;
The expression r[4] represents the single-bit expression for the value of bit-4 in r. This syntax can be used either to use its value in an expression, or in an assignment statement. The bit address can be either a constant or an expression. For example, r[i+1] will address the bit corresponding to the current value of the expression i+1. A run-time error will result if you attempt to simulate a circuit where i+1 goes out of bounds.

You can also address ranges of bits using the syntax:

     name[high:low]

For example, r[6:2] is a 5-bit value formed from bits 2 to bit 6 of r. In this syntax, the high and low values specified must be constants, although they can be constant expressions that can be evaluated at compile time.

In order to get indexable ranges of bits, you can use the syntax:

     name[low+:num]

In this expression low is the bit number of the lowest bit, and num is the number of bits. num must be a constant expression, but low may be an expression evaluated at execution time. For example r[i +: 3] will address a three bit sub-range of r starting at the bit addressed by i.

Memories are addressed using the same syntax as bits. For example, if you define the following memory of 1024 8-bit words:

   reg [7:0] mem[0:1023];
then mem[45] will address word 45 of the memory and mem[45][6] will address bit 6 of the word 45 of the memory.

4.5 Compiler Directives

Verilog compiler directives are similar to the #define and #ifdef directives that are used in C programs. Verilog compiler directives begin with the ` (back-quote) character.

4.5.1 The `define Directive

The `define directive is analogous to the #define directive in C. It allows you to associate a value or piece of text with a symbolic macro. For example:
`define THEANSWER 42
To use a macro that you have defined, you must use a back quote in front of the name. For example, to use the value of the macro defined in the example above you might write:
  x = y + `THEANSWER;

4.5.3 The `ifdef, `ifndef, `else and `endif Directives

The `ifdef, `ifndef, `else and `endif directives can be used to conditionally compile Verilog code. They are analogous to the similarly named directives in C. The `ifdef directive causes code to be compiled only if the macro given after it is defined. The `ifndef directive causes code to be compiled only if the macro given after it is not defined. You can nest `ifdef and `ifndef directives and use the `else directive to provide alternatives. The `endif directive marks the end of the conditionally compiled portion.

An example of conditionally compiled code is shown here:

  `ifdef INCBY2
     x = x + 2;
  `else
     x = x + 1;
  `endif

In this example, if the macro INCBY2 has been defined by a `define then the x = x + 2; statement is compiled, otherwise the x = x + 1; statement is compiled. Note that when using macro names in an `ifdef you do not precede them with a ` (backquote).

4.5.2 The `timescale Directive

The `timescale directive is used to set the time scale of a module or modules for simulation. It must be followed by a units value and a precision value. These values must be a 1, 10 or 100 followed by time units "s" (seconds), "ms" (milliseconds), "us" (microseconds), "ns" (nanoseconds), "ps" (picoseconds) or "fs" (femotoseconds). For example:
  `timescale 1ns / 100ps
would set the time units to 1ns and the simulation precision to 100ps. This would cause any delay specifications in modules defined after the `timescale directive to be counted as 1ns. The simulator itself would simulate in steps of 100ps. It is important not to set the precision value lower than necessary (relative to the delay values of components used in your design) since this can impact simulator performance. If the `timescale directive is not uses, the default time scale and precision is 1ns.

4.6 Module Declarations

Module declarations begin with the module keyword, and end with the endmodule keyword. Consider the simple module:
 (1) module ANDOR(z, a, b, c);
 (2) output z;
 (3) input a,b,c;
 (4) wire x;
 (5)
 (6)   or o1(z,a,x);
 (7)   and a1(z,b,c);
 (8)
 (9) endmodule
The literal ANDOR after the module keyword is the name of the module. The module name is usually followed by a list of the port names in parenthesis. The port list must be followed by a ";". Inside the body of the module are declarations for any nets used in the module. The nets declared as ports for the module, should also have declarations to indicate if they are input, output or inout ports as shown on lines (2) and (3).

The port list may be omitted for a module as in this example:

module main;
reg a,b,c;
wire x,y,z;

  mycircuit m1(a,b,c,x,y,z);

endmodule
This is typically done for top-level modules.

4.7 Netlist Modules

Netlist modules are those that are defined as a collection of connected components. The components can be built-in Verilog primitives (such as "and" and "or"), library modules, or user-defined modules. Here is a simple example of a module for a 1-bit full adder circuit (the same basic circuit as shown in
Figure 4.2):
 (1) module ADD(s, co, a, b, ci);
 (2) output s, co;
 (3) input a,b,ci;
 (4) wire w1,w2,w3 ;
 (5)
 (6)   or (co, w1, w2);
 (7)   and (w2, a, b);
 (8)   and (w1, w3, ci);
 (9)   xor (s, w3, ci);
(10)   xor (w3, a, b);
(11)
(12) endmodule
The names "or", "and" and "xor" are built-in Verilog primitives for computing the OR, AND and XOR of one or more signals. The first parameter of each of these primitives is the output signal, and the remaining parameters are the inputs. In gate-level descriptions like this, it is easy to see the mapping between the description and the hardware.

Input ports may be driven by any type of variable, but the outputs of primitives must be a "Net" type variable such as wire, tri or wand.

It is also possible to give names to the instances of each of the gates. We do this by inserting an instance name after the name of the primitive. For example, we could replace the body of the example above with:

 (6)   or g1 (co, w1, w2);
 (7)   and g2 (w2, a, b);
 (8)   and g3 (w1, w3, ci);
 (9)   xor g4 (s, w3, ci);
(10)   xor g5 (w3, a, b);
In this new body, "g1", "g2", etc. are the instance names of the gates and can be used to refer to those gates when necessary. You can also specify more than one instance in a single statement. For example, an alternative way of specifying the above design is:
 (6)   or g1 (co, w1, w2);
 (7)   and g2 (w2, a, b), g3 (w1, w3, ci);
 (8)   xor g4 (s, w3, ci), g5 (w3, a, b);

In addition to primitives, netlist modules can also combine other modules. For example, we can connect four of the adders shown above to create a module of a 4-bit adder as shown below:

 (1) module ADD4(s, co, a, b, ci);
 (2) output [3:0] s;
 (3) output co;
 (4) input [3:0] a,b;
 (5) input ci;
 (6) wire c1,c2,c3;
 (7)
 (8)    ADD a1 (s[0], c1, a[0], b[0]);
 (9)    ADD a2 (s[1], c2, a[1], b[1]);
(10)    ADD a3 (s[2], c3, a[2], b[2]);
(11)    ADD a4 (s[3], co, a[3], b[3]);
(12)
(13) endmodule
In this example, we create four instances of our ADD module named a1 through a4. Ports connections are made in the order in which they appear in the port list of the module definition. Alternative, you can explicitly specify the port connections using the syntax:

    .port(net)

For example, you could replace line (8) with:
 (8)    ADD a1 (.a(a[0]), .b(b[0]), .s(s[0]), .co(c1));
Since the ports for each connection are explicitly specified, you can list the connections in any order. However, you must either specify no ports or all ports in this manner. You must also ensure that all ports have exactly one connection.

4.7.1 Verilog Primitives

This section will introduce the primitive gates that are supported in TkGate's implementation of Verilog. By default, all primitives are single bit and all inputs and outputs must be single bit. You can declare arrays of primitives by using a bit range after the instance name. For example:
  and a1[3:0] (x, a, b);
will perform a bit-wise AND on the four-bit signals a and b and drive the four-bit result to x. The following subsections describe the various types primitives.
Logic Primitives
The logic primitives include and, or, xor, nand, nor and xnor. The first parameter is always the output, and the remaining parameters are the inputs. You can specify anywhere from one to an arbitrary number of inputs. The primitives are defined in terms of their truth tables shown below.
and 0 1 x z
0 0 0 0 0
1 0 1 x x
x 0 x x x
z 0 x x x
or 0 1 x z
0 0 0 0 0
1 0 1 x x
x 0 x x x
z 0 x x x
xor 0 1 x z
0 0 0 0 0
1 0 1 x x
x 0 x x x
z 0 x x x
nand 0 1 x z
0 0 0 0 0
1 0 1 x x
x 0 x x x
z 0 x x x
nor 0 1 x z
0 0 0 0 0
1 0 1 x x
x 0 x x x
z 0 x x x
xnor 0 1 x z
0 0 0 0 0
1 0 1 x x
x 0 x x x
z 0 x x x
Buffer Primitives
The buffer primitives include buf and not. The last parameter of these primitives is the input, and all of the preceding parameters are outputs being driven with the same value. The buf primitive simply drives its input to the output, while the not primitive drives the compliment logic value. However, if the input is unknown or floating, the output will be unknown for both primitives.

Consider the example:

   buf b1 (w1, w2, w3, a);
   not n1 (w4, w5,  b);
This will drive the value of a to w1, w2 and w3. The compliment of b will be driven to w4 and w5.
Conditional Buffer Primitives
The conditional buffer primitives include bufif1, bufif0, notif1 and notif0. These primitives have exactly three ports as shown below:
   bufif0 b1 (out, in, ctl);
   bufif1 b2 (out, in, ctl);
   notif0 n1 (out, in, ctl);
   notif1 n2 (out, in, ctl);
The first parameter is the output of the primitive, the second parameter is the input, and the third parameter is the control. The bufif1 and notif1 gates act like buf and not, respectively, when the control signal is 1. They output floating when the control line is 0. Conversely, the bufif0 and notif0 gates act like buf and not, respectively, when the control signal is 0. They output floating when the control line is 1. The truth tables for these primitives are shown in the tables below.

ctl
in bufif0 0 1 x z
0 0 z L L
1 1 z H H
x x z x x
z x z x x

ctl
in bufif1 0 1 x z
0 z 0 L L
1 z 1 H H
x z x x x
z z x x x

ctl
in notif0 0 1 x z
0 1 z H H
1 0 z L L
x x z x x
z x z x x

ctl
in notif1 0 1 x z
0 z 1 H H
1 z 0 L L
x z x x x
z z x x x
MOS Transistor Primitives
Transistor primitives model simple nmos and pmos devices. However, TkGate does not do true transistor-level simulation in the sense of Spice and other such tools. Instead, they are simulated in the same way as the gate primitives, driving an output depending on the value of the input and control signals. Examples of these primitives are:
  nmos n (out, in, ctl);
  pmos p (out, in, ctl);
The truth tables for determining the value driven to out from the values on the input in and the control line (or gate) ctl are shown in the table below.

ctl
in nmos 0 1 x z
0 z 0 L L
1 z 1 H H
x z x x x
z z z z z

ctl
in pmos 0 1 x z
0 0 z L L
1 1 z H H
x z z x x
z z z z z
This level of modeling is generally good enough to create CMOS circuits out of nmos and pmos components. For example:
  nmos n1 (out, gnd, a);
  nmos n2 (out, gnd, b);
  pmos p1 (x, vdd, a);
  pmos p2 (out, x, b);
will implement a 2-input OR gate.

4.7.2 Specifying Delay Values

In the examples presented so far, all of the primitives had zero delay. In a real circuit, there is propagation delay over a gate. The delay is specified with the syntax #delay. For example, if we add delays to our full adder circuit we get:
 (1) module ADD(s, co, a, b, ci);
 (2) output s, co;
 (3) input a,b,ci;
 (4) wire w1,w2,w3 ;
 (5)
 (6)   or #5 g1 (co, w1, w2);
 (7)   and #5 g2 (w2, a, b);
 (8)   and #5 g3 (w1, w3, ci);
 (9)   xor #7 g4 (s, w3, ci);
(10)   xor #7 g5 (w3, a, b);
(11)
(12) endmodule
The or and and gates will have delays of 5 time units (or whatever was specified as the units in the `timescale directive).

4.7.3 assign Statements

Rather than defining complex expression in terms of primitives, you can use an assign statement to assign an expression to an output, with much the same behavior as if you defined it in terms of gates. The general syntax is:

    assign wire = expression;

The wire must be a net type variable (wire, tri, wand, etc.), but the expression may contain both net and register type variables. The value driven to wire changes whenever the value of the expression changes. You can can also specify a delay value for the assign statement using the syntax:

    assign #delay wire = expression;

An example of an assign statement with a delay is:
   assign #5 x = a & (b + c);

4.8 Behavioral Modules

In behavioral models, the design is specified algorithmically, more like a C program. Unlike most C programs, however, Verilog specifications tend to be highly parallel with many threads.

4.8.1 initial and always Statements

All behavioral Verilog is defined inside an initial or always statement. Both of these statements create a new parallel thread for each statement occurring in the design. The initial statement creates a thread that executes once and terminates, while the always statement creates a thread that repeats in an infinite loop. An example of a clock-generator module defined in behavioral Verilog is shown below:
 (1)  module myclock(x);
 (2)  output reg x;
 (3)
 (4)    initial
 (5)       x = 1'b0;
 (6)
 (7)    always
 (8)       #100 x = ~x;
 (9)
(10)  endmodule
When the simulation starts, both the initial statement and the always statement begin execution in parallel. The thread started by the initial statement sets the output register x to 0 as soon as the simulator starts. The always thread waits for 100 simulation time steps, then inverts the value if x. Since always statements repeat, control will go back to the top of the always statement, and after another 100 time units, x will be inverted again. The result is that x will be 0 for the first 100 time units, 1 for time units 100 to 199, 0 again for time units 200 to 299, and so on.

4.8.2 Blocking Assignments

Blocking assignments are assignments that use the = operator, and are the most similar to assignments in a C program. Each blocking assignment is executed and the new value assigned to the left-hand side before the next statement is executed. The left-hand side of blocking assignments must be a register type variable.

An example use of blocking assignments is in the module shown below:

 (1)  module foo(z,a,b,c)
 (2)  output reg [15:0] z;
 (3)  input [15:0] a,b,c;
 (4)  reg [15:0] r1,r2;
 (5)
 (6)    always
 (7)      begin
 (8)        r1 = a + b;
 (9)        r2 = r1 *(b + c);
(10)        #5 z = r2 / r1;
(11)      end
(12)
(13)  endmodule
The statements at Lines (8) and (9) are executed sequentially. The value of r1 used in Line (9) is the value computed at Line (8). The #5 at Line (10) cause execution of the thread to be suspended for 5 time units. After the 5 unit delay has elapsed, the expression r2 / r1 is evaluated and assigned to z. Once the statement at Line (10) has completed, execution of the thread goes back to the top and Lines (8) and (9) are executed again.

The delay on Line (10) causes evaluation of the left-hand side to wait until the statement after the delay period has elapsed. If some other thread were to change the values of r1 or r2 during the 5 time units Line (10) was delayed, the new values would be used instead. If you wish to ensure that the values at the beginning of the delay period are used, you can use an intra-statement delay such as:

        z = #5 r2 / r1;
This will cause r2 / r1 to be evaluated immediately, but the statement will delay 5 time units before assigning z.

4.8.3 Non-Blocking Assignments

Non-Blocking assignments use the <= operator and are executed in parallel. The right-hand side expressions are evaluated immediately, but the assignment is deferred until the end of the current time step. Like with blocking assignments, the left-hand side of non-blocking assignments must also be a register type variable.

An example of using non-blocking assignments to swap the values of two registers are:

 (1)  always
 (2)    begin
 (3)      # 10;
 (4)      a <= b;
 (5)      b <= a;
 (6)    end
The statement at Line (3) is a delay statement. It waits 10 time units before continuing execution. The next two statements at Lines (4) and (5) are executed in parallel. The current values of b and a are read, then when the next time unit starts, the new values are written to a and b.

You can specify a delay in non-blocking assignments using a statement such as:

    a <=  #5 b + x;
This statement will evaluate b + x, and schedule the assignment of that value to a five time units in the future. Execution of statements after this non-blocking assignment will continue immediately.

While you can place the delay before a non-blocking assignment as in:

    #5 a <= b + x;    // A usually incorrect usage of non-blocking assignment
This usage will result in the thread blocking for 5 time units, then executing the non-blocking assignment. It is equivalent to writing:
    #5;
    a <= b + x;

4.8.4 Fully Qualified Path Names

You can reference variables in other modules of your design by using fully qualified path names. Fully qualified path names start with the name of the top level module, followed by the names of the path of instances down to the level in which the variable you wish to reference is. The "." character is used between each part of the name. Consider the following example:
  module top;
    wire [15:0] x;
    reg [15:0] a,b;

    foo g1(x,a,b);

    initial
      begin
        $monitor("x=%h a=%h b=%h",x,a,b);

        #1 a = 16'h45;
        #1 b = 16'h24;
        #1 top.g1.i = 16'h100;
      end
  endmodule

  module foo(x,a,b);
   output [15:0] x;
   input [15:0] a,b;

    reg [15:0] i = 0;

    assign #1 x = a + b + i;
  endmodule
The variable name "top.g1.i" used in top references the variable i in the instance g1 of module foo. When this example is simulated, it produces the output:
x=x a=45 b=x
x=x a=45 b=24
x=69 a=45 b=24
x=169 a=45 b=24
Fully qualified path names allow any module to access variables of any other module in your design. Normally, they should only be used in simulation scripts and for debugging.

4.8.5 System Tasks

There are a number of system tasks supported in TkGate. They are used somewhat like function calls and are built into the simulator. System tasks begin with a $. One useful system task is the $display task. It is similar to a printf() in a C program. Here is an example of a behavioral description using the $display task:
   reg [7:0] x;

   initial
     begin
       x = 8'hf;
       $display("Hello world.  The value of x is %d",x);
     end
Simulating this description will produce the output:
Hello world.  The value of x is 15
The $display task appends a newline to the end of the output. When simulating through the TkGate graphical interface, the output will be directed to the
simulator output console. When using the TkGate simulator stand-alone, output will go to standard output. Like C, the % symbol is used to denote conversions for output. For example:
    $display("x=%d  x=%o  x=%h  x=%04h",x,x,x,x);
will produce:
x=15  x=17  x=f  x=000f
Another useful system task is the $monitor task. It has the same calling conventions as $display, except that instead of displaying immediately, it sets a watch on all the nets that are referenced in the statement. Any time a variable referenced by the $monitor task changes value, output will be produced using the rules as in a $display. For example, the module:
   module top;
   reg [7:0] x, y, z;

     initial
       $monitor("%t: x=%02h  y=%02h  z=%02h",$time,x,y,z);

     initial
       begin
         x = 8'h42; y = 8'h23; z = 8'hfe;
         #5 x = 8'h94;
         #73 y = 8'h6d;
         #21 z = 8'h88;
       end
    endmodule
will produce the output:
0: x=42  y=23  z=fe
5: x=94  y=23  z=fe
78: x=94  y=6d  z=fe
99: x=94  y=6d  z=88
The $monitor task produces output at most once per simulation time unit. The output is produced at the end of the epoch if the simulator has detected a change on any of the variables references in the $monitor task.

You may have noticed that we also used the system task $time in this example. This system task returns the current simulation time in simulation time units. You should use the %t conversion when printing out time values. The output produced when using the %t conversion is influenced by the current `timescale in force for the module in which it is used.

These are only a small fraction of the system tasks supported in TkGate. For a complete list of all the system tasks, see Appendix D. List of System Tasks.

4.8.6 Delay Triggering

The delay operator "#delay" has already been touched upon in the previous sections. This operator can be used to suspend/delay execution of a thread for a specified period of time. It can be placed at the beginning of a statement, in-line in a assignment statement, or by itself as a pure delay.

If a timescale directive has been used, the delay value may be fractional. For example when the module:

 (1)  `timescale 1ns / 100ps
 (2)
 (3)  module top;
 (4)
 (5)    initial
 (6)      begin
 (7)         $display("%t: starting simulation",$time);
 (8)         # 1.5;
 (9)         $display("%t: after delay",$time);
(10)      end
(11)
(12)  endmodule
is simulated, the following output is produced:
0.0: starting simulation
1.5: after delay
You can also use a zero delay to ensure that a statement is executed at the end of an epoch. For example:
   initial
     i = 9;

   initial
     i = 7;

   initial
     #0 i = 42;
will set i to 42 because the #0 delay ensures that the i = 42; assignment is executed last. If the #0 were not used, then the value of i would be non-deterministic.

4.8.7 Event-Based Triggering

Event-based triggering is another way to introduce blocking into your design. Event-based triggers have the syntax:

     @( event-expr )statement;

They cause the execution of the statement to block until the event described by event-expr occurs. An example use of a event-based trigger is in the following example of a D-flip-flop:
 (1)  module dff(q, d, clock);
 (2)  input d, clock;
 (3)  output reg q;
 (4)
 (5)    always @(posedge clock)
 (6)      q = d;
 (7)
 (8)  endmodule
The @(posedge clock) expression will cause the execution of the always block to be suspended until the rising edge of the clock signal. The posedge and negedge operators indicate that we should wait for the rising/positive or falling/negative edge of the signal that follows it. If we had instead written Lines (5) and (6) as:
 (5)    always @(clock)
 (6)      q = d;
The design would have loaded q with the value of d on both the rising and falling edges of the clock signal.

You can use the or operator to trigger on the change of one or more signals. For example:

 (5)    always @(posedge clock or load)
 (6)      q = d;
would result in the assignment being executed on either the rising edge of clock or any change in the value of load.

You can also use the event-based trigger with event variables. Event variables are declared with the event keyword. The -> operator is used to raise an event on an event variable. The following example declares and uses an event variable:

  (1)   module top;
  (2)      event e;
  (3)
  (4)      initial
  (5)        @ (e) $display($time,": got event");
  (6)
  (7)      initial
  (8)        #24 -> e;
  (9)
  (10)  endmodule
when simulated, this example produces:
24: got event
The event variable e is declared at Line 2. The initial statement at Line 4 executes and uses an event trigger to wait for a signal on e. A parallel initial statement at Line 7 waits for 24 time units, then uses the -> operator to raise an event on e. The raise event operator only has an effect if there are other threads that are blocked waiting for an event.

4.8.8 The wait Statement

The wait statement block until a condition is true, then executes its statement. The general syntax is:

     wait ( expr )statement;

The expr is evaluated any time a variable in it changes, and if the expression is not satisfied, it continued to block, otherwise it executes its statement. As an example, consider the simple latch:
 (1)  module latch(q, d, load)
 (2)  input d,load;
 (3)  output reg q;
 (4)
 (5)    always
 (6)        wait (load == 1'b0)
 (7)          #10 q = d;
 (8)
 (9)  endmodule
When the load signal becomes zero, the q register is loaded with the value of d after a delay of 10 time units. It is important to include a delay in here to avoid locking up the simulator. With no delay, the statement would continue executing forever without advancing simulation time. This is because simulation time is advanced only after all statements in the current time period (epoch) have been executed.

4.8.9 Conditional Statements

Conditional statements are similar to C if statements and use essentially the same syntax. For example:
    if (load == 1'b0)
      q = d;
    else
      q = q + 1;
will load the value of d into q if load is zero, otherwise it will increment the value of q. If the value of the expression is unknown (for example, if load had any unknown bits), then the else branch will be taken. if statements may be nested, and the else branch is optional. You may also use a begin...end block in the body as in the example:
    if (u > x)
      begin
        u = u - 1;
        x = j + k;
      end

4.8.10 Case Statements

There are three variants of multi-way branching or case statements: case, casex and casez. They all have the same basic syntax except for which keyword is used. They are similar in purpose to the C switch statement, but have some important differences. An example of a case statement is:
     case (r)
        2'b00: u = 3;
        2'b0x, 2'b0z: u = 4;
        2'b10, 2'b11, 2'bx1: u = 5;
        2'bxx: u = 6;
        default: u = 7;
     endcase
This statement will compare r against each of the branches in order until a match is found. Comparison is done with case equality (===) meaning that there must be an exact bit-by-bit match including any unknown (x) or floating (z) bits. You may also specify more than one value for each branch of the case.

The casez statement differs from case in that any floating (z) bits in the case values or in the expression are treated as don't cares. You may also use a "?" in the case values in place of "z". The casex statement differs in that both floating (z) and unknown (x) bits are treated as don't cares. An example of a casex statement is:

     casez (r)
        8'b1101????: u = 3;
        8'b1001????: u = 4;
        8'b00????01: u = 5;
        default: u = 7;
     endcase
Like conditional statements, you may nest case and if statements and use begin...end block in the body of a branch.

Another difference between the Verilog case, casez and casex statements compared to the C switch statement is that the case values need not be constants. For example, you can write:

    reg [7:0] r, v1, v2,v3;

    case (r)
       v1: $display("r matched v1");
       (v2+1): $display("r matched v2+1");
       v3: $display("r matched v3");
       default: $display("r didn't match anything");
    endcase
The case expressions are evaluated as the simulator tries r against each expression looking for the first match.

4.8.11 Loops

There are four types of looping statements in Verilog that will be described in this section.
while Loops
while loops look and behave similarly to while loops in C programs. An example use of a while loop is shown below:
   always
     begin
       count = 0;
       while (count < 10)
         #12 count = count + 1;
     end
This description will set count to 0, then increment count ten times with a 12 epoch delay between each time it is incremented.
for Loops
for loops also look and behave similarly to for loops in C programs. An example use of a for loop is shown below:
   reg [7:0] r;
   integer i;

   always @(r)
     for (i = 0;i < 8;i = i + 1)
       $display("Bit %d of r is %b.",i,r[i]);
This code would wait or the value of r to change, then print out each bit of it individually.
repeat Loops
repeat loops can be used to repeat a statement a specified number of times. For example:
   repeat (10)
     #12 count = count + 1;
would increment count ten times with a 12 time unit delay between each increment.
forever Loops
forever loops can be used to repeat a statement indefinitely . For example:
   forever
     #12 count = count + 1;
would increment count every 12 time units. Control would never pass to any statements after the forever statement.

4.8.12 fork...join Blocks

fork...join blocks can be used to execute two or more statements in parallel. A new thread is started for each statement in the fork, and the threads are executed in parallel. Execution of the parent thread is suspended until all statements in the fork have completed. The statements in the fork may be begin...end blocks in which case the statements enclosed within each begin...end block will be executed sequentially. Here is an example of a module using a fork...join.
 (1)  module top;
 (2)    reg [31:0] a,b,c;
 (3)
 (4)    initial
 (5)      begin
 (6)        fork
 (7)          @(a) $display("%t: got a",$time);
 (8)          @(b) $display("%t: got b",$time);
 (9)          @(c) $display("%t: got c",$time);
(10)        join
(11)        $display("%t: done with fork",$time);
(12)      end
(13)
(14)    initial
(15)      begin
(16)        #1 a = 1;
(17)        #1 b = 1;
(18)        #1 c = 1;
(19)      end
(20)
(21)  endmodule
When simulated, this example would produce the output:
1: got a
2: got b
3: got c
3: done with fork
The three threads at Lines (7), (8) and (9) are started in parallel. Each of those threads immediately suspend waiting for changes in a, b and c, respectively. The initial block at Line (14) also begins executing at time 0, then sets a, b and c in order with a 1 time unit delay between each assignment. As each assignment occurs, one of the forks in the fork...join sees the change, prints its message and terminates. When all three forks of the fork...join have terminated, execution continues in the main thread after the fork...join and the "done with fork" message is displayed.

4.8.13 Tasks

Tasks are similar to function calls in C. Tasks are defined in the context of a module. They begin with the task keyword, and are followed by the task name, an optional parameter list, a body, and the endtask keyword. The body can contain any of the statements that may appear of the behavioral Verilog statements described in this section. Tasks may address both local variables defined within them and variables in the parent module. Here is example of a module that defines and uses a task:
 (1)  module top;
 (2)    reg [15:0] s1,s2;
 (3)
 (4)     task domult(input [15:0] a, input [15:0] b, output [15:0] z);
 (5)       begin
 (6)         #1 z = a * b;
 (7)       end
 (8)     endtask
 (9)
(10)     initial
(11)       begin
(12)         domult(3,5,s1);
(13)         $display("%t: s1=%d",$time,s1);
(14)         domult(7,11,s2);
(15)         $display("%t: s2=%d",$time,s2);
(16)       end
(17)
(18)  endmodule
The task domul take two inputs a and b, delays for one epoch, then stores their product in the output z. We make two calls in to domul in the main body of the module, assigning values to s1 and s2. When simulated, this module will produce the output:
1: s1=15
2: s2=77
Tasks can use input, output and inout ports declared in their parameter list as shown on Line (4) of the example. These ports can either be declared in a port list as shown in the example, or they may be declared in separate declarations as in:
    task domult;
    input [15:0] a, b;
    output [15:0] z;
      begin
        #1 z = a * b;
      end
    endtask
It is also possible to have tasks with no ports as in this example:
 (1)  module myclock(x);
 (2)  output reg x;
 (3)
 (4)    task initialize_clock;
 (5)      begin
 (6)        x = 1'b0;
 (7)      end
 (8)    endtask
 (9)
(10)    initial
(11)       initialize_clock();
(12)
(13)    always
(14)       #100 x = ~x;
(15)
(16)  endmodule
In this example, the initialize_clock task sets the register x to 0. By placing all the initialization code in a task, we have made our design more general. If future versions of our module become more complex, we have a place to put any additional initialization code.

Additional local variables can be declared before the begin...end block inside the task. For example:

    task printbits(input [7:0] a);
      integer i;

      begin
        $display("Here are the bits in %d:",a);
        for (i = 0;i < 8;i = i + 1)
          $display("   bit %d is %b.",i,a[i]);
      end
    endtask
will print out:
Here are the bits in 184:
   bit 0 is 0
   bit 1 is 0
   bit 2 is 0
   bit 3 is 1
   bit 4 is 1
   bit 5 is 1
   bit 6 is 0
   bit 7 is 1
when invoked with printbits(184).

One important difference between Verilog tasks, and C functions is that local variables are shared across all invocations of the task. This can cause problems when a task is invoked concurrently on two different threads. For example, consider this module in which domult is invoked concurrently in two different threads of a fork...join block:

 (1) module top;
 (2)   reg [15:0] s1,s2;
 (3)
 (4)   task domult(input [15:0] a, input [15:0] b, output [15:0] z);
 (5)     begin
 (6)       #1 z = a * b;
 (7)     end
 (8)   endtask
 (9)
(10)   initial
(11)     fork
(12)       begin
(13)         domult(3,5,s1);
(14)         $display("%t: s1=%d",$time,s1);
(15)       end
(16)       begin
(17)         domult(7,11,s2);
(18)         $display("%t: s2=%d",$time,s2);
(19)       end
(20)    join
(21)
(22)  endmodule
When this module was simulated,the following seemingly incorrect output was produced:
1: s1=77
1: s2=77
The problem is that since, the local variables a and b are shared between the two invocations, which ever invocation gets invoked second, will overwrite those values. In this case, the invocation at Line (17) was invoked second, so by the time Line (6) is called, a and b have been set to 7 and 11 in both invocations. In general, the values of a and b are non-deterministic since there is no guarantee as to which thread will execute first. To solve this problem, you can use the automatic keyword in the task declaration, writing:
 (4)   automatic task domult(input [15:0] a, input [15:0] b, output [15:0] z);
 (5)     begin
 (6)       #1 z = a * b;
 (7)     end
 (8)   endtask
in place of Lines (4) through (8) above. The automatic keyword causes local variables in a task to be private to each invocation at a slight performance penalty to the simulation. When this corrected design is simulated, the simulator output becomes:
1: s1=15
1: s2=77
This agrees more with our expectations, although technically, the order in which the two output lines is printed is still non-deterministic.

4.8.14 Functions

Functions are very similar to tasks, but have some additional restrictions. They must Unlike tasks, functions can also be used in assign statements outside of normal behavioral Verilog blocks. Here is an example of a function definition and invocation:
 (1)  module top;
 (2)    reg [15:0] s1,s2;
 (3)
 (4)     function [15:0] sqaddmult(input [15:0] a, input [15:0] b, input [15:0] c);
 (5)       reg [15:0] temp;
 (5)       begin
 (6)         temp = b + c;
 (7)         sqaddmult = a * temp * temp;
 (8)       end
 (9)     endtask
(10)
(11)     initial
(12)       begin
(13)         #1 $display("%t: s1=%d",$time,sqaddmult(3,4,5));
(14)         #1 $display("%t: s2=%d",$time,sqaddmult(6,7,8));
(15)       end
(16)
(17)  endmodule
This function adds b and c, squares the sum and puts the result into the temporary variable temp, then multiplies temp by the value of a. The return value of the function is indicated by the assignment to sqaddmult, the name of the function. The [15:0] after the function keyword in Line 4 tells us that the return value is 16 bits.

Just as with tasks, you can also use the alternate syntax:

     function [15:0] sqaddmult;
     input [15:0] a, b, c;
to declare the function and its ports. Also just like with tasks, local variables are shared among invocations unless you use the automatic keyword before function. However, due to a limitation in the TkGate Verilog simulator implementation, you can only use automatic to protect against alternate threads accessing the same function, you can not use it to write recursive functions.

One use of functions is to define complex combinational logic in a concise algorithmic manner such as:

 (1)  module mylogic(x,a,b,c);
 (2)  output [15:0] x;
 (3)  input [15:0] a,b,c;
 (4)
 (5)    function foo(input [15:0] a,input [15:0] b,input [15:0] c);
 (6)      begin
 (7)        r1 = (a[7:0] ^ b[7:0] ^ c[7:0]) + (a[15:8] ^ b[15:8] ^ c[15:8]);
 (8)        r2 = (a[7:0] ^ b[15:8] ^ c[7:0]) + (a[15:8] ^ b[7:0] ^ c[15:8]);
 (9)        r3 = (a[7:0] ^ b[7:0] ^ c[15:8]) + (a[15:8] ^ b[15:8] ^ c[7:0]);
(10)        r4 = (a[7:0] ^ b[15:8] ^ c[15:8]) + (a[15:8] ^ b[7:0] ^ c[7:0])
(11)        foo = r1 ^ r2 ^ r3 ^ r4;
(12)      end
(13)    endfunction
(14)
(15)    assign x = foo(a,b,c);
(16)
(17)  endmodule

4.9 Module Parameters

The parameter variable type was introduced in Section 4.3.6. You can also declare module parameters that can be overridden by instantiating modules. Module parameters are declared in a separate list before the port list. This list has the syntax:

     #( .name1(value1), .name2(value2), ...)

The names name1, name2, etc. are the names of the module parameters, and the values value1, value2, etc. are the default values of those parameters. As an example, consider this module implementing and AND gate with a parameter delay for specifying the delay.
 (1) module AND2 #(.delay(5)) (z, a, b);
 (2) output z;
 (3) input a,b;
 (4)
 (5)   assign #delay z = a & b;
 (6)
 (7) endmodule
The default value for delay in this example is 5. When overriding the module parameters in an instantiation, the parameter list is specified after the module name, but before any instance names. For example:
 (1) module ADDER(s, co, a, b, ci);
 (2) output s, co;
 (3) input a,b,ci;
 (4) wire w1,w2,w3 ;
 (5)
 (6)   OR2 #(6) g1 (.a(w1), .b(w2), .z(co));
 (7)   AND2 #(6) g2 (.a(a), .b(b), .z(w2));
 (8)   AND2 #(7) g3 (.a(w3), .b(ci), .z(w1));
 (9)   XOR2 #(8) g4 (.a(w3), .b(ci), .z(s));
(10)   XOR2 #(8) g5 (.a(a), .b(b), .z(w3));
(11)
(12) endmodule
This would create a design where the delay of instance g2 is 6, and the delay of g3 is 7. You can also omit the parameter values entirely writing:
      AND2 g2 (.a(a), .b(b), .z(w2));
to use the default values of the parameters.

4.10 Specify Blocks

Specify blocks are an alternative way to specify the delay of combinational logic without specifying gate-by-gate delay. They can also be used to specify setup and hold times for registers in your design.

A specify block is delimited by the specify...endspecify keywords. Each statement in a specify block is either a path delay statement, or a constraint task used to verify timing constraints.

4.10.1 Path Delay Statements

Path delay statements specify the delay from one or more input ports to one or output ports. The syntax is:

     (in1, in2, ... *> out1, out2, ...) = value;          or          (in1, in2, ... => out1, out2, ...) = value;

where in1, in2, etc. are input ports and out1, out2, etc. are output ports. These expressions specify a delay of value from each of the input ports to each of the output ports. The Verilog specification differentiates between the => and *> versions in that the => only specifies delay for corresponding bits of each port, while the *> specifies delay from each bit of each input port to every bit of each output port. However, the TkGate Verilog simulator does not support bit-by-bit delay specifications, so in TkGate, both forms are treated the same as *>.

Here is an example of a combinational logic circuit using a specify block:

 (1)   module dosomething(a,b,c,x,y,z);
 (2)   input a,b,c;
 (3)   output x,y,z;
 (4)   wire q,r;
 (5)
 (6)     specify
 (7)       (a,b *> x) = 12;
 (8)       (c *> x) = 8;
 (9)       (a *> y) = 11;
 (10)      (c *> y) = 16;
 (11)      (b *> z) = 23;
 (12)      (c *> z) = 18;
 (13)    endspecify
 (14)
 (15)    assign r = q & c;
 (16)    assign x = a ^ b ^ r;
 (17)    assign y = a & c;
 (18)    assign z = c & b;
 (19)    assign q = a & b;
 (20)
 (21)  endmodule
The statement at Line 7 states that any changes on a or b will be reflected at x after 12 time units. Changes on c will appear at x after 8 time units, and so on.

A path delay statement may also have a condition attached to it using the if keyword. For example:

module XOR(a,b,x);
input a,b;
output x;

  specify
    if (a) (a *> x) = 10;
    if (!a) (a *> x) = 21;
    (b *> x) = 12;
  endspecify

  assign x = a ^ b;

endmodule
This will implement an XOR gate that has a delay of 10 when a has a 1 value, and a delay of 21 when a has a value of 0. The delay from input b is 12, irregardless of the input values. There is no else in the if clause used with path delay statements, so you must ensure that you cover every possible condition.

4.10.2 Specparam Declarations

You can define parameters for use exclusively within a specify block using the specparam keyword. You can assign either a constant, or an expression using other specparam parameters, or parameter variables defined in the module. Here is an example using a specparam:
  specify
    specparam ab_delay = 7;
    (a *> b) = ab_delay;
  endspecify

4.10.3 Constraint Tasks

Another use of specify blocks is to ensure that certain timing constraints are met. For example, most real hardware latches require that the input data line be held constant for some time period before the clock arrives. This is called "setup" time. There is also often a constraint that the input data line hold its value for some time period after the clock pulse. This is called "hold" time. There may also be width restrictions on the clock used to drive the latch. The example below uses a specify block to encode all of these requirements. Each of the statements in the specify block will be addressed in turn.
module latch(ck,data,out);
input ck,data;
output out;
reg out = 1'bx;

  specify
    $setup(data, posedge ck, 10);
    $hold(posedge ck, data, 10);
    $width(posedge ck, 25);
  endspecify

  always @(posedge ck)
    out = data;

endmodule
The $setup Check
The $setup check has the syntax:

    $setup(data, clock, limit )

It is used to verify that a setup constraint is satisfied. The data parameter should reference the data line on which you wish to perform the setup check. The clock parameter specifies the clock event for which the data must be set up. The limit parameter specifies the minimum delay time allowed between the data event and the clock event. If this constraint is violated, the TkGate Verilog simulator will issue a warning message, but normal simulation of the circuit will continue.

You can conditionally execute a check using the &&& operator in the clock parameter. For example:

    $setup(data, posedge ck &&& enable, 10);
would only perform the setup check if the enable signal were asserted.
The $hold Check
The $hold check has the syntax:

    $hold( clock, data, limit )

It is used to verify that a hold constraint is satisfied. The clock parameter references the clock event which begins the hold period. The data parameter indicates the data signal that must be held constant during the hold period. The limit indicates the time period for which the data line can not change after the clock event. If this constraint is violated, the TkGate Verilog simulator will issue a warning message, but normal simulation of the circuit will continue.

Like with the $setup check, you can use the &&& operator in the clock parameter to set a condition on when to do the check.

The $width Check
The $width check as the syntax:

    $width( event, limit )

It is used to verify that the width of a pulse exceeds a minimum value. The event parameter indicates an event (rising or falling) on the signal that is to be tested. The posedge and negedge keyword are used to indicate on which edge to start the test. For example:
    $width(posedge ck, 25);
will check that the time the time between the positive/rising edge of ck, and the opposite (in this case negative/falling) edge of ck is at least 25 time units. If this constraint is violated, the TkGate Verilog simulator will issue a warning message, but normal simulation of the circuit will continue.

5. Using the Simulator

The TkGate simulator, Verga (VERilog simulator for GAte), is a discrete time simulator based on Verilog. All modules, including those designed graphically, are converted to Verilog for simulation. Verga is a discrete event simulator. Time advances in discrete time units called "epochs". When simulating, time remains at the current time step (or epoch) until all work scheduled for the epoch as been completed. The simulator will then advance the current simulation to the next epoch that has work scheduled for it.

5.1 Starting the Simulator


Figure 5.1: Simulator Mode Tab
To start the simulator, press the "Simulate" tab at the top of the main window. If you have not enabled "auto-start" in the circuit properties, the simulator will compile your design and wait in paused mode. If you have enabled "auto-start", the simulation will begin immediately.


Figure 5.2: Simulator Mode Module Hierarchy
The simulation will be performed with the designated root module at the top-level. The simulator internally expands any module instances in your circuit. While the simulator is active, the Module Hierarchy View changes to show the hierarchy of module instances as shown in Figure 5.2. In this mode, the display shows each instance of each module, rather than simply one entry per module. The module name for the instance are shown in angle brackets after the instance name. For example, the instance "RF" under "eunit" is an instance of a "REG16" module. That module contains an instance "RF0" of a "ZREG" module, and three instances "RF1", "RF2" and "RF3" of a "REG4" module. Double clicking on an instance in this hierarchy view will move the simulator to that module, and ensure that any probes or signals viewed are for the instance you selected.

While the simulator is active you must navigate using either the Module Hierarchy , or by right clicking on a module and using " Open" and " Close". You can not use the Module List view to navigate since TkGate can not tell which instance of a module to open.

5.1.1 Simulator Console




Figure 5.2: Simulator Console
At the bottom of the TkGate main window, is a pull up tab for the simulator console. When you start the simulator, the console will open automatically, but you can view it in edit mode as well by manually pulling up the tab.

The simulator console has four tabs with different pages of information. These four pages are:

Page Description

Message Page - This page displays messages from the console. Any output from $display or other system tasks generating output will be displayed here.
Breakpoint Page - Displays breakpoints that have been set for the simulation and the status of those breakpoints.
Script Page - Shows the loaded simulation scripts and their status.
Simulation Control Page - Allows you to set options controlling the simulation such as the number of epochs to advance at a time when stepping through a simulation.

5.1.2 Simulator Control

A set of five buttons controls the advance of time in the simulator. These buttons are:

Button Description

Run - Enters continuous simulation mode. The simulation will continue as long as there are events in the event queue. If there are any clock gates in your circuit, this will mean the simulation will continue indefinitely. If the circuit is combinational, the simulation will continue until the circuit reaches quiescence.
Pause - Causes a continuously running simulation to stop.
Step - Causes the simulation to advance a fixed number of epochs. The number of epochs to advance can be set on the simulation options menu. You can also invoke this command with the space-bar.
Clock Step - Causes the simulation to advance to the rising edge of a clock. You can set the number of clock cycles to simulate and the number of epochs past the designated cycle to step (to allow time for registers to change value). The default is to trigger on any clock, but you can designate a specific clock in the simulator options menu. You can also invoke this command with the tab key.
Stop - Causes the simulation to be terminated and all probes to be deleted.



Figure 5.3: Simulator Console Control Page
You can use the control page of the simulator console shown in Figure 5.3 to control the effects of the step and clock step buttons. Enter the number of epochs to advance for each press of (or space-bar) into the "Epoch Step Size" box. The clock step button (or tab) will advance by the number of clock steps indicated in "Clock Cycle Step Size", plus an additional number of epochs entered in the "Clock Overstep" entry.

5.2 Observing the Output

Except when otherwise noted, Verilog syntax is used to specify and display values. A Verilog syntax number contains a prefix to specify the bit width, a quote character, a radix character and the digits of the number. The radix characters using in TkGate are "b" for binary, "h" for hexadecimal, "o" for octal and "d" for decimal. For example "8'h3e" is the 8-bit hexadecimal number "3e".

5.2.1 Current Value Display


Figure 5.3: Value Display
To display the current value of a signal in a circuit, click and hold the mouse button on a wire. This will display the current value driven on the wire in Verilog syntax as shown in Figure 5.3. The value will disappear when you release the mouse button. This feature can be used both when the simulator is paused and when it is in continuous simulation mode. When the simulator is in continuous simulation mode, the value displayed will be the value at the time the mouse button was first pressed.

There is a slight delay between pressing the mouse button and displaying the value. This is because double clicks on a wire are used to set probes, so the delay must be long enough to determine that the mouse press is not a double click. By default, this delay is 333ms (1/3 of a second), but the delay may be changed through the Simulate Options dialog box.

5.2.2 Scope View




Figure 5.4: Scope Window
To set a permanent probe on a signal, double click on a wire. This will add or remove a probe. When a probe is set on a wire, its value will be continuously displayed in the scope window. The values of multi-bit signals will be displayed as hex numbers. The scope window (Figure 5.4) is viewable any time you have at least one signal with a probe on it. When there are no current probes, the scope window is hidden.

You can slide the range of time displayed in the scope window by using the scroll bar for coarse changes, or by clicking and dragging in the trace part of the window for fine grain control. To zoom in, you can press the left mouse button while holding the shift key, press the '>' key, or press the button on the toolbar. To zoom out, you can press the right mouse button while holding the shift key, press the '<' key or press the button on the toolbar.

Scope Description
 0 - Logic 0 or false (1 bit signals only)
 1 - Logic 1 or true (1 bit signals only)
 x - Unknown value
 z - Floating or high impedance
 L - Low (the signal is either floating or zero)
 H - High (the signal is either floating or one)
 Data - Data value on a multi-bit wire.

Figure 5.5: Logic Trace Values

The time-line on the scope window is displayed as a base value shown in the lower left corner, and an offset value shown along the bottom. In the example shown in Figure 5.4, the base value is 50016916ns (i.e., 0.05 simulated seconds). Each tick mark on the x-axis represents an additional 5000ns past the base time value.

The scope includes a cross hair that follows the mouse whenever it is in the scope window to help you correlate events across different traces. You can enable or disable the cross hairs with the button on the toolbar. The scope window toolbar also includes some of the same simulation control and other simulation-related buttons that are on the main TkGate window.

Figure 5.5 shows how the various logic values are displayed in the scope windows. On multi-bit wires, the value of the wire is displayed in hexadecimal, unless the scale is such that there is no room to display the value between when it starts and the next logic transition. In this case, it will first compress the value to a "#", and if there is no room to display that either, no value or symbol will be displayed. The colors used in the scope traces can be configured through the Color Options dialog box.

5.2.3 Printing Scope Traces


Figure 5.6: Print Region Selection
To print a scope trace, first use the right mouse button to select a region to be printed. Click and drag with the right mouse to select the region. The region will be highlighted as shown in Figure 5.6. Once you have an active region, you can press the right mouse button again while holding the Shift key to expand or shrink the selected region. If you print without making a selection, then the area of the trace visible in the scope window will be taken as the selection.

After you chosen a region, push the button on the toolbar of the scope window. This will bring up the Scope Print dialog box having an "Output" and a "Content" page. The "Output" page of this dialog box is the same as the "Output" page of the dialog box used to print circuits.

If you choose the "Save as Encapsulated Postscript" option, the "Content" page will be disabled, and the selected region of the trace will be written as a single Encapsulated Postscript figure suitable for inclusion in a document (e.g., by Latex).




Figure 5.7: Scope Print Dialog Box
The "Content" page (Figure 5.7) is divided into a "Range Selection" and a "Scale Selection" portion. The Range Selection portion has a selector to modify the start time of the trace plot, and the range of the selected region in the trace. A diagram showing the total amount of trace data available (in green), and the region selected for printing (in gray) is shown to the right of the selectors.

The "Scale Selection" allows you to set the amount of time to plot per line when generating output. You must be cautious to set this carefully, as too low a setting can result in trace output with a huge number of pages. To help you in setting this value, an estimate of the number of pages needed is displayed next to this selector. The default value for the "Line Length" is one full scale of the scope window at the current zoom setting.

5.3 Controlling the Input

There are two types of built-in circuit elements that can be used to control your circuit: the single-bit switch, the multi-bit dip switch. You can also provide input through VPDs (Virtual Peripheral Devices). An important VPD that is included with TkGate is the TTY which will also be described in this section.

5.3.1 Switches and Dip Switches



Figure 5.8: Dip Value
Dialog Box




Figure 5.9
TTY Device


Switches can be manipulated by simply clicking on them to toggle their values. To change a dip switch value, click on the dip switch to open the dip value dialog box for setting the value of that dip switch. Then, enter a value in the entry area, and press the "Apply" button to set the switch. The value should be a hexadecimal number. The dip value dialog box will remain open until you hit "Close", and you can open as many at once as you like.

5.3.2 TTYs

In versions of TkGate before version 2.0, the TTY device was built into TkGate as a primitive device. As of version 2.0, the TTY is now implemented as a VPD (Virtual Peripheral Device). In order to use the TTY device, you must open the Library Manager and load the "tty" library. You can then create a module of type "TTY" in your design in the same way that you would create a module instance for any other module. Figure 5.9 shows an example of a TTY device. Note that it is displayed in the magenta color of a module, rather than the blue color of built-in devices. See the section on the TTY Library Component for information on how to use this device.

5.4 Setting Breakpoints




Figure 5.10: Simulator Console Breakpoint Page
Breakpoints can be used to set conditions which will cause a continuously running simulation to pause. To display the breakpoints, press the tab on the simulator console as shown in Figure 5.10. To add a new breakpoint, press the "Add..." button or double click on the next empty slot under "Condition". You can enter any valid Verilog expression as the breakpoint condition. You can edit the expression of a breakpoint by double clicking on the expression.

Breakpoints will be activated for any non-zero value of their condition expression. When one of the registered breakpoints is activated, the simulation will stop, and a symbol will appear in the "S" column of the breakpoint. Breakpoints are only triggered on transitions. If you press the button to resume the simulation, the simulation will continue until the breakpoint expression makes a new transition to a non-zero value.

The "ID" column of the breakpoint list indicates an identifying number for the breakpoint. The column labeled "S" indicates the state of the breakpoint. The state is indicated by one of the following symbols:

State Description

Error - There is an error such as a syntax error or undefined variable error in the breakpoint expression.
Go - The breakpoint is active, but has not been tripped.
Stop - The breakpoint has tripped, and simulation is stopped at this breakpoint.
Disabled - The breakpoint has been temporarily disabled.
Stand By - The breakpoint is ready and will be active when the simulator starts.

The "Value" column shows the current value of the breakpoint expression. If the expression is binary, the value will be a 1 or 0, but if it is a multi-bit expression, then it could be an arbitrary value.

There are two buttons to "Enable" and "Disable" a breakpoint. While a breakpoint is disabled, the breakpoint will remain in the breakpoint list, but its value will be ignored until you re-enable it.

5.5 Initializing Memories

A circuit can contain one or more memories (ROM and RAM gates). You can initialize memories from a file, or dump the contents of a memory to a file. The following toolbar buttons can be used to load or dump memories.

Button Description

Load Memory - Load memories from the selected file. If a memory gate is selected, that memory will be the default memory to load. If the memory file contains one or more "memory" keywords, the specified memory(ies) will be loaded with the contents of the file. When loading a file, the current directory, the directory of the current circuit file, and the user's home directory will be searched.
Dump Memory - Dumps the contents of the selected memory to a file.

Memory files have the extension ".mem" be default. The default format for memory files is slightly different between Version 2.0 memory files, and pre-2.0 memory files, but TkGate 2.0 can read files from either format.

A memory file is composed lines that can contain commands, or memory data. Blank lines and lines beginning with '#' are ignored. The supported commands are:

Command Description

@memory name Memory data after this line will be loaded into the memory given by name. Name should be the fully qualified Verilog name of the memory comprised of a "." separated list of the modules down to the module in which the memory is located. The "@" for this command only can be omitted, since that is the pre-2.0 style for this command.
@radix radix Specify the radix to use for memory data after this line. The radix can be 2, 8, 10 or 16 with 16 being the default.
@addr Specifies the address at which to begin loading data. The address should be a hexadecimal number.

Lines that do not contain one of the above commands are assumed to be data in the current radix. For files loaded through the interface, the radix is assumed to be hexadecimal, but can be changed with the @radix command. For files loaded through the Verilog $readmemh system task, the assumed radix is hexadecimal, and for files loaded through the $readmemb system task the assumed radix is binary.

Here is an example of a memory file:

@100
e1 f0 0 0 e1 e0 0 0
81 0 0 0 12 1 bd 0

@200
e 1 e1 d0 dc 7 85 0
This file will load 16 bytes starting at address 100, then an additional 8 bytes at address 200 (assuming the memory is declared as an 8-bit wide memory).

Old style memory files use a slash after an address to indicate where to load data. For example:

130/ 2 0 ed 0 60 6 62 6
138/ ed 0 5e 6 1 0 85 0
140/ 81 0 0 0 26 4 69 f0
Here the 130, 138 and 140 are the address in hexadecimal. You can use either syntax in your memory files.

In the above examples, there was no explicit mention of the target memory. For that reason, those file can only be loaded when you explicitly indicate the target memory either by clicking on before pressing , or by specifying the memory as an argument in the $readmemh or $readmemb system task.

Using the @memory command, you can specify the memory (or memories) to be loaded within the file itself. The memory keyword requires a single argument specifying the name of a memory. For example:

@memory memory.m1
@100
e1 f0 0 0 e1 e0 0 0
81 0 0 0 12 1 bd 0

@memory memory.m2
@100
62 65 61 6b 20 69 73 0a
This memory file will load 16 bytes of data into the memory "m1" in the instance named "memory" which is a sub-module of the root module. It will also load 8 bytes into the memory "m2" in the same module as "m1".

You can also use the "x" and "z" characters in any digit of data values specified in a memory file to indicate unknown or floating values. For example:

@100
10 x 8x 3e z9 3a zx 9x
will load a memory that includes unknown and floating values.

For RAM memories, the contents of the loaded memory are frozen until first time the write line transitions to a stable value (logic 0 or 1). This prevents the data loaded from being destroyed due to unknown values on the write and address lines until the circuit has time to initialize these signals.

5.5.1 Using GMAC to Create Memory Files

In many cases, memory files are initialized to act as microstores or to contain machine instructions for a user designed processor. In these cases, it is very tedious and error prone to explicitly specify the contents of the memory. For this reason, TkGate includes a tool, gmac, for compiling microcode and macrocode to TkGate-compatible memory files. Complete documentation on Gmac is given in a later section of this document.

5.6 Simulation Scripts




Figure 5.11: Simulator Console Script Manager Page
Simulation scripts are useful for setting up a simulation before starting, or for running a simulation in batch mode. You can perform most of the operations you can do manually through the interface including setting and remove probes, changing switch values, loading memories, setting breakpoints, and stepping the simulator.

Simulator script files use Verilog syntax. This section will give some simple examples of how to write scripts. More detailed information on Verilog format can be found in the chapter on Verilog Modules.

5.6.1 Loading Simulator Scripts

To load a simulator script, first press the tab on the simulator console. You can then press the "Add..." button or double click on the next empty slot. The default extension for simulation scripts is ".vs". When loading a file, the current directory, the directory of the current circuit file, and the user's home directory will be searched.

You can also arrange for simulation scripts to be automatically executed when you start the simulator by adding one or more simulation scripts in the circuit options dialog box.

The "S" column next to each simulator script indicates the state of the script. The state is one of the following:

State Description

Error - There is an error such as a syntax error or undefined variable error in the script.
Go - The script is active and running.
Stop - The script has completed executing.
Disabled - Execution of the script is disabled.
Stand By - The script is ready and will execute when the simulator is started.

5.6.2 Simulator Script Format

Simulator scripts are fragments of Verilog code assumed to be defined in the context of the body of the top-level module. You can create one or more parallel threads as well as declare local variables for use within the script.

Most scripts are defined as a Verilog initial block. For example:

  initial
    begin
      $readmemh("test.mem");    // Load memory file "test.mem".
      $tkg$probe(a, b, c);      // Place probes on the signals a, b and c.
      repeat (5)                // Advance the simulator 5 steps of the
        @ (posedge clock);      //    clock named 'clock'.
      # 10;                     // Advance the simulator 10 epochs.
      $stop;                    // Stop the simulator and put it in "pause" mode.
    end
If you define multiple initial blocks in your script file, each block will execute in parallel. You can also use always blocks which execute their bodies in an infinite loop.

You can also define local variables in a simulator script. For example:

  integer i;

  initial
    begin
      for (i = 0;i < 10;i = i + 1)        // Loop ten times
        begin
          @ (posedge clock);              // Advance to rising edge of "clock".
          $display("%t: x=%h",$time,x);   // Print time and value of x signal.
        end
      $stop;                              // Pause the simulator.
    end
This example will step for 10 clock periods and print the value of the x signal in the simulator console at each of those clock periods.

5.6.3 Setting Signal Values


Figure 5.12: Full Adder Circuit
You can set the value of any register variables in a module using an assignment statement with the "=" operator. For netlist modules, any nets that are attached to a switch or a dip element are considered register variables. However, unlike in TkGate 1.8 script files, you must use the name of the wire, and not the name of the switch itself. For example, the following script will set probes on the inputs and output of the circuit shown in Figure 5.12, then step through each combination of inputs with a delay of 50 epochs after we set the input values:

  initial
    begin
      $tkg$probe(a,b,ci,s,co);
      ci = 0; a = 0; b = 0;
      #50 ci = 0; a = 0; b = 0;
      #50 ci = 0; a = 0; b = 1;
      #50 ci = 0; a = 1; b = 0;
      #50 ci = 0; a = 1; b = 1;
      #50 ci = 1; a = 0; b = 0;
      #50 ci = 1; a = 0; b = 1;
      #50 ci = 1; a = 1; b = 0;
      #50 ci = 1; a = 1; b = 1;
    end
Note that the left hand side of each assignment statement is a wire name. For example, a is used instead of the name of the switch (g25) to which it is attached. The right-hand side of assignment statements can be arbitrary expressions referencing variables declared in the simulation script and any nets in the design.

You can use fully qualified path names to set the value of switches/nets at levels other than the top level. For example:

    top.memctl.ttyreg = 8'h
will set the value of ttyreg in the module instance memctl which is instantiated in the top-level module.

5.6.4 Commonly Used System Tasks

Scripts can make calls to system tasks to perform various useful functions. Systems tasks begin with a "$" and are used somewhat like function calls. A complete description of system tasks are given in the
System Tasks Appendix. Some of the system tasks that are frequently used in scripts are:
Task Description

$display(arg1, arg2, ...) Display messages to the simulator console. Similar to the C printf in functionality.
$readmem(filename [, memory [, start [, stop]]]) Loads the contents of a memory file to one or more memories.
$tkg$probe(sig1, sig2, ...) Places probes on the specified signals.
$tkg$unprobe(sig1, sig2, ...) Removes probes from the specified signals.
$stop Stop the simulation and put it into "paused" mode.
$finish Terminate the simulation and return to "edit" mode.
$random[(seed)] Return a random number, or set the seed if an argument is given.
$time Return the simulation time in epochs as a 64-bit integer.
$tkg$systime Returns the actual system time in milliseconds since January 1, 1970 as a 64-bit integer.

5.6.5 Using Scripts for Test Vector Generation

Another method for using scripts is to generate random test vectors. For example, suppose we wish to test a 32-bit adder that we have designed. The following script will apply 10,000 random vectors to the design and check them against the answer calculated in the script. When a mismatch between the circuit output and the expected output is found, an error message is displayed, and the simulation is paused so the user can view internal signal values.
  reg tempCout;                                    // Declare 1-bit variable for carry out
  reg [31:0] tempS;                                // Declare 32-bit variable for sum
  integer i;                                       // Declare index variable

  initial
    begin
      $random($tkg$systime);                       // Use system clock to set random seed
      for (i = 0;i < 10000; i = i + 1)             // Loop 10,000 times
        begin
          a = $random;                             // Pick random value for A
          b = $random;                             // Pick random value for B
          cin = $random;                           // Pick random value for carry-in

          # 50;                                    // Step 50 epochs

          tempCout = (33'h0 + a + b + cin) >> 32;  // Get correct carry out value
          tempS = a + b + cin;                     // Get correct sum value

          //
          // If output from the design is not as expected, print an error message
          // and stop the simulator.
          //
          if ({cout,s} != {tempCout,tempS})
            begin
              $display("error: got 31'h%h + 31'h%h + 31'h%h = {1'h%h, 31'h%h}",a,b,cin,cout,s);
              $display(" -- should have gotten {1'h%h, 31'h%h}", tempCout, tempS);
              $stop;
            end
        end

      $display("test completed.");
      $stop;
    end

5.7 Using the Error Reporter




Figure 5.13: Simulator Error List
If there are any errors in the circuit when you start the simulator, an error list box will appear as shown in Figure 5.13. Click on an error message to gave TkGate show you the location of the error. If the error is in a netlist module, TkGate will navigate to the module and place cross-hairs over the as shown in Figure 5.14. If the error is in an HDL module, then the line with the error will be highlighted.


Figure 5.14: Error Location Cross-hairs

In the example shown here, the module "ADD32" had an internal port name of "a", but the name "A" was erroneously used on the interface. The first error message indicates that the external port name "A" does not have a corresponding port inside, and the second error message indicates that the internal port "a", does not have a corresponding port on the outside.

5.8 Gate Delay Files

Gate delay, area and power (power specifications are not used) parameters can be specified through a collection of gdf (Gate Delay File) specification files. The default file "gdf/default.gdf" in the TkGate home directory is always loaded, but the definitions may be replaced by loading additional delay files through the Simulate Options dialog box.

Each gate delay file should contain one or more technology blocks having the form:

technology cmos {
  ...
}
Technology blocks implement a new set of delay parameters and the specified technology name becomes a name that can be specified as a technology through the "Delay" page of the gate properties box. The body of the technology block should consist of a set of gate primitive declarations. Each gate primitive should be described in a block defining all the delay and possible area and power parameters of the block in terms of number of inputs, bit-widths of inputs and existence of inverters on inputs. For example, the block for the "mux" primitive might look like:
primitive mux {
  delay<I-Z> = (2*(num(S)+1) + 2*num(I)) + 2*(inv(I) || inv(Z));
  delay<S-Z> = (2*(num(S)+1) + 2*num(I));
  area = bits(Z)*((2*(num(S)+1) + 2*num(I)) + 2*inv(I));
}
The two "delay" lines define the delay from the input to the output (I-Z) and from the select to the output (S-Z), respectively. The area line defines the estimated area of the gate. Expressions may include the C-style operators "+", "-", "*", "/", "&&", "||", "==", "!=", ">", ">=", "<", "<=", and "!", the "power of" operator "**" and the functions listed in the table below.

Function Description
num(p) Normally, "p" specifies a group of related ports in which case this function returns the number of ports in the group. For example, on an n-input AND gate with inputs I1 through In, the expression num(I) would return n.
bits(p) Returns the number of bits on the specified port. If "p" represents a group of ports, the highest bit-width of the group is returned.
inv(p) When "p" is specific port, a 1 is returned if there is an inverter on the port, 0 otherwise. When "p" specifies a group of ports, the number of ports with inverters is returned.
log(expr) Returns the ceiling of the base-2 log of an expression.

It is also possible to write procedural delay/area definitions. For example, consider the parameter specification for the "and" primitive.

primitive and {
  delay<I-Z> = {
    if ((inv(I) == num(I)))     // Determine if an inverter is necessary.  An
      d = inv(Z);               //   inverter is not required if the output is
    else if ((inv(I) == 0))     //   inverting and all inputs are non-inverting
      d = !inv(Z);              //   (i.e., it is an AND gate), or if all inputs
    else                        //   are inverting and the output is non-inverting
      d = 1;                    //   (i.e., it is a NOR gate).

    if (num(I) == 1) {          // If one input, treat this as a reduction gate
      return 2*bits(I0) + 2*d;  //   one Tr. delay per bit plus inverter delay.
    } else {                    // If multiple inputs, treat this as a normal gate
      return 2*num(I) + 2*d;    //   one Tr. delay per input plus inverter delay.
    }
  }

  area = {
    if ((inv(I) == num(I)))     // Estimate number of inverters required.  If all
      d = inv(Z);               //   inputs are inverted, an inverter is required
    else if ((inv(I) == 0))     //   iff the output is inverted.  If all inputs
      d = !inv(Z);              //   are non-inverted, an inverter is required iff
    else                        //   the output is non-inverted.  Otherwise we need
      d = inv(I);               //   an inverter for each inverted input.

    if (num(I) == 1) {          // If one input, treat this as a reduction gate
      a = 2*bits(I0) + 2*d;	//   one Tr. per bit plus inverter Trs.
    } else {			// If multiple inputs, treat this as a normal gate
      a = 2*num(I) + 2*d;	//   one Tr. per input plus inverter Trs.
    }
    return bits(Z)*a;           // Multiply by number of bit slices.
  }
}
In a procedural specification, statements are executed sequentially until a "return" statement sets the value for the parameter. C-style if and switch statements may be used in procedural specifications, but there are no looping constructs.

A technology block need not specify every single primitive type. For example, suppose we have a technology definition for CMOS which includes a definition for a buffer as shown here:

technology CMOS {
  primitive buf {
    delay<I-Z> = 2 + 2*(inv(I) == inv(Z));
    area = bits(Z)*(2 + 2*(inv(I) == inv(Z)));
  }
  ...rest of CMOS definition...
}
We might create a special technology definition "HP_CMOS" which contains high-power versions of a subset of the standard CMOS gates. In the example here, we provide a special high-power buffer that has the delay of a standard buffer, but twice the area (and power).
/*
 * A high-power buffer with half the delay and double the area/power.
 */
technology HP_CMOS {
  primitive buf {
    delay<I-Z> =  1 + (inv(I) == inv(Z));
    area = 2*bits(Z)*(2 + 2*(inv(I) == inv(Z)));
  }
}

If you assign the technology type HP_CMOS to any gates for which there is no primitive definition, delay values from the technology "default" will be used.

The table below lists all of the primitive gates for which delay may be specified and their delay parameters. The default values given are the values for a "basic" gate created from the "Make" with no changes to the number of inputs, input/output inverters, or bit widths of any of the ports.

Gate Type Parameter Default Description
and I-Z 6 Delay from input to output.
or I-Z 6 Delay from input to output.
xor I-Z 8 Delay from input to output.
buf I-Z 4 Delay from input to output.
bufif1 E-Z 4 Delay from enable to output.
I-Z 6 Delay from data input to output.
nmos I-Z 2 Delay from data input to output.
G-Z 1 Delay from gate to output.
pmos I-Z 2 Delay from data input to output.
G-Z 1 Delay from gate to output.
add A/B-S 68 Delay from operand input to sum.
A/B-CO 70 Delay from operand input to carry out.
CI-S 62 Delay from carry in to sum.
CI-CO 64 Delay from carry in to carry out.
register setup 10 Time before clock edge data-in must be stable.
hold 10 Time after clock edge data-in must remain stable.
CK-Q 20 Time from clock edge until output changes.
mux S-Z 8 Delay from select line to output.
I-Z 8 Delay from data input to output.
demux E-Z 6 Delay from enable line to output.
I-Z 6 Delay from data input to output.
mult A/B-P 252 Delay from operand input to output.
div A/B-Q 236 Delay from operand input to quotient.
A/B-R 236 Delay from operand input to remainder.
ram OE-D 10 Delay from output enable to data-out.
CS-D 10 Delay from chip select to data out.
A-D 70 Delay from the address line to the data out.
addr_setup 10 Time before write is asserted that address must be stable.
data_setup 10 Time before write is asserted that data-in must be stable.
addr_hold 10 Time after write is unasserted that address must remain stable.
data_hold 10 Time after write is unasserted that data-in must remain stable.
rom OE-D 10 Delay from output enable to data-out.
A-D 50 Delay from address to data-out.
lshift S-Z 16 Delay from shift select to output.
I-Z 18 Delay from data-in to output.
rshift S-Z 16 Delay from shift select to output.
I-Z 18 Delay from data-in to output.
arshift S-Z 16 Delay from shift select to output.
I-Z 18 Delay from data-in to output.
roll S-Z 16 Delay from shift select to output.
I-Z 18 Delay from data-in to output.

6. Gate Microcode and Macrocode Compiler

Gmac is a simple microcode and macrocode compiler which can be used to create memory files for TkGate. You can write declarations to define your own microcode and macrocode. The current version of gmac is fairly simple; no attempt is made to handle arbitrary instruction sets with complex addressing modes, but it should be sufficient to create simple micro- and macro-programs. To compile a micro/macro program description, use the command:
gmac infile.gm -o outfile.mem -l outfile.map
This will read the description in "infile.gm", and generate the memory file "outfile.mem" and and the optional map file "outfile.map". The memory file will contain the appropriate "memory" keywords to load multiple memories. The map file contains the list of symbols defined in the input file in a human-readable form.

Gmac input files consist of a sequence of declarations. Whitespace and newlines are ignored, and comments are indicated by a '//'. A complete example of a Gmac input file is given in Appendix E.

6.1 Defining Memory Banks

The memories which will be generated are declared with "bank" statements. A bank statement has the form:

type bank [msb:lsb] name ?[first:last]?;

The type parameter is one of "microcode", "macrocode" or "map" and specifies what type of memory this is a bank for. Microcode and macrocode banks specify the memories to be used for the micro- and macro-programs respectively. Map banks are typically used to look up the microaddress for implementing a macro-instruction. Each of the memory types is optional and can be omitted.

The bit range [msb:lsb] specifies the portion of the word which will be stored at an address in the memory name. The specified memory should be the full path name of a memory in the circuit file. A word can be broken across multiple memories by using multiple bank statements. A single bank is limited to 32 bits, so memories with very long words, such as a micro-program will probably have to be broken into multiple memories. An optional address specifier after the memory name specifies the range of address which this memory will hold. If no address range is specified, this memory will be used for all words. As an example, consider the set of bank declarations:

microcode bank[31:0] iunit.m1;
microcode bank[63:32] iunit.m2;
map bank[7:0] iunit.map;
macrocode bank[7:0] memory.m1;
This indicates that the mirco-program will be consist of 64-bit instructions with the low half of the instructions being stored in "iunit.m1" and the high half of the instructions being stored in "iunit.m2". The map memory contains 8-bit data and is stored in "iunit.map". The main macrocode memory has 8-bit words and is stored in "memory.m1".

6.2 Defining the Micro-Instruction Set

Microcode instructions are defined using the 'field' declaration. A field declaration consists of the 'field' keyword followed by one or more comma-separated declarators. A simple declarator is simply a name followed by a bit range in square brackets. For example:
field cin[18], idata[9:2];
declares that bit 18 of the microinstruction has the name "cin", and the bits 2 through 9 have the name "idata". Single bit fields can optionally be prepended with the "~" to indicate they are active low signals. A set of symbolic values can also be supplied for a field. For example:
field mpcop[1:0]={
	 next=0,	// Increment mpc
	 reinit=1,	// Restart CPU
	 jmap=2,	// Jump from map value
	 jump=3		// Jump from condition
};
declares a 2-bit field "mpcop" with a set of symbolic names for field values. Hexadecimal symbolic values can be specified by prepending them with a "0x".

6.3 Writing the Micro-Program

A block of microcode is defined using the "begin microcode @" specifier. A start address must be specified after the declaration. The end of the block is indicated with the "end" keyword. The micro-program defined in the block will be stored starting at the specified address. Microinstructions consists of a list of fields (defined by the 'field' keyword described above), with an optional value specification. The microinstruction is ended by a ";". Micro instructions can be labeled by specifying an identifier followed by a colon.

For each single-bit field, the presence of a field name in a microinstruction indicates that that bit should be asserted. For normal fields, this means setting the bit to 1 in the microinstruction. For active low fields (indicated with a "~" symbol in the field declaration) this means setting the bit to 0. The bit corresponding to any active low fields not specified in an instruction will be set to 1. For multi-bit fields, a value should be specified after an '=' symbol. The value can be either a fixed constant, a symbolic value defined for the field, or a label which will be resolved to the appropriate microinstruction address when the specification is compiled.

For example, consider the microcode fragment:

begin microcode @ 0
start:	clq;								// Q <- 0
	idata=0x1 ALU_AZERO ALU_OP=add bop=idata ldqh;			// Q.H <- 1
	ALU_AZERO ALU_OP=add bop=qreg dout ldpc;			// PC <- Q
	mpcop=jump mpccond=jmp mpcaddr=fetch;				// jump to 'fetch'
	mpcop=next;
end
This specifies that this fragment will begin at address 0. The first mirco instruction is labeled 'start' and simply asserts the signal 'clq'. In this case, this clears the contents of the Q register. The next micro-instruction puts the value '1' on the field name "idata", asserts the signal "ALU_AZERO", puts the symbolic value "add" on the field "ALU_IP", the symbolic value "idata" on the field "bop" and asserts the signal "ldqh". This set of operations causes a 1 to be stored in the high half of the Q register. The subsequent instructions load the program counter PC from the value stored in the Q register (0x100), and jumps to the label 'fetch'.

6.4 Defining the Macro-Instruction Set

The macro-instruction set is defined using "register", "operands" and "op" declarations. The "register" keyword is used to define the register names used in the macrocode and assign them a numeric value.

For example:

registers R0=0, R1=1, R2=2, R3=3, R4=4, R5=5, FP=6, SP=7;
declares 8 registers name R0 through R5, FP and SP. With numeric values as specified in the declaration. The numeric value here will be used in building the macroinstruction using a register.

6.4.1 Operand Declarations

The "operands" declaration specifies a set of operand types that an instruction can have. An operands declaration consists of the "operand" keyword, a symbolic name, and a list of addressing modes. Currently only four addressing modes are recognized: immediate, register direct, register indirect, and indexed. Since the syntax of the operand declarations is rather complicated, it will be explained with an example. For instance, suppose we wish to define an 'add' instruction which can use the following addressing mode combinations:
   add	R1,#1234	// register direct, immediate
   add  R1,R2		// register direct, register direct
   add  R1,(R2)		// register direct, register indirect
   add  R1,#Array(R3)	// register direct, indexed
We can define the addressing mode combinations with the declaration:
operands myopts {
    %1,#2     = { +0[1:0]=0; +1[7:4]=%1; +1[3:0]=0; +2=#2[7:0]; +3=#2[15:8]; };
    %1,%2     = { +0[1:0]=1; +1[7:4]=%1; +1[3:0]=%2; };
    %1,(%2)   = { +0[1:0]=2; +1[7:4]=%1; +1[3:0]=0; +2=#2[7:0]; +3=#2[15:8]; };
    %1,#2(%3) = { +0[1:0]=3; +1[7:4]=%1; +1[3:0]=%3; +2=#2[7:0]; +3=#2[15:8]; };
};
This block defines a set of 4 operand combinations for a two-operand instruction. Each operand combination is specified by a comma separated list of addressing modes. A '-' can be used in place of the operand list to indicate instructions with no operands. Registers are indicated by a '%' followed by an id number, and numbers are indicated by a '#' followed by an id number. The statements in braces following an address mode combination specify a sequence of assignments used to build the macroinstruction. Each assignment contains a word number within the macroinstruction, an optional bit range, and a value to store at the specified location. If the value is a simple number, the specified number will be stored. If the value is prepended with a '%' or '#', then the value is taken to be the id number for a register or number specified in the instruction. For example, on a machine with a byte-addressable main memory, the previous operand declarations will generate the bytes:
   +-+-+-+-+-+-+-+-+   +-+-+-+-+-+-+-+-+  +-+-+-+-+-+-+-+-+  +-+-+-+-+-+-+-+-+
   | undefined |1 1|   |0 0 0 1|0 1 0 1|  |0 0 1 1|1 0 0 0|  |0 0 0 1|0 0 1 0|
   +-+-+-+-+-+-+-+-+   +-+-+-+-+-+-+-+-+  +-+-+-+-+-+-+-+-+  +-+-+-+-+-+-+-+-+
for the instruction:
   add  R1, #0x1234(R5)
Since the addressing modes are "register" and "indexed", the last entry in the list of operand combinations applies. The "+0[1:0]=3" statement sets the low two bits of the first byte to binary '11'. The "+1[7:4]=%1" statement sets the high half of the second bytes to the code for the R1 register, and the "+1[3:0]=%3" statement sets the low half of the second word to the code for the R5 register. The "+2=#2[7:0]" and "+3=#2[15:8]" statements store the index value '0x1234' in the third and fourth bytes of the instruction low byte first.

One last type of memory assignment is the relative address assignment. Relative addresses are indicated with the '@' operator. The pair of statements "+2=#2@[7:0]" and "+3=#2@[15:8]" will store the difference of the operand with id 2 and the address of the first byte of the instruction. This feature can be used to define relative branching instructions. An optional value can be specified after the '@' as a further fixed offset.

6.4.2 Macro-Instruction Declarations

After defining a set of operand declarations, you must define the macro-instruction set. A macro-instruction declaration consists of the 'op' keyword, an identifier, and a definition in braces. The definition can include memory assignment statements as described in the section on operands above, 'map' statements and an operand declaration. For example:
op add {
  map add_ri : 0x68;	// register direct, immediate
  map add_rr : 0x69;	// register direct, register direct
  map add_rd : 0x6a;	// register direct, register indirect
  map add_rx : 0x6b;	// register direct, indexed
 +0[7:0]=0x68;
  operands myopts;
};
defines the 'add' instruction with the operand set defined by myopts. The statement '+0[7:0]=0x68' defines the base opcode for the instruction and the position to store it in the instruction word. The 'map' statements define map memory values that can be used to map an opcode to a microinstruction address. The left side of the 'map' statement specifies a label in the microprogram. The right side specifies an opcode value which maps to that address. Since the low two bits of the first byte are used to indicate the addressing mode, we need four map statements for each of the four operand combinations. The map declarations shown here will cause the map memory to contain the addresses for the microinstructions add_ri, add_rr, add_rd and add_ri at the addresses 0x68, 0x69, 0x6a and 0x6b in the map memory.

6.5 Writing the Macro-Program

Similar to the micro-program block, the macro-program block is started by the "begin macrocode" specifier and ended by the 'end' specifier. For example:
  begin macrocode @ 0x100
will start a block of macrocode at address 0x100. Unlike other portions of the gmac specification, newlines are significant in the macrocode block and indicate the end of a macroinstruction. A macro program consists of a list of instructions and directives. Any of the macroinstructions defined can be used, and labels can be defined by starting a line with an identifier followed by a ":".

Directive Description
label: .symbol value Defines a label to be a specified value.
.short value, ... Inserts a set of two-byte values at successive memory locations.
.long value, ... Inserts a set of four-byte values at successive memory locations.
.byte value, ... Inserts a set of byte values at successive memory locations. This directive can also take strings denoted by double quotes with each byte of the string being stored at successive locations.
.bss value Reserves value bytes of unallocated memory at the current position. This directive can be used to define uninitialized global variables.
.proc label This directive can be used in conjunction with ".end" to define a procedure. The label specified will be assigned the current address, but any labels defined between the ".proc" and the ".end" directives will be treated as local labels.
.end Ends a procedure definition.


Here is a short example of a macro program:
begin macrocode @ 0x100
ttydata:	.symbol	0x11	// tty data in/out register
ttystatus:	.symbol	0x10	// tty status register
start:
	movw	SP, 0		// Initialize the stack pointer to 0
	movw	FP, 0		// Initialize the frame pointer to 0

	push	msg		// Push msg on the stack.
	call	print		// Call the procedure 'print'
	add	SP,2		// Reset the stack pointer.

sdone:	jmp	sdone		// Hang in an infinate loop.

////////////////////
//	print(s)	Print the string s
//
.proc print
	movw	R1, 4(FP)	// Store the argument value in R1
loop:	movb	R2, (R1)	// Store the byte addressed by R1 in R2
	jeq	done		// If the byte was a zero, jump to 'done'
	movb	(ttydata),R2	// Store the byte in the tty data register
	movb	(ttystatus),#1	// Write a 1 to the tty status register to output the byte
	add	R1, #1		// Add 1 to the R1 register
	jmp	loop		// Jump back to 'loop' to get the next byte.
done:	ret
.end

msg:	.byte	"Hello World.\n", 0
Given the appropriate circuit, mircrocode and macrocode definitions, this program will print the message "Hello World." on a tty and halt by entering an infinite loop


7. Making Virtual Peripheral Devices (VPDs)

Virtual Peripheral Devices (VPDs) are a mechanism whereby new and interesting user-define input/output devices can be created and used by TkGate. They can be used to create devices such as TTY Devices, Vending Machine, Robots and any other device that can be imagined.

TkGate handling of VPDs is designed to be user extensible. Users create a VPD by writing a Tcl/Tk script to define the physical behavior of the device, and a Verilog library file containing a stub module that interacts with the Tcl script. The Tcl/Tk script can create its own GUI through which the user can interact.

7.1 Overview of Virtual Peripheral Device Design

Virtual Peripheral Devices are comprised of a Tcl/Tk script file, and a Verilog stub module. The script file implements the graphical interface for the device and handles user interaction. The Verilog stub module encapsulates the behavior of the device into a module that can be included in user circuits. The VPD Tcl/Tk script files are read both from a read-only directory that is part of the TkGate installation, and from a user-defined list of directories that can be specified through the Library Options dialog box. These script files are read at start-up time, so you must restart TkGate if you change the Tcl/Tk portion of a VPD.

Since a Verilog description can contain multiple instances of a VPD, the Tcl script must be written in such a way so as to allow multiple instances of that interface. This is done by giving each instance a unique instance name. The VPD instance name is typically the same as the Verilog instance name of the stub module for the VPD. The fully instantiated Verilog instance name is dot separated path such as "top.bus1.dm1".

7.1.1 Named Channels

Communication between the Tcl-side and Verilog-side of the VPD implementation is performed through a Verga extension to Verilog called a "named channel". A named channel is basically a queue that has a string identifier. TkGate provides Tcl-side and Verilog-side access the named channels allowing data to be passed through the channel. Named channels can be used both to send data from the Tcl side to the Verilog side, and to send data from the Verilog side to the Tcl side.

7.1.2 Direct Execution of Tcl Commands

It is also possible for the Verilog stub module to execute Tcl commands directly using the $tkg$exec() system task provided by Verga. However, use of the $tkg$exec() is restricted due to the fact that allowing arbitrary Tcl commands implies allowing arbitrary shell commands. This means that untrusted circuit files could result in damage to the user's system when simulated. For this reason, TkGate provides the capability of choosing a security policy to control the use this system task through the Security Options dialog box.

Because of the potential security issues and the fact the a user could choose to use a "high" security policy, it is generally recommended that VPD implementers should avoid use of $tkg$exec() and use only named channels when possible.

7.2 Installing VPDs

To install a VPD named name, you must install both the "name.tcl" file containing the Tcl implementation, and the "name.v" file containing the Verilog stub module. You can place these files either in the TkGate home directory, or in a user defined directory. To place them in the TkGate home directory, put the "name.tcl" file in the "vpd" sub-directory, and the "name.v" file in the "vlib" sub-directory.

To place your VPD files a user defined location, you must set the paths for library and VPD files. Open the Library Options dialog box and add the directory containing your "name.v" file to the "Verilog Library Path", and the directory containing your "name.tcl" file to the "VPD () File Path".

The VPD files will be automatically loaded and registered when TkGate starts. To use a VPD, you should open the library manager and load your VPD.

7.3 The Tcl-Side Interface

The Tcl script for a VPD is responsible for creating a window for the device, responding to user input, and communicating with the Verilog stub module.

7.3.1 Basic Concepts

A VPD script is a normal Tcl/Tk script and can execute any of the commands that are available through Tcl/Tk. However, since it is loaded with the rest of the TkGate interface, certain design guidelines should be followed to prevent conflicts between the VPD and TkGate. A Tcl-Side API is provided to enable communication between the Verilog portion and the script portion. The VPD API is defined in a Tcl/TK name-space called "VPD". As such, all of the API commands have the prefix "VPD::".

VPD scripts should begin by executing the VPD::register command to register the name of the VPD. It may then optionally use the VPD::allow (and VPD::disallow) command(s) to register specific commands that are allowed to be executed from the Verilog side using the $tkg$exec() system task if that interface method is used. The body of the VPD should be defined within a name-space having the same name as the registered VPD name. All VPD functions should be defined in that name-space. At a minimum, each VPD is required to provide a "post" method. The " post" method should take an instance name as its first argument, and may optionally define one or more additional arguments. This results in the following recommended structure for a VPD Tcl script:

VPD::register TTY
VPD::allow TTY::data

namespace eval TTY {
   proc post {name} {
     ...body of post method...
   }

   ...other functions used by TTY VPD...
}

7.3.2 Writing the post Method

The " post" method is generally responsible for taking the following actions:
  • Create the top-level window for the VPD using the VPD::createWindow command. This window will be automatically destroyed when TkGate exists simulation mode. The -shutdowncommand switch can be used with this command to specify a script to execute when the window is destroyed.
  • Build the widgets for the VPD in the top-level window and set up handlers for user actions.
  • Register input and output signals using the VPD::insignal and VPD::outsignal commands.
Note that for some VPDs there may be exceptions to these rules. It is also possible to use Tcl as glue to interface the simulation to a real-world device without using a GUI. For example, one could write a VPD to give a Verilog description the ability to access to the Internet. With such a VPD, the VPD::shutdownnotify command can be used to register a script to execute when the simulation is terminated so as to close any open connections.

An example of a simple post method is given below:

  proc post {name} {
    variable tty_w

    //
    // Create a VPD window.  The title of the window will be "TTY" followed by the name
    // of the instance.  When the simulation is terminated, the TTY::unpost method
    // will be called.
    //
    set tty_w($name) [VPD::createWindow "TTY $name" -shutdowncommand "TTY::unpost $name"]

    ...build GUI...

    VPD::outsignal $name.TD TTY::TD($name)
    VPD::insignal $name.RD -command "TTY::data" -format %d
  }
Since there can be multiple instances of a VPD, it is important to keep state information for each instance separate. This can be done by keeping all such state information in Tcl arrays. For example, the VPD::createWindow method automatically chooses a name for the top-level window in which the GUI will be constructed. Instead of keeping the window id in a flat variable such as "$tty_w", the information should be kept in a variable such as " $tty_w($name)" indexed by the name of the VPD instance, "$name".

The VPD::outsignal and VPD::insignal methods are used to link Tcl variables or commands to named channels. In the above example, the named channel $name.TD (where $name is the instance name) is associated with the Tcl variable TTY::TD($name). Any time a value is assigned to that variable, the value is transmitted to the Verilog side of the VPD over the named channel. The named channel $name.RD is associated with the Tcl command "TTY::data". Any time data from the Verilog side is available on the named channel $name.RD, that command is executed with the data received on the channel. The -format switch can be used to specify the formatting of the data.

7.3.3 Tcl/TK Side API for VPDs

TkGate provides the following Tcl-side API for creating VPDs:

Command Description

VPD::register name Register a new VPD named name. Registering a VPD allows it to be posted using the Verilog $tkg$post() task.
VPD::allow names... Register Tcl commands that can be executed from the Verilog simulation when running TkGate with medium or lower security. The '*' character can be used as a wildcard.
VPD::disallow names... Register Tcl commands for which execution from the Verilog simulation is explicitly disallowed when running TkGate with medium or higher security. The '*' character can be used as a wildcard.
VPD::isallowed name Test a procedure name to see if it can be executed from the Verilog simulation.
VPD::shutdownnotify script Register a script to be executed when TkGate exits simulation mode. The registration is deleted after executing the script.
VPD::createWindow title [options] Create a top-level window that can be used for a VPD and return the name of the window. The window name is automatically generated. Top-level windows created with this command are automatically destroyed when TkGate exits simulation mode. A command to be executed when the simulator shuts down can be specified with the -shutdowncommand option. The shut-down command does any additional cleanup needed by the VPD besides destroying the window.
VPD::outsignal chan var Cause any value assigned to var to be sent to the simulator over the named channel chan. The channel name is typically formed by using the VPD instance name as a prefix and appending a local name with a dot separator. By default, values assigned to var are interpreted as a decimal integer, but Verilog format constants can also be assigned as well. For example, assigning a value of "8'h3f" would cause the value to be interpreted as the 8-bit hexadecimal value '3F'. The association between the channel and the variable is automatically deleted when TkGate exits simulation mode.
VPD::insignal chan [options] Register an action to be taken when data is available on the named channel chan. Channel names are chosen in the same manner as VPD::outsignal. One or more options are usually given with this command. The -command option takes a Tcl command to be executed when data is received on chan. The value received on the channel is appended to the command before execution. The -variable option indicates a variable to be assigned. Additionally, the -format switch indicates the format in which data should be reported. The format is given as a Verilog style format string such as "%d" for decimal or "%h" for hexadecimal. The association between the channel and the variable is automatically deleted when TkGate exits simulation mode.

7.4 The Verilog-Side Interface

The purpose of the Verilog stub module for a VPD is to encapsulate the channel I/O operations between the Tcl side and the Verilog side into a module that can be included and used like as a regular Verilog module within a user circuit. The stub module is usually defined in a library that is included by user's circuit.

7.4.1 Writing the Stub Module

The easiest way to understand how to write a stub module is to look at the following simple example:

(1)   module TTY(..., TD, RD, ...);
(2)   output [7:0] TD;
(3)   reg [7:0] RD;
(4)   input RD;
(5)
(6)     //
(7)     // Execute the TTY::post command to start up the Tcl/Tk interface.
(8)     //
(9)     initial $tkg$post("TTY","%m");
(10)
(11)    ...
(12)
(13)    //
(14)    // Respond to changes in the Tcl/Tk TD variable.
(15)    //
(16)    always #10 TD = $tkg$recv("%m.TD");
(17)
(18)    //
(19)    // Send updated value of RD signal to Tcl/Tk side of VPD.
(20)    //
(21)    always @ (RD) $tkg$send("%m.RD",RD);
(22)
(23)  endmodule
Verilog modules implementing a VPD should invoke the $tkg$post() task in a Verilog "initial" block as shown at Line (9). This will cause each instance of the VPD include in the user design to execute the "TTY::post" method of the Tcl/Tk side as soon as the simulation is started. The $tkg$post() task takes two or more arguments. The first argument is the name of the VPD to be created. The name must be the name of a registered VPD. The second argument is the instance name of the VPD. It must be unique for each instance that VPD. You can use the string "%m", which will be replaced with the name of the module instance in which $tkg$post is invoked. This will guarantee a unique name for the VPD instance. Additional optional arguments of $tkg$post are passed as additional parameters to the VPD::post command.

Signals received from the Tcl side of the VPD can be handled using a Verilog "always" block as shown on Line (16). An "always" block is essentially an infinite loop executed in its own thread. The "\#10" in this example indicates a delay of 10 time units after which the $tkg$recv task will be executed to read data on the named channel "%m.TD". Again, the "%m" is replaced with the name of the current module instance. The naming convention of prefixing each channel name with "%m." ensures that each instance of the TTY device has its own set of named channels. When a value is received on the named channel, it is placed in the Verilog variable TD, which is declared as a register output.

Line 21 is an example of how to transmit a Verilog variable to the Tcl side of the VPD implementation. The "always @(RD)" causes the "$tkg$send" task to be called every time the "RD" variable changes value. This will cause the action associated with that channel by the "VPD::insignal" command to be executed in the Tcl side of the VPD implementation.

The input/output examples at Lines (16) and (21) represent the most simple and direct methods of communication between the Verilog-side and Tcl-side of a VPD implementation. You can also define more complex interactions by encoding some of the communication protocol in Verilog. For example:

 (1)   input CTS;
 (2)   reg output RTS;
 (3)   reg output [7:0] TD;
 (4)
 (5)     always
 (6)       begin
 (7)         @ (negedge CTS);               // Wait for CTS falling edge
 (8)         # 10 TD = $tkg$recv("%m.TD");  // Get the next character data
 (9)         # 10 RTS = 1'b1;               // Indicate that data is ready
 (10)        @ (posedge CTS);               // Wait for peer to acknowledge
 (11)        # 10 RTS = 1'b0;               // Reset the protocol
 (12)      end
This fragment encodes the protocol for receiving data from a TTY as described in the discussion on
Simulation of TTYs. The lines in the "always" block perform the following functions in the protocol: ".
Line Description

(7) Wait for a falling edge of the "CTS" signal. This signal indicates that the peer device is ready to receive data.
(8) Wait 10 epochs, read the next available character from the "%m.TD" channel, and place it in the "TD" variable.
(9) Wait 10 epochs, then assert the "RTS" signal to indicate to the peer that new data is available on the "TD" line.
(10) Wait for the "CTS" line go high, indicating that the peer has read the data from "TD
(11) Wait 10 epochs, then unassert the "RTS" line to reset the protocol for the next character.

7.4.2 Instantiating VPDs

Once the stub module for a VPD has been written, it can then be included in a client Verilog module. For example, the top-level module
module top;
  wire [7:0] TD1, TD2;
  wire [7:0] RD1, RD2;
  ...
  TTY tty1(..., TD1, RD1, ...);
  TTY tty2(..., TD2, RD2, ...);
  ...
endmodule
includes two instances of the TTY VPD. When this Verilog description is simulated, two windows will pop-up with the the titles "TTY top.dm1" and "TTY top.dm2". The names "top.dm1" and "top.dm2" are the instance names of the VPDs (and of their module instances). Each instance can be connected to a separate set of input and output signals.

7.4.3 Verilog Side API for VPDs

TaskDescription
$tkg$exec(arg1, arg2,...) Constructs a string for a Tcl command and sends an execution request from the simulation to the main TkGate executable. The string is constructed similar to the Verilog $display() task (which is in turn similar to the C printf() function). The Tcl command is executed asynchronously with $tkg$exec() not waiting for the command to complete. A "%m" in any string argument will be substituted with the name of the current instance. Use of this task in VPDs is discouraged since it may be disabled through the security settings. $tkg$post(vpdname, instname, [arg1, ...]) Post an instance of the VPD named "vpdname" by invoking the command

    vpdname::post instname

in the Tcl portion of the VPD. If any arg parameters are specified, they will be passed as additional parameters to the "vpdname::post" function after the instname parameter. Any "[" and "]" characters are treated as ordinary characters. The "vpdname::post" command is executed asynchronously, with $tkg$post returning immediately. A "%m" in any parameter will be substituted with the name of the current instance. In most cases, "%m" should be passed as the instname. $tkg$send(name, data) Send data on the named channel name. If being used to send data to the Tcl side of a VPD, the channel name should correspond to a channel name specified in a VPD::insignal command. The transmitted data will cause either a Tcl variable to be set or a Tcl command to be invoked. A "%m" used in the channel name will be substituted with the name of the current instance. This can be used to create a compound name such as "%m.RD" which will be unique to the instance. data = $tkg$recv(name) Returns data received on the named channel name. If being used to receive data from the Tcl side of a VPD, the channel name should correspond to a channel name specified in a VPD::outsignal command. When the variable declared in the VPD::outsignal command is set, the value of that data, interpreted as a decimal value, will be available to be read by this task. The task will block if there is no data in the channel. A "%m" can be used in the channel name will be substituted with the name of the current instance. This can be used to create a compound name such as "%m.TD" which will be unique to the instance.


8. Setting TkGate Options

TKGate has several user configurable parameters that can be set by selecting "Options..." under the "File" menu. The options are divided into nine main tabs. Option settings are persistent, being saved in the file ".tkgate2-preferences" in the user's home directory which is read whenever starting TkGate.

8.1 General Options


These options control the general behavior of TkGate. The options that can be set through this page are:

8.2 Interface Options


These options control features of the TkGate interface. The options that can be set through this page are:

8.3 Toolbar Options


These options control which toolbars will be available. The options that can be set through this page are:

8.4 HDL Options


These options control the behavior of TkGate when editing text HDL modules. The options that can be set through this page are:

8.5 Print Options


These options control the default printing behavior of TkGate. The options that can be set through this page are:

8.6 Simulate Options


These options control the simulation behavior of TkGate. The options that can be set through this page are:

8.7 Library Options


These options control where TkGate looks for libraries and which libraries should be automatically loaded on start-up. The options that can be set through this page are:

8.8 Security Options


These options control simulator features such as VPDs (Virtual Peripheral Devices) that have potential security implications. Set these carefully when simulating an unknown or untrusted circuit.

8.9 Color Options


These options control the colors used for various elements in the TkGate interface. The options that can be set through this page are:

8.10 HTML Options


These options control what TkGate should do when the user clicks on certain hyperlinks in their circuit.

9. TkGate Library Components

The TkGate distribution includes four libraries that can be loaded through the library manager. Components from these libraries can be used to add additional functionality to your designs. To use one of the libraries, open the library manager with the toolbar button and select the library you wish to load. You can then locate the module you wish to use in the module hierarchy and drag it onto the main window canvas to create an instance of it. The libraries that are available with TkGate are described in the following sections.

9.1 TTY




Figure 9.2: TTY Device Window
The TTY library, tty provides a Virtual Peripheral Device (VPD) modeling a TTY device. The TTY supports input and output and models an interactive TTY terminal. You can use it in your design to provide textual interaction with the user. When you start the simulator, one TTY window will appear for each instance of a TTY VPD in your design. Your design can send characters to and receive characters from the TTY device.

To use the TTY device, open the library manager with the toolbar button and load the "tty" library. You can then locate the module in the module list or module hierarchy and drag it onto the main window canvas to create an instance of it. Figure 9.1 shows the external view of the TTY instance which is defined with a symbolic module interface.

When a design containing the TTY device is simulated, it will open a window such as the one shown in Figure 9.2 (after the design has been running for a while). The text shown in this window was transmitted from the "Menagerie CPU" example. Keys pressed while the window has focus will be transmitted back to the controlling design.

To send a character to a TTY device, use the following protocol (see Figure 9.3):
  • Unassert the DSR line.
  • Wait for the DTR (Data Terminal Ready) line to be low.
  • Drive the RD line with the ASCII code of the character to be transmitted.
  • Assert the DSR (Data Set Ready) line.
  • Wait for the DTR line to go high.
  • Unassert the DSR line

To receive a character from the TTY device, use the following protocol(see Figure 9.4):
  • Unassert the CTS line.
  • Wait for the RTS line to go high.
  • Read the received value on the TD line.
  • Assert the CTS line.
  • Wait for the RTS line to go low.
  • Unassert the CTS line.

Note that transmitting a character to the TTY uses "RD" and receiving a character from the TTY uses "TD" because the RD and TD are named from the perspective of the TTY device.

9.1.1 Forcing TTY Characters from Simulation Scripts

You can use a simulation script to force input into a TTY device. This is done by using a special named channel "instaname.FORCE" where "instaname" is the fully instantiated instance name of the TTY. You can use the
$tkg$send system task to send the string to be forced. For example, to force the input "Hello World" into the TTY named "main.memory.tty0", you could use the simulation script:
  initial
    begin
      # 10000 ;
      $tkg$send("main.memory.tty0.FORCE","Hello World");
  end

9.2 Drink Vending Machine



Figure 9.5: Drink Machine Device




Figure 9.6: Drink Machine GUI
The drink vending machine library, "coke", provides a Virtual Peripheral Device (VPD) modeling a drink vending machine. The device provides input and output signals for the actuators and sensors in the virtual vending machine. You can use this device to design a vending machine circuit that controls an interactive model of a vending machine.

To use the drink machine device, open the library manager with the toolbar button and load the "coke" library. You can then locate the cokemachine module in the module hierarchy and drag it onto the main window canvas to create an instance of it. Figure 9.5 shows the external view of the cokemachine instance.

The virtual drink vending window that appears when you start the simulation is shown in Figure 9.6. It is comprised of an external view (left side) and an internal view (right side). The external view includes buttons that can be pressed to make a drink selection, a coin slot, and a bill reader.

The following interactions can be made through the drink machine GUI:

The internal view shows the columns of drinks that are available in the machine, the status of the bill scanner, the coins that have been inserted (but not used for a purchase), the coins that have been committed to a purchase, and coins that are available to make change.

The input signals on the drink machine and their function are as follows:

Signal Description

DISP Dispense - Each of the six bits dispenses a drink from one of the six columns. Drinks are dispensed on the rising edge of the signal. Only one bit should be asserted at a time, and only after ensuring the the DISPACK (Dispense Acknowledge) signal is low. The asserted bit should remain high until the DISPACK signal goes high.
ELIGHT Empty Light - Each bit corresponds to the empty light on each of the six buttons. If a bit is asserted, then the corresponding empty light will be illuminated.
CHGQ
CHGD
CHGN
Dispense Change - Drop a quarter (CHGQ), dime (CHGD) or nickel (CHGN) from the "Change" bin. Only one of these signals (or the CHGCOM or RETINS signals) may be asserted at a time. Change is dropped on the rising edge of the signal. The signal should not be raised unless the CHGACK (Change Acknowledge) signals is low. The Dispense Change signal should be held high until the CHGACK signal goes high.
NOCHG No Change - If asserted, illuminates the "No Change" light on the vending machine to indicate that change can not be made.
BILLIN Bill In - Drives the bill motor to feed the inserted bill into the bill tester. Bills in the bill tester will be dropped into the "Collected" bin. The motor is activated on a positive edge of this signal. This signal should not be raised unless the BILLACK (Bill Acknowledge) signal is low. The Bill In signal should be held high until the BILLACK signal goes high.
BILLLT Bill Light - This signal illuminates the light on the bill reader.
BILLOUT Bill Out - Drives the bill motor to feed out the bill currently in the bill tester. The motor is activated on a positive edge of this signal. This signal should not be raised unless the BILLACK (Bill Acknowledge) signal is low. The Bill Out signal should be held high until the BILLACK signal goes high.
CHGCOM Change Commit - This signal commits any change in the "Inserted" area to the "Collected" area. Change is committed on the rising edge of this signal. The signal should not be raised unless the CHGACK (Change Acknowledge) signals is low. The Dispense Change signal should be held high until the CHGACK signal goes high.
RETINS Coin Return - This signal returns any change in the "Inserted" area back to the user. Change is returned on the rising edge of this signal. The signal should not be raised unless the CHGACK (Change Acknowledge) signals is low. The Dispense Change signal should be held high until the CHGACK signal goes high.
INSACK Insert Acknowledge - This signal should be raised after seeing a positive edge on the INSQ, INSD or INSN signals indicating that a coin was inserted.
COST Drink Cost - This signal drives the drink cost displayed on the external view of the vending machine. It should be the cost in nickels.
RESET Reset - Resets the drink machine. This is an active low signal. It should be driven low to reset the machine, then kept high while using the device.

The output signals on the drink machine and their function are as follows:

Signal Description

PRESS Button Pressed - Each bit is for one of the six buttons on the drink machine and is asserted while the corresponding button is pressed.
EMPTY Empty Sensor - Each bit corresponds to one of the drink columns and is asserted when the sensor indicates that there are no more drinks in a column.
INSQ
INSD
INSN
Coin Inserted - These signals indicate that a quarter (INSQ), dime (INSD) or nickel (INSN) have been inserted. A rising edge indicates the insertion. The controlling circuit should assert the INSACK signal to acknowledge the insertion.
NUMQ
NUMD
NUMN
Change Available - Indicates the number of quarters (NUMQ), dimes (NUMD) and nickels (NUMN) available for making change (up to 7). If there are more than 7 coins of a type available, then the signal will indicate 7.
BILLSNS Bill Sense - A high value indicates that a bill is sensed in the bill insertion slot. The controlling circuit should assert BILLIN to feed the bill into the bill tester
BILLOK Bill OK - A high value indicates that the bill in the bill tester is a valid bill.
CNRET Coin Return Request - A high value indicates that the user is pressing the coin return lever.
BILLACK Bill Acknowledge - This signal is asserted to acknowledge a BILLIN or BILLOUT signal.
CHGACK Change Acknowledge - This signal is asserted to acknowledge a CHGQ, CHGD, CHGN, CHGCOM or RETINS signal.
DISPACK Dispense Acknowledge - This signal is asserted to acknowledge a DISP signal.
BILLNG Bill No Good - This signal is asserted to indicate that a fake bill is in the bill tester.

9.3 Timers and One-Shots

Timers and one-shots are devices that operate in real time. To use a timer or one-shot device, open the library manager with the toolbar button and load the "timer" library. You can then locate the desired device in the module list or module hierarchy and drag it onto the main window canvas to create an instance of it.

9.3.1 OSCILLATOR Device


Figure 9.7:
Oscillator
The OSCILLATOR device provides a signal that oscillates at a set frequency in real-world time. You can use it to design circuits such as digital clocks using the LED gates. The default frequency is one hertz. To change the frequency, double click on the OSCILLATOR module to open the
Gate Properties dialog box and select the "Parameters" tab. Set the "HZ" parameter to the desired period in milliseconds.

9.3.2 ONESHOT Device


Figure 9.8:
One Shot
A one-shot device asserts its output a set time period after being reset by the input. The ONESHOT is triggered by an active low signal. You can connect the input to ground, to cause the ONESHOT device to assert its output after a set amount of real-world time after the simulation begins. Raising the input and lowering it again resets the one-shot. The default delay is one second. To change the time delay of the ONESHOT, double click on it to open the Gate Properties dialog box and select the "Parameters" tab. Set the "HZ" parameter to the desired delay in milliseconds.

9.4 TTL Devices

The TTL device library provides access to a small number of commonly used 7400 series TTL modules. To use this library, open the library manager with the toolbar button and load the "74xx" library. You can then locate the desired device in the module list or module hierarchy and drag it onto the main window canvas to create an instance of it. The supported devices are:

Device Description

7400 Quad 2-input NAND gates
7402 Quad 2-input NOR gates
7404 Hex Inverters
7408 Quad 2-input AND gates
7410 Tri 3-input NAND gates
7430 8-input NAND gate
7432 Quad 2-input OR gate
7474 Dual D-Latch, edge triggered with clear and preset
7486 Quad 2-input XOR gates
74154 4-16 Decoder
74157 Quad 2-1 Mux
74163 4-bit Synchronous Counter
74175 Quad D-Latch, edge triggered with clear

Please see standard TTL data sheets for a description of these devices.


A. List of Built-In Gate Types

Name Keywords(s) Shortcut(s) Symbol Description
AND and, nand
a
A
Outputs the logical AND of the input signals.  All of the inputs must be have either the same bit width as the output, or be single bit.  In the case of a gate with mixed multi- and single-bit inputs, the single-bit inputs are assumed to be connected to across all bits of the multi-bit signals.
OR or, nor
o
O
Outputs the logical OR of the input signals.  All of the inputs must be have either the same bit width as the output, or be single bit.  In the case of a gate with mixed multi- and single-bit inputs, the single-bit inputs are replicated to all of the other bits.
XOR xor, xnor
x
X
Outputs the logical XOR of the input signals.  All of the inputs must be have either the same bit width as the output, or be single bit.  In the case of a gate with mixed multi- and single-bit inputs, the single-bit inputs are replicated to all of the other bits.
Buffer buf, not
b
I
Buffers the input signal.  If the input signal is a value other than logic-1 or logic-0 (e.g., floating) the output will be the unknown signal.  The inputs and outputs must have the same bit width.
Tri-State Buffer bufif1, bufif0,
notif1,notif0
t
Outputs a buffered signal when the enable line (top) is logic-1.  Note that an inverter placed on the output of a tri-state buffer is not equivalent to connecting the output to a separate inverter element.  With an inverted output, a logic-0 on the enable line will result in floating output (i.e., Verilog notif behavior).  The input and output must have the same bit width, and the enable line must be single bit.
Reduction Gates and, nand,
or, nor,
xor, xnor
Ctl-r a Ctl-r A
Ctl-r o Ctl-r O
Ctl-r x Ctl-r X
Outputs the logical AND of all of the bits on the input.  Reduction gates for OR and XOR are also available.  The output must be single-bit.
Constants supply1, supply0
g v
Outputs a logic-1 or logic-0.  There are no bit-width restrictions.
Switch switch
s
Outputs a single-bit logic value according to the switch setting.  Clicking on a switch while in simulation mode will toggle its state.  The output must be single-bit.
Dip Switch dip
d
Outputs a multi-bit signal according to the switch setting.  Clicking on a dip switch while in simulation mode will enable a hex value for the switch to be entered.
Tty tty
d
Tty gates can be used to model an interactive terminal.  Your circuit can send characters to be displayed on the tty, or receive characters that were typed in the tty.  See the section on ttys in the simulator manual for details on how to use ttys.
Clock clock
c
Outputs a clock signal with a specified pulse-width (f), phase (p), and duty width(dw).  The output must be single bit.
Concat concat
w
Combines multiple wires into a single multi-bit wire.  The sum of the bits on the left side must add up to the bit width of the right side.  The current implementation has a limitation that all signals must flow in the same direction.  That is, you must put all of the driving gates on either the left or right.
Wire Tap n/a
n/a
Pulls off a sub-range of a multi-bit wire.  This gate type is not created through the menus like most other gates, but by dropping the end of a wire on a wire of a larger bit-width.  You can use the "Properties..." option from the "Gate" menu to select the bit range to be extracted.  This gate can only be used to "read" the value of a wire, and cannot be used to "wire" a value on a multi-bit bus.
Adder add
+
Outputs the sum of the inputs.  The inputs must have the same bit width as the output, and the carry-in and -out must be single bit.  The carry-in line is indicated by the dot.
Divider div
/
Outputs the quotient and remainder of the inputs.  The left input is the dividend, and the right input is the divisor.  There are no bit-width restrictions, but if the result does not fit in the supplied bit-width, it may be truncated.
Multiplier mult
*
Outputs the product of the inputs.  There are no bit-width restrictions, but if the result does not fit in the supplied bit-width, it may be truncated.
Multiplexer mux
m
Outputs the input selected by the select line.  The bit-width of the inputs must match the bit-width of the output, and the bit-width of the select line must be the ceiling of the base-2 log of the number of input lines.
Decoder demux
D 2
Outputs the input selected by the select line.  The bit-width of the inputs must match the bit-width of the output, and the bit-width of the select line must be the ceiling of the base-2 log of the number of input lines.
Left Shift lshift
S L
Performs a logical shift-left of the input.  The input and output must have the same bit width, and the bit-width of the shift select line must be the ceiling of the base-2 log of the input/output bit-width.
Right Shift rshift
S R
Performs a logical shift-right of the input.  The input and output must have the same bit width, and the bit-width of the shift select line must be the ceiling of the base-2 log of the input/output bit-width.
Arithmetic Shift arshift
S A
Performs an arithmetic shift-right of the input.  The input and output must have the same bit width, and the bit-width of the shift select line must be the ceiling of the base-2 log of the input/output bit-width.
Roll roll
S O
Rolls the input bits to the left.  The input and output must have the same bit width, and the bit-width of the shift select line must be the ceiling of the base-2 log of the input/output bit-width.
Register register
r
Outputs the current value of the register, and stores the input value on a positive edge on the clock line. The data value is only loaded when the active-low enable (EN) line is asserted. The register can be cleared asynchronously with the active-low clear (CL) line. The input and output must have the same bit width, and the clock, EN and CL lines must be single-bit.
Flip-Flop ff
f
Outputs the current value and inverted value of the flip-flop, and stores the input value on a positive edge on the clock line. The data value is only loaded when the active-low enable (E) line is asserted. The flip-flop can be cleared asynchronously with the active-low clear (C) line. The input and output must have the same bit width, and the clock, E and C lines must be single-bit.
RAM ram
R
When the chip-select line (CS) and output enable (OE) lines are low, the memory value addressed by the address line (A) is output to the data line (D).  When the chip select line and the write enable line (WE) are low, the value present on the data line is stored in the memory at the address specified by the address line.  The control lines, CS, OE, WE must be single-bit.  The address and data lines must be no more than 32 bits.  RAMs can be initialized from a file at simulation time. 
ROM rom
u
When the output enable line (OE) is low, the memory value addressed by the address line (A) is output to the data line (D).  The output enable line must be single bit, and the address and data lines must be no more than 32 bits.  ROMs can be initialized from a file at simulation time. 
NMOS Trans. nmos
Ctl-t n
Implements a Verilog-style NMOS element.  All signals must have the same bit width.  NMOS elements with multi-bit inputs and outputs are treated as parallel transistors.
PMOS Trans. pmos
Ctl-t p
Implements a Verilog-style PMOS element.  All signals must have the same bit width.  PMOS elements with multi-bit inputs and outputs are treated as parallel transistors.
LED led
l L 8
 
L s L h
 
L d
LED Indicator elements which can display the values of signals in your circuit. The different types of LED are indicated by a property flag of the basic LED circuit element. The types from upper left are: bit, bar graph, direct, hexadecimal and decimal. The number of digits or bars is automatically determined from the size of the input wire. For the three types of 7-segment LEDs, the interpretation of the input data depends on the type. For direct LEDs, each 7-bits of the input signal directly control one segment of the LED numbered from top-to-bottom, left-to-right. For hexadecimal LEDs, each four bit group controls one digit. For decimal LEDs, the value of the input signal is displayed as an unsigned decimal value.
Module name
M
Implements a user-defined module.  An arbitrary number of inputs, outputs and inout ports can be used with no restrictions on bit width.  The name of the module is used in the save file and cannot be the same as any of the built-in gates.
Comment comment
C
Comments can be used to place embed text into a circuit diagram. Comments have no effect on the behavior of circuits.
Frame frame
F
Frames can be used to create visual segmentation of a circuit diagram. Frames have no effect on the behavior of circuits.

B. List of System Tasks

B.1 Data Display Tasks

The system tasks described in this section generate output to the console. When simulating through the TkGate interface, the output will go to the simulator output console. When simulating in stand-alone mode with "Verga", output will go to the standard output.

B.1.1 $display

Usage: $display(arg1, arg2, ... );

This tasks outputs a message to the standard output when running Verga in standalone mode, and to the simulation console when running under TkGate. A newline is appended to the output. It is similar to the printf() function used in C.

Unlike the C printf function, the first argument is not necessarily a control string. The $display function can have from zero up to an arbitrary number of control strings. For example:

     integer r1,r2;

     r1 = 7; r2 = 12;
     $display(r1,r2);
will print:
7 12
The same output could have been produced with a control string using the statement:
    $display("%d %d",r1,r2);
The general rule is that if an argument is a string, then it is treated as a control string and consumes as many arguments after it as there are conversions (e.g., "%d", etc.). If an argument is not a string, its value is printed. The format in which it is printed depends on the variable type. Unsized values are printed in decimal, sized values are printed using the Verilog hexadecimal notation. For example:
     integer r1;
     reg [16:0] r2;

     r1 = 7; r2 = 12;
     $display(r1,r2);
will print
7 16'hc
When printing constants and expressions, $display will use the base specified in any constants. For example $display(1'o0+r2) would produce 16'o14.

You can get greater control of the output using control strings with conversions. Strings are enclosed by the double quote character ("). Use a backslash (\) before a double quote to output the double quote itself and use \\ to output a single backslash character. In addition, you can use "\n" to insert extra newlines into the output.

The conversions supported in Verga are (case is ignored):

Conversion Description

%% Output a single percent character.
%m Output the name of the current module instance.
%t Output a simulation time value. The raw simulation time in epochs is converted to the units specified in any `timescale declaration for the current module. If a precision smaller than the units is specified, then the time value will be output with a decimal point and the appropriate number of digits after the decimal point.
%d Output a value in decimal format.
%b Output a value in binary format.
%h Output a value in hexadecimal format.
%o Output a value in octal format.
%c Output the character with the specified ASCII value.
%s Output the value as a string.
%f Output the value in floating point format.
%g Output the value in floating point format while minimizing the number of characters needed.

The binary, hexadecimal and octal conversions output the raw number without the leading radix and bit-size indication. For example,
   $display("%h",8'h1e);
will output only "1e".

Like the C printf(), you can specify a number of spaces with the conversion. For example %4h will output a hexadecimal value in a four character field, padding with spaces if necessary. If you use leading zero as in %04h, then Verga will pad the output with zeros if necessary (e.g., "16'h2e" will be output as "002e").

As a final example including multiple control strings, consider the statement:

   $display(9," a=%d b=%d ",42, 77, 12,13," c=%d d=%d ",5,9, 88);
This will produce:
9 a=42 b=77 12 13 c=6 d=9 88

B.1.2 $monitor

Usage: $monitor(arg1, arg2, ... );

The $monitor task formats the output from its arguments in exactly the same way as the $display task. The difference is that instead of outputting the text immediately, it places a watch on all variables referenced in the statement and outputs its message any time a variable changes. This can be used to generate a trace of any variable changes.

As an example, consider this module:

   module top;

     reg [15:0] a,b,c;

     initial
       $monitor("%t: a=16'h%04h  b=16'h%04h  c=16'h%04h",$time,a,b,c);

     initial
       begin
         #1 a = 'h4ef; b = 'h6def;
         #1 c = 'h84ff;
         #5 b = 42;

       end

   endmodule
When simulated, it will produce the following output:
1: a=16'h04ef  b=16'h6def  c=16'hxxxx
2: a=16'h04ef  b=16'h6def  c=16'h84ff
7: a=16'h04ef  b=16'h002a  c=16'h84ff
Each time any of the variables a, b or c change, the $monitor task will output the stored line. In this example, we set a and b after one time step. The value of c was printed as 16'hxxxx because it is still uninitialized. One time step later, we set c, then five time steps later we set b. This results in output from $monitor at times steps 1, 2 and 7.

Only one $monitor may be active at a time. If you call $monitor more than once, only the last call will remain in effect.

B.1.3 $monitoroff

Usage: $monitoroff;

This task temporarily disables a $monitor that has been placed. The monitor remains registered, but all output from it is disabled.

B.1.4 $monitoron

Usage: $monitoron;

This task re-enables a $monitor that has been disabled by $monitoroff.

B.1.5 $strobe

Usage: $strobe(arg1, arg2, ... );

The $strobe task formats the output from its arguments in exactly the same way as the $display task. The primary difference is that instead of outputting the message immediately, it waits until the end of the time step and does the output with the values at that time. It can be used when you want to ensure that you are outputting the final value of a variable for the time step. As an example, consider the module:

 (1)    module top;
 (2)     reg [15:0] a, b, c;
 (3)
 (4)     initial
 (5)       begin
 (6)         a = 4; b = 7; c = a + b;
 (7)         $strobe("%t: strobe a=%d b=%d c=%d",$time,a,b,c);
 (8)         $display("%t: display a=%d b=%d c=%d",$time,a,b,c);
 (9)         a = c*b;
 (10)        c = a + 9;
 (11)        #1 b = a + 99;
 (12)      end
 (13)
 (14)  endmodule
when this module is simulated, the following output is produced:
0: display a=4 b=7 c=11
0: strobe a=77 b=7 c=86
This $display tasks output the values as they are when $display is called, but $strobe waits until the end of the time step at Line 11 before it generates its output.

B.1.6 $write

Usage: $write(arg1, arg2, ... );

The $strobe task formats the output from its arguments in exactly the same way as the $display task. The only difference is that it does not append a newline. Use this task when you want to use multiple $write statements to construct a line of output. You can use a \n in a string to generate a newline.

B.2 File Tasks

The tasks described in this section allow Verilog descriptions to open and write to files.

B.2.1 $fopen

Usage: descriptor = $fopen(file-name);

The $open task opens the specified file and returns a descriptor. The descriptor should be declared as an integer. Files are always open for writing, so any file you specify will be overwritten. You may have at most 31 files open at once. Below is an example, of how to use $open to open a file.

  integer d;

  initial
    begin
      d = $fopen("myfile.log");
      ...
You may use the bit-wise OR operator "|" to combine two or more descriptors. This will cause an write operations on that descriptor to output to all of the files combined in this way. You may also OR in the value "1" to include output to the console. For example, given the statements:
    d1 = $fopen("myfile1.log");
    d2 = $fopen("myfile2.log");
    d = d1 | d2 | 1;
writing to d will cause that output to be written to myfile1.log, myfile2.log and the standard output.

B.2.2 $fclose

Usage: $fclose(descriptor);

Closes the file or files referenced by the descriptor. If descriptor has been formed by ORing together two or more descriptors, all of the referenced files will be closed.

B.2.3 $fdisplay, $fmonitor, $fstrobe and $fwrite

Usage: $fdisplay(descriptor, arg1, arg2, ... );
Usage: $fmonitor(descriptor, arg1, arg2, ... );
Usage: $fstrobe(descriptor, arg1, arg2, ... );
Usage: $write(descriptor, arg1, arg2, ... );

These tasks operate in exactly the same way as their counter-parts without the leading "f", except that they take a descriptor as the first argument and write their output to the file (or files) referenced by that descriptor.

B.3 Timing Check Tasks

The timing check tasks may only be used in the context of a specify...endspecify block. They are described briefly here, but more information on their use can be found in Section 4.10.3 Constraint Tasks.

B.3.1 $hold

Usage: $hold( clock, data, limit );

The $hold task generates a warning message when a hold-time constraint is not satisfied. clock specifies a clock event after which a data line must be held constant. data specifies the data signal that must not change for the hold period, and limit specifies the time periods for which changes in data are forbidden. The $hold task may only be used in a specify block.

B.3.2 $setup

Usage: $setup(data, clock, limit );

The $setup task generates a warning message when a setup-time constraint is not satisfied. data specifies the data signal that must not change for the setup period. clock specifies a clock event that ends the setup period, and limit specifies the length of the setup period. If the clock event occurs less than limit time steps from the last change of data, a warning message is generated. The $setup task may only be used in a specify block.

B.3.3 $width

Usage: $width( event, limit );

The $width task generates a warning message when a pulse width is shorter than a specified limit. event specifies the the leading edge of the pulse that is to be tested. Use the posedge or negedge keyword to indicate which edge is to be tested. limit is the minimum allowable pulse width. If the opposite transition is detected in fewer than limit time steps, then warning message will be generated. The $width task may only be used in a specify block.

B.4 Simulation Information/Control and Miscellaneous Tasks

The tasks described in this section can be used to control the simulation, get information about the simulation, and do other miscellaneous tasks.

B.4.1 $finish

Usage: $finish;

This task terminates the simulation. If called while running Verga in stand-along mode, Verga will terminate immediately. If called from a description running under the TkGate, the simulator will terminate, and TkGate will revert to edit mode.

B.4.2 $stop

Usage: $stop;

This task suspends the simulation and puts it into pause mode. If called while running Verga in stand-along mode, it will have the same effect as $finish, terminating the simulation immediately. If called from a description running under the TkGate, the simulator will enter paused mode allowing you to examine the values of variables before unpausing the simulation.

B.4.3 $time

Usage: variable = $time;

This task returns the current simulation time. When assigning it to a variable, that variable should be declared as a time variable. This is normally a 64-bit integer. One of the most useful places to use $time is in conjunction with a $display or $monitor task to show the simulation time at which the output is produced. When using $time in this way, you should use the %t conversion in the control string.

B.4.4 $stime

Usage: variable = $stime;

This task returns the low 32 bits of the current simulation time. You may assign it to an integer variable.

B.4.5 $random

Usage: variable = $random;
Usage: $random(seed);

When called without an argument, this task returns a random number. When called with an argument, that argument is used as the seed value for the random number generator.

B.5 Memory Tasks

These tasks can be used to save and load the contents of a memory to or from a file.

B.5.1 $readmemb and $readmemh

Usage: $readmemb(file);
Usage: $readmemb(file, memory);
Usage: $readmemb(file, memory, start);
Usage: $readmemb(file, memory, start, stop);

Usage: $readmemh(file);
Usage: $readmemh(file, memory);
Usage: $readmemh(file, memory, start);
Usage: $readmemh(file, memory, start, stop);

These tasks initialize one or more memories in a design from the contents of a file. The forms ending in "b" assume binary formatted data, while the forms ending in "h" assume hexadecimal formatted data. If only a file name is specified, then the name(s) of the memories to be initialized must be encoded in the file. If a memory is specified, than the file is loaded into that memory. You may also specify a start and stop address.

The file should contain a space separated list of values for the memory. The unknown (x) and floating (z) values may also be used in one or more bits. There are also several special control lines beginning with "@" that can be used to specify the memory, the address, or the radix of the file. An example of a memory file is shown below:

@memory top.m
@0
3 4 9 16 25
36 49 64 81

@C
100 x x 99 z 12 x8 z12

@100
4 3 9x x x11

@1000000
12 13 14 z 19 20 x x x 30 31
The @memory top.m specifies the name of the memory to be loaded. It should be a fully qualified path name. The @0 line indicates that the following data is to be load beginning at address 0, the @C line indicates data to be loaded beginning at address hexadecimal C, and so forth. You can also use a line such as:
@radix 8
To change the radix for data in the file to binary (2), octal (8), decimal (10) or hexadecimal (16).

B.5.2 $writememb and $writememh

Usage: $writememb(file);
Usage: $writememb(file, memory);
Usage: $writememb(file, memory, start);
Usage: $writememb(file, memory, start, stop);

Usage: $writememh(file);
Usage: $writememh(file, memory);
Usage: $writememh(file, memory, start);
Usage: $writememh(file, memory, start, stop);

These tasks write the contents of one or more memories to a file. The forms ending in "b" write the data in binary, while the forms ending in "h" write the data in hexadecimal. The files written are of a format suitable for being read by the $readmemb and $readmemh tasks. If only a file name is given, the contents of all memories in the design will be written. If a memory name is given, only the contents of that memory will be saved. You can also specify a start and stop address to save a portion of a memory. If there are large regions of uninitialized values in a memory, those values will not be explicitly saved. Only memory pages in which data has been stored will be saved to the file.

B.6 TkGate-Specific Tasks

These tasks are tasks that are not part of the normal Verilog specification, but are specific to TkGate descriptions. These tasks all begin with the $tkg prefix. Most of these tasks are only useful when running a simulation under the TkGate GUI.

B.6.1 $tkg$command

Usage: $tkg$command(arg1, arg2, ... );

When the simulator is running under the TkGate GUI, TkGate and the simulator communicate over a pipe. This task writes a command from the simulator to TkGate directly on that pipe. The arguments are combined into a string in the same way as for $display, and the resulting string is feed directly to the TkGate GUI. When running Verga directly, this task performs identically to $display.

This task is subject to security settings set through the TkGate Security Options dialog box.

B.6.2 $tkg$exec

Usage: $tkg$exec(arg1, arg2, ... );

This task executes a Tcl/Tk command in the context of the TkGate GUI. It can be used to control a Virtual Peripheral Device (VPD), although its use for this purpose is currently discouraged. The $tkg$post, $tkg$send and $tkg$recv tasks are now the preferred tasks to use for interacting with VPDs.

When executing this task, the arguments are combined into a string in the same way as for $display, and the resulting string is sent to the TkGate GUI to be executed as a Tcl/Tk command. The command will only be executed if it is enabled through the TkGate Security Options dialog box. There are three possible security settings: fully enabled, enabled for registered functions, and disabled. When enabled only for registered functions, only Tcl commands that have been registered by a VPD on loading can be executed. Furthermore, the "[" and "]" characters are not allowed in the string. For more information on how to use this task in designing VPDs, see the section on Creating Virtual Peripheral Devices.

If running Verga directly, this task will simply print the simulator command requesting a Tcl/Tk command to be executed.

B.6.3 $tkg$post

Usage: $tkg$post(name, id);

This task is used to start/post a Virtual Peripheral Device (VPD). The name argument specifies the name of the VPD. The id argument specifies the name of a unique identifier for this instance of the VPD. Normally, the string "%m" should be specified as the id. The "%m" will be replaced with the instance name of the calling module in the same way that "%m" is expanded in the $display task. For more information on how to use this task in designing VPDs, see the section on Creating Virtual Peripheral Devices.

If running Verga directly, this task will simply print the simulator command requesting a VPD to be posted.

B.6.4 $tkg$probe

Usage: $tkg$probe(signal1, signal2, ...);

This task is used to set a probe on a signal or signals. You can list an arbitrary number of signals specified either with their local names, or with fully qualified path names. This task will cause the TkGate GUI to display the probed signals in the logic analyzer window and begin displaying value changes on those signals. You can use this task in a simulator script to setup the signals you want to probe at the beginning of a simulation.

If running Verga directly, this task will simply print the simulator command requesting a probe to be set, and print value changes on that signal until the probe is removed with and $unprobe.

If running Verga directly, this task will simply print the simulator command setting a probe and reporting the values on the probed signals.

B.6.5 $tkg$recv

Usage: value = $tkg$recv(channel);

The $tkg$recv task is used receive value on a channel from a Virtual Peripheral Device (VPD). A channel is essentially a queue that can be read to or written from either from the Tcl/Tk side of a VPD, or the Verilog side. The $tkg$recv task dequeues and returns the next value in the queue. If the queue is empty, the task blocks until a value is placed on the queue.

The channel name is an arbitrary string. Channels are dynamically created when referenced by a $tkg$recv or $tkg$send task. When used for communicating with a VPD, the channel name should be constructed from the VPD instance name (the same name used as the id in $tkg$post), followed by a "." character and a local name. This convention ensures that each instance of a VPD has its own set of channels to use. You can use "%m" in place of the instance name which will be replaced with the instance name for the current module.

For more information on how to use this task in designing VPDs, see the section on Creating Virtual Peripheral Devices.

While this task is primarily intended to be used for communication with VPDs, you can use it in conjunction with the $tkg$send task to send values from one Verilog thread to another.

B.6.6 $tkg$reportbreak

Usage: $tkg$reportbreak(id, value);

This task pauses the simulation and sends a command indicating that a breakpoint has fired. id is the name of the breakpoint, and value is the value that has caused the break. This task is primarily used for TkGate testing purposes.

B.6.7 $tkg$send

Usage: $tkg$send(channel, value);

The $tkg$send task is used send a value on a channel from the Verilog description to a Virtual Peripheral Device (VPD). A channel is essentially a queue that can be read to or written from either from the Tcl/Tk side of a VPD, or the Verilog side. The $tkg$send task enqueues a value on the queue for the specified channel.

The channel name is an arbitrary string. Channels are dynamically created when referenced by a $tkg$recv or $tkg$send task. When used for communicating with a VPD, the channel name should be constructed from the VPD instance name (the same name used as the id in $tkg$post), followed by a "." character and a local name. This convention ensures that each instance of a VPD has its own set of channels to use. You can use "%m" in place of the instance name which will be replaced with the instance name for the current module.

For more information on how to use this task in designing VPDs, see the section on Creating Virtual Peripheral Devices.

While this task is primarily intended to be used for communication with VPDs, you can use it in conjunction with the $tkg$recv task to send values from one Verilog thread to another.

B.6.8 $tkg$systime

Usage: value = $tkg$systime;

This task returns the current system time as a 64-bit time value with the number of milliseconds since January 1, 1970.

B.6.9 $tkg$unprobe

Usage: $tkg$unprobe(signal1, signal2, ...);

This task removes one or more probes set either with the $probe system task, or manually through the TkGate GUI.

B.6.10 $tkg$wait

Usage: $tkg$wait(value);

This task will block the calling thread for the specified number of milliseconds. The simulation itself will continue, and all other threads will advance normally (unless the calling thread is the only unblocked thread). As an example, consider the code fragment:

   always
     begin
       $tkg$wait(1000)
       $display("Hello World");
     end
This will cause the message "Hello World" to be printed once per second.

B.6.11 $tkg$waituntil

Usage: $tkg$waituntil(value);

This task blocks a thread in the same way as $tkg$wait until a specified absolute time. The $tkg$systime task should be used to get a reference time to be used with $tkg$waituntil. If the time specified by value is less than the current time, $tkg$waituntil will return immediately and do nothing. This task can be used to create real time clocks. For example, the following module generates a clock signals that oscillates once per second:

module realclock(z);
output reg z;
time t;

  initial
    z = 1'b0;

  initial
    begin
      t = $tkg$systime;
      forever
        begin
          t = t + 500;
          $tkg$waituntil(t);
	  z = ~z;
        end
    end

endmodule

The advantage of using $tkg$waituntil instead of $tkg$wait to generate the clock signal is to avoid drift. If $tkg$wait were used instead, there would be additional real time delay as the simulation was running between calls to $tkg$wait. This small error would accumulate over time causing the number of oscillations to diverge from the proper value over a long time period.

Real time clocks such as this can be used to create real-time simulations of devices such as digital clocks, or other simulation using LEDs where you wish to run them synchronized with real time.

B.6.12 $tkg$zoom

Usage: $tkg$zoom(factor);

This task sets the zoom level in the TkGate scope window reletive to the default zoom factor. The factor parameter is an integer representing the zoom factor. A factor of 0 sets the default zoom factor. Negative values represent the number of zoom-out steps (showing more time on the x-axis), and positive values represent the number of zoom-in steps (showing less time on the x-axis).

C. List of Simulator Error Messages

C.1 Run-Time Warnings/Errors

C.2 Compile-Time Warnings

C.3 Compile-Time Errors


D. Using the Verga Simulator in Stand-Alone Mode

While Verga was written to support use through the TkGate graphical interface, it is possible to use Verga by itself in stand-alone mode. The input files need not be TkGate generated Verilog, and can be arbitrary Verilog code, as long as it uses only the subset of constructs supported by Verga.

To invoke Verga, use a command of the form:

verga[options] [files]

The supported options are:

Option Description

-i Interactive mode - In this mode, Verga will continue to read from the standard input while the simulation is running to accept commands controlling the simulation. This is the mode that the TkGate GUI uses when invoking Verga, and is generally only useful to TkGate.
-e List error messages - Dump a list of all the error messages that Verga can generate.
-l Show License - Print out the license information to the screen.
-S script Load Script - Load a simulation script on start-up. This switch can be specified multiple times to load multiple scripts.
-W mode Warning Mode - Sets the warning mode indicating how warnings are to be treated. Mode is a numeric value between 1 and 4. If mode is 1, warning will be ignored. If mode is 2, warnings will be reported only if there are errors. If mode is 3, warnings will always be reports. If mode is 4, warnings will be treated as errors, and the simulator will not start if there are warnings. The default mode value is 3.
-s Scan Mode - Scans the modules in the input file and reports the names of the modules, and the variables declared in them. No simulation is performed.
-q Quite Mode - Suppress printing of unnecessary messages.
-P Print Modules - Echos back the input file from the internal parsed data.
-B dir Base Directory - Makes dir the default directory when searching for files.
-t name Set Top Module - Makes name the top-level module rather than automatically selecting one.
-d type Delay Type - Sets the delay values to use. type can be one of min, max or typical.

Verga will scan all Verilog files specified on the command line. If the -t switch is not used to specify the top-level module, Verga will assume that the first module with no ports is the top-level module. Any output generated by the simulation is printed to the screen.

If you are using Verga to simulate a file saved from TkGate, you must be sure that you have enabled "Include cells in save files" from the General Options dialog box. If you do not, Verga will be unable to find the definitions for TkGate's built-in devices

E. Sample Gmac Input File

This appendix contains a complete listing of the Gmac input file for the Menagerie CPU including as a sample circuit.


//
//    Copyright (C) 1987-2015 by Jeffery P. Hansen
//
//    This program is free software; you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation; either version 2 of the License, or
//    (at your option) any later version.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License along
//    with this program; if not, write to the Free Software Foundation, Inc.,
//    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//
//    Last edit by hansen on Thu Jul 26 20:56:44 2007
//

// Microcode memory bank declarations

microcode bank[31:0] iunit.m1;
microcode bank[63:32] iunit.m2;
map bank[7:0] iunit.map;

macrocode bank[7:0] memory.m1;

// Microcode field declarations

//
// Microcode branching.  mpcop specifies the basic operation.
//
field mpcop[1:0]={
	 next=0,	// Increment mpc
	 reinit=1,	// Restart CPU
	 jmap=2,	// Jump from map value
	 jump=3		// Jump from condition
};

//
// Specifies the condition on which to jump if mpcop is "jump".
//
field mpccond[12:10]={
	jne=0,		// Jump if not equal
	jcarry=1,	// Jump on carry
	jeq=2,		// Jump if equal
	jlt=3,		// Jump if less than
	jgt=4,		// Jump if greater than
	jle=5,		// Jump if less than or equal
	jge=6,		// Jump if greater than or equal
	jmp=7		// Jump always
};

//
// Address to jump to if mpcop is "jump" and condition specified
// by mpccond is true.  This field can not be used at the same
// time as the idata field.
//
field mpcaddr[9:2];

//
// Specifies 8 bits of data to be used by the EUNIT.  This field
// can not be used on jump microinstructions.
//
field idata[9:2];

//
// Specifies the A and B operands of the ALU.
//
//	qreg		Use Q register
//	din		Use data in
//	idata		Use idata field from microinstruction
//	reg		Use register file
//
field aop[15:14]={qreg=0, din=1, idata=2, reg=3};
field bop[17:16]={qreg=0, din=1, idata=2, reg=3};

field ~ldir[13];	// Load instruction register
field cin[18];		// Carry in
field ~clq[19];		// Clear Q register
field ~ldq[20];		// 16-bit load of Q register
field ~lddata[21];	// Load EUNIT data from external bus
field ~ldopr[22];	// Load operand register
field ~wa[23];		// Write register file on SA
field sa[27:24];	// Register address A
field sb[31:28];	// Register address B

//
// These fields specify the ALU function.
//
field ALU_FUNC[36:32];
field ALU_SHOP[33:32]={arshift=0, lshift=1, rshift=2, roll=3};
field ALU_BCOMP[32];
field ALU_AZERO[33];
field ALU_OP[36:34]={shift=0,xor=1,and=2,or=3,mul=4,add=5,mod=6,div=7};

field ~incpc[37];	// Increment PC
field ~ldmar[38];	// Load MAR
field ~ldmdr[39];	// Load MDR
field ~ldpc[40];	// Load PC
field ~rd[41];		// Read main memory
field ~rdmdr[42];	// Read MDR onto external data bus
field ~wrt[43];		// Write main memory
field spc[44];		// Address main memory from PC
field ~isa[45];		// Use sa address from macro instruction
field ~isb[46];		// Use sb address from macro instruction
field ~ifunc[47];	// Use function code from macro instruction
field ~icond[48];	// Use branch condition from macro instruction
field ~ldql[49];	// 8-bit load of lower half of Q register
field ~ldqh[50];	// 8-bit load of upper half of Q register
field ~dout[51];	// Output EUNIT data to external bus
field ~ldcc[52];	// Load condition code register
field ~ldhmdr[53];	// Load mdr from high byte of data bus
field ~rdpc[54];	// Read PC onto external bus
field ~incmar[55];	// Increment mar (can't use with incpc)

field extra[63:56];	// Extra bits

//////////////////////////////////////////////////////////////////////
//
// +-+-+-+-+-+-+-+-+
// |7|6|5|4|3|2|1|0|
// +-+-+-+-+-+-+-+-+
//
// Basic instruction types are encoded by the high two bits of the
// first byte of the instruction.  For certain types (e.g., ALU
// types) some bits are masked when forming the map address.  Bits
// contributing to the map vector are marked with a *.
//
// Move Instruction
// +-+-+-+-+-+-+-+-+   +-+-+-+-+-+-+-+-+
// |1 1 1 s|a a|b b|   | reg1  |  reg2 |
// +-+-+-+-+-+-+-+-+   +-+-+-+-+-+-+-+-+  ...
//  * * * * * * * *
//
//  s  = size (1 = byte, 0 = word)
//  aa = operand mode 1
//  bb = operand mode 2
//
// Single operand instruction (push, pop, call, etc.)
// +-+-+-+-+-+-+-+-+   +-+-+-+-+-+-+-+-+
// |1 0|0| op  |b b|   | reg1  |0 0 0 0|
// +-+-+-+-+-+-+-+-+   +-+-+-+-+-+-+-+-+  ...
//  * * * * * * * *
//
//
// Branch instruction (jmp, jne, etc.)
// +-+-+-+-+-+-+-+-+   +-+-+-+-+-+-+-+-+
// |1 0|1|cond |b b|   | reg1  |0 0 0 0|
// +-+-+-+-+-+-+-+-+   +-+-+-+-+-+-+-+-+  ...
//  * * *       * *
//
//
// ALU Instruction
// +-+-+-+-+-+-+-+-+   +-+-+-+-+-+-+-+-+
// |0 1|  func   |b|   | reg1  |  reg2 |
// +-+-+-+-+-+-+-+-+   +-+-+-+-+-+-+-+-+  ...
//  * *           *
//
//  func = ALU function
//  a = operand mode
//
// Other instructions
// +-+-+-+-+-+-+-+-+   +-+-+-+-+-+-+-+-+
// |0 0|   op    |b|   | reg1  |  reg2 |
// +-+-+-+-+-+-+-+-+   +-+-+-+-+-+-+-+-+  ...
//  * * * * * * * *
//
//

registers R0=0, R1=1, R2=2, R3=3, R4=4, R5=5, R6=6, R7=7, R8=8, R9=9, R10=10, R11=11, R12=12, R13=13, FP=14, SP=15;

operands basic {
    %1,%2 = { +0[0] = 0; +1[7:4]=%1; +1[3:0]=%2; };
    %1,#2 = { +0[0] = 1; +1[7:4]=%1; +1[3:0]=0; +2=#2[7:0]; +3=#2[15:8]; };
};

operands runiop {
    %1     = { +0[1:0] = 0; +1[7:4]=%1; +1[3:0]=0; };
    #1     = { +0[1:0] = 1; +1=0; +2=#1[7:0]; +3=#1[15:8]; };
    (%1)   = { +0[1:0] = 2; +1[7:4]=%1; +1[3:0]=0; };
    #2(%1) = { +0[1:0] = 3; +1[7:4]=%1; +1[3:0]=0; +2=#2[7:0]; +3=#2[15:8]; };
};

operands wuniop {
    %1     = { +0[1:0] = 0; +1[7:4]=%1; +1[3:0]=0; };
    (%1)   = { +0[1:0] = 2; +1[7:4]=%1; +1[3:0]=0; };
    #2(%1) = { +0[1:0] = 3; +1[7:4]=%1; +1[3:0]=0; +2=#2[7:0]; +3=#2[15:8]; };
};

//
// Operands for move instructions
//
operands movoprs {
    %1,%2         = { +0[3:2] = 0; +0[1:0] = 0; +1[7:4]=%1; +1[3:0]=%2; };
    %1,#2         = { +0[3:2] = 0; +0[1:0] = 1; +1[7:4]=%1; +1[3:0]=0; +2=#2[7:0]; +3=#2[15:8]; };
    %1,(%2)       = { +0[3:2] = 0; +0[1:0] = 2; +1[7:4]=%1; +1[3:0]=%2; };
    %1,#3(%2)     = { +0[3:2] = 0; +0[1:0] = 3; +1[7:4]=%1; +1[3:0]=%2; +2=#3[7:0]; +3=#3[15:8]; };
    %1,(#2)       = { +0[3:2] = 0; +0[1:0] = 3; +1[7:4]=%1; +1[3:0]=0; +2=#2[7:0]; +3=#2[15:8]; };


    (%1),%2       = { +0[3:2] = 2; +0[1:0] = 0; +1[7:4]=%1; +1[3:0]=%2; };
    (%1),#2       = { +0[3:2] = 2; +0[1:0] = 1; +1[7:4]=%1; +1[3:0]=0; +2=#2[7:0]; +3=#2[15:8]; };
    (%1),(%2)     = { +0[3:2] = 2; +0[1:0] = 2; +1[7:4]=%1; +1[3:0]=%2; };
    (%1),#3(%2)   = { +0[3:2] = 2; +0[1:0] = 3; +1[7:4]=%1; +1[3:0]=%2; +2=#3[7:0]; +3=#3[15:8]; };
    (%1),(#2)     = { +0[3:2] = 2; +0[1:0] = 3; +1[7:4]=%1; +1[3:0]=0; +2=#2[7:0]; +3=#2[15:8]; };

    #1(%2),%3     = { +0[3:2] = 3; +0[1:0] = 0; +1[7:4]=%2; +1[3:0]=%3; +2=#1[7:0]; +3=#1[15:8]; };
    #1(%2),#3     = { +0[3:2] = 3; +0[1:0] = 1; +1[7:4]=%2; +1[3:0]=0; +2=#1[7:0]; +3=#1[15:8]; +4=#3[7:0]; +5=#3[15:8]; };
    #1(%2),(%3)   = { +0[3:2] = 3; +0[1:0] = 2; +1[7:4]=%2; +1[3:0]=%3; +2=#1[7:0]; +3=#1[15:8]; };
    #1(%2),#4(%3) = { +0[3:2] = 3; +0[1:0] = 3; +1[7:4]=%2; +1[3:0]=%3; +2=#1[7:0]; +3=#1[15:8]; +4=#4[7:0]; +5=#4[15:8]; };
    #1(%2),(#3) = { +0[3:2] = 3; +0[1:0] = 3; +1[7:4]=%2; +1[3:0]=0; +2=#1[7:0]; +3=#1[15:8]; +4=#3[7:0]; +5=#3[15:8]; };

    (#1),%2     = { +0[3:2] = 3; +0[1:0] = 0; +1[7:4]=0; +1[3:0]=%2; +2=#1[7:0]; +3=#1[15:8]; };
    (#1),#2     = { +0[3:2] = 3; +0[1:0] = 1; +1[7:4]=0; +1[3:0]=0; +2=#1[7:0]; +3=#1[15:8]; +4=#2[7:0]; +5=#2[15:8]; };
    (#1),(%2)   = { +0[3:2] = 3; +0[1:0] = 2; +1[7:4]=0; +1[3:0]=%2; +2=#1[7:0]; +3=#1[15:8]; };
    (#1),#3(%2) = { +0[3:2] = 3; +0[1:0] = 3; +1[7:4]=0; +1[3:0]=%2; +2=#1[7:0]; +3=#1[15:8]; +4=#3[7:0]; +5=#3[15:8]; };
    (#1),(#2)   = { +0[3:2] = 3; +0[1:0] = 3; +1[7:4]=0; +1[3:0]=0; +2=#1[7:0]; +3=#1[15:8]; +4=#2[7:0]; +5=#2[15:8]; };
};

//
// +-+-+-+-+-+-+-+-+   +-+-+-+-+-+-+-+-+
// |0 0|1 1 1 1 1|0|   |0 0 0 0|0 0 0 0|
// +-+-+-+-+-+-+-+-+   +-+-+-+-+-+-+-+-+
//
op nop {
  map nop : 0x3e;
  +0=0x3e;
  operands {
    - = { +1=0; };
  };
};

//
// +-+-+-+-+-+-+-+-+   +-+-+-+-+-+-+-+-+
// |0 0|0 0 0 0 0|0|   |0 0 0 0|0 0 0 0|
// +-+-+-+-+-+-+-+-+   +-+-+-+-+-+-+-+-+
//
op halt {
  map halt : 0x0;
  +0=0;
  operands {
    - = { +1=0; };
  };
};

//
// +-+-+-+-+-+-+-+-+   +-+-+-+-+-+-+-+-+
// |0 0|0 0 0 0 1|0|   | reg1  |  reg2 |
// +-+-+-+-+-+-+-+-+   +-+-+-+-+-+-+-+-+
//
op cmp {
  map cmp_rr : 0x2;
  map cmp_ri : 0x3;
  +0[7:1]=0x1;
  operands basic;
};

// Generic branch operation
// +-+-+-+-+-+-+-+-+   +-+-+-+-+-+-+-+-+
// |1 0|1|x x x|b b|   |0 0 0 0| reg1  |
// +-+-+-+-+-+-+-+-+   +-+-+-+-+-+-+-+-+  ...
//
// Example:
//    br  #7, loop
//
// Jump always to loop.  7 is a code indicating the condition always.
//
op jp {
  map br_r : 0xa0;
  map br_i : 0xa1;
  map br_d : 0xa2;
  map br_x : 0xa3;
  +0[7:5] = 0x5;
  operands {
    #1,%2     = { +0[1:0] = 0; +0[4:2] = #1; +1[7:4]=%2; +1[3:0]=0; };
    #1,#2     = { +0[1:0] = 1; +0[4:2] = #1; +1=0; +2=#2[7:0]; +3=#2[15:8]; };
    #1,(%2)   = { +0[1:0] = 2; +0[4:2] = #1; +1[7:4]=%2; +1[3:0]=0; };
    #1,#3(%2) = { +0[1:0] = 3; +0[4:2] = #1; +1[7:4]=%2; +1[3:0]=0; +2=#3[7:0]; +3=#3[15:8]; };
  };
};

op jne {
  +0[7:5] = 0x5;
  +0[4:2] = 0x0;
  operands runiop;
};

op jcarry {
  +0[7:5] = 0x5;
  +0[4:2] = 0x1;
  operands runiop;
};

op jeq {
  +0[7:5] = 0x5;
  +0[4:2] = 0x2;
  operands runiop;
};

op jlt {
  +0[7:5] = 0x5;
  +0[4:2] = 0x3;
  operands runiop;
};

op jgt {
  +0[7:5] = 0x5;
  +0[4:2] = 0x4;
  operands runiop;
};

op jle {
  +0[7:5] = 0x5;
  +0[4:2] = 0x5;
  operands runiop;
};

op jge {
  +0[7:5] = 0x5;
  +0[4:2] = 0x6;
  operands runiop;
};

op jmp {
  +0[7:5] = 0x5;
  +0[4:2] = 0x7;
  operands runiop;
};


// Generic ALU operation
//
// Note this is not a real instruction but is here for illustrative
// purposes only.  Map entries must be made for each function code
// to use this instruction for real.
//
// +-+-+-+-+-+-+-+-+   +-+-+-+-+-+-+-+-+
// |0 1|x x x x x|b|   | reg1  |  reg2 |
// +-+-+-+-+-+-+-+-+   +-+-+-+-+-+-+-+-+  ...
//
// Example:
//    alu #0x14, R1, R2
//
// Does ALU operation 0x14 (addition) on R1 and R2, storing result in R1.
//
//op alu {
//  map alu_rr : 0x40;
//  map alu_ri : 0x41;
// +0[7:6] = 0x1;
//  operands {
//    #1,%2,%3 = { +0[0] = 0; +0[5:1]=#1; +1[7:4]=%2; +1[3:0]=%3; };
//    #1,%2,#3 = { +0[0] = 1; +0[5:1]=#1; +1[7:4]=0; +1[3:0]=%2; +2=#3[7:0]; +3=#3[15:8]; };
//  };
//};

//
// +-+-+-+-+-+-+-+-+   +-+-+-+-+-+-+-+-+
// |0 1|1 0 1 0 0|b|   | reg1  |  reg2 |
// +-+-+-+-+-+-+-+-+   +-+-+-+-+-+-+-+-+  ...
//
op add {
  map alu_rr : 0x68;
  map alu_ri : 0x69;
 +0[7:0]=0x68;
  operands basic;
};

//
// +-+-+-+-+-+-+-+-+   +-+-+-+-+-+-+-+-+
// |0 1|1 0 1 0 1|b|   | reg1  |  reg2 |
// +-+-+-+-+-+-+-+-+   +-+-+-+-+-+-+-+-+  ...
//
op sub {
  map alu_rr : 0x6a;
  map alu_ri : 0x6b;
 +0[7:0]=0x6a;
  operands basic;
};

//
// +-+-+-+-+-+-+-+-+   +-+-+-+-+-+-+-+-+
// |0 1|1 0 0 0 0|b|   | reg1  |  reg2 |
// +-+-+-+-+-+-+-+-+   +-+-+-+-+-+-+-+-+  ...
//
op mul {
  map xalu_rr : 0x60;
  map xalu_ri : 0x61;
 +0[7:0]=0x60;
  operands basic;
};

//
// +-+-+-+-+-+-+-+-+   +-+-+-+-+-+-+-+-+
// |0 1|1 1 1 0 0|b|   | reg1  |  reg2 |
// +-+-+-+-+-+-+-+-+   +-+-+-+-+-+-+-+-+  ...
//
op div {
  map xalu_rr : 0x78;
  map xalu_ri : 0x79;
 +0[7:0]=0x78;
  operands basic;
};


//
// +-+-+-+-+-+-+-+-+   +-+-+-+-+-+-+-+-+
// |0 1|1 1 0 0 0|b|   | reg1  |  reg2 |
// +-+-+-+-+-+-+-+-+   +-+-+-+-+-+-+-+-+  ...
//
op mod {
  map xalu_rr : 0x70;
  map xalu_ri : 0x71;
 +0[7:0]=0x70;
  operands basic;
};


//
// +-+-+-+-+-+-+-+-+   +-+-+-+-+-+-+-+-+
// |0 1|0 1 0 0 0|b|   | reg1  |  reg2 |
// +-+-+-+-+-+-+-+-+   +-+-+-+-+-+-+-+-+  ...
//
op and {
  map alu_rr : 0x50;
  map alu_ri : 0x51;
 +0[7:0]=0x50;
  operands basic;
};


// Call subroutine
// +-+-+-+-+-+-+-+-+   +-+-+-+-+-+-+-+-+
// |1 0|0|0 0 0|b b|   | reg1  |0 0 0 0|
// +-+-+-+-+-+-+-+-+   +-+-+-+-+-+-+-+-+  ...
//
// Example:
//
//   call	foo, #8
//
// This will perform the following actions:
//   sp = sp - 2
//   [sp] = pc
//   sp = sp - 2
//   [sp] = fp
//   fp = sp
//   sp = sp+8
//   pc = foo
//
op call {
  map call_ri : 0x80;
  map call_ii : 0x81;
  map call_di : 0x82;
  map call_xi : 0x83;
  +0[7:4] = 0x8;
  operands {
    #1,%2     = { +0[1:0] = 0; +1[7:4]=%2; +1[3:0]=0; +2=#1[7:0]; +3=#1[15:8]; };
    #1,#2     = { +0[1:0] = 1; +1=0; +2=#1[7:0]; +3=#1[15:8]; +4=#2[7:0]; +5=#2[15:8]; };
    #1,(%2)   = { +0[1:0] = 2; +1[7:4]=%2; +1[3:0]=0; +2=#1[7:0]; +3=#1[15:8]; };
    #1,#2(%3) = { +0[1:0] = 3; +1[7:4]=%3; +1[3:0]=0; +2=#1[7:0]; +3=#1[15:8]; +4=#2[7:0]; +5=#2[15:8]; };

    #1        = { +0[1:0] = 1; +1=0; +2=0; +3=0; +4=#1[7:0]; +5=#1[15:8]; };
  };
};

// Return from subroutine
// +-+-+-+-+-+-+-+-+   +-+-+-+-+-+-+-+-+
// |0 0|0 0 0 1 0|0|   |0 0 0 0|0 0 0 0|
// +-+-+-+-+-+-+-+-+   +-+-+-+-+-+-+-+-+  ...
//
// Example:
//
//    ret
//
// This will perform the following actions:
//    sp = fp
//    fp = [sp]
//    sp = sp + 2
//    pc = [sp]
//    sp = sp + 2
//
//
op ret {
  map ret : 0x4;
  +0=4;
  operands {
    - = { +1=0; };
  };
};

// Push a word on the stack
// +-+-+-+-+-+-+-+-+   +-+-+-+-+-+-+-+-+
// |1 0|0|0 0 1|b b|   | reg1  |0 0 0 0|
// +-+-+-+-+-+-+-+-+   +-+-+-+-+-+-+-+-+  ...
//
// Example:
//
//    pushw	R1
//
op pushw {
  map pushw_r : 0x84;
  map pushw_i : 0x85;
  map pushw_d : 0x86;
  map pushw_x : 0x87;
  +0[7:2]=0x21;
  operands runiop;
};


op movb {
  map movb_rr : 0xf0;
  map movb_ri : 0xf1;
  map movb_rd : 0xf2;
  map movb_rx : 0xf3;

  map movb_dr : 0xf8;
  map movb_di : 0xf9;
  map movb_dd : 0xfa;
  map movb_dx : 0xfb;

  map movb_xr : 0xfc;
  map movb_xi : 0xfd;
  map movb_xd : 0xfe;
  map movb_xx : 0xff;

  +0[7:4]=0xf;
  operands movoprs;
};

op movw {
  map movw_rr : 0xe0;
  map movw_ri : 0xe1;
  map movw_rd : 0xe2;
  map movw_rx : 0xe3;

  map movw_dr : 0xe8;
  map movw_di : 0xe9;
  map movw_dd : 0xea;
  map movw_dx : 0xeb;

  map movw_xr : 0xec;
  map movw_xi : 0xed;
  map movw_xd : 0xee;
  map movw_xx : 0xef;

  +0[7:4]=0xe;
  operands movoprs;
};

/////////////////////////////////////////////////////////////////////////////
//
// The microcode for the Menagerie CPU begins here.
//
// The CPU begins executing microinstuctions at address 0.  The instructions
// in the start block are executed only once.  The next block consists of
// the instuction fetch sequnce.  The multiple labels account for partial
// fetches that are done by some of the macrocode routines.
//
// For macro instructions which have no operands or have only only one operand,
// there is usually a plain label for that instruction.  For example the
// microinstruction labeled 'ret' implements the 'ret' macroinstruction.  This
// mapping is defined by the 'map ret : 0x4;' line in the operand declaration
// for the ret macroinstruction.
//
// For macro instructions with multiple addressing modes, labels are generally
// of the form 'op_??' where each character after the '_' denotes an addressing
// mode for an operand.  By convension, the characters:
//
//     r	register direct
//     i	immediate
//     d	register indirect
//     x	indexed
//
// are used.
//
begin microcode @ 0
start:	clq;								// Q <- 0
	idata=0x1 ALU_AZERO ALU_OP=add bop=idata ldqh;			// Q.H <- 1
	ALU_AZERO ALU_OP=add bop=qreg dout ldpc;			// PC <- Q
	mpcop=jump mpccond=jmp mpcaddr=fetch;				// jump to 'fetch'
	mpcop=next;

fetch: 	incpc spc rd ldmdr;						// mdr <- [PC]; PC++;
fetch1: incpc spc rd ldmdr rdmdr ldir;					// mdr <- [PC]; PC++; ir <- mdr;
fetch2:	mpcop=jmap rdmdr ldopr;						// opr <- mdr; jump to opcode
	mpcop=next;

halt:	mpcop=jump mpccond=jmp mpcaddr=halt extra=0x1;
	mpcop=jump mpccond=jmp mpcaddr=halt extra=0x1;

//
// Standard ALU operations (add, sub, and, or, etc.)
//
alu_rr:	mpcop=jump mpccond=jmp mpcaddr=fetch2 ifunc
		isa isb wa aop=reg bop=reg ldcc incpc spc rd ldmdr;	// Ra = Ra (op) Rb; CC; jump to fetch
	incpc spc rd ldmdr rdmdr ldir;

alu_ri:	incpc spc rd ldmdr;						// mdr <- [PC]; PC++;
	incpc spc rd ldmdr rdmdr ldql
		ALU_OP=add ALU_AZERO lddata bop=din;			// mdr <- [PC]; PC++; Q.L <- mdr
	rdmdr ldqh ALU_OP=add ALU_AZERO lddata bop=din;			// Q.H = mdr;
	mpcop=jump mpccond=jmp mpcaddr=fetch ifunc
		aop=reg bop=qreg wa isa ldcc;				// Ra = Ra (op) Q;  CC; jump to fetch
	mpcop=next;

//
// 2-cycle ALU operations (mul, div, mod).  These are the same as the alu_??
// ops except the ALU inputs and function code are held for 2 clock cycles
// due to the longer delay in computing these functions.
//
xalu_rr: mpcop=next ifunc isa isb aop=reg bop=reg;			// Ra (op) Rb;
	mpcop=jump mpccond=jmp mpcaddr=fetch2 ifunc
		isa isb wa aop=reg bop=reg ldcc incpc spc rd ldmdr;	// Ra = Ra (op) Rb; CC; jump to fetch
	incpc spc rd ldmdr rdmdr ldir;

xalu_ri: incpc spc rd ldmdr;						// mdr <- [PC]; PC++;
	incpc spc rd ldmdr rdmdr ldql
		ALU_OP=add ALU_AZERO lddata bop=din;			// mdr <- [PC]; PC++; Q.L <- mdr
	rdmdr ldqh ALU_OP=add ALU_AZERO lddata bop=din;			// Q.H = mdr;
	mpcop=next ifunc isa isb aop=reg bop=qreg;			// Ra (op) Q;
	mpcop=jump mpccond=jmp mpcaddr=fetch ifunc
		aop=reg bop=qreg wa isa ldcc;				// Ra = Ra (op) Q;  CC; jump to fetch
	mpcop=next;

cmp_rr:	mpcop=jump mpccond=jmp mpcaddr=fetch ALU_OP=add ALU_BCOMP extra=0x22
		isa isb aop=reg bop=reg ldcc;				// Ra - Rb; CC; jump to fetch
	mpcop=next;

cmp_ri:	incpc spc rd ldmdr;						// mdr <- [PC]; PC++;
	incpc spc rd ldmdr rdmdr ldql
		ALU_OP=add ALU_AZERO lddata bop=din;			// mdr <- [PC]; PC++; Q.L <- mdr
	rdmdr ldqh ALU_OP=add ALU_AZERO lddata bop=din;			// Q.H = mdr;
	mpcop=jump mpccond=jmp mpcaddr=fetch ALU_OP=add ALU_BCOMP
		aop=reg bop=qreg isa ldcc;				// Ra - Q; CC; jump to fetch
	mpcop=next;

br_i:	incpc spc rd ldmdr;						// mdr <- [PC]; PC++;
	incpc spc rd ldmdr rdmdr ldql
		ALU_OP=add ALU_AZERO lddata bop=din;			// mdr <- [PC]; PC++; Q.L <- mdr
	rdmdr ldqh ALU_OP=add ALU_AZERO lddata bop=din;			// Q.H = mdr;

btest:	mpcop=jump mpcaddr=bdojmp icond;				// jump to bdojmp if cond
	mpcop=next;
	mpcop=jump mpccond=jmp mpcaddr=fetch;				// jump to fetch
	mpcop=next;
bdojmp:	mpcop=jump mpccond=jmp mpcaddr=fetch ALU_AZERO ALU_OP=add
		bop=qreg dout ldpc;					// PC <- Q, jump to fetch
	mpcop=next;


br_r:	mpcop=jump mpccond=jmp mpcaddr=btest extra=0x10
		isa aop=reg sb=0 bop=reg ALU_OP=add ldq;		// Q <- Ra; jump to btest
	mpcop=next;

br_d:	mpcop=jump mpccond=jmp mpcaddr=btest;
	mpcop=next;

br_x:	mpcop=jump mpccond=jmp mpcaddr=btest;
	mpcop=next;

call_ii:
	// Push the PC
	aop=reg sa=0xf bop=idata idata=2 ALU_OP=add ALU_BCOMP
		dout ldmar wa extra=0x11;				// SP-2 -> mar; SP = SP - 2;
	rdpc lddata aop=din bop=idata idata=4 ALU_OP=add ldq;		// Q = PC+4
	ALU_AZERO ALU_OP=add bop=qreg dout ldmdr;			// Q.L -> mdr
	wrt;								// [mar] <- mdr;
	incmar ALU_AZERO ALU_OP=add bop=qreg dout ldhmdr;		// Q.H -> mdr; mar++;
	mpcop=next;
	wrt;								// [mar] <- mdr;
	mpcop=next;

	// Push the FP
	aop=reg sa=0xf bop=idata idata=2 ALU_OP=add ALU_BCOMP
			dout ldmar wa;					// SP-2 -> mar; SP = SP - 2;
	ALU_AZERO bop=reg sb=0xe ALU_OP=add dout ldmdr;			// FP.L -> mdr
	wrt;								// [mar] <- mdr;
	incmar ALU_AZERO  bop=reg sb=0xe ALU_OP=add dout ldhmdr; 	// SP.H -> mdr; mar++;
	mpcop=next;
	wrt;								// [mar] <- mdr;

	aop=reg sa=0xe bop=reg sb=0xf
		ALU_AZERO ALU_OP=add wa;				// FP <- SP

	// Dec SP
	incpc spc rd ldmdr;						// mdr <- [PC]; PC++;
	incpc spc rd ldmdr rdmdr ldql
		ALU_OP=add ALU_AZERO lddata bop=din;			// mdr <- [PC]; PC++; Q.L <- mdr
	rdmdr ldqh ALU_OP=add ALU_AZERO lddata bop=din;			// Q.H = mdr;
	aop=reg sa=0xf bop=qreg ALU_OP=add wa;				// SP = SP + Q

	// PC = branch address
	incpc spc rd ldmdr;						// mdr <- [PC]; PC++;
	incpc spc rd ldmdr rdmdr ldql
		ALU_OP=add ALU_AZERO lddata bop=din;			// mdr <- [PC]; PC++; Q.L <- mdr
	rdmdr ldqh ALU_OP=add ALU_AZERO lddata bop=din;			// Q.H = mdr;
	mpcop=jump mpccond=jmp mpcaddr=fetch ALU_AZERO bop=qreg
		ALU_OP=add dout ldpc;					// PC <- Q
	mpcop=next;

call_ri: mpcop=jump mpccond=jmp mpcaddr=fetch;
	mpcop=next;

call_di: mpcop=jump mpccond=jmp mpcaddr=fetch;
	mpcop=next;

call_xi: mpcop=jump mpccond=jmp mpcaddr=fetch;
	mpcop=next;

ret:	ALU_AZERO bop=reg sb=0xe ALU_OP=add sa=0xf wa dout ldmar extra=0x12;	// SP <- FP; mar <- FP;
	aop=reg sa=0xf bop=idata idata=4 ALU_OP=add wa rd incmar ldmdr;	// SP <- SP+4; mdr <- [mar++]
	ALU_AZERO bop=din lddata ALU_OP=add ldql rd rdmdr incmar ldmdr;	// Q.L <- mdr; mdr <- [mar++]
	ALU_AZERO bop=din lddata ALU_OP=add ldqh rdmdr;			// Q.H <- mdr;
	ALU_AZERO bop=qreg ALU_OP=add sa=0xe wa rd incmar ldmdr;	// FP <- Q; mdr <- [mar++]
	ALU_AZERO bop=din lddata ALU_OP=add ldql rd incmar rdmdr ldmdr;	// Q.L <- mdr; mdr <- [mar++]
	ALU_AZERO bop=din lddata ALU_OP=add ldqh rdmdr;			// Q.H <- mdr;
	mpcop=jump mpccond=jmp mpcaddr=fetch1
		ALU_AZERO bop=qreg ALU_OP=add dout ldpc;		// PC <- Q; jump fetch1
 	incpc spc rd ldmdr;						// mdr <- [PC]; PC++;


pushw_d: mpcop=next;
pushw_x: mpcop=next;

pushw_r: aop=reg sa=0xf bop=idata idata=2 ALU_OP=add ALU_BCOMP
		extra=0x13 dout ldmar wa;				// mar <- SP-2; SP = SP - 2;
	aop=reg isa bop=idata idata=0 ALU_OP=add dout ldmdr;		// mdr <- Rb.L;
	wrt;								// [mar] <- mdr;
	aop=reg isa bop=idata idata=0 ALU_OP=add dout ldhmdr incmar;	// mdr <- Rb.H; mar++;
	mpcop=next;
	mpcop=jump mpccond=jmp mpcaddr=fetch wrt;			// [mar] <- mdr; jump to fetch
	mpcop=next;

pushw_i: incpc spc rd ldmdr extra=2;					// mdr <- [PC]; PC++;
	incpc spc rd ldmdr rdmdr ldql ALU_OP=add ALU_AZERO lddata bop=din; // mdr <- [PC]; PC++; Q.L <- mdr
	rdmdr ldqh ALU_OP=add ALU_AZERO lddata bop=din;			// Q.H = mdr;
	aop=reg sa=0xf bop=idata idata=2 ALU_OP=add ALU_BCOMP
		extra=0x13 dout ldmar wa;				// mar <- SP-2; SP = SP - 2;
	aop=qreg bop=idata idata=0 ALU_OP=add dout ldmdr;		// mdr <- Q.L;
	wrt;								// [mar] <- mdr;
	aop=qreg bop=idata idata=0 ALU_OP=add dout ldhmdr incmar;	// mdr <- Q.H; mar++;
	mpcop=next;
	mpcop=jump mpccond=jmp mpcaddr=fetch wrt;			// [mar] <- mdr; jump to fetch
	mpcop=next;

movw_rr: mpcop=jump mpccond=jmp mpcaddr=fetch2 ALU_AZERO ALU_OP=add
		isa isb wa aop=reg bop=reg ldcc extra=1
		incpc spc rd ldmdr;					// Ra <- Rb; jump to fetch2; mdr <- [PC]; PC++;
	incpc spc rd ldmdr rdmdr ldir;					// mdr <- [PC]; PC++; ir <- mdr;

movb_rr: ALU_AZERO ALU_OP=add isb aop=reg bop=reg ldcc extra=1 ldql;	// Q.L <- Rb;
	mpcop=jump mpccond=jmp mpcaddr=fetch2 aop=idata bop=idata
		ALU_OP=add ldqh incpc spc rd ldmdr;			// Q.H <- 0; jump to fetch2;mdr <- [PC]; PC++;
	ALU_AZERO ALU_OP=add bop=qreg isa wa
		incpc spc rd ldmdr rdmdr ldir;				// Ra <- Q; mdr <- [PC]; PC++; ir <- mdr;


movw_ri: incpc spc rd ldmdr extra=2;					// mdr <- [PC]; PC++;
	incpc spc rd ldmdr rdmdr ldql ALU_OP=add ALU_AZERO lddata bop=din; // mdr <- [PC]; PC++; Q.L <- mdr
	rdmdr ldqh ALU_OP=add ALU_AZERO lddata bop=din;			// Q.H = mdr;
	mpcop=jump mpccond=jmp mpcaddr=fetch ALU_OP=add ALU_AZERO
		aop=reg bop=qreg wa isa ldcc;				// Ra = Q;  CC; jump to fetch
	mpcop=next;

movb_ri:incpc spc rd ldmdr extra=2;					// mdr <- [PC]; PC++;
	incpc spc rd ldmdr rdmdr ldql ALU_OP=add ALU_AZERO lddata bop=din; // mdr <- [PC]; PC++; Q.L <- mdr
	rdmdr ldqh ALU_OP=add ALU_AZERO bop=reg sb=0;			// Q.H = 0;
	mpcop=jump mpccond=jmp mpcaddr=fetch ALU_OP=add ALU_AZERO
		aop=reg bop=qreg wa isa ldcc;				// Ra = Q;  CC; jump to fetch
	mpcop=next;

movb_rd:isb aop=idata bop=reg idata=0 ALU_OP=add ldmar dout  extra=3;	// mar <- Rb
_mbrd:	rd ldmdr;							// mdr <- [mar]
	mpcop=jump mpccond=jmp mpcaddr=fetch lddata rdmdr isa wa;	// Ra <- mdr
	ALU_OP=add idata=0 bop=idata aop=reg isa ldcc;			// CC

movw_rd: isb aop=idata bop=reg idata=0 ALU_OP=add ldmar dout  extra=3;	// mar <- Rb
_mwrd:	rd ldmdr incmar;						// mdr <- [mar++]
	rd ldmdr lddata rdmdr bop=din ALU_AZERO ALU_OP=add ldql;	// Q.L <- mdr; mdr <- [mar++]
	lddata rdmdr bop=din ALU_AZERO ALU_OP=add ldqh;			// Q.H <- mdr;
	mpcop=jump mpccond=jmp mpcaddr=fetch ALU_AZERO bop=qreg
		ALU_OP=add isa wa ldcc;					// Ra <- Q; CC
	mpcop=next;

movb_rx: incpc spc rd ldmdr extra=9;					// mdr <- [PC]; PC++;
	incpc spc rd ldmdr rdmdr ldql
		ALU_OP=add ALU_AZERO lddata bop=din;			// mdr <- [PC]; PC++; Q.L <- mdr
	mpcop=jump mpccond=jmp mpcaddr=_mbrd rdmdr ldqh
		ALU_OP=add ALU_AZERO lddata bop=din;			// Q.H = mdr; goto _mbrd
	isb aop=qreg bop=reg
		ALU_OP=add ldmar dout;					// mar <- Rb+Q;

movw_rx: incpc spc rd ldmdr extra=9;					// mdr <- [PC]; PC++;
	incpc spc rd ldmdr rdmdr ldql
		ALU_OP=add ALU_AZERO lddata bop=din;			// mdr <- [PC]; PC++; Q.L <- mdr
	mpcop=jump mpccond=jmp mpcaddr=_mwrd rdmdr ldqh ALU_OP=add
		 ALU_AZERO lddata bop=din;				// Q.H = mdr;
	isb aop=qreg bop=reg ALU_OP=add
		ldmar dout;						// mar <- Rb+Q

movb_dr: isa aop=reg bop=idata idata=0 ALU_OP=add ldmar dout ldcc;	// mar <- Ra
_mbdr:	isb aop=idata bop=reg idata=0 ALU_OP=add ldmdr dout;		// mdr <- Rb
	mpcop=jump mpccond=jmp mpcaddr=fetch wrt;			// [mar] <- mdr; jump to fetch
	mpcop=next;

movw_dr: isa aop=reg bop=idata idata=0 ALU_OP=add ldmar dout ldcc;	// mar <- Ra
_mwdr:	isb aop=idata bop=reg idata=0 ALU_OP=add ldmdr dout;		// mdr <- Rb.L
	wrt;								// [mar] <- mdr;
	isb aop=idata bop=reg idata=0 ALU_OP=add ldhmdr dout incmar;	// mdr <- Rb.H; mar++;
	mpcop=next;
	mpcop=jump mpccond=jmp mpcaddr=fetch wrt;			// [mar] <- mdr; jump to fetch
	mpcop=next;

movb_xr: incpc spc rd ldmdr extra=9;					// mdr <- [PC]; PC++;
	incpc spc rd ldmdr rdmdr ldql ALU_OP=add ALU_AZERO lddata bop=din; // mdr <- [PC]; PC++; Q.L <- mdr
	mpcop=jump mpccond=jmp mpcaddr=_mbdr rdmdr ldqh ALU_OP=add
		 ALU_AZERO lddata bop=din;				// Q.H = mdr; jump to _mbdr
	isa aop=reg bop=qreg ALU_OP=add	ldmar dout ldcc;		// mar <- Ra+Q


movw_xr: incpc spc rd ldmdr extra=9;					// mdr <- [PC]; PC++;
	incpc spc rd ldmdr rdmdr ldql ALU_OP=add ALU_AZERO lddata bop=din; // mdr <- [PC]; PC++; Q.L <- mdr
	mpcop=jump mpccond=jmp mpcaddr=_mwdr rdmdr ldqh ALU_OP=add
		 ALU_AZERO lddata bop=din;				// Q.H = mdr; jump to _mwdr
	isa aop=reg bop=qreg ALU_OP=add	ldmar dout ldcc;		// mar <- Ra+Q

movw_xx: mpcop=next  extra=7;
movb_xx: mpcop=next  extra=7;
movw_xd: mpcop=next  extra=7;
movb_xd: mpcop=next  extra=7;
movw_dd: mpcop=next  extra=7;
movb_dd: mpcop=next  extra=7;
movw_dx: mpcop=next  extra=7;
movb_dx: mpcop=next  extra=7;
	mpcop=next  extra=7;

movw_di: isa bop=idata idata=0 ALU_OP=add dout ldmar;			// mar <- Ra
_mwdi:	incpc spc rd ldmdr extra=2;					// mdr <- [PC]; PC++;
	incpc spc rd ldmdr rdmdr ldql ALU_OP=add ALU_AZERO lddata bop=din; // mdr <- [PC]; PC++; Q.L <- mdr
	rdmdr ldqh ALU_OP=add ALU_AZERO lddata bop=din;			// Q.H = mdr;
	aop=idata bop=qreg idata=0 ALU_OP=add ldmdr dout;		// mdr <- Q.L
	wrt;								// [mar] <- mdr;
	aop=idata bop=qreg idata=0 ALU_OP=add ldhmdr dout incmar;	// mdr <- Q.H; mar++;
	mpcop=next;
	mpcop=jump mpccond=jmp mpcaddr=fetch wrt;			// [mar] <- mdr; jump to fetch
	mpcop=next;

movb_di: aop=reg isa bop=idata idata=0 ALU_OP=add dout ldmar extra=0x21;// mar <- Ra
_mbdi:	incpc spc rd ldmdr;						// mdr <- [PC]; PC++;
	incpc spc rd ldmdr rdmdr ldql ALU_OP=add ALU_AZERO lddata bop=din; // mdr <- [PC]; PC++; Q.L <- mdr
	aop=idata bop=qreg idata=0 ALU_OP=add ldmdr dout;		// mdr <- Q.L
	mpcop=jump mpccond=jmp mpcaddr=fetch wrt;			// [mar] <- mdr; jump to fetch
	mpcop=next;

movw_xi: incpc spc rd ldmdr extra=2;					// mdr <- [PC]; PC++;
	incpc spc rd ldmdr rdmdr ldql ALU_OP=add ALU_AZERO lddata bop=din; // mdr <- [PC]; PC++; Q.L <- mdr
	mpcop=jump mpccond=jmp mpcaddr=_mwdi rdmdr ldqh
		ALU_OP=add ALU_AZERO lddata bop=din;			// Q.H = mdr; goto _mwdi
	isa aop=reg bop=qreg ALU_OP=add dout ldmar;			// mar <- Ra+Q

movb_xi: incpc spc rd ldmdr extra=2;					// mdr <- [PC]; PC++;
	incpc spc rd ldmdr rdmdr ldql ALU_OP=add ALU_AZERO lddata bop=din; // mdr <- [PC]; PC++; Q.L <- mdr
	mpcop=jump mpccond=jmp mpcaddr=_mbdi rdmdr ldqh
		ALU_OP=add ALU_AZERO lddata bop=din;			// Q.H = mdr; goto _mwdi
	isa aop=reg bop=qreg ALU_OP=add dout ldmar;			// mar <- Ra+Q



nop:	mpcop=jump mpccond=jmp mpcaddr=fetch extra=0x15;
	mpcop=next;
end

/////////////////////////////////////////////////////////////////////////////
//
// General notes on assembler
// Registers are not saved accross function calls unless explicitly saved.
// Functions return values in R1.
// The register R0 is a special "always 0" register.  Writing to R0 will have no
//  effect other than to set condition codes.
//
//
begin macrocode @ 0x100
ttydata:	.symbol	0x11	// tty data in/out register
ttystatus:	.symbol	0x10	// tty status register

//
// Nodes have the format (note that pointers are two bytes):
//
// struct node {
//   struct node *yes_b;
//   struct node *no_b;
//   char text[100];
// };
//
nsize:	.symbol 104		// Size of a tree node.  That is "sizeof(struct node)".
yes_b:	.symbol	0		// Pointer to yes side
no_b:	.symbol	2		// Pointer to no side
text:	.symbol 4		// Text of node (animal name or question)

//
// Execution starts here.
//
start:
	movw	SP, 0		// Initialize stack pointer
	movw	FP, 0		// Initialize frame pointer

	call	main		// Call main program

sdone:	jmp	sdone		// Infinite loop when main ends


////////////////////
//	main()
//
.proc main
	movw	R13, mend	// Use R13 as a sort of heap pointer

	pushw	welcome		// Push the "Welcome to" message
	call	printf		// Print the message
	add	SP,2		// Restore stack pointer

	movw	(root),top_node	// Initialize root node
	movw	(known),1	// Know one animal

loop:
	pushw	think		// Push the "Think of an animal..." message
	call	printf		// Print the message
	add	SP,2		// Restore stack pointer

	movw	R1, (known)	// Put number of known animals in R1
	pushw	R1		// Push that number on the stack
	pushw	numk		// Push the "I know %d animals" message
	call	printf		// Call printf to print message
	add	SP,4		// Restore stack

	movw	R1,(root)	// Put the root pointer in R1
	pushw	R1		// Push the pointer on the stack
	call	find_animal	// Ask questions to find animal
	add	SP,2		// Restore the stack
	movw	(root),R1	// Save new root

	jmp	loop		// Go back and guess another animal

	ret			// Return to call (actually we should never get here)
.end

////////////////////
//	find_animal(p)		Find the animal. p is the root node in question tree
//
.proc find_animal
tree:	.symbol	-2		// Local variable for tree pointer
	sub	SP,2		// Allocate space for local variables

	movw	R1, 4(FP)	// Get the pointer to top node
	movw	tree(FP),R1	// Store it in "tree"

	movw	R2,yes_b(R1)	// Get pointer in "yes" branch of tree
	cmp	R2,0		// Compare against 0
	jne	get_response	// If not 0, then treat as question node

	pushw	R1		// Push pointer to top node
	call	get_final	// As the final question, and insert new node if necessary
	add	SP,2		// Restore stack
	movw	tree(FP),R1	// Save new root node in "tree"

	jmp	done		// All done, go to final cleanup

get_response:
	movw	R1,tree(FP)	// Put current node pointer in R1
	movw	R4,R1		// R4 = R1
	add	R4,text		// Add offset to make R4 pointer to question
	pushw	R4		// Push text of question
	call	print		// Print question in tree node
	add	SP,2		// Restore stack

	pushw	qprompt		// Push "?" prompt
	call	print		// Print the "? "
	add	SP,2		// Restore stack

	pushw	buf		// Push address of buffer in which to get response
	call	gets		// Read response from user
	add	SP,2		// Restore stack

	pushw	buf		// Push users response on stack
	pushw	yes		// Push "yes" on stack
	call	strcmp		// Compare the strings
	add	SP,4		// Restore stack
	cmp	R1,0		// Check if R1 was 0
	jeq	say_yes		// If so, user answered "yes", goto say_yes

	pushw	buf		// Push users response on stack
	pushw	no		// Push "no" on stack
	call	strcmp		// Compare the strings
	add	SP,4		// Restore stack
	cmp	R1,0		// Check if R1 was 0
	jeq	say_no		// If so, user answered "no", goto say_no

	pushw	yesno		// Push the "yes or no" error message
	call	print		// Print error message
	add	SP,2		// Restore stack
	jmp	get_response	// Go back and ask again

say_yes:
	movw	R1,tree(FP)	// Put current tree pointer in R1
	movw	R2,yes_b(R1)	// Put "yes" branch pointer in R2
	pushw	R2		// Push the yes branch on the stack
	call	find_animal	// Recursively ask the next question
	add	SP,2		// Restore the stack
	movw	R2,tree(FP)	// Get tree pointer again
	movw	yes_b(R2),R1	// Update the "yes" branch
	jmp	done		// Finish up and prepare to return to caller
say_no:
	movw	R1,tree(FP)	// Put current tree pointer in R1
	movw	R2,no_b(R1)	// Put "no" branch pointer in R2
	pushw	R2		// Push the no branch on the stack
	call	find_animal	// Recursively ask the next question
	add	SP,2		// Restore the stack
	movw	R2,tree(FP)	// Get tree pointer again
	movw	no_b(R2),R1	// Update the "no" branch
	jmp	done		// Finish up and prepare to return to caller

done:
	movw	R1,tree(FP)	// Put new root node in R1
	ret			// Return to caller
.end

////////////////////
//	get_final(p)		Ask final question (at node p), and update tree if necessary.
//
.proc get_final
tree:	.symbol	-2		// Local variable for tree pointer
animal_node:	.symbol	-4	// Local variable for new animal node pointer
discrim_node:	.symbol	-6	// Local variable for new question node pointer
	sub	SP,6		// Allocate space for local variables

get_response:
	movw	R1, 4(FP)	// Get pointer to current node
	movw	tree(FP),R1	// Save it in the 'tree' variable

	movw	R1, 4(FP)	// Put pointer to tree in R1
	add	R1,text		// Advance R1 to animal name text
	pushw	R1		// Push pointer to animal name on stack
	pushw	isita		// Push the "is it a..." string pointer
	call	printf		// Print the final question
	add	SP,4		// Restore stack

	pushw	buf		// Push buffer for response
	call	gets		// Get the response
	add	SP,2		// Restore the stack

	pushw	buf		// Push response on stack
	pushw	yes		// Push "yes" on stack
	call	strcmp		// Compare strings
	add	SP,4		// Restore stack
	cmp	R1,0		// See if a 0 was returned
	jeq	win		// If so, we guessed the animal

	pushw	buf		// Push response on stack
	pushw	no		// Push "no" on stack
	call	strcmp		// Compare strings
	add	SP,4		// Restore stack
	cmp	R1,0		// See if a 0 was returned
	jeq	loose		// If so, we did not guess the animal

	pushw	yesno		// Push the "yes or no" error message
	call	print		// Print it
	add	SP,2		// Restore stack
	jmp	get_response	// Go back and ask again

win:
	pushw	winmsg		// Push the (computer) win message
	call	printf		// Print it
	add	SP,2		// Restore stack
	jmp	done		// All done, go finish up and return

loose:
	pushw	loosemsg	// Push the (computer) loose message
	call	printf		// Print it
	add	SP,2		// Restore stack

	pushw	nsize			// Push number of bytes in a node
	call	malloc			// Allocate memory for new animal node
	add	SP,2			// Restore stack
	movw	animal_node(FP),R1	// Put address of new node in animal_node

	movw	yes_b(R1),0		// Intialize yes branches
	movw	no_b(R1),0		// Intialize no branches
	add	R1,text			// Move R1 to point to text area
	pushw	R1			// Push text buffer
	call	gets			// Get animal name
	add	SP,2			// Restore stack

	movw	R1,tree(FP)		// Put pointer to exiting node in R1
	add	R1,text			// Advance to the text field
	pushw	R1			// Push pointer to old animal name
	movw	R1,animal_node(FP)	// Put pointer to new node in R1
	add	R1,text			// Advance to the text field
	pushw	R1			// Push new animal name on stack
	pushw	dscrim			// Push "what is difference" question
	call	printf			// Print the question
	add	SP,6			// Restore pointer

	pushw	nsize			// Allocate memory for discrimination node
	call	malloc			// Allocate memory for new discrimination node
	add	SP,2			// Restore stack
	movw	discrim_node(FP),R1	// Put address of new node in discrim_node

	add	R1,text			// Advance pointer to text field of discrimination node
	pushw	R1			// Push pointer to text buffer to input question
	call	gets			// Input the question
	add	SP,2			// Restore pointer

	movw	R1, (known)		// Put number of known animals in R1
	add	R1,1			// Increment R1
	movw	(known),R1		// Store number of known animals back in "known"

L1:	movw	R1,animal_node(FP)	// Put pointer to new animal node in R1
	add	R1,text			// Advance R1 to the text field
	pushw	R1			// Push new animal name on stack
	pushw	which			// Push the "..correct answer for..." message.
	call	printf			// Print the message
	add	SP,4			// Restore stack

	pushw	buf			// Push text buffer on stack
	call	gets			// Input a "yes" or "no"
	add	SP,2			// Restore stack

	pushw	buf			// Push user response
	pushw	yes			// Push string "yes"
	call	strcmp			// Compare strings
	add	SP,4			// Restore stack
	cmp	R1,0			// Is the result 0?
	jeq	L2			// If so, goto L2.  New animal is on "yes" branch

	pushw	buf			// Push user response
	pushw	no			// Push string "no"
	call	strcmp			// Compare strings
	add	SP,4			// Restore stack
	cmp	R1,0			// Is the result 0?
	jeq	L3			// If so, goto L3.  New animal is on "no" branch

	pushw	yesno			// Push the "yes or no" error message
	call	printf			// Print it
	add	SP,2			// Restore stack
	jmp	L1			// Go back and ask again

//
// Insert new animal on yes branch of new question
//
L2:	movw	R1,discrim_node(FP)	// Put new question node in R1
	movw	R2,tree(FP)		// Put old animal node in R2
	movw	R3,animal_node(FP)	// Put new animal node in R3
	movw	yes_b(R1),R3		// Set yes branch to new node
	movw	no_b(R1),R2		// Set no branch to old node
	movw	tree(FP),R1		// Save R1 to tree pointer
	jmp	done			// Finish up and return

//
// Insert new animal on no branch of new question
//
L3:	movw	R1,discrim_node(FP)	// Put new question node in R1
	movw	R2,tree(FP)		// Put old animal node in R2
	movw	R3,animal_node(FP)	// Put new animal node in R3
	movw	yes_b(R1),R2		// Set yes branch to old node
	movw	no_b(R1),R3		// Set no branch to new node
	movw	tree(FP),R1		// Save R1 to tree pointer
	jmp	done			// Finish up and return

done:
	movw	R1,tree(FP)		// Put tree pointer into return register R1
	ret				// Return to caller
.end

////////////////////
//	malloc(n) -> p	Allocate n bytes and return address in R1.  Allocated memory
//			can not be freed.
//
.proc malloc
	movw	R2, 4(FP)	// Get number of bytes
	movw	R1,R13		// Pointer to block of memory
	add	R13,R2		// Update heap pointer
	ret
.end

////////////////////
//	print(s)	Print the string s
//
.proc print
	movw	R1, 4(FP)	// Get parameter (string address)
loop:	movb	R2, (R1)	// Put character R1 is pointing to in R2
	jeq	done		// If it was a 0, this is the end of the string
	movb	(ttydata),R2	// Move char to the tty data register
	movb	(ttystatus),#1	// Signal tty controller to print character
	add	R1, #1		// Move pointer to next char
	jmp	loop		// Go back and print more
done:	ret
.end

////////////////////
//	printf(s,...)	Print the format string
//
.proc printf
ptr:	.symbol	-2		// Local var for string pointer
arg:	.symbol -4		// Local var for argument pointer
	sub	SP,4		// Allocate 4 bytes for local variables

	movw	R3,FP		// Assign R3 and add an offset so that it points
	add	R3,6		//   to the argument after the control string

	movw	R1, 4(FP)	// Put the control string pointer in R1
loop:	movb	R2, (R1)	// Get the next char from control string
	jeq	done		// If it was a 0, this is the end of the string

	cmp	R2, '%'		// See if it is the '%' character
	jne	cout		// If not goto cout, and simply print it

	add	R1,1		// Advance the string pointer
	movb	R2,(R1)		// Get the next char
	add	R1,1		// Advance the string pointer again

//switch (R2)
L1:	cmp	R2,'s'		// See if this is a "%s" conversion
	jne	L2		// If not, goto L2

// case 's' :
	movw	ptr(FP),R1	// Save the string pointer value
	movw	arg(FP),R3	// Save the argument pointer value
	movw	R2,(R3)		// Get address of string (from arguments) to print
	pushw	R2		// Push it on the stack
	call	print		// Print the string
	add	SP,2		// Restore stack pointer
	movw	R1,ptr(FP)	// Restore string pointer value
	movw	R3,arg(FP)	// Restore argument pointer value
	add	R3,2		// Advance argument pointer to next argument
	jmp	loop		// Go back and print more

L2:	cmp	R2,'d'		// See if this is a "%d" conversion
	jne	loop		// If not, ignore unknown conversion

// case 'd' :
	movw	ptr(FP),R1	// Save the string pointer value
	movw	arg(FP),R3	// Save the argument pointer value
	movw	R2,(R3)		// Get number (from arguments) to print
	pushw	R2		// Push it on the stack
	call	nprint		// Go print the decimal number
	add	SP,2		// Restore stack pointer
	movw	R1,ptr(FP)	// Restore string pointer value
	movw	R3,arg(FP)	// Restore argument pointer value
	add	R3,2		// Advance argument pointer to next argument
	jmp	loop		// Go back and print more

cout:
	movb	(ttydata),R2	// Put character in tty data register
	movb	(ttystatus),#1	// Signal tty controller to print character
	add	R1, #1		// Advance to next char in control string
	jmp	loop		// Go back and print more

done:	ret			// All done, return to caller
.end

////////////////////
//	nprint(d)	Print the number d in decimal
//
.proc nprint
	movw	R1, 4(FP)	// Move argument value to R1
	jeq	zprint		// If it was zero, goto zprint

	pushw	R1		// Push value to print on stack
	call	nprint_aux 	// Call aux function to print number
	add	SP,2		// Restore stack pointer
	ret			// All done, return to caller

zprint:	movb	(ttydata), '0'	// Put the ascii value of '0' in tty data register
	movb	(ttystatus), #1	// Signal tty controller to print character
	ret			// All done, return to caller
.end

////////////////////
//	nprint_aux(d)	Print the number d in decimal (but prints nothing if d is zero)
//
.proc nprint_aux
digit:	.symbol	-2		// Digit to print
	sub	SP, 2		// Allocate space for local variables

	movw	R1, 4(FP)	// Get argument
	jeq	done		// If zero, return

	// To print the number n we compute
	//     R2 = n / 10, and
	//     R3 = n % 10
	// We can then recursively call nprint_aux to print all
	// but the lowest digit, then print the lowest digit ourselves
	//
	movw	R2, R1		// R2 = R1
	div	R2, 10		// R2 = R2 / 10
	movw	R3, R1		// R3 = R1
	mod	R3, 10		// R3 = R3 % 10
	movw	digit(FP),R3	// save lowest digit value

	pushw	R2		// Push the higher digits
	call	nprint_aux	// Recursively call ourselves to print them
	add	SP,2		// Restore stack pointer

	movw	R3,digit(FP)	// Restore digit value
	add	R3, '0'		// Add ascii for '0' to get ascii for digit
	movb	(ttydata),R3	// Put char in tty data register
	movb	(ttystatus),#1	// Signal tty controller to print character
done:
	ret			// All done, resturn to caller
.end

////////////////////
//	strcmp(a,b)		Compare strings a and b
//
.proc	strcmp
	movw	R2,4(FP)	// Get pointer to first string
	movw	R3,6(FP)	// Get pointer to second string

loop:	movb	R1,(R2)		// Get next char from R2
	jeq	eos		// If end of string, goto eos
	movb	R4,(R3)		// Get next char from R3
	jeq	eos		// If end of string, goto eos

	sub	R1,R4		// Compute difference in R1
	jne	done		// If difference was not 0, we are done

	add	R2,1		// Advance to next char in R2
	add	R3,1		// Advance to next char in R3
	jmp	loop		// Go compare more

eos:
	movb	R4,(R3)		// Get next char from R3 again (in case R1 was eos)
	sub	R1,R4		// Compute difference in R1

done:
	ret			// Return to caller, result is in R1
.end


////////////////////
//	gets(b)		Reads chars from tty into b.
//
.proc gets
	movw	R1, 4(FP)	// Get pointer to buffer
	movw	R4,R1		// Save starting pointer in R4

	//
	// Poll for a characcter
	//
cwait:	movb	R2,(ttystatus)	// Get ready status
	and	R2, #2		// Test if a char is ready
	jeq	cwait		// If not, go back and wait some more

	movb	R3,(ttydata)	// Get char from tty data register
	movb	(ttystatus),#2	// Signal char received
	cmp	R3,'\r'		// Compare against return char
	jeq	done		// Exit if return received

	cmp	R3,'\b'		// Compare against backspace char
	jeq	del_char	// Delete char if backspace

	cmp	R3,0x7f		// Compare against delete char
	jeq	del_char	// Delete char if delete character

	movb	(ttydata),R3	// Put char to echo in tty data register
	movb	(ttystatus),#1	// Signal tty controller to print character

	movb	(R1),R3		// Save in buffer
	add	R1,1		// increment pointer

	jmp	cwait		// Go back and get another char

del_char:
	cmp	R1,R4		// Compare current pointer against start of line
	jle	bell		// If already at start of line, ring bell

	movb	R3, 0x7f	// Make sure delete char is in R3
	sub	R1,1		// Decrement pointer
	movb	(ttydata),R3	// Echo backspace
	movb	(ttystatus),#1	// Signal tty controller to print character

	jmp	cwait		// Go back and get another char

bell:
	movb	R3, 7		// Put bell char in R3
	movb	(ttydata),R3	// Echo bell
	movb	(ttystatus),#1	// Signal tty controller to print character
	jmp	cwait		// Go back and get another char

done:
	movb	(ttydata),'\n'	// Output newline
	movb	(ttystatus),#1	// Signal tty controller to print character
	movb	(R1),0		// Put eos in the string we just read
	ret			// Return to caller
.end

buf:	.bss	128		// Buffer for input
known:	.bss	2		// Number of animals known
root:	.bss	2		// root of animals tree

top_node:
	.short	0		// Pointer to "yes" node
	.short	0		// Pointer to "no" node
	.byte	"aardvark",0	// Animal name or question text

welcome: .byte	"\nWelcome to Animals.\n", 0
think:	.byte	"\nThink of an animal and I will try to guess what it is.\n", 0
numk:	.byte	"I currently know %d animals.\n\n", 0
isita: 	.byte	"Is the animal you are thinking of a %s? ",0
yes:	.byte	"yes",0
no:	.byte	"no",0
yesno:	.byte	"Please type 'yes' or 'no'.\n",0
winmsg:	.byte	"I guessed your animal!!!\n\n",0
loosemsg: .byte	"I could not guess your animal.  What was your animal? ",0
dscrim:	.byte	"Enter a question that would distinguish a %s from a %s.\n> ",0
which:	.byte	"The correct answer for a %s is? ",0
nl:	.byte	"\n",0
qprompt: .byte	"? ",0
mend:

end