Your First FPGA Project

This tutorial will walk you though creating your first project and making the LED on the Mojo light up when you press the reset button.

Before you start you need to have the Mojo IDE installed and you should be installing (or downloading) ISE as it is needed for the end of this tutorial. For more info click here.

Creating a New Project

Launch the Mojo IDE. After click through the welcome screen, you should see the following.

Mojo IDE

Click on File->New Project. In the dialog that pops up enter a Project Name of LEDtoButton. You can leave the default workspace if you want or browse for a different one. This is where all your projects will be stored

Use the drop-down menu to choose the board you are using.

Leave the language set to Lucid and From Example set to Base Project. This will copy a bare-bones project as the starting point.

It should now look something like this. Note that your workspace will be different.

New Project Dialog

Click Create to create your project.

Back in the main window, expand the tree on the left to find mojo_top.luc inside of Source. Double click on it to open it in the editor.

Opened mojo_top.luc

The top file contains all the external inputs and outputs of your design. Your future projects can be built out of many modules, but they all must be sub-modules to this top module. This will be covered more in a later tutorial. For now, we will make all our edits to this file.

Conveniently, the default file has all the inputs and outputs that are hardwired to the FPGA on the Mojo already declared. This way you can just use them without having to look up what pins they connect to. However, you may have noticed the yellow underline under all of the inputs. This is because we aren't using them currently and the IDE is just giving us a warning.

If you hover your cursor over a warning or error, text will show up to let you know what is going on. Also when you attempt to build your project (click the hammer icon, you need ISE installed for this) all the warnings and errors will be printed in the console.

The Contents of a Module

Before we continue on, let's cover the basics of what makes up a module and what a module even is. A module is way to organize your project into smaller pieces broken apart by function. It is very similar to the concept of functions when programming where you break down your program into smaller more manageable pieces. Modules are also nice because they can be reused in multiple parts of your design.

Module Declaration

The first part of any module is the module declaration. This is lines 1 through 14, reproduced below.

