Binary-to-Seven-Segment Decoder

A Combinational Block With a Purpose

The questions below are due on Wednesday September 11, 2024; 11:59:00 PM.
 
You are not logged in.

Please Log In for full access to the web site.
Note that this link will take you to an external site (https://shimmer.mit.edu) to authenticate, and then you will be redirected back to this page.

The previous couple designs were really just to get your feet wet. Now we're going to make something with a purpose! Something that will let us visualize numbers in a human-friendly format!

A 4-bit binary-to-seven-segment decoder takes a 4-bit binary number x_3 x_2 x_1 x_0 as input and produces seven outputs, one for each “segment” in a standard seven-LED display. The input will be wrapped into the value [3:0] x. Diagramatically, the decoder would be used like the following1.

Seven-Segment Controller with input 'b0110 (or 6)

Given the appropriate binary input (where the input represents a number 0 through 15 or ('h0 through 'hF), inclusive), this decoder produces outputs that light up the display in the following manner:

seven segment decoder

The symbols of a standard 0-15 seven-segment decoder

There's no deep/elegant/beautiful/"a-ha" mapping of these four input bits to the seven output bits of a seven-segment display, or at least none that human-kind has figured out since the format arose ~120 years ago. Instead, the easiest way to design this system is to break it down into seven parallel problems (one for each segment), each comprising a four-bit-to-one-bit Boolean function, and focus on each one independently. For example, start with Segment a:

By studying Figure 2 above, we can see that segment a is on/high when x[3:0] is 'h0 or 'h2 or 'h3 or 'h5 or 'h6 or 'h7 or 'h8 or 'h9 or 'hA or 'hC or 'hE or 'hF. This expression of all the ways in which a can be ON, separated with the or conjunction/operator, is known as a "sum" in Boolean algebra (this is because in Boolean algebra, addition is equivalent to the logical OR operator). This sum could be written as:

\displaylines{a = (x==h0)+(x==h2)+(x==h3)+(x==h5)+(x==h6)+(x==h7)+...\\(x==h8)+(x==h9)+(x==hA)+(x==hC)+(x==hE)+(x==hF)}

Using C-style symbols for the or operator, this would look like the following:

\displaylines{a = (x==h0)||(x==h2)||(x==h3)||(x==h5)||(x==h6)||(x==h7)||...\\(x==h8)||(x==h9)||(x==hA)||(x==hC)||(x==hE)||(x==hF)}

Furthermore, and this might seem silly since it is so fundamental, but we also need to specify what it means for x to be 'h0 or 'h2 or any number for that matter. That comes from analyzing the four bits, and we can use traditional binary numbering like you should be familiar with:

  • 'h0 is when x[3] is 0 and x[2] is 0 and x[1] is 0 and x[0] is 0
  • 'h2 is when x[3] is 0 and x[2] is 0 and x[1] is 1 and x[0] is 0
  • etc... for all the others.

In each one of these individual conditions, they are joined with the and conjunction/operator, which is known as a product in Boolean algebra (this is because in Boolean algebra, multiplication is equivalent to the AND operator). These products would look like the following:

  • 'h0 = \bar{x_3}\cdot\bar{x_2}\cdot\bar{x_1}\cdot\bar{x_0}
  • 'h2 = \bar{x_3}\cdot\bar{x_2}\cdot x_1 \cdot \bar{x_0}

The bar over individual values indicates the negation of the variable (0->1 and 1->0). Also in Boolean algebra just like in regular algebra, just putting two variables next to one another is "implicit" multiplication/and-operating, so you'll sometimes seen things like above written as:

  • 'h0 = \bar{x_3}\bar{x_2}\bar{x_1}\bar{x_0}
  • 'h2 = \bar{x_3}\bar{x_2} x_1 \bar{x_0}

Putting it all together, therefore we could come up with what is called a "sum of products" expression for a. This would look like:

\displaylines{a = \bar{x_3}\bar{x_2}\bar{x_1}\bar{x_0} + \bar{x_3}\bar{x_2}x_1\bar{x_0} + \bar{x_3}\bar{x_2}x_1x_0 + \bar{x_3}x_2\bar{x_1}x_0 + \bar{x_3}x_2x_1\bar{x_0} +\bar{x_3}x_2x_1x_0...\\+ x_3\bar{x_2}\bar{x_1}\bar{x_0} + x_3\bar{x_2}\bar{x_1}x_0 + x_3\bar{x_2}x_1\bar{x_0} + x_3x_2\bar{x_1}\bar{x_0} + x_3x_2x_1\bar{x_0} + x_3x_2x_1x_0 }

Just like how in in the set of Real numbers (\mathbb{R}) we often use plots to graphically depict functions, we use something similar in the Boolean set (\mathbb{B}). Often a x-y plot is overkill and not the easiest way to consume the data/trends, so instead you'll often see the function in a "truth table.":

x[3]x[2]x[1]x[0]a
11111
11101
11010
11001
10110
10101
10011
10001
01111
01101
01011
01000
00111
00101
00010
00001
The Truth Table for Segment A of a binary-to-seven-segment decoder. The entire input space (all sixteen possible values of x) are shown and their resulting value for a.

With the Boolean expression at the top, one could then use the following Laws/Identities from Boolean Algebra to simplify the equation:

  • Or Annulment: a+1=1
  • Or Identity: a+0=a
  • And Annulment: a\cdot0=0
  • And Identity: a\cdot1=a
  • Or Indempotent: a+a=a
  • And Indempotent: a\cdot a = a
  • Or Complement: a+\bar{a} = 1
  • And Complement: a\cdot\bar{a}=0
  • Or Commutative: a+b = b+a
  • And Commutative: a\cdot b = b\cdot a
  • DeMorgan's #1: \overline{a+b} = \bar{a}\cdot \bar{b}
  • DeMorgan's #2: \overline{a\cdot b} = \bar{a} + \bar{b}
  • Double Negation: \bar{\bar{a}}=a
  • Or Distributive Law: a\cdot\left(b+c\right) = a\cdot b + a\cdot c
  • And Distributive Law: a + b\cdot c = \left(a+b\right)\cdot\left(a+c\right)
  • Or Absorption Law: a + a\cdot b = a
  • And Absorption Law: a\cdot\left(a+b\right) = a
  • Or Associative Law: a+\left(b+c\right) = a+b+c
  • And Associative Law: a\cdot\left(b\cdot c\right) = a\cdot b\cdot c

Graphical techniques such as Karnaugh maps were also developed to allow you to use a modified form of a truth table to perfom simplifications and factoring. In an era before CAD tools (like what you'll use in this class), the engineer would use these tools to get to the purest circuit. The motivation for this was, the simpler you could make your expression, the simpler your circuit needed to be and the easier (and cheaper) it would be to build. Engineers became very good at learning how to factor and reduce logical circuits using the rules of Boolean Algebra. Eventually through simplifications and factoring, a reduced expression for segment a would be derived, most likely something like this:

a = \overline{\bar{x_3}\bar{x_2}x_1\bar{x_0} + \bar{x_3}x_2\bar{x_1}\bar{x_0} + x_3\bar{x_2}x_1x_0 + x_3x_2\bar{x_1}x_0}

This simplified expression for the value of a could then be built with the knowledge that there was no simpler way to do so. The engineer would then repeat this for the other six segments of the seven-segment display. This is how a lot of digital design was done. Hell, when I took my first proper digital design class in 2005, we did a decent amount of work with this. Once completed, for everything (and then resimplified since some segments could "resuse" eachother's intermediate logic) the circuit would be built, either by hand, or if you were working for a company, you'd produce a hardened circuit of it. The 74LS47 shown below was an example of a 1970s/80s chip that did just the task we describe:

seven segment decoder

An old school implementation of a four-bit-to-seven-seg decoder

One of the reasons digital design has advanced at the exponential rate it has is that we've been able to use the results of early labor to assist in newer development. The first digital computers were designed by hand, but after we had them working, because they can aid us in computation and calculations, we used them to help design the next generation at a greater complexity than the previous. Over time, it became less and less important to be able to "by-hand" derive a logical function simply because a computer could do that for us much more quickly, and it soon became the job of the digital designer to learn how to use these tools effectively. Hardware Description Languages (like SystemVerilog/Verilog) came about as one of these tools.

Design

For this design, you'll set up a sum-of-products for each segment, but not worry about all the simplifications. The tool (Verilog and Vivado) will carry out all the simplifications and factoring and optimizations instead.

For starters generate your "product" terms, basically bits that each on for one and only one input value. (we call this a "one-hot" encoding).

// array of bits that are "one hot" with numbers 0 through 15
logic [15:0] num;

// true when x_in is 4'b0000
assign num[0] = ~x_in[3] && ~x_in[2] && ~x_in[1] && ~x_in[0];

// true when x_in i 4'b0001
assign num[1] = ~x_in[3] && ~x_in[2] && ~x_in[1] && x_in[0];

// a different way. 4'd2 is 4'b0010;
assign num[2] = x_in == 4'd2;
// this is equivalent to ~x_in[3]&&~x_in[2]&&x_in[1]&&~x_in[0];

// ...
assign num[15] = x_in == 4'd15;

Once you have these products, then generate a sum from them. For example, segment a, which we went over above would be something like:

assign sa = num[0] || num[2] || num[3] || num[5] || num[6] || num[7] || num[8] || num[9] || num[10] || num[12] ||num[14] ||num[15];

The module above is a little annoying, since chances are the outputs sa through sg will almost always be used together. We might as well merge them into one unified bus such that s_out = [sg, sf, se, sd, sc, sb, sa];. Update bto7s so that there is only one 7-bit output bus called s_out.

Once you're done, move on to the next part of this week's assignments :D


 
Footnotes

1approximately...there will be some modifications in actual deployment based on how circuits often work. (click to return to text)