FPGA

From Ggl's wiki

Jump to: navigation, search

Contents

What is it?

FPGA stands for Field-Programmable Gate Array. It contains an array of programmable logic (think logic operators like AND, OR, XOR, etc...) and a hierarchy of reconfigurable interconnects between them. In other word, it allows to program the function of logic blocks, and to choose how to wire their inputs and outputs together. It's a bit like a virtual breadboard with a big box that contains a lot of digital electronic components at its side ;).

Most FPGAs only work with digital low-voltage (CMOS, TTL, ...) signals. Digital means the signal is discrete: there is a finite number of values across an interval of time. The opposite, analog, represents real values. A digital signal is decomposed in bits (binary digits). Two ranges of voltage are defined to encode a bit: high and low. Between them there is a forbidden range where the value is undefined. If components with different voltage level are connected they should respect the static discipline.

Examples of applications:

  • simple digital electronics
  • signal processing
  • processor development
  • high performance computing
  • robotics
  • audio controller

Introduction

Some weeks ago I started to read The Elements of Computing Systems by Noam Nisan and Shimon Schocken. I bought the paper version. The authors also provides the entire book as PDF chapters in the study plan. This book is a gem, it introduces the core principles of computing and ends every chapter with a project. Each project builds a part of a computer system:

  • logic gates
  • ALU
  • control unit and micro-architecture of a custom 16-bit computer
  • assembler
  • virtual machine
  • compiler from a high-level language to the virtual machine
  • operating system

The authors publish all the necessary tools to build and test the projects.

I bought another book at the same time, Digital Design and Computer Architecture by David and Sarah Harris. You will find the companion website here. It is also a great book you should not judge on its cover. The book is very well written and explain deeply and clearly how to design a computer. It takes as example the MIPS architecture as well as the x86. It drives the reader from digital design through computer architecture and micro-architecture. After the chapter on hardware representation languages (HDLs), the authors provide example code in Verilog and VHDL. Topics that are not mandatory to understand the main matter are marked with a '*'. The book answers many small questions and defines almost every concepts it introduces.

When I was reading these books, I wanted to test what I was learning. I thought using a real world HDL would allow me to test and even implement the example on a real device. From the examples I read, I found Verilog source codes shorter and clearer than VHDL ones. Though VHDL allows to define new types and seems to provide greater modularity, I'll try Verilog first.

Digilent Nexys2-1200k board

After having written the HDL source, I need to synthetize it to a representation that will be use for simulation. I found opensource software to do this. However I wondered how to go further into the toolchain and implement the design onto a FPGA. The two main constructors are Xilinx and Altera. I almost arbitrary chose Xilinx. It seems to be the most common platform, especially for educational purpose. As I live in France, I ordered a Digilent Nexys 2 1200k board from lextronic.fr . If I had searched a bit more before, I should have ordered a Nexys 3 from trenz electronics but nevermind, it's a good board with a Spartan-3e (the Nexys 3 has the newer Spartan-6).

File:Nexys2_overview.png

Depending on the projects you intend to develop with the board, you should take a look at the Digilent pmods extension modules.

File:Nexys2_pmod.png

File:Nexys2_pmod_table.png

The board routes digital signals and you'll need to convert analog signals in order to process them. The pmod-ad1 comes with two A/D (Analog to Digital) inputs.

Xilinx Toolchain

Xilinx provides a free version of its toolchain for FPGA development. Its ISE webpack. The full package is 4GB large and is available as 32-bit and 64-bit linux binaries. By default, ISE is a graphical IDE that calls in background the other tools. I usually program with vim, so I wanted to write Verilog in vim and use command line tools to synthetize, simulate, and burn the design on the FPGA.

The first tool is xst.

To download Xilinx ISE Webpack I needed to register myself on Xilinx website. It's a bit 80's style, it wastes time, and maybe they could free their users from this steps. Then there is a bunch of docs. The Nexys 2 board comes with an USB port. You use the Digilent software Adept to program the board through JTAG over USB.

Digilent provides short tutorials. Xilinx ISE WebPACK Verilog Tutorial is based on the Nexys 2 board. See also the Adept Software Basic Tutorial to program the board. These tutorials shows examples on Windows. Here I use linux as the development environment.


Development Flow

