IO Shield

This tutorial will introduce the IO Shield and walk through some basic examples to get your feet wet. If you haven't already, I highly recommend reading through the Your First FPGA Project and Synchronous Logic tutorials before continuing.

Create the Project

In the Mojo IDE, open the new project dialog (File->New Project...) and name it whatever you want. I named mine IO Shield Demo. Before you click Create, select IO Shield from the From Example drop-down menu. This will give you a bare bones base to start working with the shield.

The only difference between this project and the standard base project is that this has the connections to the IO Shield already defined for you.

Let's take a look at mojo_top.luc.

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
    output io_led [3][8],   // LEDs on IO Shield
    output io_seg [8],      // 7-segment LEDs on IO Shield
    output io_sel [4],      // Digit select on IO Shield
    input io_button [5],    // 5 buttons on IO Shield
    input io_dip [3][8]     // DIP switches on IO Shield
  ) {

  sig rst;                  // reset signal

  .clk(clk) {
    // The reset conditioner is used to synchronize the reset signal to the FPGA
    // clock. This ensures the entire FPGA comes out of reset at the same time.
    reset_conditioner reset_cond;
  }

  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

    io_led = 3x{{8h00}};    // turn LEDs off
    io_seg = 8hff;          // turn segments off
    io_sel = 4hf;           // select no digits
  }
}

You can see we have a handful of inputs and outputs named io_*. These are the IO Shield specific signals.

io_led connects to the 24 LEDs. This signal is organized as a 2D array to make it easier to access the three groups of 8 LEDs. For example, if you want to get the first group of LEDs you would use io_led[0] and if you wanted to get the third LED in the first group, you would use io_led[0][2].

Because io_led is a 2D array, when we set all the LEDs to 0, we have a little fancy syntax.

    io_led = 3x{{8h00}};    // turn LEDs off

We need the dimensions of our constant to match io_led. In this case, io_led is a 3 by 8 array. We use 8h00 as the base constant which is a single dimensional array of width 8. The x{} syntax takes the outer most dimension and duplicates it. If we wrote 3x{8h00}, we would end up with 24h000000 since the outer most dimension is 8. This isn't what we want as it is still a 1D array. Instead, we need to first create a 1 by 8 array and duplicate it three times. To do this we use the array building syntax, {}. The value {8h00} is a 1 by 8 2D array. We then use 3x{{8h00}} to get a 3 by 8 array of all zeros. Note that we could have also written {8h00, 8h00, 8h00}, but the duplication syntax is a bit cleaner.

io_seg and io_sel are used to control the 4 seven-segment LED displays. They are active low (0 = on, 1 = off) and they will be covered in more detail later.

io_button is simply the 5 push buttons. io_button[0] is up, io_button[1] is center, io_button[2] is down, io_button[3] is left, and io_button[4] is right.

Finally, io_dip is the 24 DIP switches. This is arranged in a 2D array exactly the same way as io_led.

Logic Gates

The first example we will work though is connecting a few DIP switches to the LEDs through logic gates. If you aren't familiar with what logic gates (AND, OR, NOT, XOR) are, you should take a look at the Digital Logic tutorial.

In this example we are going to start by making the first LED light up when switch 0 and 1 are turned on.

  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

    io_led = 3x{{8h00}};    // turn LEDs off
    io_seg = 8hff;          // turn segments off
    io_sel = 4hf;           // select no digits

    io_led[0][0] = io_dip[0][0] & io_dip[0][1];
  }

Take a look at line 42. First we need to index the first LED. The first LED is in the first group (group 0) and is the first in its group, so we used the index [0][0]. We then index the first and second switches in the same manner. Finally, we use the & operator to AND the bits together. & is the bit-wise AND operator.

Notice that I didn't change the line where we assigned all LEDs a value of 0. This is because the assignment to the first LED will simply be overruled by our second assignment. It's good to remember that while always blocks have a sequential order of precedence, they aren't actually sequential. This means that io_led[0][0] will only have the value of the second assignment and it as if the first assignment never happened. The tools will simply wire an AND gate to the two switch inputs and the led output.

Go ahead and build/load the project onto your Mojo. Try playing with the first two switches. If either switch is off, the LED should be off. Only when both switches are on should the LED turn on.

You can now go back and replace the & operator with the operators for OR, |, XOR, ^, and XNOR, ~^. Play with the switches and make sure you understand the different operators.

