FPGA – Hex LED Display

After finishing the beginner’s guide for the Mimas V2 Development Board, I decided to try my hand at a circuit that I previously built with the GAL16V8 chip.  I already had the PDS file for the BCD to Hexadecimal LED display, so I decided to try that.  First, I needed to figure out what I could use for inputs.  The Mimas board has an 8-position DIP switch.  I used switch 1 through 4 for the BCD input.  I also used push button switch number 1 for the lamp test input.  If you go to this website, you can find all the schematics that show inputs and outputs.  Here are the inputs:

According to the diagram above, I chose to use M18 for the lamp test and dip switches F17, F18, E16, E18 for my BCD inputs.  All switches are active low, so I needed pull-up resistors and had to reverse the input logic to the module (a zero is inputted when the switch is ON).

Next, the outputs.  There are three 7-segment displays, so I chose display 1.  To enable the display, I applied a zero to the enable transistor.  Also, I had to disable the other two displays by applying a “1”.  Here’s the schematic:

So B3 must be set to “0” and A2 and B2 must be set to a “1”.  Then segment A is A3, segment B is B4, segment C is A4, etc.  The UCF file for the program will look like this:

# User Constraint File for BCD to 7-Segment Hex Display implementation on Mimas V2

NET "A" LOC = A3;
NET "B" LOC = B4;
NET "C" LOC = A4;
NET "D" LOC = C4;
NET "E" LOC = C5;
NET "F" LOC = D6;
NET "G" LOC = C6;
NET "EN1" LOC = B3;
NET "EN2" LOC = A2;
NET "EN3" LOC = B2;

# Switches.
# Internal pull-ups need to be enabled since 
# there is no pull-up resistor available on board
NET "D0" PULLUP;
NET "D1" PULLUP;
NET "D2" PULLUP;
NET "D3" PULLUP;
NET "LT" PULLUP;

NET "D0" LOC = F17;
NET "D1" LOC = F18;
NET "D2" LOC = E16;
NET "D3" LOC = E18;
NET "LT" LOC = M18;

Now, it’s time to create the logic.  I discovered the AND, OR, NOR, etc. circuits from this tutorial: Verilog Tutorial.  So I created OR/AND combinations for each segment like this:

and (a1, D0, D2);
and (a2, D0, !D3);
and (a3, !D1, !D2);
and (a4, !D1, D2, D3);
and (a5, !D0, !D2, D3);
and (a6, D1, D2, !D3);
nor (A, a1, a2, a3, a4, a5, a6, !LT);

This came from the following formula used in my GAL16V8 circuit:

/A = /RBO*/D0*/D2 + /RBO*/D0*D3 + /RBO*D1*D2 + /RBO*D1*/D2*/D3 + /RBO*D0*D2*/D3 + /RBO*/D1*/D2*D3 + LT

I did not implement the RBO (Ripple Blanking Output).  So, I removed all the /RBO terms.  Then I flipped all the “NOT” to be positive and all the positive to be “NOT”.  Instead of !D0 AND !D2, the first term is D0 AND D2.  Last, I ORd the lamp test (LT) with the results.  The LT button is also inverted because pressing the button produces a zero.  So you’ll notice that there is a !LT in each OR term. Since the segments are active low, I had to change all of my OR’s to NOR (the display looked rather interesting with the segments inverted).

The last part of the logic was forcing which display was on:

assign EN1 = 0; // enable digit 1
assign EN2 = 1; // disable digit 2
assign EN3 = 1; // disable digit 3

I wasn’t sure how large each Spartan 6 logic block was, so I just typed this in and expected the program to give me an error if I exceeded the number of AND/OR gates available.  Apparently, I didn’t reach that limit.  The program console shows that my program used 4 LUTs out of 5,720:

Slice Logic Utilization:
  Number of Slice Registers:                     0 out of  11,440    0%
  Number of Slice LUTs:                          4 out of   5,720    1%
    Number used as logic:                        4 out of   5,720    1%
      Number using O6 output only:               1
      Number using O5 output only:               0
      Number using O5 and O6:                    3
      Number used as ROM:                        0
    Number used as Memory:                       0 out of   1,440    0%

Apparently, the LUT (Look Up Table) is used to represent combinatorial circuits.  There is a description of the LUT in this document: Spartan-6 FPGA Configurable Logic Block User Guide