Overview

  • Hardware Description Language (HDL) code (Verilog or VHDL)
  • write constraint (.ucf) file
  • RTL testbench and simulation (timing and functional simulation)
  • synthesize (xst): HDL into generic gate-level components
  • implementation:
    • translate (ngdbuild): merge multiple design file into a single netlist
    • map (map): generic gates in the netlist => FPGA's logic cells and IOBs
    • place and route (par): physical layout inside the FPGA chip

Synthesize

Xilinx Synthesis Technology (xst) is a Xilinx application that synthesizes Hardware Description Language (HDL) designs to create Xilinx specific netlist files called NGC files. The NGC file:

  • Is a netlist that contains both logical design data and constraints.
  • Takes the place of both Electronic Data Interchange Format (EDIF) and Netlist

Constraints File (NCF) files.

$ echo 'run -ifn toplevel.prj -ifmt mixed -ofn toplevel -ofmt NGC -p xc3s1200e-4-fg320 -top toplevel' | xst

It generated the file toplevel.ngc and a log file toplevel.srp.

Translate

ngdbuild reads in a netlist file in EDIF or NGC format and creates a Xilinx Native Generic Database (NGD) file that contains a logical description of the design in terms of logic elements, such as AND gates, OR gates, LUTs, flip-flops, and RAMs. The NGD file contains both a logical description of the design reduced to Xilinx primitives and a description of the original hierarchy expressed in the input netlist. The output NGD file can be mapped to the desired device family.

$ ngdbuild -uc sevensegment.ucf -p xc3s1200e-fg320-4 toplevel.ngc toplevel.ngd

It generated the file toplevel.ngd and a log file toplevel.bld.

Map

The map program maps a logical design to a Xilinx FPGA. The input to map is an NGD file, which is generated using the ngdbuild program. The NGD file contains a logical description of the design that includes both the hierarchical components used to develop the design and the lower level Xilinx primitives. The NGD file also contains any number of NMC (macro library) files, each of which contains the definition of a physical macro. Finally, depending on the options used, map places the design. map first performs a logical DRC (Design Rule Check) on the design in the NGD file. map then maps the design logic to the components (logic cells, I/O cells, and other components) in the target Xilinx FPGA. The output from map is an NCD (Native Circuit Description) file a physical representation of the design mapped to the components in the targeted Xilinx FPGA. The mapped NCD file can then be placed and routed using the par program.

$ map -cm area -ir off -p xc3s1200e-fg320-4 -c 100 -o toplevel.ncd toplevel.ngd toplevel.pcf
  • -cm area | speed | balancd
  • -ir all | off | place
  • -c 0 | 1 | 100


Place and Route

After you create a Native Circuit Description (NCD) file with the map program, you can place and route that design file using par. par accepts a mapped NCD file as input, places and routes the design, and outputs an NCD file to be used by the bitstream generator (bitgen).

$ par -w -ol high -t 1 toplevel2.ncd toplevel2.pcf
  • -w: overwrite existing files
  • -t [1-100]
  • -ol std | high : overall effort level


Analyze timing

The Timing Reporter And Circuit Evaluator (TRACE) tool provides static timing analysis of an FPGA design based on input timing constraints.

trace performs two major functions:

  • Timing Verification: Verifies that the design meets timing constraints.
  • Reporting: Generates a report file that lists compliance of the design against the input constraints. trace

can be run on unplaced designs, only placed designs, partially placed and routed designs, and completely placed and routed designs.

Generate the target FPGA image

bitgen is a Xilinx command line tool that generates a bitstream for Xilinx device configuration. After the design is completely routed, you configure the device using files generated by BitGen. bitgen takes a fully routed Native Circuit Description (NCD) file as input and produces a configuration Bitstream (BIT) file as output. A BIT file is a binary file with a .bit extension. The BIT file contains the configuration information from the NCD file. The NCD file defines the internal logic and interconnections of the FPGA device, together with device-specific information from other files associated with the target device. The binary data in the BIT file is then downloaded into the memory cells of the FPGA device, or used to create a PROM file.

References

7-segment LED example

File:Nexys2_7segment-0.jpg File:Nexys2_7segment-3.jpg

This part is heavily inspired by Getting Started With the NEXYS2 Spartan 3E Kit and Xilinx ISE Webpack A Beginner’s Tutorial. However my developement environment run on a linux distribution and I'll try to give more details.

