Practical Electronics for Inventors, Fourth Edition - Paul Scherz, Simon Monk (2016)

Chapter 14. Programmable Logic

Designs that use the combinational and sequential logic described in Chap. 12 can be built using lots of separate ICs. Maybe you have a design that requires a 10-stage counter divider and binary-to-decimal decoder plus a NAND gate or two. While you can still buy the chips to make such a circuit, this would never be done for a commercial product. The chip count for the logic part of all but the most complex designs is rapidly heading toward one. This one chip might be a microcontroller as described in Chap. 13, but this essentially moves the design problem from hardware design to software programming. Programming is a discipline with different roots to electronics and is fraught with problems of maintaining code and managing complexity. Software solutions can also be slow, as the software effectively has to ape what the equivalent logic circuit would be doing.

When using a microcontroller, you write code to be run on the device as the device is in use. An alternative approach (programmable logic) is to use a field-programmable gate array (FPGA) or for smaller projects a complex programmable logic device (CPLD).

Using an FPGA or CPLD involves creating a combinational or sequential logic design, either by actually drawing out the gates, counter, shift registers, etc., into a CAD system, or describing the logic design using a hardware definition language (HDL). This description of the logic, either pictorial or textual, is then used to configure general-purpose logic cells in the chip to the hardware that you want. It’s like creating your own custom chip containing just the logic that you need.

Programmable logic has a reputation for being difficult and inaccessible to the nonprofessional. While the technology of reconfigurable hardware takes some getting used to, the manufacturers of programmable logic devices have become less proprietary in their approach in recent years and even offer free to use design software for the inventor. In this chapter, you will learn how to get started with programmable logic, in particular using the Xilinx software tool ISE Studio and the Verilog hardware description language.

