Quick Quartus: Verilog

Please contact me if you find any errors or other problems (e.g., something is unclearly stated) in this web page

This document presents a (very) quick introduction to the use of Quartus to design a system using verilog.

The Basics
Simulation
Running on hardware.
Adding a counter

The Basics

  1. Create a folder on your desktop for the files for this lab.
  2. Start Quartus (There should be an icon on the desktop, if not go to (Start Menu→All Programs→Intel FPGA...→Quartus (Quartus Prime 17.0)) and select "New Project Wizard". It may take some time to open.
  3. On the next page ("Directory, Name, Top-Level Entit]") choose the directory you just created and name the project "lab0" and hit "Next >". It is a good idea to have the name of the project the same as that of the top level entity, which you will create below). In the example below I used a directory call E15Lab0 on my desktop, and called the project (and top level entity) "hadd" (short for half adder).

  4. Select "Empty Project" and hit "Next >" (from "Project Type").
  5. Hit "Next >" (from "Add Files").
  6. On the next page ("Family, Device & Board Settings"):
    1. Choose the "Board" tab.
    2. Choose "DE0-CV Development Board"

    3. hit "Next >"
  7. On the next page ("EDA Tool Settings"), under "Tool Type→Simulation" pick "ModelSim-Altera" for the "Tool name" and "Verilog HDL" for format.  Then hit "Next >".  (Note: this is only necessary for projects that you will be simulating; if you don't expect to simulate, you don't need to do this.  Also, if you only want to simulate, you can use ModelSim without doing a full compilationas we did in class (that process is much faster).).
  8. Hit "Finish" ("Summary"). This may take a minute or two.
  9. From the Quartus main menu choose "File→New→Design Files→Verilog HDL File" then "OK"
  10. Create a file and call it "hadd.v" (module name is the same as file name).  This file is shown below.  It implements a half adder; it adds the bits labelled A and B into a sum (S) and carry out (cout). Don't worry if you don't understnad the syntax completely, you'll learn that soon. It is useful to think of the half adder module as a black box as shown at the left with inputs (A,B) at the top, and output (S, cout) at the bottom. We will write Verilog code for each module, and then connect modules together to form more complex functions. The "top level entity" is denoted by a pink box.
    Block Diagram Code
    
    
    module hadd(a,b,cout,s);  // Implement a half-adder
    input a, b;               // Inputs to be added together
    output cout, s;	       // Output, carry (cout) and sum (s)
    
    assign {cout,s} = a+b;  // add a and b,
                            // ...result in cout and s.
    
    endmodule
  11. Add the file to your project. Go to Project→Add/Remove Files in Project... and add hadd.v and remove some extraneous files that we don't need (everything but DE0_CV_Default.sdc - if the .sdc file isn't there download it from the link and put it in the directory). The window should look like the one shown.

  12. Before moving forward we need to make the "hadd" module our top level entity - i.e., that part of the design with which we interact directly. To do this make sure that the "hadd.v" file is in the editing window and then go to Project→Set as Top Level Entity. Your window should look a bit like the one below.

  13. Go to Processing→Start→Analysis and Synthesis (or type ctrl-k).  This takes your code and generates a circuit that implements the desired functionality. After  a little wait you should get an message announcing success. You may get a warning like "Warning (18236): Number of processors has not been specified which may cause overloading on shared machines. Set the global assignment NUM_PARALLEL_PROCESSORS in your QSF to an appropriate value for best performance."

Simulation

Note: Before simulation you must perform Analysis and Synthesis (previous section).

  1. First make sure the location of the simulator is properly set. Go to Tools→Options→EDA Tool Options and set the ModelSim directory.
  2. Go to File→New→Verification/Debugging Files→University Program VWF  (VWF = vector waveform file).
  3. When the "Simulation Waveform Editor" window appears click Edit→Insert→Node or Bus.
  4. When the "Insert Node or Bus" window  appears, click on Node Finder...
  5. When the "Node Finder" window appears, click on List
  6. Select all the nodes on the left side and hit the ">>" button to move them to "Selected Nodes."  Hit OK.
  7. Hit OK to close "Insert Node or Bus".
  8. You should get a window like the one below.  It shows the inputs as 0, and the outputs as undefined (since we have not run the simulation).
  9. We first define the inputs.  Select the variable "a" on the left side of the window and then choose "Overwrite clock" () from the menu above the timing diagram.  Choose a 10 ns period and 50% duty cycle clock.  Hit OK.
  10. Now do the same for "b" but make it a 20 ns period (half as fast as before).
  11. The resulting graph shows many more oscillations of the inputs than is necessary, so go to Edit→Set End Time and set the end time to 80 ns.
  12. Go to Simulation→Run Functional Simulation.  After some time the output should look like below.  Recall that we are adding "a"and "b", and the sum is "s" and carry is "cout". You should be able to see that the results are as expected.