Practice: Add two more lines to the always block so that the first LED of the first group lights up as before, the first LED of the second group lights up when either (OR) of the first two switches in the second group are on, and the first LED of the last group lights up only when exactly one (XOR) switch of the first two switches in the last group are on.

Bit-wise

The operators we have talked about so far are all bit-wise operators. That means that they operate on two equally sized arrays and will perform their operation on each pair of bits individually.

For example, if we want to light up the LED in the first group only when the corresponding switch in the first and second groups are both on we could do the following.

  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

    io_led = 3x{{8h00}};    // turn LEDs off
    io_seg = 8hff;          // turn segments off
    io_sel = 4hf;           // select no digits

    io_led[0] = io_dip[0] & io_dip[1];
  }

Note that io_led[0] is an array of width 8, and so is io_dip[0] and io_dip[1]. This single statement will create 8 individual AND gates.

You can again use the OR, XOR, and XNOR operators in the same way.

We can also chain multiple gates together. For example, what if we want the LED to light only when the corresponding switch in all three groups is on?

    io_led[0] = io_dip[0] & io_dip[1] & io_dip[2];

Bit-wise operators are evaluated from left to right so in this case io_dip[0] will be ANDed with io_dip[1] and the result will then be ANDed with io_dip[2]. In the case of AND gates, the order actually doesn't matter. However, if you start mixing and matching operators the order can matter. You can also use parenthesis to make the order you want things evaluated explicit.

Practice: Use two bit-wise operators so that the LEDs in the first group light up when the corresponding switch in the third group is on or both switches in the first two groups are on.

Reduction Operators

Reduction operators are similar to the bit-wise operators but they work on a single input array and always output one bit. You could say they reduce an array to a single bit.

You can think of these operators as one huge logic gate with many inputs. The number of inputs is equal to the size of the array the operator is used on.

In our example, we want to turn the first LED on only if every switch in the first group is turned on. To do this we can use the & reduction operator.

  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

    io_led = 3x{{8h00}};    // turn LEDs off
    io_seg = 8hff;          // turn segments off
    io_sel = 4hf;           // select no digits

    io_led[0][0] = &io_dip[0];
  }

This is equivalent to ANDing each bit individually as shown below.

    io_led[0][0] = io_dip[0][0] & io_dip[0][1] & io_dip[0][2] & io_dip[0][3] & io_dip[0][4] & io_dip[0][5] & io_dip[0][6] & io_dip[0][7];  

However, as you can tell, the reduction operator is much more compact.

It is even possible to use the reduction operators on multi-dimensional arrays. For example, if we want the LED to turn on when any switch is on we could use the following.

    io_led[0][0] = |io_dip; 

I tend to use the AND version to easily detect if a signal is at its maximum value (all 1's). The OR version basically tells you if the value is not 0 and the XOR version will tell you if there is an odd number of 1's.

Each of these operators can also be negated with the bit-wise negate operator, ~. You can simply place this in front of the expression to get the NAND, NOR, and XNOR equivalents.

Practice: Using your newly acquired bit-wise and reduction operator skills, make the first LED light up when any switch in the third group is on, or all the switches in the second group are on and the first group has an odd number of switches on. This is a fairly tricky challenge but everything you need has been mentioned in this tutorial. If you get stuck feel free to head over to the forum for some help.

Math

This part of the tutorial we will look at addition, subtraction, and multiplication. We will be performing different operations using the DIP switches as our number inputs. This means everything will be in binary, so if you aren't familiar with binary check out the Encodings tutorial.

Let's start with some addition. We will add the binary values from the first and second groups of DIP switches.

  sig result [24];          // result of our operations

  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

    io_seg = 8hff;          // turn segments off
    io_sel = 4hf;           // select no digits

    result = io_dip[1] + io_dip[0]; // add the switch values

    io_led = {result[23-:8], result[15-:8], result[7-:8]};    // connect result to LEDs
  }

Because the result of many of our operations will be more than 8 bits, we can create a signal, result, to hold the value. You can think of signals (sig) simply as wires. They don't add any cost to your design as they simply define the connection from something to something else.

We set result to be the output of our addition. The addition of two 8 bit numbers results in a 9 bit result. This is because an 8 bit number has the range 0-255 so adding two of them together will have the range 0-510, which requires 9 bits to represent.

We then take result and connect it to the LEDs so that we can see all 24 bits.

