You are on page 1of 22

A short and brief tutorial for Digital Circuit IC Design using VHDL in Altera Quartus II

Objective
The objective of this tutorial is to provide some introduction and basic knowledge about
how to use Altera Quartus II and VHDL to do some basic Digital IC design and test and verify
your design using testbench and ModelSim.
Note: again, this tutorial is not meant to bring you to the depth and fully understand and
whole subject/field of Digital IC design, but provide you basic understanding and teach you how
to use use some fundamental functions of the design tools and software suite.
Terminology and Keyword
(Please do not worry if you dont understand all the terms deeply, it will be more clear as you
follow the tutorial)
Digital IC (including concepts of I/O ports, wire, bus, register, memory, address and clock; you
may skip if you know all these terms.) - In an abstract and top level of point of view, digital IC is
Integrated Circuit that can be imagined or abstractly deemed(because the physics of how
exactly the digital IC is fabricated/manufactured is beyond the scope of this tutorial) as all the
signals and data transmitted, communicated, stored or even processed are in the representation
of logic 1 or 0; meaning True or False; or, Voltage High or Low, respectively. Digital IC, like
most IC, has input port(s) and output port(s) that to take signal/data in and send signal/data out,
respectively. The medium used to transmit the signal could be single wire(can be used to
represent single bit signal) or buses(a collection of more than one wire, e.g. 8 wires form an 8bit(one byte) bus can be used to carry/transmit signal 11010011 at the same instantaneous
time). Wires and buses are used to transmitted data or signal depending the data or signal are
single bit or multi-bits. Very often, you want to temporarily store signal or data for some
moments, either wait to be processed/analyzed or wait to be used as a reference to make
decision, then a concept called register can be used to hold/store the data for a desired period
of time. Signals or data on buses are transient; won't last long. Please imagine two registers as
two storage warehouses apart, and wires or buses are the roads paved between the
warehouses. You may want to move the merchandise(data) between warehouses(registers) via
the roads(wires or buses), but you dont want to use the roads(wires or buses) to store your
merchandises(data). And, when you want to look/find/acquire some merchandise for specific
purposes(e.g. analyzed or processed), you follow the roads(wires, buses) to the specific
warehouses(registers). Usually, the register resembles as bus, a one bit register can store
single bit data; an 32-bit register can store 4-byte(4*8=32) data. When you want to store data
more than certain amount of bits(or bytes) and each data has specific meaning/purpose, you
want to make it very organized, in this case the concept of memory comes into play. A memory
can be deemed as a collection(stack) of registers of equal length(size) e.g. a stack of sixteen
32-bit registers, each 32-bit register can hold/store 8 byte data. So, how do you know which 8byte register you want to read or write? Yes, you need address to identify them. Once you
assign unique(i.e. no duplication) address to each 8-byte register, you can easily
access(read/write) the data from/to the specific register you want and keep your data storage
somewhat more organized and easier to manage. Because the address used to identify the

