VHDL: Enter the code monkey
|
Now we're going to start laying down some actual code. This section develops the framework of a VHDL file and prepares your black box, which will be filled in by the next section.
Some basics
First things first: comments! If you've ever done any programming, you know how wonderful comments are. If you've not done much coding, then remember that comments are not as pointless as they seem. Add descriptive comments or be murdered in your sleep 15 years from now by an irate engineer who has to decipher your legacy code. VHDL has no block comments, only line comments (the comment goes from the comment marker to the end of the line). The comment marker is a double dash with no spacing between them. Many development environments (for example Xilinx ISE) will auto-generate a large block of comments at the top of each file to be used to describe the file (who, what, where, when, why, how, and so on).
Secondly are the libraries. These are like the include statements in C/C++. Honestly I forget which libraries have exactly what tools in them, but a standard block will cover mostly any design you work on:
-- Lines beginning with "--" are comments -- This standard block will cover you for most designs. -- Some development environments will auto-generate library "use" statements that may or may not include this set library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL;
Coding your black box
Every VHDL file defines an entity. Every entity has two parts to it: a port (pin) list and an architecture. The port list defines the black box of your component. First you declare that you are defining an entity, then inside the entity you declare your ports.
As discussed above, there are three types of ports: in, out, inout. There are also lines and buses. A line is referred to as a STD_LOGIC and a bus is referred to as a STD_LOGIC_VECTOR. There are also two flavors of STD_LOGIC_VECTORS: downto and to. A "downto" bus has the most significant bit (MSB) associated with the largest subscript, and a "to" bus has the least significant bit (LSB) associated with the largest subscript. A bus with N lines need not have subscripts running from 0 to N-1. They can run from 1 to N or N+84 downto 85; there is complete freedom as to the subscript offset. Most people who learned coding in C or Java or something similar will generally stick to 0 to N-1 or N-1 downto 0 out of habit unless the situation calls for something else (for clarity, usually). Often an engineer will choose either "to" or "downto" (I happen to prefer "downto") and stick with it. However mixing "to" and "downto" has it's uses; for example connecting a "downto" to a "to" will reverse the order of the bits in the bus. For a beginner, I recommend picking one and sticking to it.
entity component_name is Port ( line_in : in STD_LOGIC; -- a single input line bus_in1 : in STD_LOGIC_VECTOR (7 downto 2); -- a 6-bit "downto" input bus bus_in2 : in STD_LOGIC_VECTOR (0 to 8); -- a 9-bit "to" input bus line_out : out STD_LOGIC; -- a single output line bus_inout : inout STD_LOGIC_VECTOR (1 to 10) -- a 10-bit bidirectional bus ); end component_name;
Note that each port in the list is separated by a semicolon, but the last port does not have a semicolon after it. The semicolons in this case are not end-of-line markers, but are list delimiters.
Example: coding your black box
The library use statements and port list for the DAC emulator are shown here. Note the terribly non descriptive comments for the input lines. This kind of comment is not only useless (as it merely repeats the signal name), but is mocking the pain of later engineers who have to figure out what a signal like invSYNC might do. This will result in ninjas attacking your home. To avoid this fate, I recommend better comments such as, "an active-low flag to begin transmission," for invSYNC or, "serial clock line," for SCLK.
library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity DAC_emulator is Port ( SCLK : in STD_LOGIC; -- SCLK invRESET : in STD_LOGIC; -- /RESET invSYNC : in STD_LOGIC; -- /SYNC D_in : in STD_LOGIC; -- D_in -- 32 14-bit output channels ch00 : out STD_LOGIC_VECTOR (13 downto 0); ch01 : out STD_LOGIC_VECTOR (13 downto 0); ch02 : out STD_LOGIC_VECTOR (13 downto 0); ch03 : out STD_LOGIC_VECTOR (13 downto 0); ch04 : out STD_LOGIC_VECTOR (13 downto 0); ch05 : out STD_LOGIC_VECTOR (13 downto 0); ch06 : out STD_LOGIC_VECTOR (13 downto 0); ch07 : out STD_LOGIC_VECTOR (13 downto 0); ch08 : out STD_LOGIC_VECTOR (13 downto 0); ch09 : out STD_LOGIC_VECTOR (13 downto 0); ch10 : out STD_LOGIC_VECTOR (13 downto 0); ch11 : out STD_LOGIC_VECTOR (13 downto 0); ch12 : out STD_LOGIC_VECTOR (13 downto 0); ch13 : out STD_LOGIC_VECTOR (13 downto 0); ch14 : out STD_LOGIC_VECTOR (13 downto 0); ch15 : out STD_LOGIC_VECTOR (13 downto 0); ch16 : out STD_LOGIC_VECTOR (13 downto 0); ch17 : out STD_LOGIC_VECTOR (13 downto 0); ch18 : out STD_LOGIC_VECTOR (13 downto 0); ch19 : out STD_LOGIC_VECTOR (13 downto 0); ch20 : out STD_LOGIC_VECTOR (13 downto 0); ch21 : out STD_LOGIC_VECTOR (13 downto 0); ch22 : out STD_LOGIC_VECTOR (13 downto 0); ch23 : out STD_LOGIC_VECTOR (13 downto 0); ch24 : out STD_LOGIC_VECTOR (13 downto 0); ch25 : out STD_LOGIC_VECTOR (13 downto 0); ch26 : out STD_LOGIC_VECTOR (13 downto 0); ch27 : out STD_LOGIC_VECTOR (13 downto 0); ch28 : out STD_LOGIC_VECTOR (13 downto 0); ch29 : out STD_LOGIC_VECTOR (13 downto 0); ch30 : out STD_LOGIC_VECTOR (13 downto 0); ch31 : out STD_LOGIC_VECTOR (13 downto 0) ); end DAC_emulator;
Filling the black box
Now that you have your black box, you need to fill it in. The interior of the box is called the architecture. First you declare that you are making an architecture and what it is an architecture of. Then inside of there you have two sections: declaring your components, signals, and variables, and the actual control flow of the circuit.
Declarations come in three types (that I know of; there may be more):
- Components are other blocks that you are including in your circuit. Inside of a component is another circuit, which may contain still further components.
- Signals are internal signals. They are the wires connecting all of your components. As with pins they come in STD_LOGIC and STD_LOGIC_VECTOR, but they do not have I/O polarity as they are strictly internal. As a general rule you attach a signal to a pin so that you can either read from or write to that signal.
- Variables are like variables in a normal software language. They can only be used inside of a process (which I will discuss later). Often in simple designs you can work around using variables and stick with just signals. Unfortunately, you'll have to do a bit of online research to find out much detail about variables, as I hardly use them in these designs.
Components and signals should all be declared up front in the declaration section. As I mentioned, you can only use a variable inside of a process, so you would declare the variable within the process.
The framework for the architecture is shown here.
architecture arch_name of component_name is signal line_sig : STD_LOGIC; -- an internal signal line signal bus_sig : STD_LOGIC_VECTOR (4 downto 2); -- a 3-bit internal signal bus begin -------------------- -- code goes here -- -------------------- end arch_name;
As you can see, declarations happen before the "begin" and code goes after the "begin". Even if you have no signals, components, or variables to declare, you must include the "begin" statement.
You may have noticed that I did not show a component. Components require more discussion and will be demonstrated later. Because the DAC emulator uses components, I will discuss that code later. First we will discuss the components inside of the emulator and the final product will come later.