This example only implements combinational logic, the outputs only depend on a combination of logical operators applied to the inputs. We will see sequential logic in later examples.

I/O Mapping in the constraint file

File:Nexys2_io.png

They are also referenced in the Master UCF file for the Nexys2-1200 listed in the Nexys 2 product page. They are written on the board too.

With respect to this mapping we define a constraint file sevensegment.ucf to address the anodes (an), switches sw, and seven segment LEDs (seg):

NET "an<0>" LOC = F17;
NET "an<1>" LOC = H17;
NET "an<2>" LOC = C18;
NET "an<3>" LOC = F15;

NET "seg<0>" LOC = L18;
NET "seg<1>" LOC = F18;
NET "seg<2>" LOC = D17;
NET "seg<3>" LOC = D16;
NET "seg<4>" LOC = G14;
NET "seg<5>" LOC = J17;
NET "seg<6>" LOC = H14;

NET "sw<0>" LOC = G18;
NET "sw<1>" LOC = H18;
NET "sw<2>" LOC = K18;
NET "sw<3>" LOC = K17;

note: I kept the same names as in the UCF file Digilent provides.

VHDL source code

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

---- Uncomment the following library declaration if instantiating
---- any Xilinx primitives in this code.
--library UNISIM;
--use UNISIM.VComponents.all;

entity toplevel is
    Port ( sw  : in STD_LOGIC_VECTOR (3 downto 0);
           seg : out STD_LOGIC_VECTOR (6 downto 0);
           an  : out STD_LOGIC_VECTOR (3 downto 0));
end toplevel;

architecture Behavioral of toplevel is
 
begin
    with sw select
        seg <=
            "1000000" when x"0" ,
            "1111001" when x"1" ,
            "0100100" when x"2" ,
            "0110000" when x"3" ,
            "0011001" when x"4" ,
            "0010010" when x"5" ,
            "0000010" when x"6" ,
            "1111000" when x"7" ,
            "0000000" when x"8" ,
            "0010000" when x"9" ,
            "0001000" when x"A" ,
            "0000011" when x"B" ,
            "1000110" when x"C" ,
            "0100001" when x"D" ,
            "0000110" when x"E" ,
            "0001110" when others;

    an <= "1110";
end Behavioral;

Verilog

module sevensegment (input      [3:0] sw,
                     output reg [6:0] seg,
                     output     [3:0] an);

  always @ (*)
    case (sw)
      0: seg = 7'b1000000;
      1: seg = 7'b1111001;
      2: seg = 7'b0100100;
      3: seg = 7'b0110000;
      4: seg = 7'b0011001;
      5: seg = 7'b0010010;
      6: seg = 7'b0000010;
      7: seg = 7'b1111000;
      8: seg = 7'b0000000;
      9: seg = 7'b0010000;
    'hA: seg = 7'b0001000;
    'hB: seg = 7'b0000011;
    'hC: seg = 7'b1000110;
    'hD: seg = 7'b0100001;
    'hE: seg = 7'b0000110;
    'hF: seg = 7'b0001110;
    endcase

  assign an = 4'b1110;
endmodule

note: the 7-segment LED works in active low: 0 => ON, 1 => OFF. Bits in Most-Significant Bit (msb) order. The hexadecimal values are automatically converted to bits value from the combination of switches.

Project layout

$ ls sevensegment/
Makefile
sevensegment.prj
sevensegment.ucf
sevensegment.vhdl
sevensegment.v

For the VHDL version:

$ cat sevensegment.prj
vhdl work "sevensegment.vhdl"

For the Verilog one:

verilog work "sevensegment.v"

See the contents of sevensegment.ucf and sevensegment.vhdl above.

Makefile

PROJECT ?= sevensegment
PLATFORM ?= xc3s1200e-4-fg320 # for Nexys2 board
CONSTRAINTS ?= sevensegment.ucf
SOURCES = sevensegment.vhdl
FORMAT ?= mixed #| vhdl | verilog

XST = xst
NGDBUILD = ngdbuild
MAP = map
PAR = par
BITGEN = bitgen

all: $(PROJECT).bit

