Alchitry

Navigation: Main menu

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.

Creating a New Project

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

Click onFile->New Project. In the dialog that pops up enter aProject NameofLEDtoButton. 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 andFrom Exampleset toBase 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.

ClickCreateto create your project.

Back in the main window, expand the tree on the left to findmojo_top.lucinside ofSource. Double click on it to open it in the editor.

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 themodule declaration. This is lines 1 through 14, reproduced below.

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

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

After that is theport declaration. This is where you specify all theinputs,outputs, andinoutsof your module. Aninoutis 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 outputsledandspi_channelare multi-bit signals.ledconsists of 8 bits andspi_channelis 4 bits. This is declared by the [8] and [4] after the signal's names, respectively.

The reasonledis 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,ledandrst_n. As you may have guessed,rst_nconnects to the reset button. The_npart 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 isalwayshappening. When the tools see an always block, they need to generate a digital circuit that will replicate thebehaviorthat the block describes. Let's take a look at the always block in our code.

Notice I said the tools need to replicate the block'sbehaviorand not the block. That is because inside an always block, statements that appear lower in the block havepriorityover 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 theledsignal to8hFF(all 1s).

Back to our design. Were we are assigning six signals values. Note that every output of a modulemusthave 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 theEncodings Tutorialbefore 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),1010will be four bits wide, andhACBwill 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,4d2will 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 meansdon'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 ishigh-impedanceor 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 inputrst_nto the input of thereset_condmodule. 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 therst_nsignal is active low (0 when the button is pressed, 1 otherwise) while therstsignal is active high.

The next line assigns theledoutput 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. Theledoutput 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, whereledis assigned.

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

The outputledis an 8 bit array. That means when you assign it a value you need to provide an 8 bit array. However, the signalrstis 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 constant7h0withrst. 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 used7b0, or7d0, 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,4b11would the same as4b0011.

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,b1101is exactly the same as4b1101. If you are using decimal, you can even drop the d so4d12is the same asd12which is the same as just12. 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 toSettings->ISE Location...and find the folder that isXilinx/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.

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

Loading Your Project

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.

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

Now push the reset button.

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 thereset_conditioneris 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 thereset_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 simplyexist. 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 isM 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.

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 torst.

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.

Always Blocks

Because the second statement has priority over the first,led[0]will actually NEVER have the value 0! It will be permanently connected torst. Note that the second line only assigns the first bit ofled. 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.