The function generators in Spartan-6 FPGAs are implemented as six-input look-up tables (LUTs). There are six independent inputs (A inputs – A1 to A6) and two independent outputs (O5 and O6) for each of the four function generators in a slice (A, B, C, and D). The function generators can implement any arbitrarily defined six-input Boolean function. Basically, the LUT is a logic table representation of my discrete boolean logic.  Technically, I could have provided a lookup table definition, similar to mapping out a circuit in Read Only Memory.

The Code

Here’s the full listing of the UCF file:

# User Constraint File for BCD to 7-Segment Hex Display implementation on Mimas V2

NET "A" LOC = A3;
NET "B" LOC = B4;
NET "C" LOC = A4;
NET "D" LOC = C4;
NET "E" LOC = C5;
NET "F" LOC = D6;
NET "G" LOC = C6;
NET "EN1" LOC = B3;
NET "EN2" LOC = A2;
NET "EN3" LOC = B2;

# Switches.
# Internal pull-ups need to be enabled since 
# there is no pull-up resistor available on board
NET "D0" PULLUP;
NET "D1" PULLUP;
NET "D2" PULLUP;
NET "D3" PULLUP;
NET "LT" PULLUP;

NET "D0" LOC = F17;
NET "D1" LOC = F18;
NET "D2" LOC = E16;
NET "D3" LOC = E18;
NET "LT" LOC = M18;

And the V File:

module HexLED(D0,D1,D2,D3,LT, A, B, C, D, E, F, G, EN1, EN2, EN3);
    input wire D0;
    input wire D1;
    input wire D2;
    input wire D3;
    input wire LT;
    
    output wire A;
    output wire B;
    output wire C;
    output wire D;
    output wire E;
    output wire F;
    output wire G;
    output wire EN1;
    output wire EN2;
    output wire EN3;
    
    // /D0*/D2 + /D0*D3 + D1*D2 + D1*/D2*/D3 + D0*D2*/D3 + /D1*/D2*D3
    and (a1, D0, D2);
    and (a2, D0, !D3);
    and (a3, !D1, !D2);
    and (a4, !D1, D2, D3);
    and (a5, !D0, !D2, D3);
    and (a6, D1, D2, !D3);
    nor (A, a1, a2, a3, a4, a5, a6, !LT);
    
    // /D2*/D3 + /D0*/D2 + /D0*/D1*/D3 + D0*D1*/D3 + D0*/D1*D3
    and (b1, D2, D3);
    and (b2, D0, D2);
    and (b3, D0, D1, D3);
    and (b4, !D0, !D1, D3);
    and (b5, !D0, D1, !D3);
    nor (B, b1, b2, b3, b4, b5, !LT);
    
    // D0*/D1 + D0*/D2 + /D1*/D2 + D2*/D3 + /D2*D3
    and (c1, !D0, D1);
    and (c2, !D0, D2);
    and (c3, D1, D2);
    and (c4, !D2, D3);
    and (c5, D2, !D3);
    nor (C, c1, c2, c3, c4, c5, !LT);
    
    // /D0*/D1*D3 + /D0*/D2*/D3 + D0*D1*/D2 + /D0*D1*D2 + D0*/D1*D2
    and (d1, D0, D1, !D3);
    and (d2, D0, D2, D3);
    and (d3, !D0, !D1, D2);
    and (d4, D0, !D1, !D2);
    and (d5, !D0, D1, !D2);
    nor (D, d1, d2, d3, d4, d5, !LT);
    
    // /D0*/D2 + D2*D3 + /D0*D1 + D1*D3
    and (e1, D0, D2);
    and (e2, !D2, !D3);
    and (e3, D0, !D1);
    and (e4, !D1, !D3);
    nor (E, e1, e2, e3, e4, !LT);
    
    // /D0*/D1 + /D2*D3 + D1*D3 + /D0*D2 + /D1*D2*/D3
    and (f1, D0, D1);
    and (f2, D2, !D3);
    and (f3, !D1, !D3);
    and (f4, D0, !D2);
    and (f5, D1, !D2, D3);
    nor (F, f1, f2, f3, f4, f5, !LT);
    
    // D1*/D2 + D0*D3 + /D2*D3 + /D0*D1 + /D1*D2*/D3
    and (g1, !D1, D2);
    and (g2, !D0, !D3);
    and (g3, D2, !D3);
    and (g4, D0, !D1);
    and (g5, D1, !D2, D3);
    nor (G, g1, g2, g3, g4, g5, !LT);
    
    assign EN1 = 0; // enable digit 1
    assign EN2 = 1; // disable digit 2
    assign EN3 = 1; // disable digit 3
    
endmodule

Leave a Reply