Running on Hardware

Now that we have simulated the circuit to verify that it operates properly, we want to actually create the circuit on the DE0 board. Download the E15DE0.qsf file into your working directory. (Note: Right-click on the link and choose "Save Link As..." If you open it and then save it, you will save the file in a format that cannot be properly read. This file has all the information needed to connect the internal circuitry of the FPGA to the hardware on the board.

Our new block diagram will like the one shown in the table below. The "top level entity now connect to the real world (inputs are switches "SW[0]" and "SW[1]", and outputs are "LEDR[0]" and "LEDR[1]". The switches are connected to the inputs of the module we just created, and the outputs of the module are connected to two LED's. In this case the connections are trivial. In general there can be multiple modules withing the "top level entity" and the connections will be more complicated.

  1. Go to File→New→Design Files→Verilog HDL File. Enter the text below into the file and save in a file "lab0.v". There are no rules about indenting - but be neat and be consistent. The names SW and LEDR must be entered exactly as shown since they correspond to physical locations on the DE0 board. The top level entity is a pink box. The black box is a module defined inside the top level module.
    Block Diagram Code
    module lab0(SW,LEDR);
    input [1:0]SW;     // add SW[0] and SW[1] are inputs
    output [1:0]LEDR;  // LED's are outputs 
    
    // instantiate a half adder.  The inputs are SW[0] (a) and SW[1] (b),
    // the outputs are LERG[0] (s, sum) and LEDR[1] (cout, carry out).
    	hadd myHalfAdder(SW[0], SW[1], LEDR[1], LEDR[0]);
    
    endmodule
  2. To do this, go to Project→Add/Remove Files in Project...and add the lab0.v file to your project.
  3. Now make the lab0 module the top level entity (i.e., the module with which we, as users, interact). To do this, go to Project→Set as Top Level Entity.
  4. Go to "Assignment→Import Assignments..." and choose the E15DE0.qsf file.  This should automatically assign the proper pins on the FPGA (i.e., so that the variable we called SW[0] is connected to the physical device SQ0 (the rightmost switch)).
  5. Go to "ProcessingStart Compilation" (or hit the triangular "play" icon at the top of the window).  This may take a few minutes. You will get several warnings - ignore these for now.
  6. Make sure the DE0 board is turned on and connected to the computer through the USB port labeled "Blaster". Go to "Tools→Programmer" or choose the "Programmer" icon (). The "Hardware Setup" should show "USB-Blaster". 
  7. Your window should now look something like the one shown below.  If  so, hit "Start".  If the "USB-Blaster" is not listed by the "Hardware Setup" button,  you'll have to start by  setting up the hardware and follow the directions for "JTAG programming"
  8. If no file is listed we have to choose the file to be download.  Click on "Add File..." and and choose the .sof (SRAM Object File) file (this may be in a subdirectory named "output_files."  You'll also have to delete any lines without file.  When the dialog box resembles the one above,  hit "Start".  When finished, close the programmer window (and save the configuration - you'll be able to reuse this configuration later if desired).
  9. Your design is now running, with the inputs connected to SW[0] and SW[1], and the output to LEDR[0] (the sum) and LEDR[1] (the carry). Try all four positions for the switches and verify that you get the expected result.

Making a More Complex Circuit.

Often you will want to create a more complex circuit out of simpler circuits. You will have to do this for the lab. The example below shows how you can use a half-adder, and a full-adder to make a 2 bit adder.

  1. Go to File→New→Design Files→Verilog HDL File. Enter the text below into the file and save in a file "fadd.v". This implements a full adder with three inputs and two outputs.
    Block Diagram Code
    module fadd(a,b,cin,cout,s);  //Implement a full adder.
    input a, b, cin;   	// Inputs to be added together with a carry in
    output cout, s;   	// Output, carry (cout) and sum (s)
    
    assign {cout,s} = a+b+cin;   // add a and b and put result in c and s.
    
    endmodule
  2. Add the file to the project (Project→Add/Remove Files in Project...).
  3. Change the code in "lab0.v" to look like the following (along with the corresponding block diagram). This code takes a two bit number represented by {SW[1], SW[0]} and adds it to a two bit number represented by {SW[1], SW[2]}. We add the two LSB's (SW[0] and SW[2]) in a half adder, and take the carry out and add it to the sume of the two MSB's (SW[1] and SW[3]) in a full adder.
    module lab0(SW,LEDR);
    input [3:0]SW;  	 // add SW[1:0] and SW[3:2] represent 2 bit numbers
    output [2:0]LEDR;  // LED's are outputs 
    
    wire cout1;
    
    // create a two bit adder, a half adder and a full adder
    	hadd myHalfAdder(SW[0], SW[2], cout1, LEDR[0]);			// Add low bits
    	fadd myFullAdder(SW[1], SW[3], cout1, LEDR[2], LEDR[1]);// Add high bits
    
    endmodule


    The module "lab0" now has four inputs (the four switches, representing the two bit inputs) and three output (the three LED's representing the binary sum of the two bit number). Inside the top level entity (pink box) are two modules (black boxes) with a connection (represented by a "wire") between them.
    Examine the code and the block diagram carefully and make sure you can explain how they are related.
  4. Make sure you understand the code.
  5. Before we can do another simulation it is useful to remove the hardware assignments, so go to Assignments→Remove Assignment→Pin, Location & Routing Assignments.
  6. Do another Analysis and Synthesis (ctrl-k) - there is no need to do the other steps since we are only doing a simulation (and this saves a lot of time).
  7. Create another Vector Waveform File, but this time pick LEDR and SW as your variables (this selects all bits).
  8. In the "Simulation Waveform Editor" select "SW" and then choose "Count Value" ().   Select Radix as binary, start value as 1, Increment by 1, count type binary, and count every 10 ns.
  9. Set the end time of the simulation to 60 nS and perform the simulation.
  10. Verify that the result is as expected.  Make sure you can explain these results. See below for a description of what to do if the simulation fails.

A potential error while simulating

You may get an error window that looks something like the one below:

This can occur if you perform the simulation after importing the pin assignments.   An easy way to fix this is just to change variable names while simulating (e.g., change SW to SWx); just remember to change them back when you want to test on the hardware.  

Using a counter

Instead of using the switches as an input, you can add a counter to generate all possible inputs. At this point you don't have to worry about how the counter works (you'll do that in class soon).

  1. Now add the file "E15Counter1HzB.v" to your working directory and include it in your project.  There are 3 connections - a 50 MHz clock input, a four bit terminal count input and a four bit count number output. The counter output increments once a second starting from 0 and going up to the terminal count.
  2. Change your "lab0.v" file to look like the one below. A block diagram is also shown - multibit "buses" are shown as thick arrows.
    module lab0(CLOCK_50,LEDR);
    input  CLOCK_50;   // 50 MHz clock input
    output [9:0]LEDR;  // LED's will represent input and output of adder
    
    wire cout1;
    wire [3:0]Q;    // output from counter and input to adder
    wire [2:0]sum;  // output from adder
    
    assign LEDR[9:6] = Q;    // output from counter and input to adder
    assign LEDR[5:3] = 3'b0; // turn off these LED's
    assign LEDR[2:0] = sum;  // output from adder
    
    // Instantiate counter
       E15Counter1HzB myCounter(CLOCK_50, 4'd15, Q);  // count from 0 to 15.
    
    // create a two bit adder, a half adder and a full adder
    	hadd myHalfAdder(Q[0], Q[2], cout1, sum[0]);			// Add low bits
    	fadd myFullAdder(Q[1], Q[3], cout1, sum[2], sum[1]);// Add high bits
    
    endmodule  


    Examine the code and the block diagram carefully and make sure you can explain how they are related.
  3. Compile the file, download to the DE0 board and make sure it operates as expected.
  4. Note (in the flow summary) how many logic elements were used.

Please contact me if you find any errors or other problems (e.g., something is unclearly stated) in this web page