module mojo_top(
    input clk,              // 50MHz clock
    input rst_n,            // reset button (active low)
    output led [8],         // 8 user controllable LEDs
    input cclk,             // configuration clock, AVR ready when high
    output spi_miso,        // AVR SPI MISO
    input spi_ss,           // AVR SPI Slave Select
    input spi_mosi,         // AVR SPI MOSI
    input spi_sck,          // AVR SPI Clock
    output spi_channel [4], // AVR general purpose pins (used by default to select ADC channel)
    input avr_tx,           // AVR TX (FPGA RX)
    output avr_rx,          // AVR RX (FPGA TX)
    input avr_rx_busy       // AVR RX buffer full
) {

It always starts off with the module keyword followed by the name of the module. In this case it is mojo_top. It is convention to name your module the same thing as the file name.

After the module name comes an optional parameter declaration, this isn't used in our module and it will be covered in a later tutorial.

After that is the port declaration. This is where you specify all the inputs, outputs, and inouts of your module. An inout is a bi-directional port and will also covered in a later tutorial.

Most of the ports in this module are single bit signals. However, the outputs led and spi_channel are multi-bit signals. led consists of 8 bits and spi_channel is 4 bits. This is declared by the [8] and [4] after the signal's names, respectively.

The reason led is 8 bits wide, is because the Mojo has 8 LEDs on it! Each bit of the signal connects to one of the LEDs on the board.

We only care about two of these ports for our project, led and rst_n. As you may have guessed, rst_n connects to the reset button. The _n part of it's name is because the signal is active low (as stated in the comment). This means that the value is typically 1 (high), but when the button is pressed (active) it is 0 (low).

The Always Block

There is some stuff before the always block, but we will get back to that later (it will make more sense then). So for now, just ignore it.

Always blocks are where all the magic happens. It is where you can perform computation and read/write signals. The always block gets its name because it is always happening. When the tools see an always block, they need to generate a digital circuit that will replicate the behavior that the block describes. Let's take a look at the always block in our code.

  always {
    reset_cond.in = ~rst_n; // input raw inverted reset signal
    rst = reset_cond.out;   // conditioned reset

    led = 8h00;             // turn LEDs off
    spi_miso = bz;          // not using SPI
    spi_channel = bzzzz;    // not using flags
    avr_rx = bz;            // not using serial port
  }

Notice I said the tools need to replicate the block's behavior and not the block. That is because inside an always block, statements that appear lower in the block have priority over earlier statements. This is similar to programming, and this abstraction makes it so much easier to program complex logic. However, it is important to understand you are not programming and this is just an abstraction. To make this clear take a look at this example.

  always {
    led = 8h00;             // turn LEDs off
    led = 8hFF;             // turn LEDs on
  }

What would you expect this always block to do? If you have a programming background, you may be tempted to think that the LEDs would continuously turn on and off. You're not in Kansas anymore Dorthy, this isn't programming and that's not what happens. Remember, there is no processor to run code (that is unless you explicitly make one, like a boss). When the tools see this block, they completely ignore the first line. This is because the second line has higher priory. If you were to synthesize this design, the tools would hard-wire the led signal to 8hFF (all 1s).

Back to our design. Were we are assigning six signals values. Note that every output of a module must have a value assigned to it at all times. Since we aren't using these outputs, they are assigned with some reasonable defaults. Let's take a quick detour and look at how values are represented in Lucid.

Representing Values

A value is made of one or more bits. The number of bits a value has is known as it's width. There are a few ways to specify a value, some use an implied width while others allow you to explicitly set a width. If you are unfamiliar with binary or hexadecimal please read the Encodings Tutorial before continuing.

The first way to represent a value is to just type a decimal number. For example, 9.

Sometimes it’s easier to specify a number with a different radix than 10 (the implied default). Lucid supports three different radix, 10 (decimal), 16 (hexadecimal), and 2 (binary). To specify the radix, prepend d, h, or b to specify decimal, hexadecimal, or binary respectively. For example, hFF has the decimal value 255 and b100 has the decimal value 4. If you don’t append a radix indicator, decimal is assumed.

It is important to remember that all number will be represented as bits in your circuit. When you specify a number this way, the width of the number will be the minimum number of bits required to represent that value for decimal. For binary, it is simply the number of digits and for hexadecimal it is four times the number of digits. For example, the value 7 will be three bits wide (111), 1010 will be four bits wide, and hACB will be 12 bits wide.

Sometimes you need more control over the exact width of a number. In those cases you can specify the number of bits by prepending the number of bits and radix to the value. For example, 4d2 will be the value 2 but using 4 bits instead of the minimum 2 (binary value 0010 instead of 10). You must specify the radix when specifying the width to separate the width from the value.

If you specify a width smaller than the minimum number of bits required, the number will drop the most significant bits. When this happens you will get a warning.

Z and X

When you specify a decimal number, all the bits in your value will be either 0 or 1. However, each bit can actually be one of four values, 0, 1, x, or z. The values of 0 and 1 are fairly self explanatory, it just means the bit is high (1) or low (0). The value of x means don't care. It means you want to assign a value, but you really don't care if it is 1 or 0. This is useful for the synthesizer because your circuit may be simpler in one of the cases and it gives the tools the freedom to choose. During simulation, x also means unknown. Z means that the bit is high-impedance or tri-stated. This means that it is effectively disconnected. Note that FPGAs can't realize high-impedance signals internally, so the only time you should use z is for outputs of the top module.

Back to our always block, the first two lines connect the input rst_n to the input of the reset_cond module. Modules can be nested which makes it possible to reuse them and helps make your project manageable. This is all covered later in more detail so don't get hung up over this yet. The only important thing to know about these two lines, is that the rst_n signal is active low (0 when the button is pressed, 1 otherwise) while the rst signal is active high.

The next line assigns the led output to all zeros. This turns off all the LEDs.

The next three lines assign the outputs to z. This is because they aren't being used and we shouldn't drive these as they connect directly to the microcontroller on the Mojo (to use these you have to wait for the microcontroller to signal that it is ready first, this is also covered later).

Looking at this always block, we can see there are no redundant assignments (like in our led on/off example). That means these six signals will literally be connected to these values. The led output will be tied low, and the other three outputs will be floating.

Connecting the Button

We are going to modify the module to connect the reset button to the first LED so that when you push the button the LED turns on.

To do this we need to modify line 28, where led is assigned.

    led = c{7h00, rst};     // connect rst to the first LED

The output led is an 8 bit array. That means when you assign it a value you need to provide an 8 bit array. However, the signal rst is a single bit wide. To compensate for this we use the concatenation operator.

To concatenate multiple arrays into one, you can use the concatenation operator, c{ x, y, z }. Here the arrays (or single bit values) x, y, and z will be concatenated to form a larger array.

In our code we are concatenating the constant 7h0 with rst. The constant here is seven zeros. The 7 represents the size, h represents the number base (in this case hexadecimal) and 0 represents the value. Since we just need a bunch of zeros, the number base doesn’t really matter and we could have used 7b0, or 7d0, for binary or decimal respectively.

Note that values are zero padded if the specified width is larger than the size required to store the value. For example, 4b11 would the same as 4b0011.

If you don’t care about how many bits a values takes up, you don’t have to specify it. The minimum number of bits that will still fit the value will be used. For example, b1101 is exactly the same as 4b1101. If you are using decimal, you can even drop the d so 4d12 is the same as d12 which is the same as just 12. However, when you are concatenating values, you should always specify a width to make it obvious how big the array will be.

Building Your Project

Go ahead and click the little hammer icon in the toolbar to build your project. If you installed ISE (the Xilinx tools) in the default place, your project should start building. If you installed ISE somewhere else, go to Settings->ISE Location... and find the folder that is Xilinx/14.7. Once set you should be able to build your project.

As the project builds you should see a bunch of text spit out. Just wait for it to finish building. It should look like this.

Project Built

The important line here is where it says impl_1 finished. This means your project was built without errors.

Loading Your Project

With your project built, plug your Mojo into your computer if you haven’t already. Then go to Settings->Serial Port... and select the serial port the Mojo is connected to.

It is time to load your project onto the Mojo. You have two options. The first is the hollow arrow, this will write your configuration directly to the FPGA's RAM. The second is the solid arrow, this will write your configuration to the FLASH memory on the board (as well as the FPGA). If you program to the FPGA's RAM, your configuration will be lost when the board loses power. However, if you program to the FLASH, your configuration will be automatically loaded when the board is powered up.

If you hold shift while clicking on the solid arrow, the contents of the FLASH memory will be read back to verify the load was successful. This generally isn't needed and is only helpful when debugging a bad board. You don't need to worry about damaging the FPGA with a bad configuration because there is a CRC check built into the configuration data. That means the FPGA won't start if the data was corrupted.

If you program to RAM and then power cycle the board, the last configuration written to FLASH will automatically be loaded. This is helpful when you want to temporarily try out some configuration.

If you want to stop the FPGA from being configured at power up, you can click the eraser icon. This will wipe the FLASH memory and clear the FPGA's current configuration.

Go ahead and click the hollow arrow to program just the FPGA since we will be making some modifications soon.

Project Loaded

Now look at your Mojo. You should see the DONE LED on. This means that the configuration was loaded successfully to the FPGA.

LED to Button Loaded

Now push the reset button.

Reset Pressed

Notice the LED turned on!

Some Notes on Hardware

When you press the button, how long does it take for the LED to turn on? If this was a processor instead of an FPGA, the processor would be in a loop reading the button state and turning the LED on or off based on that state. The amount of time between pressing it and when the LED turns on would vary depending on what code the processor was executing and how long it is until it gets back to reading the button and turning the LED on. As you add more code to your loop, this time and variation gets bigger.

However, an FPGA is different. With this design (note I said design and not code), the button input is directly connected to the LED output. You can imagine a physical wire bridging the input to the output inside the FPGA. In reality, it's not a wire but a set of switches (multiplexers) that are set to route the signal directly to the output. Well, this is only partially true since the reset_conditioner is there which does some stuff to clean up the reset signal.

Since the signal doesn’t have to wait for a processor to read it, it will travel as fast as possible through the silicon to light the LED. This is almost instant (again, forget about the reset_conditioner)! The best part is that if you wire the button the LED then go on to create some crazy design with the rest of the FPGA, the speed of this will not slow down. This is because the circuits will operate independently as they both simply exist. It is this parallelism where FPGAs get their real power.

Duplication

What if we want all the LEDs to turn on and off with the press of the button instead of just one?

Well we could do it using concatenation just like before by replacing line 20 with the following.

    led = c{rst,rst,rst,rst,rst,rst,rst,rst};

However, there is a better way! This is where the array duplication syntax comes in handy. The syntax is M x{ A }. Here M is the number of times to duplicate A. That means we can make line 20 look like this.

    led = 8x{rst};

Much cleaner! This does exactly the same thing as before, but requires a lot less typing.

Array Indexing

There is an alternative way to write the code where we only want one LED to light. This is by assigning the parts of the led array separately.

    led[7:1] = 7h0;         // turn these LEDs off
    led[0] = rst;           // connect rst to led[0]

On line 20, the bit selector [7:1] is used to select the bits 7 through 1 of the led array. These seven bits are set to 0.

On line 21, [0] is used to select the single bit, 0, and set it to rst.

There are two ways to select a group of bits. The first one (and most common) is the one used above where you specify the start and stop bits (inclusive) explicitly. The other way is to specify a start bit and the total number of bits to include above or below the start bit.

Line 20 could be rewritten as any of the following.

led[7:1] = 7b0;  // select bits 7-1
led[7-:7] = 7b0; // select 7 bits starting from 7 and going down
led[1+:7] = 7b0; // select 7 bits starting from 1 and going up

The benefit of using the start-width syntax is the width is guaranteed to be constant. This means you can use a signal to specify the start index. This will be covered in a later tutorial.

Always Blocks

Due to the nature of always blocks, you could also write the LED assignment as follows.

    led = 8b0;              // turn the LEDs off
    led[0] = rst;           // connect rst to led[0]

Because the second statement has priority over the first, led[0] will actually NEVER have the value 0! It will be permanently connected to rst. Note that the second line only assigns the first bit of led. That means that the other 7 bits will still receive their value from the first statement.

This is one of the weird things of working with hardware. The code you write is not run on a processor like it is when you program. Instead, the code you write is interpreted by the tools to figure out what behavior you want. The tools then create a circuit that will match that behavior.

Always blocks are an easy way to describe complex behavior, but the way you describe the behavior and it's actual implementation can vary.

Congratulations! You've finished your first tutorial on Lucid. When you're ready for more click here for the Synchronous Logic Tutorial.