Build and load your project to your Mojo. Congratulations! You've just build a very basic calculator. To make sure everything is working properly, let's calculate 2+2. 2 has the binary representation of 0000 0010. Set both groups of switches to 2. The result on the LEDs should be 4, or 0000 0100.

Now change the + to a - to subtract the first switch value from the second.

    result = io_dip[1] - io_dip[0]; // subtract the switch values

What happens when you set the second switch to 0 and the first to 1 so you're performing 0-1? All 24 LEDs turn on! You can interpret this answer in two different ways. The first is that the result simply underflowed and restarted back at the maximum value. The second way is to assume the number is a 2's compliment representation of -1. Both are valid interpretations and what you use will depend on the scenario.

Even though when the number is negative all 24 LEDs light up, you only need 9 bits to represent any 8 bit by 8 bit addition or subtraction. This is because when the value would be negative, dropping the leading 1's doesn't change its value.

Finally, change the - to * to test out multiplication.

    result = io_dip[1] * io_dip[0]; // multiply the switch values

You should notice that multiplying two numbers can generate a much bigger result than the operands. The result will generally be twice the size of the inputs. So in our case, up to 16 bits wide.

Multiplication is much more expensive to perform than a simple addition or subtraction. This is because a multiplication is essentially many additions. Multiplying two 8 bit numbers will result in a series of eight additions.

Also note that we didn't talk about division. This is because there isn't a division operator. Division is much more complicated than even multiplication and there are some trade offs you need to decide for your design so a default option doesn't make sense. If you don't need real division, you can approximate it by multiplying with fractions.

To approximate division, you can first multiply by any number and then shift the value to the right. Shifting to the right effectively divides by a power of two (rounding down). For example, if you want to divide a number by 3, you could multiply by 85 then shift right 8 bits. This will effectively multiply your value by 85/256 = 0.33203 which is pretty close to 1/3. If you need higher precision, you can use more bits. For example, you could instead multiply by 21845 and shift 16 bits to the right. This is effectively multiplying by 0.333328, but 16 bit multiplication is significantly more expensive than 8 bit.

Seven-Segment Displays

The IO Shield has four seven-segment displays that are multiplexed. This means that we have two groups of signals, the first, io_seg, connects to the segments of each display.

Seven Segment Display

Note that even though we call these seven-segment displays there are actually 8 LEDs in each one because of the decimal point.

All four displays have all their segments connected together. That means if you apply power to segment 0, all four displays segment 0 receive power! This is done to save on the number of pins required to drive the displays. However, it means that we need a way to disable all but one display so that we can show a unique number on each one.

This is where the second group of signals, io_sel, comes in handy. These are used to select which digit is active. Typically, only one digit will be active at any time. If you have more than one digit active, all active digits will show exactly the same thing since their segments are wired together.

The way we are going to display four unique numbers is by cycling which digit is active really fast. If we can do this fast enough, our eyes can't tell and it looks like all four digits are always on.

However, before we do that, let's play around with the displays a bit to get familiar with how they work.

Indexing Segments

We are going to setup some modules so that we light one segment at a time. To do this we will be using some of the components built into the Mojo IDE.

Click Project->Add Components.... Under Miscellaneous check off Counter and Decoder. Click Add to add them to your project.

You can now find the source for these components under Components in the left project tree in the Mojo IDE. Note that while you are free to look at the source, components are read-only (no editing!). If you ever want to modify a component, simply copy and paste the code into a module of your own.