registers in the memory needs to be unique, so a memory formed by 16 equal size registers
needs at least a 4-bit register(or bus) to specify the address of the register in the
memory(address 0000 to address 1111 in binary). Finally, you may want to ask how fast you
want to be able to transfer or process data in a Digital IC, the answer is that it depends on how
fast of clock signal you can provide/supply? Remember the earlier mentioned scenario that the
merchandise(data) are transferred between warehouses(registers) via the roads(wires or
buses). A clock can be analogized as how many car per second you want to use to transfer the
merchandise(assuming each merchandise is one bit). If you supply a 10MHz clock, that means
you have the capability of using 10*10^6 cars to help you transfer merchandise(data) between
warehouses(registers), i.e. with a 10 MHz clock and single wire, you could possibly transfer 10M
bit data from one register to another register in just one second. And if you use a 10 bit bus to
transfer, you could reduce the time to 0.1 second (transmission time = amount of data/
(transmission speed per wire * # of wires) => 10M bit/(10M bit/second/wire * 10 wire) = 0.1
second.). For more detail about Digital IC, you may need to do google search for some
reference, that includes logic gates, flip-flops, latches, etc.
Digital IC Design - The functions of registers, memory and data transfer mentioned in the Digital
IC(section above) as well as if one wants to use Digital IC to perform any mathematical
operation or algorithm implementation all require small to huge amount of logic gates, and
components. The jobs and tasks to think and use the logic gates and components and put
together to achieve what you want to do e.g. mathematical operation, algorithm, signal
processing, image processing, data transfer, interface with other ICs, etc is called Digital IC
design. For more detail about Digital IC design, you may need to do google search.
FPGA - abbreviated from Field Programmable Gate Array. As mentioned in the Digital IC Design
section, it could require huge amount(more than 10,000) of logic gates and components to
implement your idea to design a Digital IC that executes your desired task. Therefore, nowaday,
its unrealistic and time consuming to hand design the digital IC using physical discrete logic
components. FPGA is an IC, which contains millions of reprogrammable logic gates and
components(i.e. you can use a language called VHDL(discussed in next section) to
program/configure it), so that it replaces the tedious jobs/works to design the digital ICs using
physical discrete components and hard wires/buses. Two major FPGA vendors/manufactures in
2015 were Altera and Xilinx.
VHDL - abbreviated from VHSIC Hardware Description Language, where VHSIC stands for Very
High Speed Integrated Circuit. VHDL is a high level language that can be used to describe the
relation, interconnection, interaction and behavior of the logic gates and components, such that it
replaces the work using hard wires and physical discrete components to design the ICs. Once
designers implement/express their idea in VHDL, VHDL can be compiled to represent the design of
digital IC and be programed to FPGA. Then FPGA behaves just like the digital IC that performs
designers idea if implementation is correct. Therefore, each completely written VHDL script can be
deemed as a Digital IC component.
Quartus II - an Integrated Development Environment software suite, released by Altera, can be used
to do the VHDL Programming, design check, testing and verification for Alteras FPGA product. In

2015, version 14.1, 15.0 and 15.1 are the stable and latest version, respectively. In this tutorial, we
are using Quartus II 64-bit Version 15.0 Web Edition.
Timing Diagram - Remember the clock mentioned in the Digital IC and Digital IC Design section,
where the speed of clock really determines how fast the data can be transferred and processed.
Timing issue is probably the most important thing need to be considered in the Digital IC Design,
because it does not just determine the speed, it also determines how a data/signal could be
interpreted/perceived. Timing Diagram is a diagram that shows how the signals change with respect
to time of your design, and it also shows how you should design a Digital IC to interact/communicate
with the other ICs and vice versa. Imagine how traffic lights/signs are important to the traffic, so
understand how to read all the traffic lights/signs is very important. Timing is critical to the design and
signal transmission, so understand how to read the complex timing diagram is very important.
ModelSim - ModelSim is a software suite that provide the tools and environment to show the timing
diagram, which is indispensable for debug, test and verify the VHDL design.
Testbench - Designers usually first write their own Digital IC design in VHDL, and remember that
each Digital IC has input ports and output ports. In order to debug, test and verify their design,
designers write another VHDL script(acts as another IC to interact/communicate with the one just
designed), which has a generic name called Testbench. Testbench is another VHDL file that can
simulate some signal or data to the inputs of the IC just designed, and monitor the signals outputs to
see the responses. All these tasks need to be done under ModelSim by looking at the timing
diagram.
Materials
Quartus II 64-bit Web Edition version 15.0 - Download and install from Alteras website. Since its
straight forward, so no instruction for installation here. Please make sure you also have ModelSimAltera Starter Edition downloaded and installed, it should be installed when you installed the Quartus
II.

Start a project in Quartus II

1. Screenshot speaks more than words (Click on the highlighted Application):

2. File -> New Project Wizard. A dialogue box should pop up. And click Next. Then
follow the following Screenshot:

It may ask you to create a C:/Projects directory, click Yes

3. Click Next:

And, Next again.

4. Select an FPGA Device:

Click Next.

5. Make sure the following and click Next:

Verify the following and click Finish:

6. File -> New -> VHDL File -> click OK


VHDL Design/Coding(*.vhd) - an up and down counter driven by a 50 MHz clock
1.
I suggest you type the following and read each line and comment carefully. And please
correct me if you think my comments are wrong/inaccurate or not appropriate enough. And for any
doubt, feel free to ask me or consult google.
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
-- Entity IC name
ENTITY ProjectOne IS
-- All input and output ports/bus of the entity
PORT(
CLOCK_50: IN STD_LOGIC; --50MHz = 20 ns per count(increment); 20*10^-9 sec
LEDG: OUT STD_LOGIC_VECTOR(7 downto 0);
LEDR: OUT STD_LOGIC_VECTOR(9 downto 0);
KEY: IN STD_LOGIC_VECTOR(3 downto 0)

);
END ProjectOne;
--Architecture to describe the behavior/task/functions of this Entity IC
Architecture counter of ProjectOne is
-- Declare, Define types for all internal signals, wires, buses, registers, variables
signal prescaler : integer range 0 to 500000:=0;
signal result: integer range 0 to 1023 :=0;
signal prescalerMonitorInsideProcess : integer range 0 to 500000:=0;
signal prescalerMonitorOutsideProcess : integer range 0 to 500000:=0;
begin --begin to describe the behavior/task/function of this Entity IC
------------------------------------------------------------------statements outside the process, known as Concurrent Statements,
--are interpreted/updated immediately and concurrently as soon as any righ-hand-side signal
changes;
--i.e. the value assignment is valid/seen by other signal without any clock cycle delay
--even if the right hand side signal is updated inside some process, the left hand side signal of the
concurrent statement
--updates immediately(in the same cycle or same sensitivity triggering run)
---------------------------------------------------------------------------------------------10 indicates that to convers to 10 bits vector
LEDR<= std_LOGIC_VECTOR(to_unsigned(result,10)); --happens/occurs/updates immediately
whenever result changes; like direct wiring
--as soon as the result is updated somewhere else, the LEDR is
updated simultaneously(no clock delay nor signal triggering delay)
prescalerMonitorOutsideProcess<=prescaler; --concurrent assignment outside the process
becomes valid and seen by other signals
--right after(i.e. the same clock cycle or sensitivity triggering run) the
porcess of the right hand side signal
--completed(suspended)

--down counter or up counter process


process(clock_50)
--declare or initialize some signal used inside this process
begin --begin to describe the function/tasks of this process below

--All statemens inside the process executed sequentially and


--value assign in a process WON'T be seen/become valid to other signals that are also inside the
porcess until "next" clock cycle or "next" sensitivity triggering;
--i.e. the new assigned value is not valid and cannot be seen at the same process executing run
--statements inside the processs executes sequentially; one statement after one
if ( rising_edge(clock_50)) then
if (prescaler<4) then
--increment 5 times, reseet at the 5th:
0,1,2,3,4,0,1,2,3,4
prescaler<=prescaler+1; -- 5* 20*10^-9 sec = 10^-7 sec = 100 ns
else
prescaler<=0;
end if;
end if;
prescalerMonitorInsideProcess<=prescaler; --prescalerMonitorInsideProcess won't
see the just updated prescaler value in "this" clock cycle
--nor "this" sensitivity triggering run until next time the
process is resumed(step in)

if(rising_edge(clock_50) and (prescaler=0)) then


case key(0) is --case statement is used with "WHEN"; use case to detect the change
of key(0)
when '0' =>
if(result<1023)then
result<=result+1; --again the updated result value can only be seen
and valid to the signals in the process at next step in. However, is valid and seen to other signals
outside the process.
else
result<=0;
end if;
when '1' =>
if(result>0)then
result<=result-1;
else
result<=1023;
end if;
when others => null;

end case;

end if;
prescalerMonitorInsideProcess<=prescaler;
end process;
--multiple processes are running/executing parrallel to each other, but the code inside the process is
executed sequentially
--progress counter
process(clock_50)
begin
--ledr<=std_LOGIC_VECTOR(to_unsigned(result,10)); --statement right here, leads to unwanted
one clock cycle(or sensitivity signal triggering) delay to assign to ledr
if(rising_edge(clock_50)) then
if(key(1)='0') then
null;
--ledr<=std_LOGIC_VECTOR(to_unsigned(result,10)); --statement right here, leads to
unwanted one clock cycle(or sensitivity signal triggering) delay to assign to ledr
else --if key(1)/='0'
null;
-for i in 1 to 10 loop
-if(result>102*i) then
-ledr(i-1) <='1';
-else
-ledr(i-1) <='0';
-end if;
--end loop;
end if;
end if;

end process;

end counter;
2.
Ctrl+s to save, click Save. And Project -> Add Current File to Project. On the Project
Navigator Panel, click the File tab at the bottom, you should see ProjectOne.vhd under Files folder.

3.
Processing -> Start Compilation. This should take couple minutes. After Compilation
completed, you should see the following:

4.
Tools -> Netlist Viewers -> RTL Viewer. This shows a top level view of your digital IC design,
which may or may not contains any logic gates, components, input and output port(s) and clock
signal:

Minimize or Close this window.

Create a Testbench(*.vht)
1.
Processing -> Start -> Start Test Bench Template Writer. It may seem nothing happened, but
a test bench file (*.vht) has been created.
2.
File -> Open -> At Files of type:, select Test Bench Output Files(*.vht,*.vt). And, check Add
file to current project
Then, select folder simulation -> modelsim -> select ProjectOne.vht -> Open:

3.
I suggest to look at my following code and comments, and compare with and type into the
ProjectOne.vht
-- Copyright (C) 1991-2015 Altera Corporation. All rights reserved.
-- Your use of Altera Corporation's design tools, logic functions
-- and other software and tools, and its AMPP partner logic
-- functions, and any output files from any of the foregoing
-- (including device programming or simulation files), and any
-- associated documentation or information are expressly subject
-- to the terms and conditions of the Altera Program License
-- Subscription Agreement, the Altera Quartus II License Agreement,
-- the Altera MegaCore Function License Agreement, or other
-- applicable license agreement, including, without limitation,
-- that your use is for the sole purpose of programming logic
-- devices manufactured by Altera and sold by Altera or its
-- authorized distributors. Please refer to the applicable
-- agreement for further details.
-- ***************************************************************************
-- This file contains a Vhdl test bench template that is freely editable to
-- suit user's needs .Comments are provided in each section to help the user
-- fill out necessary details.
-- ***************************************************************************
-- Generated on "12/09/2015 11:23:57"
-- Vhdl Test Bench template for design : ProjectOne
--- Simulation tool : ModelSim-Altera (VHDL)

-LIBRARY ieee;
USE ieee.std_logic_1164.all;
use ieee.numeric_std.all;

--testbench entity IC
ENTITY ProjectOne_vhd_tst IS
--no input/output ports declaration of the testbench entity
END ProjectOne_vhd_tst;
--Arcitecture of the testbench entity; decribes what you want thie testbench entity do
ARCHITECTURE ProjectOne_arch OF ProjectOne_vhd_tst IS
-- internal signals/buses/registers declaration used inside this testbench entity
--usually will be connected to I/O ports of the component IC under tests
-- clock signals declaration to simulate a clock signal
SIGNAL clk_50 : STD_LOGIC := '0';--'X';
--define clock period for the simulated clock signal
constant clk50_period : time := 20 ns;
SIGNAL KEY : STD_LOGIC_VECTOR(3 DOWNTO 0) := "0000";
SIGNAL LEDG : STD_LOGIC_VECTOR(7 DOWNTO 0);
SIGNAL LEDR : STD_LOGIC_VECTOR(9 DOWNTO 0);
--declare the components(ICs) might be used
--declare the unit(component) under test
COMPONENT ProjectOne
PORT (
CLOCK_50 : IN STD_LOGIC;
KEY : IN STD_LOGIC_VECTOR(3 DOWNTO 0);
LEDG : BUFFER STD_LOGIC_VECTOR(7 DOWNTO 0);
LEDR : BUFFER STD_LOGIC_VECTOR(9 DOWNTO 0)
);
END COMPONENT;

BEGIN --begin describe the function/tasks/behavior of thie testbench entity


--instantiate the components(ICs) want to use
--instantiate the unit(component) under test
i1 : ProjectOne
PORT MAP (

-- list connections between master ports and signals


CLOCK_50 => clk_50,
KEY => KEY,
LEDG => LEDG,
LEDR => LEDR
);
init : PROCESS
-- variable declarations inside this process
BEGIN
-- code that executes only once
WAIT;
END PROCESS init;
-- Clock process definitions(clock with 50% duty cycle is generated here).
clk_50_process :process
begin
clk_50 <= '0';
wait for clk50_period /2; --for clk50_period /2 signal is '0'.
clk_50 <= '1';
wait for clk50_period /2; --for next clk50_period /2 signal is '1'.
end process;

--stimulus process
always : PROCESS --(
-- variable declarations

) optional sensitivity list

begin --stimulus statements to Unit Under Test begin below; code executes for every event on
sensitivity list
wait for 10*5*20 ns;
key<= "0001";
wait for 10*5*20 ns;
WAIT;
END PROCESS always;
END ProjectOne_arch;

Launch and Setup ModelSim to debug, test and verify

1. Please look at the Tasks panel to see whether there is any question mark, if there
is, please Processing -> Start Compilation to compile again. There should be no errors
before proceeding.
2. Tools -> Run Simulation Tool -> RTL Simulation. A ModelSim windows application
should be launched.
3. On the left Library Panel, you should see work. Press the + to expand it and see
your entity name, projectone. Right click on the projectone and click Recompile. You should
see some message running in the Transcript window down below and should show Errors: 0,
Warnings: 0
4. Then, go to Compile -> Compile, make sure you are under Library: work and Look
in: modelsim folder. Select ProjectOne.vht, and click Compile.

You should again see some messages running in the Transcript panel down below, and
show Errors: 0, Warnings: 0 in the end. Close the Compile Source Files dialogue box.
5. On the left Library panel, you should see the projectone_vhd_tst appear.
6. Right click the projectone under work and select Edit, and right click the
porjectone_vhd_tst and select Edit. You should see both scripts embedded on your right
hand panel.

7. Right click on the projectone_vhd_tst and click Simulate. You should see the
following:

8. Click the Wave tab at the bottom of the right panel(If you dont see it, please go to
View -> Wave).
9. From left panel, now is on the sim tab, select the projectone_vhd_tst and drag into
the gray area of the Wave panel. Again, from the left panel, select i1, and drag into the gray
area of the Wave panel. You should have some view like the following:

The names in the gray area of in the Wave tab are the signals/data/registers whatever you
should be interested in monitoring.
Start simulation, debug, test and verify
1.
Click on the ProjectOne.vht tab on the right panel. Set some breakpoints on the line where
you want to monitor the progress and signals/data/register by clicking the line number on the left, a
red dot show appear. For this script, I click on lines: 91, 92, 94, 106, 107.
2.
Now, select the ProjectOne.vhd tab, again set the breakpoints at lines where you are
interested in. I click on lines: 36, 39, 53, 54, 55, 61, 64, 78, 93
3.
Click Wave tab to view the timing diagram. Look for the following icons and know their
names because I will refer them by names not by icons.

4.
Change the 100 ps to 100 ns and hit enter, this is the time step/increment, the simulation will
proceed.
5.
Click on Run icon. You should be brought to the 1st breakpoint you set, mine is Line 91 of
the ProjectOne.vht. You can hover your mouse cursor to the signal name e.g. clk_50 to see the
value. You can change to Wave tab and click on the Zoom Full icon to see if anything changed or
updated. The vertical yellow bar is the cursor allowing you to watch the values of the
signals/register/data/buses at that time instant. The values are shown on the 2nd vertical column of
the gray area. You may want to look up some VHDL symbols meaning on google.

6.
Please proceed by keep clicking the Run icon, change the time step if you want and toggle
your tabs back and forth to monitor how signal timing diagram change with respect to the code, and
see if the timing diagram shows what you meant and what you want. The signal with two parallel
lines in the timing diagram indicating that signal is more than one bit, and if any bit of the multiple
bits changed, the two parallel lines cross/intercept at that time instant, and stay parallel till next time
any bit toggles. Read my comments at the same time might also be helpful.
7.
After several runs, you may go to the script(s) and clear most of the breakpoints and hit Run
icon. The following is several runs after 1600 ns:

You can add addition vertical yellow cursor by clicking the Insert Cursor icon, which help you to
measure the time difference.
8.
Then, you can click the Zoom between Cursors icon to zoom in the area between two
cursors:

Notice how the KEY bus toggles at the 1000 ns.


Notice how the LEDR and result change synchronously; the only difference is that LEDR is a 10 bit
vector represented in binary, and result is a decimal variable, but the values are equivalent.
Notice how the prescalerMonitorInsideProcess is delayed from the prescaler.
Notice how the prescalerMonitorOutsideProcess is synchronized to the prescaler.
Notice after KEY signal toggles at 1000 ns, the result and LEDR changed from up-counter to downcounter.
9.
To restart the simulation, click break icon and Restart icon and click OK.
10.
Anyway, if you find some timing or values that do not meet your expectation or thoughts, you
need to go back to your *.vhd file in Quartus to modify the code and recompile. Then redo the step 3
and 4 in Launch and Setup ModelSim to debug, test and verify section, to restart the
simulation.

You might also like