$(PROJECT).bit: $(SOURCES)
	echo "run -ifn $(PROJECT).prj -ifmt $(FORMAT) -ofn $(PROJECT) -ofmt NGC -p $(PLATFORM) -top $(PROJECT)" | $(XST)
	$(NGDBUILD) -uc $(CONSTRAINTS) -p $(PLATFORM) $(PROJECT).ngc $(PROJECT).ngd
	$(MAP) -cm area -ir off -p $(PLATFORM) -c 100 -o $(PROJECT).ncd $(PROJECT).ngd $(PROJECT).pcf
	$(PAR) -w -ol high -t 1 $(PROJECT).ncd $(PROJECT).pcf
 	$(BITGEN) -w -g StartUpClk:JtagClk -g CRC:Enable $(PROJECT).pcf.ncd $(PROJECT).bit $(PROJECT).pcf

clean:
	rm -f *.ncd *.pcf *.ngd *.ngc *.srp *.bld *.xrpt *.lso *.bgn $(PROJECT).bit

Programming the FPGA

Digilent provides two Adept packages for linux: runtime and utilities. They come in i686 (digilent.adept.runtime_2.8.2-i686.tar.gz and digilent.adept.utilities_2.1.1-i686.tar.gz) and amd64 (digilent.adept.runtime_2.8.2-x86_64.tar.gz and digilent.adept.utilities_2.1.1-x86_64.tar.gz) versions. Each package provides a install.sh script that installs files in /usr/local/ by default.

dadutil queries and configure attributes like the device name, serial number, and user name.

$ dadutil enum
Found 1 device(s)

Device: Nexys2
    Product Name:   Onboard USB
    User Name:      Nexys2
    Serial Number:  100XXXXXXXXX
$ dadutil showinfo -d Nexys2
Product Name:           Onboard USB
User Name:              Nexys2
Serial Number:          100XXXXXXXXX
Product ID:             00100005
Firmware Version:       0305
Device Transport Type:  00010001 (USB)
Device Capabilities:    0000000D
    DJTG - JTAG scan chain access
    DEPP - Asynchronous Parallel Input/Output
    DSTM - Streaming Synchronous Parallel Input/Output

djtgcfg use the JTAG interface to program the board.

$ djtgcfg enum
Found 1 device(s)

Device: Nexys2
    Product Name:   DOnbUsb1 V2.0
    User Name:      Nexys2
    Serial Number:  10054D273764   
$ djtgcfg init -d Nexys2
Initializing scan chain...
Found Device ID: f5046093
Found Device ID: 21c2e093

Found 2 device(s):
    Device 0: XC3S1200E
    Device 1: XCF04S
$ djtgcfg prog -d Nexys2 --index 0 --file toplevel.bit
Programming device. Do not touch your board. This may take a few minutes...
Programming succeeded.

Below is an example of the 7 segment LEDs with all four bits set (0xF):

File:Nexys2_7segment-1.jpg File:Nexys2_7segment-2.jpg

Adding new features

Now let's light the LED above each switch that is enabled. We need:

- to update the constraint file sevensegment.ucf - add led as an output of the sevensegment module - add a statement in the module

In sevensegment.ucf:

NET "<led0>" LOC = J14;
NET "<led1>" LOC = J15;
NET "<led2>" LOC = K15;
NET "<led3>" LOC = K14;

In sevensegment.v:

output [3:0] led);

and

assign led = sw;

A simple animation

The previous example only requires combinational logic. If we want to display an animated pattern, it requires to implement multiples states wit next state logic et output logic. A way to do that is synchronous sequential logic. Each state is stored on registers all synchronized on a common clock.

module animation (input      clk,
                  input      reset,
                  output     [6:0] seg,
                  output     [3:0] an);

    parameter WAIT = 25000000; // clock frequency is 50Mhz e.g. 1s/5x10^6 on the Nexys2

    reg [3:0] state;
    reg [3:0] select;
    reg [31:0] cnt;

    //state register
    // S0: 1110
    // S1: 1101
    // S2: 1011
    // S3: 0111
    // goto S0
    always @(posedge clk, posedge reset)
      if (reset) begin
        state <= 4'b1110;
        cnt <= 0;
        select <= 4'b0001;
      end
      else begin
        if (cnt < WAIT) begin
          cnt <= cnt + 1;
          state <= state;
        end
        else begin
          select <= select << 1;
          if (state == 4'b0111) select <= 4'b0001;
          state <= 4'b1111 & (~select);
          cnt <= 0;
        end
      end

    // output logic
    assign an = state;
    assign seg = 7'b1000000;
endmodule
Personal tools