It's always a good idea to check out the source and read the description of the component. Many components have parameters that you can set to customize them for your use. We'll be doing exactly that with these two.

  .clk(clk) {
    // The reset conditioner is used to synchronize the reset signal to the FPGA
    // clock. This ensures the entire FPGA comes out of reset at the same time.
    reset_conditioner reset_cond;

    .rst(rst) {
      counter ctr (#SIZE(3), #DIV(24));
    }
  }

  decoder num_to_seg (#WIDTH(3));

Here we add the counter and decoder to mojo_top. The counter component requires both a clock and reset signal so we have it in the nested in both the .clk and .rst connection blocks. On the other hand, decoder doesn't require either, so it is outside of both.

The counter is simply a counter. It will output a value starting from zero and increment to whatever the maximum value you set is. After that it will reset back to zero. It is also possible to configure it to count down from the max value to zero.

The counter has four parameters you can set to get it to operate how you want, but here we only need to change two. SIZE dictates how wide the output of the counter is. In this case we need it to count from 0 to 7 for all 8 segments of our displays. Three bits is exactly 0 - 7. If we wanted to count to a value that wasn't conveniently a power of 2 minus one, we could set the parameter TOP to control its maximum value.

The DIV parameter is used to set how many bits to use to divide the clock. By setting this to 24, the counter will only increment its output every 224, or 16,777,216, clock cycles. This is helpful since the clock runs at 50MHz (50,000,000 cycles per second) so if the counter incremented every clock cycle it would be way too fast for us to see.

The decoder is a binary to one-hot decoder. If you need a refresher on one-hot representation check out the Encodings tutorial. Basically, the decoder will take our three bits and convert it to eight bits. If the input is 0, then the 0th bit of the output will be 1 and everything else will be 0. If the input is 4, the 4th bit will be 1 and everything else will be 0. This allows us to use the binary counter to light individual LEDs.

Note that the output of the decoder is 2WIDTH bits wide. So in our case, 23 = 8 bits wide.

Now we can hook up the modules.

    num_to_seg.in = ctr.value;

    io_seg = ~num_to_seg.out; // connect segments to counter
    io_sel = 4h0;             // select all digits

We feed the binary counter value into the decoder and the output of the decoder to io_seg. Note that io_seg and io_sel are both active low. This means that when the signal is 0, it is active. To turn only one LED on we need to invert the output of the decoder with the bit-wise inversion, ~, operator. This will make the signal zero-hot.

Also notice we need to set io_sel to 0 so that the LED segments are selected. Setting all of them to 0 will turn them all on.

Build and load your project to your Mojo. You should now see the segments of all 4 displays lighting one at time. Try to light only a single display by setting io_sel to ~4h1 instead.

Now what if we wanted to cycle through each digit? We need another counter that increments each time the original counter overflows. That way once each LED of a specific digit is lit, the next digit is selected. However, we don't actually need another counter to do this! We can simply make our existing counter 2 bits wider. By adding extra bits, these will increment only when the lower three bits overflow, creating the behavior we want.

  .clk(clk) {
    // The reset conditioner is used to synchronize the reset signal to the FPGA
    // clock. This ensures the entire FPGA comes out of reset at the same time.
    reset_conditioner reset_cond;

    .rst(rst) {
      counter ctr (#SIZE(5), #DIV(24));
    }
  }

  decoder num_to_seg (#WIDTH(3));
  decoder num_to_digit (#WIDTH(2));

Here we increase the SIZE of ctr to 5. We also add another decoder with WIDTH of 2. We need 2 since we have 4 digits to select from and an input of 2 bits gives us possible values of 0-3.

    num_to_seg.in = ctr.value[2:0];   // lower three bits used for segments
    num_to_digit.in = ctr.value[4:3]; // upper two bits used for digits

    io_seg = ~num_to_seg.out;   // connect segments to counter
    io_sel = ~num_to_digit.out; // connect digit select to counter

We can select the lower three bits of ctr.value by using the bit selector [2:0]. This says we want bits 2 down-to 0 inclusive. We can do the same thing for the upper two bits.

Build and load your project again. This time you should see only one digit and one segment lit at any time. Once each segment of a digit has been lit, the next digit is selected.

You may have also noticed that the segments are now much brighter. This is because the resistor that is in-line with the LEDs is sized for a single LED, not all four. When you have all four on at the same time, the current is split between them making them all dimmer.

Getting Fancy With Numbers

Lighting up the segments is super cool and all, but it's much cooler to actually show some numbers.

Before we jump into multiplexing, we need a way to convert a number into the segments that need to be lit to represent that number. To do this we can use a look-up table.

Create a new module called seven_seg and add the following.

module seven_seg (
    input char[4],
    output segs[7]
  ) {

  always {
    case (char) {
      0: segs = 7b0111111;
      1: segs = 7b0000110;
      2: segs = 7b1011011;
      3: segs = 7b1001111;
      4: segs = 7b1100110;
      5: segs = 7b1101101;
      6: segs = 7b1111101;
      7: segs = 7b0000111;
      8: segs = 7b1111111;
      9: segs = 7b1100111;
      default: segs = 7b0000000;
    }
  }
}

This module will take a binary number, char, and output the segments, segs, that need to be on.

This modules uses a case statement. Case statements are equivalent to a bunch of if statements. One of the entries will be selected based on the value of char. If char is 1, then only the block after the 1: is used. If none of the blocks match, the default: block is used. The default block is optional, but it is generally a good idea to include it even if you think you have all the cases covered.

If char is an invalid number (not 0-9), then all the segments will be off.

To test out our look-up table, let's connect it to our counter.

  .clk(clk) {
    // The reset conditioner is used to synchronize the reset signal to the FPGA
    // clock. This ensures the entire FPGA comes out of reset at the same time.
    reset_conditioner reset_cond;

    .rst(rst) {
      counter ctr (#SIZE(4), #TOP(9), #DIV(24));
    }
  }

  seven_seg seg;

We need ctr to count from 0-9 so we set TOP to 9 to cap its value. Also we need to change SIZE to 4, as we only need 4 bits to represent 0-9.

Because we don't need the decoders anymore, we can remove them and add our seven_seg module.

Finally, we need to wire it up the LEDs

    seg.char = ctr.value;

    io_seg = ~seg.segs;   // connect segments to counter
    io_sel = ~4h1;        // first digit only

io_sel is set to ~4h1 so that only the right most digit is on.

Build and load the project to your Mojo to make sure the first digit is correctly counting from 0 to 9.

Now we need to make a module that will take four values in and display them on all four digits instead of only using one.

Create a new module called multi_seven_seg and add the following code.

module multi_seven_seg #(
    DIGITS = 4 : DIGITS > 0,
    DIV = 16 : DIV >= 0
  )(
    input clk,                // clock
    input rst,                // reset
    input values [DIGITS][4], // values to show
    output seg [7],           // LED segments
    output sel [DIGITS]       // Digit select
  ) {

  // number of bits required to store DIGITS-1
  const DIGIT_BITS = $clog2(DIGITS);

  .clk(clk), .rst(rst) {
    counter ctr (#DIV(DIV), #SIZE(DIGIT_BITS), #TOP(DIGITS-1)); 
  }

  seven_seg seg_dec;                      // segment decoder
  decoder digit_dec (#WIDTH(DIGIT_BITS)); // digit decoder

  always {
    seg_dec.char = values[ctr.value];     // select the value for the active digit
    seg = seg_dec.segs;                   // output the decoded value

    digit_dec.in = ctr.value;             // decode active digit to one-hot
    sel = digit_dec.out;                  // output the active digit
  }
}

This module is parameterized so that it could be used to drive more or less than 4 digits, but the defaults are set to work well with our project.

The constant DIGIT_BITS is the number of bits we need to cover all DIGITS number of digits. We use the function $clog2() which computes the ceiling log base 2 of a constant value.

We only want the counter to count from 0 to DIGITS-1 so we set the TOP parameter accordingly.

DIV is used so that we don't switch between the digits too fast. If we switch too fast, the transistors that drive the LEDs don't have time to fully turn off and we get bleed between digits.

The way this module works is ctr is used to select the active digit. The value from values is then selected, decoded, and sent to the segments of the LEDs displays. The binary value of the active digit is then decoded into a one-hot value as before and used to select which digit is on. Because ctr keeps cycling the active digits, all the displays will appear on.

We can tweak DIV, but if it is set too high, we will start to see the digits flicker. If it is set too low, we will see bleeding between digits.

Let's test out this module by feeding it some constant numbers to show.

  .clk(clk) {
    // The reset conditioner is used to synchronize the reset signal to the FPGA
    // clock. This ensures the entire FPGA comes out of reset at the same time.
    reset_conditioner reset_cond;

    .rst(rst) {
      multi_seven_seg seg;
    }
  }

We don't need the counter from before, but we need the multi_seven_seg now.

    seg.values = {4h8,4h5,4h3,4h1};

    io_seg = ~seg.seg; // connect segments to the driver
    io_sel = ~seg.sel; // connect digit select to the driver

On line 44 we feed the values to display into the driver.

Build and load the design to your Mojo to make sure each digit is displaying the correct number.

Decimal Counters

Alright, now that we have a way to show a value on our display, let's make a the display count! For this we need a decimal counter. We could use a regular counter and convert the binary value into four decimal values, but that's a bit trickier. Instead, we will create a special counter that counts in base 10.

To do this we will create two modules. The first will be a single digit counter and the second will chain these together for a multi-digit counter.

Create a new module called decimal_counter with the following code.

module decimal_counter (
    input clk,      // clock
    input rst,      // reset
    input inc,      // increment the counter
    output ovf,     // counter overflowed
    output value[4] // current value
  ) {

  .clk(clk), .rst(rst) {
    dff val[4];     // value storage
  }

  always {
    value = val.q;           // output the value

    ovf = val.q == 9 && inc; // if max value and incrementing, overflow!

    if (inc) {               // should add 1
      if (val.q == 9)        // if max value
        val.d = 0;           // reset to 0
      else                   // otherwise
        val.d = val.q + 1;   // add one
    }
  }
}

Our decimal counter has an input inc that is used to signal when the value should be increased. When the digit is about to overflow to 0, the flag ovf is set to 1. The current value of the counter is output on value.

We now need to chain these together to make a multi-digit counter.

Create a new module named multi_dec_ctr and add the following code.

module multi_dec_ctr #(
    DIGITS = 4 : DIGITS >= 2  // number of digits
  ) (
    input clk,                // clock
    input rst,                // reset
    input inc,                // increment counter
    output digits[DIGITS][4]  // digit values
  ) {

  .clk(clk), .rst(rst) {
    decimal_counter dctr [DIGITS]; // digit counters
  }

  always {
    dctr.inc[0] = inc;   // increment the first digit
    digits = dctr.value; // output the values

    // if the previous digit overflows, increment the next
    dctr.inc[1+:DIGITS-1] = dctr.ovf[0+:DIGITS-1];
  }
}

Here we take DIGITS decimal_counters and chain them together. Each counter inc signal is connected to the previous counter's ovf signal. This means that once a counter overflows from 9 to 0, the next one will be incremented. Of course, the first counter doesn't have a previous counter's ovf signal, so we use an external increment signal instead.

We use the bit selectors [1+:DIGITS-1] and [0+:DIGITS-1] to select bits from dctr.inc and dctr.ovf. What [1+:DIGITS-1] means is starting from bit 1, select DIGITS-1 bits going up. This is an easy way to select a specified number of bits given some start index. You can use -: instead of +: to select bits going down from the given start index instead of going up.

All that is left now is to generate the inc signal and connect the counter to our display driver.

The inc signal needs to be a pulse that stays high for only one clock cycle every time we want to increment our counter. To create this we are going to use the counter component as before, but there is a problem with this. If we create a counter that only outputs one bit, the bit will be high for have of the time and not just one cycle at a time. To fix this we will use another component, the Edge Detector.

Go into the component selector and add the Edge Detector to your project. You can find it under Miscellaneous.

This component takes a signal and sends out a pulse when it detects a rising, falling, or either type of edge. You can configure which edges you care about.

Let's add all the modules to mojo_top.

  .clk(clk) {
    // The reset conditioner is used to synchronize the reset signal to the FPGA
    // clock. This ensures the entire FPGA comes out of reset at the same time.
    reset_conditioner reset_cond;

    edge_detector edge_detector (#RISE(1), #FALL(0));

    .rst(rst) {
      multi_seven_seg seg;
      multi_dec_ctr dec_ctr;
      counter ctr (#SIZE(1), #DIV(24));
    }
  }

For the edge detector, we set RISE to 1 and FALL to 0. This means we will generate a pulse only on rising edges.

We now have the multi-digit driver, the multi-digit counter, and a way to generate a pulse from the counter.

Let's wire them all up!

    edge_detector.in = ctr.value;
    dec_ctr.inc = edge_detector.out;
    seg.values = dec_ctr.digits;

    io_seg = ~seg.seg; // connect segments to the driver
    io_sel = ~seg.sel; // connect digit select to the driver

The value from the counter is fed into the edge detector. The pulse from the edge detector is fed into the decimal counter. The decimals from the decimal counter are sent to the multi-digit driver. Finally, the multi-digit driver is connected to the actual display.

This is what is so cool about FPGAs. Each one of these modules operates completely independently from one another since they all exist in hardware!

This example is fairly complicated, but by breaking down the problem in little chunks (modules) we were able to create a design that is relatively easy to understand and flexible for future upgrades.

Now for the moment of truth, build and load the project onto your Mojo! With any luck, you should see the seven-segment display counting!

If you want to make it count slower or faster you can mess with ctr's DIV value.

Using the Buttons

Coming Soon!