Since the best way to learn is to actually try things out, this chapter will use the Elbert 2 FPGA development board from Numato Labs (http://numato.com/elbert-v2-spartan-3a-fpga-development-board.html).

14.1 Programmable Logic

In the early days of digital computing, large numbers of logic gate ICs were combined onto printed circuit boards (PCBs) to make computer boards that would then be attached to a back-plane. You might have found hundreds of ICs on a single PCB, each of these chips containing a handful of gates or shift registers.

The arrival of large-scale integration (LSI) and the invention of the microprocessor reduced the IC count enough to use in home computing. However, open up a home computer from the early 1980s and you may still see rows of logic chips providing all the other functions that the computer needed: keyboard scanning, a cassette tape data storage interface, video output, etc.

All this clutter could easily fit on a single LSI chip, but having your own application-specific IC (ASIC) is a seriously expensive thing to do and was only possible for large production runs. Enter the programmable array logic (PAL). The PAL designed by Monolithic Memories Inc. was not the first programmable logic IC, but it was the first to really take off commercially.

The internal structure of the PAL is a sum of products arrangement of gates (see the section “AND-OR-INVERTER Gates” in Chap. 12). Figure 14.1 shows such an arrangement.

img

FIGURE 14.1 A sum of products term.

The inverted and noninverted inputs to the AND (sum) part of the logic are switched using “fuses” that can be blown during the manufacturing process or using special programming hardware. Some devices could only be programmed once while others were reprogrammable.

This idea of using cells of configurable logic underpins all modern types of programmable logic, although the scale and complexity has increased and the effort involved in programming them has decreased.

FPGAs generally have hundreds of thousands of logic cells. This is great if your FPGA is doing something complex, but may be excessive if you just want the equivalent of a few logic gates. To address such small needs, CPLDs are used. These are the natural successors to PALs and operate in a similar manner, using “macro cells” that implement the sum of products–type terms of Fig. 14.1. FPGAs use a different arrangement that will be explained in the next section.

The design tools that are used to “program” CPLDs and FPGAs are now so sophisticated that there is little need for the designer to think in terms of the actual logic gates on the silicon. Instead, they can simply draw their design using logic gates chosen from a palette, connect them all up, specify the inputs and outputs, and then let the tool manage the process of translating that design into configuration of the programmable logic device (see Fig. 14.2).

img

FIGURE 14.2 Designing with logic gates.

Taking this a step further, you can skip the logic diagram stage entirely and express your design in a hardware description language like Verilog or VHDL and then have your software tool convert that into configuration information for the programmable logic chip.

Getting into mind-bending territory, you can (and people do) design a microcontroller on the FPGA (along with other logic circuitry) that then runs a program.

14.2 FPGAs

The main difference between FPGAs and CPLDs is that FPGAs do not use logic cells in a sum of products arrangement, but rather the cells use a lookup table (LUT). The lookup table will have a number of inputs say six inputs and a single output. You can think of this as a 64 × 1 bit ROM, with the inputs being the address lines of the ROM and the output being the bit stored at that address. The contents of these LUTs, combined with other routing information, are what give the FPGA its logic.

LUTs are often not exactly arranged as a single six-input unit, but may comprise two five-input units, the sixth input being a select input that selects between the two LUTs. This allows extra flexibility when it comes to how the design software connects everything together.

The LUT will often be combined with extra components like a flip-flop to make an individual logic block.

Figure 14.3 shows a logical view of how this is all arranged.

img

FIGURE 14.3 The logical structure of an FPGA.

General-purpose IO (GPIO) pins on the FPGA chip are connected to special-purpose IO (input/output) blocks that provide buffered microcontroller-like inputs and outputs that can typically source or sink a few tens of milliamps.

The vast bulk of the functional units in the FPGA will be logic blocks and a typical modern FPGA may have from 200,000 to several million of these blocks. There may also be a fixed RAM block for use when the FPGA is to be configured as a processor. Taking this to its extreme you find high-end SoC (systems on a chip) FPGAs that include fixed high-performance processor cores and memory on a chip that also includes configurable logic cells. FPGAs are also often used to prototype an ASIC for very large production runs.

Routing between such vast numbers of logic blocks is pretty tricky, but fortunately for us, we don’t have to do it, that’s what the design software is for.

The information in the LUTs and the routing matrix (that defines the interconnections) is volatile. When you lose power, all that information disappears and the FPGA reverts to its original state. To configure the FPGA the configuration is usually stored outside of the FPGA in EEPROM. The FPGA will generally have a fixed hardware loading interface built in to it that will pull in the configuration as the FPGA starts up. This typically takes less than 200 ms.

14.3 ISE and the Elbert V2

There are quite a few FPGA vendors now, but the two biggest players are Xilinx (Xilinx.com) and Altera (altera.com); between them, they have almost 90 percent of the programmable logic market. Of the two, Xilinx has the largest share.

All the FPGA manufacturers have their own design tools that work specifically with their hardware. In this chapter we will use the Xilinx Sparta 3A FPGA chip with Xilinx’s design tool called ISE.

To get some practical experience of using an FPGA, there are a number of FPGA development boards that combine an FPGA with its configuration ROM and a selection of input/output devices, such as LEDs, switches, audio and video interfaces. The device that has been selected for this chapter is the Elbert V2 board, which is widely available and low cost, and includes its own USB interface for programming.

14.3.1 Installing ISE

The design tools of the FPGA manufacturers are frankly bloated monsters. The ISE design tool is a 7-GB (that’s right, gigabyte!) download. In many ways getting and installing the design tool is the hardest part of getting started with FPGAs. Once you have done that everything else seems relatively simple.

The first step in obtaining ISE is to visit Xilinx.com with your web browser and find the ISE download page, which you will find by following the links: Developer Zone->ISE Design Suite->Downloads. Scroll down the “downloads” area to ISE Design Suite (we used version 14.7). Do not be tempted to download the newer Vivado Design Suite. This is only for newer Xilinx FPGAs and does not support the Spartan 3A used on the Elbert 2.

There are Windows and Linux versions of the tool. In this book we will just describe the process of getting up and running in Windows.

When you click on the “Download” button, a long complicated survey will appear that you have to complete followed by a second long and complicated registration form. Persevere and eventually the download will start and you can go and do something else for a few hours while the download completes.

After installation you have to click on the link to request a free license key for ISE Web. This will be e-mailed to you as an attachment; save the file and then from ISE open the license manager from the Manage License option on the Help menu of ISE and add the license.

14.4 The Elbert 2 Board

Figure 14.4 shows the Elbert 2 board.

img

FIGURE 14.4 The Elbert 2–Spartan 3A FPGA development board.

These boards are available direct for Numato Labs (http://numato.com) or from Amazon.com and various other sources. The price of the entire board was just $29.95 at the time of writing. The only other thing you will need is a USB to mini-USB lead.

The board has the following features:

·        Spartan XC3S50A FPGA in TQG144 package

·        16-MB SPI flash memory for configuration

·        USB 2.0 interface for on-board flash programming

·        Eight LEDs

·        Six push buttons

·        8-way DIP switch

·        VGA connector

·        Stereo jack

·        Micro SD card adapter

·        Three seven-segment displays

·        39 IOs for user-defined purposes

·        On-board voltage regulators

14.4.1 Installing the Elbert Software

The Elbert board has a software utility for programming the board. This handles just the final step of copying the binary file generated by ISE onto the Elbert V2’s flash memory. There is also a USB driver to install for Windows users. To set up your computer to use the Elbert, visit the product page for the Elbert V2 at numato.com and click on the Downloads tab.

You will need to download:

·        Configuration tool: This is used to program the board

·        Numato Lab USB CDC driver

·        User manual

To install the USB driver on Windows, plug the Elbert V2 board into your computer and the New Hardware Wizard should start. Point the wizard at the extracted “numatocdc” folder and the driver should install and the new hardware be recognized.

After this, the Elbert V2 will be connected to one of the virtual COM ports of your PC. To find out which port, open the Windows Device Manager and you should find it listed in the Ports section as shown in Fig. 14.5.

img

FIGURE 14.5 Finding the Elbert V2 port.

14.5 Downloads

All the code examples used in this book can be found in the GitHub repository (https://github.com/simonmonk/pefi4).

To install the examples on your PC, click on the “Download ZIP” button in the bottom right of the GitHub page and then extract the file. You will find the examples in the folder “fpga.”

It is worth working through the examples below to build-up the projects and get used to using ISE, but if you get stuck and need to compare what you have done with the final working design, then these files will come in handy.

14.6 Drawing Your FPGA Logic Design

The ISE design tool gives you two ways of programming your FPGA. One is to draw a familiar logic diagram and the second is to use the Verilog hardware description language (HDL). We will start with the schematic approach, although seasoned FPGA designers nearly always use Verilog or its rival VHDL.

For this first example, we will go into quite a lot of detail on using the ISE tool to get you up and running.

14.6.1 Example 1: A Data Selector

The first example that we will make is the data selector that you first met in Chap. 12 (Fig. 12.32). The schematic for this is repeated in Fig. 14.6.

img

FIGURE 14.6 A simple data selector.

The three inputs (A, B, and “data select”) for this circuit will be hooked up to three of the push buttons on the Elbert V2 and the output will be connected to one of the LEDs so that we can see the circuit actually in use.

Step 1: Create a New Project

The first step is to fire up ISE and then select File->New Project from the menu. This will open the New Project Wizard shown in Fig. 14.7.

img

FIGURE 14.7 The New Project Wizard in ISE.

Enter “data_selector” in the Name field. In the Location field navigate to the folder where you want to keep your ISE designs. The Working Directory field will automatically update to match this directory, so you don’t need to change the Working Directory field.

Change the “Top-level source type” drop-down to “Schematic” and then click “Next.”

This will take you to the Project Settings shown in Fig. 14.8

img

FIGURE 14.8 The New Project Wizard—Project Settings.

Change the settings so that they match Fig. 14.8 and then click “Next” again. The Wizard will then show you a summary of the new project and you can then click “Finish.”

This will create for you the new, but empty project shown in Fig. 14.9.

img

FIGURE 14.9 A new project.

The screen is divided into four main areas:

In the top left you have the Project View. This is where you can find the various files that go to make up a project. It is organized as a tree structure. Initially there are two entries in this area. There is the entry that says “data_selector” and the second entry that has a seemingly random name (xc3s50a-4tq144). The latter will eventually contain two files, the schematic drawing that we are about to create and an implementation constraints file that defines how the inputs and outputs in the schematic connect to the actual switches and LEDs on the Elbert V2.

You can also double-click on “xc3s50a-4tq144” to open the project properties. So, if you made a mistake setting the project properties using the New Project Wizard, you can always correct it by double-clicking on this entry.

To the left, beneath the Project View is the Design View. This will eventually list useful actions that we can apply to our design including generating the binary file for programming the Elbert V2.

The wide area at the bottom of the window is the console. This is where error messages will appear.

The large area to the right of the window is the editor area. When it comes to drawing the schematic, this is where you will do it.

Step 2: Create a New Schematic Source

To create a new schematic, right click on data_selector in the Project View and select the option “New Source.” This will open the New Source Wizard (Fig. 14.10).

img

FIGURE 14.10 The New Source Wizard.

Select a Source Type of “Schematic” and enter “data_selector” in the File name field and then click “Next.” A summary screen will appear, to which you can respond by clicking “Finish.” This will result in a blank canvas being prepared for us in which we can draw the schematic.

This is shown in Fig. 14.11, labelling the parts that you are going to need.

img

FIGURE 14.11 The schematic editor.

The icon menu bar running vertically to the left of the editor area controls the mode of the window and also what appears on the left-hand side of the window:

·        The top icon (an arrow) puts the window into select mode. You will need to click on this before you can drag circuit symbols about or change their properties.

·        Click on the “Add wire” mode when you are connecting the gates and other circuit symbols together.

·        IO markers are used to indicate the boundary between the schematic you are designing and the actual pins of the FPGA IC. This mode lets you add these symbols.

·        Add logic symbols. This is the mode selected in Fig. 14.11. The left hand panel then divides into a top half that shows categories of circuit symbol and a bottom half that has a list of the component symbols in that category.

Step 3: Add the Logic Symbols

Put the screen into “Add logic symbols” mode. You are going to need to add two two-input AND gates, a two-input OR gate and two invertors.

Click on the category “Logic” the select “and2” (2 input AND). Then click twice in the editor area to drop the two and gates. Then select “or2” and drop an OR gate in roughly the right location to the right of the AND gates, and final add in the two invertors (“inv”) below and to the left of the AND gates.

Zoom in a bit (Toolbar at the top of the window) and the editor area should look something like Fig. 14.12.

img

FIGURE 14.12 The logic gates in position.

Step 4: Connect the Gates

Click on the “Add wire” icon and then connect the gates together in the arrangement of Fig. 14.6. To make a connection, click the mouse on one of the square connection points and drag out to the connection point or line that you want to connect to. The software will automatically put bends in the line for you. If you need to more things to tidy the diagram up then you can change to “Select” mode and drag the symbols and wires about.

The end result should be Fig. 14.13.

img

FIGURE 14.13 Connecting the symbols with wires.

Step 5: Add the IO Markers

Click on the “Add IO Markers” icon and then add markers to all the inputs and the output by dragging the mouse out from the wire in question. Notice how the software figures out that the output is an output.

Initially, the connections are all given names like XLXN_1, etc. To change these names to more meaningful names, change to “Select” mode, right-click on an IO connector and chose the menu option “Rename Port.” Change the port names so that they agree with Fig. 14.14.

img

FIGURE 14.14 The completed schematic.

Notice that we have called the output Q. This is because the word “OUT” is reserved for use by ISE, so you cannot call any of your connections “OUT” or you will get an error when you try and build the project.

The schematic is now complete, and now would be a good time to do “File->Save” to save the schematic design to file.

Step 6: Create an Implementation Constrains File

Now you need to get back to the original Project View to be able to create a new source file. Click on the little “Xs” in the top right corners of the various views that will have become layered on top of the Project View. You may also have to click the “Design” tab at the bottom of the Design View.

Right-click on “data_selector” and again select “New Source” to open the New Source Wizard. This time, select “Implementation Constraints File” and enter the file name “data_selector_elbert” as shown in Fig. 14.15. Click “Next” and finish the wizard.

img

FIGURE 14.15 Creating an Implementation Constraints File.

This will open an empty text editor window where you need to type the following text:

# User Constraint File for data selector

# implementation on Elbert V2

# Push buttons

NET “A” LOC = P80;   # SW1

NET “B” LOC = P79;   # SW2

NET “SEL” LOC = P78; # SW3

# Internal pull-ups need to be enabled

NET “A” PULLUP;

NET “B” PULLUP;

NET “SEL” PULLUP;

# LED

NET “Q” LOC = P46;  # LED8

The lines that begin with a # are comment lines. That is, like the lines of program code starting with // in Arduino C take no part in the functioning of the program, the lines starting with # are not part of the configuration information, they are just to make it easier to see what is going on.

In the section that starts with “# Push Buttons” you can see the link between the IO Connector names on the schematic and the FPGA GPIO pins that are connected to the switches. So, SW1 is connected to P80, etc.

The connections from FPGA GPIO pins to the hardware provided on the Elbert V2 are all detailed in the Elbert user manual. Figure 14.16 taken from the manual with kind permission of Numata shows the pin allocations associated with the switches and LEDs.

img

FIGURE 14.16 The switch and LED pin allocations for the Elbert V2.

The GPIO pins of the FPGA have configurable pull-up resistors and these are enabled in the implementation constraints file. The final line of the constraints file associates the output Q with pin P46.

Step 7: Generate the Programming File

You are now ready to generate the programming file to be downloaded onto the Elbert V2. So, select the data_selector entry in the hierarchy and a number of options for things to do will appear below it in the Processes section. One of those processes will be “Generate Programming File” (see Fig. 14.17). Right click on this option and select “Run.”

img

FIGURE 14.17 Generating the programming file.

If all is well, there will be lots of text appearing in the Console as the programming file is generated. If there are any errors this is where they will appear, so read the error message carefully and it should point to where the problem is.

Step 8: Program the Elbert V2

The end result of all this activity will be a file within the working directory of the project called data_selector.bit. It is this file that we need to transfer onto the Elbert V2 using the Elbert V2 Configuration Tool that you downloaded earlier.

Start up the program and then click on Open File. Navigate to the working directory for the project and select the file data_selector.bit. Change the COM port in the drop-down list to match the COM port allocated to the Elbert that you discovered earlier (see Fig. 14.5) and then press the Program button. After a while, you should see a reassuring message appear (Fig. 14.18).

img

FIGURE 14.18 Programming the Elbert V2.

The FPGA on the Elbert V2 is now configured to be the data selector.

Testing the Result

Initially, you should see the D8 LED on the Elbert V2 lit. If you press SW2 the LED will turn off. Release the button and LED 8 will turn on again. Pressing SW1 will have no effect. Now hold SW3 down and you will notice that SW2 no longer has any effect but SW1 does alter the LEDs state.

This is the data selector working how it should. The logic is a little confusing, because the inputs are effectively negated as they are pulled to GND when you press the button.

Viewing the Technology Schematic and Floorplan

Although it is perfectly ok to just trust ISE to do all the work of laying out and connecting up the FPGA for us, it is fun, if not particularly useful to peek under the hood and see what exactly it did.

You can see some of this information using other tools that are accessible from the Project view.

If you expand the Synthesize XST line in the Processes, you will see an option “View Technology Schematic.” If you run this, you will get to see the schematic generated by ISE that includes additional IO buffers (Fig. 14.19).

img

FIGURE 14.19 The automatically generated schematic.

You can actually see which parts of the FPGA silicon were used for your design if you select the menu option Tools->Planahead->Floorplan Area (Fig. 14.20).

img

FIGURE 14.20 The FPGA floorplan.

14.6.2 Example 2: A 4-bit Ripple Counter

In the second example using the schematic approach, we move beyond simple combinational logic to a ripple counter, again taken from Chap. 12, where you will find it in Fig. 12.75. This is repeated here as Fig. 14.21 for convenience.

img

FIGURE 14.21 A simple data selector.

As you did with the selector, start by creating a new project. Give it the name “ripple_counter.” You should find that when you run the New Project Wizard, this time, it remembers all the project settings from the last project.

Create a new Schematic source (“ripple_counter”) and draw the schematic.

It can be hard to find the right symbols, so you will probably need to drop a selection of symbols onto the canvas before you find the right ones. Delete the ones you don’t want by selecting then and then pressing the delete key. The symbol we used for each JK Flipflop was found in the category Flip_Flop and called fjkc. Add some IO Markers for CLK, CLR and Q0 to Q3. You will also need to add four VCC symbols (“General” category) to pull the J and K pins high. The end result of this should look like Fig. 14.22.

img

FIGURE 14.22 The ISE schematic.

You also need to create an implementation constraints file and place the following contents in it.

# User Constraint File for 4-bit ripple counter

# implementation on Elbert V2

NET “CLK” CLOCK_DEDICATED_ROUTE = FALSE;

# Push button switch 1 is connected to pin 80

NET “CLK” LOC = P80;   # SW1

NET “CLR” LOC = P79;   # SW2

# Internal pull-ups need to be enabled

NET “CLK” PULLUP;

NET “CLR” PULLUP;

# LEDs

NET “Q0” LOC = P55;   # LED1

NET “Q1” LOC = P54;   # LED2

NET “Q2” LOC = P51;   # LED3

NET “Q3” LOC = P50;   # LED4

The new first like specifies that although the CLK is a clock pin it does not need the specialized clock connectivity lines that the FPGA can provide, since we are just going to be driving the clock using the push buttons.

Generate the Programming file and then deploy it onto the Elbert V2 using the Configuration tool.

When you come to test the project using the button, you will need to keep SW2 depressed, as this is the CLR (clear button) and the input from the switch is inverted.

You will also notice that there is a fair bit of key bouncing from the push switches and the LEDs may skip past some of the binary numbers.

14.7 Verilog

Verilog is a hardware description language. Along with its rival language VHDL, it is what is most commonly used to program an FPGA.

You probably found that programming a FPGA using schematic is familiar and easy to understand, so why would you want to learn a complicated programming language to do the same thing? Well, the answer is that actually as designs become more and more complex, it can be easier to represent a design using a programming language than to draw it.

While it is feasible to use the schematic approach when designing some simple logic that could fit on a CPLD, as complexity increases, the problem of drawing all those gates and connecting them up gets impractical.

Verilog looks like a programming language and indeed you will find “if” statements, code blocks and other software-like constructions.

14.7.1 Modules

Software programmers will recognise a Verilog module as being very like a class in object-oriented programming. It defines a collection of logic with public and private properties that can be instantiated a number of times in your design.

For nonprogrammers, it is probably best to think of it as a sub-assembly of the design with defined connections to be able to wire it up to other modules.

A simple design may be all contained in a single module, but when things start to get a little complex, the design will become a load of modules that are then interconnected.

14.7.2 Wires, Registers, and Busses

What would be variables in a conventional programming language are, in Verilog, wires (connecting one thing to another) or registers (that store state and are therefore more like a programming variable). These refer to a single binary digit. Often, you don’t want to work on a single bit and so you can group a number of bits into a bus and operate on the bus as a whole (sometimes also called a vector). This is rather like using a word of arbitrary length in a conventional programming language.

14.7.3 Parallel Execution

Because Verilog is describing hardware rather than software, there is an implicit parallelism in Verilog. If you have three counters in a design, all connected to different clocks, that is just fine. Each will do its own thing. It is not like using a microcontroller where there is a single thread of execution.

14.7.4 Number Format

A lot of the time, in Verilog, you will be dealing with a bus and it is convenient to assign values using numbers of any bit size in any radix. To accomplish this, Verilog uses a special number syntax. If you do not specify the number of bits and the radix, then the number is assumed to be decimal and unused bits are set to 0.

The number format starts with the number of bits, then there is an apostrophe, followed by a radix indicator (b, binary; h, hex; d, decimal) and then the number constant.

Here are some Verilog integer constants:

·        4’b1011—4-digit binary constant

·        8’hF2—8-bit hex constant

14.8 Describing Your FPGA Design in Verilog

In this section, you will work though using ISE to replicate the two earlier designs of a data selector and ripple counter in Verilog rather than using the schematic editor. You will then go on to look at some more complex designs where the modular and concise nature of using a HDL starts to pay dividends.

14.8.1 A Data Selector in Verilog

Rather than just looking at the Verilog code in isolation, let’s combine it with learning how to use it in ISE.

The first step is to create a new project. This time, when the New Project Wizard appears (Fig. 14.23), give it the name “data_selector_verilog,” change the drop-down list at the bottom (top-level source type) to be HDL, and click “Next” and then “Finish” at the summary screen.

img

FIGURE 14.23 The New Project Wizard.

Now we need to create a new source file for the Verilog version of the data selector. So, right-click on the project and select the option “New Source.” This will open the New Source Wizard (Fig. 14.24).

img

FIGURE 14.24 Creating a new Verilog source file.

Select a source type of Verilog Module and give the source the name “data_selector” and then click “Next.” This will allow you to define the inputs and outputs to the module (Fig. 14.25).

img

FIGURE 14.25 Defining inputs and outputs for the new Verilog source.

Use the wizard window to define three inputs (A, B, and SEL) and one output (Q). Then click “Next” and then after the summary screen click “Finish.” The wizard will then generate a template file for your Verilog module using the information you entered in the wizard (Fig. 14.26).

img

FIGURE 14.26 The generated module code.

At present, this module does not actually do anything. We will add that Verilog code to it shortly.

Let’s analyze what has been generated by the Wizard. Here is the code that was generated.

module data_selector(

    input A,

    input B,

    input SEL,

    outputQ

    );

endmodule

The module starts with the “module” keyword and is followed by the name of the module. Inside the parentheses, the inputs and outputs to the module. The word “endmodule” marks the end of the module definition.

Modify the text so that it appears as below. Note that the additions are marked in bold.

module data_selector(

    input A,

    input B,

    input SEL,

    outputreg Q

    );

always @(A or B or SEL)

begin

        if (SEL)

               Q = A;

        else

               Q = B;

end

endmodule

The first change is the addition of the word “reg” to the output definition for Q. This indicates that Q is a register and can therefore be modified.

The other addition is the “always” block. Immediately after “always” is the “sensitivity” list that follows “@.” This specifies the signals (separated by the word “or”) to which the “always” block is sensitive. That is the code between “begin” and “end” comes into play. It is very easy to think of this code as if it were a programming language rather than a hardware definition language.

If SEL is 1 then Q will be assigned to whatever the state of A is. Otherwise Q will be set to the value at the B input. This is exactly what the selector should do.

That’s all there is to the Verilog, however you still need an implementation constraints file if you want to try out the example on the Elbert V2. The one that you created for the schematic version of this project will work just fine. You can copy the implementation constraints file from the other project by right-clicking on the project name and clicking “Add Copy of Source.” This will allow you to take a copy of “data_selector_elbert.ucf.”

Build the project and then install it on the Elbert in the same way as you did for the schematic project. The project should work in exactly the same way.

14.8.2 A Ripple Counter in Verilog

The ripple counter schematic project can also be implemented in Verilog. This time, when you create the new project (you could call it “ripple_counter_verilog,””), add the inputs and outputs as shown in Fig. 14.27.

img

FIGURE 14.27 Defining the inputs and outputs for the ripple counter.

The output Q is defined as being a bus by checking the Bus checkbox. The MSB column indicates its most significant bit number (in this case 3) and the LSB of 0 is entered in the LSB column.

Finish of the wizard and the generated code will start like this:

module ripple_counter(

    input CLK,

    input CLR,

    output [3:0] Q

    );

You now need to add the counting logic for the counter, so edit the code to be:

module ripple_counter(

    input CLK,

    input CLR,

    outputreg [3:0] Q

    );

always @(posedge CLK, posedge CLR)

begin

        if (CLR)

               Q <= 0;

        elseif (CLK)

               Q <= Q + 1;

end

endmodule

The added code is shown in bold. The sensitivity list in the always block includes positive edges of either the CLK or CLR signals. If either of these happen, then the code between begin and end comes into play. This simply says that if CLR goes high, then the count Q gets set back to 0 and if CLK is high then 1 is added to Q.

Note that in this case, since 0 and 1 are the same in any radix, we have not specified the radix or number of bits in the number constants.

You now need to add an implementation constraints file for the project that looks like this:

# User Constraint File for 4-bit ripple counter implementation on Elbert V2

NET “CLK” CLOCK_DEDICATED_ROUTE = FALSE;

# Push button switch 1 is connected to pin 80

NET “CLK” LOC = P80;   # SW1

NET “CLR” LOC = P79;   # SW2

# Internal pull-ups need to be enabled

NET “CLK” PULLUP;

NET “CLR” PULLUP;

# LEDs

NET “Q[0]” LOC = P55;   # LED1

NET “Q[1]” LOC = P54;   # LED2

NET “Q[2]” LOC = P51;   # LED3

NET “Q[3]” LOC = P50;   # LED4

This is very similar to the one for the schematic-based counter, but in this case the separate bits of the Q bus are linked to the LEDs using a square bracket notation to indicate the bit linked to a particular LED.

Generate the binary file and install it on your Elbert V2 board and you should have something that behaves just like the schematic version.

14.9 Modular Design

When designing a complex system for a FPGA there is nothing to stop you putting all your Verilog code into one module. However, by splitting things up, it firstly makes it easier for others to understand what you have done as they can assume that the component modules perform the role they are supposed to and therefore see a bigger picture of how all the modules work together before getting into the nitty-gritty of how each one works.

Breaking things up into a number of modules also makes it a log easier to take a module that you used in one project and use it in another, or to share it with someone else to use in their project.

When you create a project with more than one module, you will always have a top-level module. This is the module that brings all the sub-modules together and also the module that will have an implementation constraints file associated with it to map the IO pins of the FPGA to the signals in the design.

14.9.1 Counter/Decoder Example

In this example, you will build on the Verilog version of the counter module and add a 7-segment LED decoder module, so that it can count in decimal on one of the 7-segment LEDS on the Elbert 2 board.

See Sec. 13.3.2 for information on 7-segment decoding. The basic idea is that a four-digit binary input will be decoded into 7 segment bits for a segment display for the decimal values 0 to 9.

Figure 14.28 shows the relationship between the three modules that will be defined for this project.

img

FIGURE 14.28 Counter/decoder modules.

In this case, the top-level module will use one counter and one decoder module and combine them into a new module (counter_decoder).

Let’s start with the counter module, because we have already made this module once in the project ripple_counter_verilog. So, from the file menu use the option “Add copy of source” and go and find “ripple_counter.v” in the other project and add it to this project. It should look like this:

module ripple_counter(

    input CLK,

    input CLR,

    outputreg [3:0] Q

    );

always @(posedge CLK, posedge CLR)

begin

        if (CLR)

               Q <= 0;

        elseif (CLK)

               Q <= Q + 1;

end

endmodule

Next, you are going to create a new Verilog source called “decoder_7_seg.v.” It should have a 4-bit bus input called D (the numeric digit 0 to 9 in 4 bits) and an 8-bit bus output SEG for each of the 7 segments of the display (plus 1 for the decimal point). The module it contains should look like this:

module decoder_7_seg(

    input [3:0] D,

    outputreg [7:0] SEG

    );

always @(D)

begin

    case(D)

        0: SEG <= 8’b00000011;

        1: SEG <= 8’b10011111;

        2: SEG <= 8’b00100101;

        3: SEG <= 8’b00001101;

        4: SEG <= 8’b10011001;

        5: SEG <= 8’b01001001;

        6: SEG <= 8’b01000001;

        7: SEG <= 8’b00011111;

        8: SEG <= 8’b00000001;

        9: SEG <= 8’b00001001;

        default: SEG <= 8’b11111111;

    endcase

end

endmodule

This time the always block just has a sensitivity list of the input data (D). The “case” statement will be familiar to C and Java programmers as being a “switch” statement. It is a short-hand way of chaining together a whole load of “if”statements. The “case” command takes a parameter (in this case D) and if value of D is 0, it sets the bit pattern for SEG to 8’b00000011. If D is 1 then SEG is set to the second bit pattern down, and so on. Note that the segment bits are inverted. A 0 means that that segment will be lit—that’s just how the Elbert V2 is wired up.

Both the decoder and counter modules are both going to be used by the top-level module that we will call “counter_decoder.” Create a new Verilog source file called counter_decoder.v with the single input CLK and the 8-bit bus output SEG that will be connected to the segment LEDs. Edit the generated code so that the module appears as below:

module counter_decoder(

    input CLK,

    output [7:0] SEG

);

wire [3:0] data;

wire clear = 0;

decoder_7_seg decoder(.D (data), .SEG (SEG));

ripple_counter counter(.Q (data), .CLK (CLK),

 .CLR (clear));

endmodule

This is a pretty sparse module, as most of the work is taking place in the two modules that it uses.

To link the data output from the counter (D) to the data input to the 7-segment decoder (a different D) a wire bus is defined called “data.” Although the counter has a CLR (clear) input, this is not going to be used in the counter_decoder module and so a second wire (“clear”) is defined and its value set to 0.

Next comes the part where the two sub-modules are “instantiated” and their outputs and inputs coupled.

One way to think of a module is as the name for a logic gate (perhaps an AND gate). So instantiating an AND gate would mean adding one to a schematic design. You might add (instantiate) several AND gates onto the schematic. In this case, we are instantiating first a decoder_7_seg module and then a ripple_counter module.

Looking at the line starting “decoder_7_seg”: the syntax for instantiating a module is to first specify the module name (“decoder_7_seg” and then the name of the instance (“decoder”). So if your design needed more than one “decoder_7_seg” then you could call them “decoder_1”, “decoder_2” and so on.

After the name of the instance comes the bit that allows you to associate the inputs and outputs of the instance with signals inside the containing module (in this case “counter_decoder”). This is contained in parentheses and is a mechanism that software programmers would consider to be like using named parameters. So, “D (data) means that the D input to the decoder should be linked to the wire bus called “data” in the module counter_decoder. Similarly, “SEG (SEG)” links the SEG output of the decoder to the SEG output of “counter_decoder” that will in turn be linked to the segment LEDs on the Elbert V2.

A “ripple_counter” instance is created in the same way, linking its Q output to “data,” passing through the CLK signal and setting the CLR input to “ripple_counter” to be “clear.”

Before you can build this example, you need to tell ISE which module is the top-level module. The top-level module is marked by an icon that looks like a triangle of little squares (Fig. 14.29).

img

FIGURE 14.29 The Hierarchy View indicating the top-level module.

You can set a module to be the top-level module by right clicking on it in the Hierarchy View and selecting the option “Set as Top Module.”

You also need an implementation constraints file that associates SW1 on the Elbert V2 with CLK and the segments of the LED with the SEG bus.

The pinouts associated with the 7-segment display are shown in Fig. 14.30 and the constraints file that you will need to create is listed below.

img

FIGURE 14.30 The Elbert V2 7-segment display.

NET “CLK” CLOCK_DEDICATED_ROUTE = FALSE;

# Push buttons

NET “CLK” LOC = P80;   # SW1

NET “CLK” PULLUP;

# 7-segments

NET “SEG[7]” LOC = P117;

NET “SEG[6]” LOC = P116;

NET “SEG[5]” LOC = P115;

NET “SEG[4]” LOC = P113;

NET “SEG[3]” LOC = P112;

NET “SEG[2]” LOC = P111;

NET “SEG[1]” LOC = P110;

NET “SEG[0]” LOC = P114;

Build the project and install it on your Elbert and you should see all three digits counting as one when you press SW1. All three digits are counting, because we are not controlling the common anodes of the display digits. This is something you will remedy in the next example.

14.9.2 Multiplexed 7-Segment Counter Example

This example makes the three-digit 7-segment display of the Elbert V2 count upward from 0 to 999 incrementing the three digit number displayed once per second.

The three-digit 7-segment LED display on the Elbert V2 is multiplexed. Referring back to Fig. 14.30, you can see that three PNP transistors are used to enable the three anodes of the display and the cathode connections to the segments for the three displays are controlled by eight FPGA output pins (one for the decimal point). See the section “Multiplexed LED Displays” and Fig. 12.129 for background information on multiplexing LED displays.

To display a different number on each digit, it is necessary to fool the eye by turning one digit on (and the others off), setting the segment pattern to the number you want for that digit, then turning that digit off and enabling the next digit, resetting the segment pattern and so on.

In this example you will reuse the “segment_decoder” module that you created earlier and also create two new modules. The modules used in the project, which is called “seconds_counter,” are:

·        second_counter.v: The top-level module

·        multiplexed_7_seg_display.v: The multiplexed display driver logic

·        decoder_7_segment.v: The decimal digit to 7 segment decoder

Starting at the bottom of the modules, the “decoder_7_segment” module is exactly as described in Sec. 14.9.1.

The “multiplexed_7_seg_display” module makes use of the decoder_7_segment module.

module multiplexed_7_seg_display(

    input CLK,

    input [3:0] units, tens, hundreds,

    output [7:0] SEG,

    outputreg [2:0] DIGIT

    );

reg [3:0] digit_data;

reg [2:0] digit_posn;

reg [23:0] prescaler;

decoder_7_seg decoder(.SEG     (SEG), .D (digit_data));

always @(posedge CLK)

begin

        prescaler<= prescaler + 1;

        if (prescaler == 12000) // 1 kHz

        begin

               prescaler<= 0;

               digit_posn<= digit_posn + 1;

               if (digit_posn == 0)

               begin

                       digit_data<= units;

                       DIGIT <= 3’b110;

               end

               if (digit_posn == 2’d1)

               begin

                       digit_data<= tens;

                       DIGIT <= 3’b101;

               end

               if (digit_posn == 2’d2)

               begin

                       digit_data<= hundreds;

                       DIGIT <= 3’b011;

               end    

               if (digit_posn == 2’d3)

               begin

                       digit_posn<= 0;

                       DIGIT <= 3’b111;

               end    

        end

end

endmodule

The module has a clock input (CLK) that will be used to control the switching between one digit and another. CLK will be connected to the 12-MHz clock that the Elbert V2 provides on pin P129.

There are also three 4-bit inputs—“units,” “tens,” and “hundreds”—that will contain the three digits to be displayed. Note how these three inputs can just follow the initial input declaration without the need to repeat the bus size for each of the inputs.

The two outputs of this module are the segment driver pins (SEG) and the digit driver pins (DIGIT).

Three registers are needed for the mechanics of the multiplexing:

·        digit_data: Provides the link to the data input to the 7-segment decoder.

·        digit_posn: Digit position; this cycles round from 0 to 2 to indicate which of the three digits is active during the refresh cycle.

·        Prescaler: This counter is used to divide the 12-MHz clock down to a 100-Hz refresh clock signal.

An instance of the 7-segment decoder is created with the line:

decoder_7_seg decoder(.SEG (SEG), .D (digit_data));

This passes through the segment pins SEG from this module to the 7-segment decoder and links the digit_data register to the D input of the 7-segment decoder.

The “always” block is sensitive to the positive edge of the clock and pre-scales the 12-MHz clock by using the “if” to only actually do something when the prescaler gets to 12000 (decimal). When this happens, the prescaler is reset and then the business of refreshing the next digit of the display takes place.

This involves first incrementing the digit_posn register and then using a series of “if” statements to set the digit_data to either “units,” “tens,” or “hundreds” depending on the digit position. The DIGIT control pins are also set for the digit currently being displayed. Note that the digit control is active low.

The top-level module (seconds_counter) has one input (CLK) that uses the same 12-MHz clock as the multiplexed_7_seg_display module. The two outputs SEG and DIGIT will be linked to the FPGA pins that drive the segments and digits on the Elbert V2 multiplexed display.

module second_counter(

    input CLK,

    output [7:0] SEG,

    output [2:0] DIGIT

    );

reg [3:0] units, tens, hundreds;

reg [23:0] prescaler;

multiplexed_7_seg_display display(.CLK (CLK),

               .units (units), .tens (tens), .hundreds (hundreds),

               .SEG (SEG), .DIGIT (DIGIT));

always @(posedge CLK)

begin

        prescaler<= prescaler + 1;

        if (prescaler == 24’d12000000)

        begin

               prescaler<= 0;

               units<= units + 1;

               if (units == 9)

               begin

                       units<= 0;

                       tens<= tens + 1;

               end

               if (tens == 9)

               begin

                       tens<= 0;

                       hundreds<= hundreds + 1;

               end

               if (hundreds == 9)

               begin

                       hundreds<= 0;

               end

        end

end

endmodule

This module uses three registers to contain the units, tens and hundreds digits as well as a prescaler to divide the 12-MHz clock signal down to 1 Hz.

The “always” block is sensitive to the positive edge of CLK and divides the clock by 12,000,000 (decimal) to tick over the units, tens and hundreds registers. Once the units register has reached 9, it increments then tens register and then resets to 0.

If you want to try out this example and install it on the Elbert V2, load the project “seconds_counter” from the book downloads, build it and deploy it.

14.9.3 Parameterized Modules

Some modules like the ripple_counter module, that you created earlier, would benefit from parameterization. That is instead of always being the same size, it would be able to specify their size when instantiating them.

As an example of using parameters you can modify the ripple_counter module that you created earlier. The modified code is shown below:

module ripple_counter#(parameter SIZE=4) (

    input CLK,

    input CLR,

    outputreg [SIZE-1:0] Q

    );

always @(posedge CLK, posedge CLR)

begin

        if (CLR)

               Q <= 0;

        else if (CLK)

               Q <= Q + 1;

end

endmodule

The addition of the size parameter is shown in bold. After the parameter name (SIZE) a default value is specified, so that if the parameter is not specified when the module is instantiated, then it will still have a size.

The parameter can then be used anywhere within the module, so in this case, the MSB of the Q output register is set to be SIZE-1.

When it comes to instantiating a parameterized module, you can specify the parameter like this:

ripple_counter#(4) counter(.Q (data), .CLK (CLK), .CLR (clear));

14.10 Simulation

A board like the Elbert 2 allows you to test out your designs using its switches and LEDs to exercise the FPGA and see how it is behaving. You could also use the Elbert’s GPIO pins and test the design using a logic analyzer.

However, ISE includes a simulator that allows you to write a Verilog test fixture and then use it to exercise a module and make sure that its doing what it should, before going anywhere near real hardware.

As an example you can add a text fixture to the Verilog ripple counter project that you created earlier.

To do this, re-open the project ripple_counter_project and then add a new source to it. But, this time, when the New Source wizard opens, select the source type of Verilog Test Fixture and name it “counter_tester” (Fig. 14.31).

img

FIGURE 14.31 Creating a new test fixture.

Change the contents of counter_tester.v so that it appears as below:

module counter_tester;

// Inputs

reg CLK;

reg CLR;

// Outputs

wire [3:0] Q;

// Instantiate the Unit Under Test (UUT)

ripple_counteruut (

        .CLK(CLK),

        .CLR(CLR),

        .Q(Q)

);

initial

begin

        // Initialize Inputs

        CLK = 0;

        CLR = 0;

        // Wait 100 ns for global reset to finish

        #100;

end

always

begin

        #10

        CLK =!CLK;

end

always

begin

        #320

        CLR = 1;

        #1

        CLR = 0;

end

endmodule

The new source wizard creates a lot of the code for us. This includes some code to create an instance of the module to be tested, which is given the name “uut” (unit under test).

The new parts are highlighted in bold.

The “initial” block is run just once as the FPGA resets and just sets both CLK and CLR low. The line #100 instructs the test code to delay for 100 ns, to allow everything to stabilize.

There are then two “always” blocks. The first drives the CLK pin by first delaying for 10 ns then inverting CLK.

The second block drives the CLR pin on a much slower clock. It delays for 320 ns then provides a 1-ns pulse on the CLR pin.

To run the test fixture, click on the Simulation radio button in the Hierarchy View (Fig. 14.32).

img

FIGURE 14.32 Running the test fixture.

Notice that when you select “counter_tester” in the Hierarchy View, the process “Simulate Behavioral Model” appears in the Processes area of the window. Right-click on “Simulate Behavioral Model” and run it. This will result in the ISim module opening and displaying the results of the simulation (Fig. 14.33).

img

FIGURE 14.33 Simulation results for the counter.

To see the detail, you need to click on Q[3:0] in the name column so that the individual lines of the bus are expanded. You will also need to click on the zoom-out icon about 10 times to get to the right time scale and then use the horizontal scrollbar at the bottom of the simulation area to pan over to an interesting area of the simulation results.

14.11 VHDL

Verilog is not the only HDL. The FPGA community is almost evenly split between Verilog and VHDL. In fact, ISE can use both Verilog and VHDL source code and you can even mix them in a project.

VHDL tends to be more popular in some industry sectors such as aerospace and defence, as it has what programmers call “strong typing” that makes the code more rigorous and allows you to catch more potential problems at the time the project is being built rather than during simulation.

Most software programmers will tell you that once you have programmed in one language, learning a second is much easier to learn as you can just map things you have learnt in the new language to construct in the language you already know. The basic concepts are the same in both.