Presentation is loading. Please wait.

Presentation is loading. Please wait.

Seven-Segment LED Display & Finite-State Machines

Similar presentations


Presentation on theme: "Seven-Segment LED Display & Finite-State Machines"— Presentation transcript:

1 Seven-Segment LED Display & Finite-State Machines

2 Introduction Seven-segment displays are common, and you have likely seen them all over the place, we will continue building on this project to create a stopwatch that will serve as an example for a finite-state machine. Finite-state machines, or FSMs, are an important design idea that will allow you to create designs with complex behavior. The basic idea is that your design has a finite set of states that it can be in, and various inputs will cause it to transition between states. The classic example of an FSM is a traffic light. The light can be in a few states: green in one direction, red in both directions, green in the other direction, and so forth.

3 Introduction

4 Single Digit On the IO Shield, each segment from a digit is connected to the same segment of the other three digits. Given this, it seems as if all four digits would always display the same thing. However, each digit can be turned on and off individually. This is accomplished by connecting the common pin of each digit to a transistor. By turning the transistors on and off, we can enable and disable each digit. By quickly cycling through the digits, we can create the illusion that all the digits are on and that they are all displaying unique numbers. This technique is known as multiplexing. In the mojo_top module, we have two relevant outputs: io_seg and io_sel. The output io_seg connects to the eight LEDs in the digit (the seven segments plus a decimal point). The output io_sel connects to the transistors that turn the digits on and off. Both of these signals are active low.

5 Single Digit We can edit these values to display a 2 on the first

6 Single Digit We can use a counter and a decoder to index through the LEDs The Counter component has a handful of parameters that make it flexible. In this case, we are using SIZE and DIV. The parameter SIZE specifies the width of the output. In our case, we want 3 bits so that the counter will count from 0 to 7. The DIV parameter specifies the number of bits to use as a pre-counter to divide the clock. By specifying 24 here, the counter will increment its output every 2exp24, or 16,777,216, clock cycles

7 Single Digit With a clock frequency of 50 MHz, this means it will change about every third of a second. Finally, we need the decoder to turn the binary value to a one-hot value to turn on the LED segment. By setting WIDTH to 3 bits, we get an output of 8 bits, which is exactly what we want! We can use a counter and a decoder to index through the LEDs All that’s left to do is to connect the counter to the decoder and the decoder to the LEDs:

8 Single Digit The LEDs are active low and the decoder output is one-hot. We need it to be one-cold. We can get that by inverting the bits. If you build and load the project now, each segment should cycle through. You can adjust the speed by changing the DIV parameter on the counter. A higher value will result in a slower cycle. What if we want to also use other digits? We can modify the counter and add a second decoder to cycle the io_sel output

9 Single Digit Notice that we made the counter’s output 5 bits wide and added another decoder with a 2-bit input. We are going to use the 2 extra bits to feed into the new decoder, which we will connect to the io_sel signal. This way, each time the single digit has gone through a full cycle, we will select a new digit for the next cycle.

10 Lookup Tables An important technique in hardware design is using lookup tables, also known as LUTs. These are tables from which you can select one of many constant values. You can think of a lookup table as an arbitrary function mapping a range of inputs to whatever output values you want. We are going to use one to convert a binary number to the LEDs that should be lit. The Mojo IDE will automatically populate the file with a bare-bones module:

11 Lookup Tables We are going to make this fully combinational so we can remove the clock and reset inputs and replace them with a value input. We also need to replace the out output with a segs output that is 7 bits wide. In the always block, we can remove the default assignment and replace it with a case statement.

12 Lookup Tables A case statement works by looking at the value in the expression after the case keyword and then using the branch with that value. It is important to assign a value to segs no matter what value value is. That is where the default entry comes in. This statement will be used if value doesn’t match any of the other values. A case statement is identical to using a bunch of if-else statements. It is more compact and easier to write out in some situations. Unlike programming, there is no performance benefit to. We can test out the digit_lut module by connecting a new counter to it and connecting it to the LEDs in mojo_top.luc. We no longer need the decoders and the old counter, so we can remove them:

13 Lookup Tables We also changed SIZE down to 4 and set a new parameter, TOP, to 9. The parameter TOP sets the maximum value for the counter. If you don’t set it, it will overflow naturally. However, with 4 bits, it would count from 0 to 15, and we don’t have a digit to display from 10 to 15, so we really want it to count only from 0 to 9

14 Binary to Decimal We need to convert the binary number from the DIP switches into a decimal number. Binary to Decimal component can be found in the Components Library. This component will output the decimal numbers, assuming our input is valid. It uses the special value of 10 to indicate the digit should be blank The value of 11 to indicate that there was overflow

15 Binary to Decimal

16 Binary to Decimal

17 Binary to Decimal The two parameters should be fairly straightforward at this point. DIGITS specifies the number of digits the decimal number can have. LEADING_ZEROS specifies that 0 or 10 should be used for leading zeros. Basically, since 10 isn’t a decimal digit, we can use this as a blank digit value. For a number such as 15 with LEADING_ZEROS set to 1 and DIGITS set to 4, we would get 0015, but with LEADING_ZEROS set to 0, we get 15. The function $pow(a,b) returns a exp b. So here we are using it to get the maximum value of a decimal number with DIGITS digits can have, plus 1. For example, if DIGITS is 2, the maximum value is 99, and 10exp2 is 100.

18 Binary to Decimal The $clog2() function is then used to tell us the minimum number of bits needed to store the maximum value plus 1. Inside an always block, the types sig and var can be read and written, and their value can change throughout the block.

19 Binary to Decimal The first part of the schematic consists of a bank of comparators that detect whether the input is less than all the possible digits These are then fed into an arbiter that will select the smallest number that is still larger than the input. For example, if the input is 24, the arbiter would select the < 30 comparator. The output from the arbiter is then used to select the digit via a multiplexer being used as a lookup table. We also need to output the remainder for the next digit, so we then have to subtract off the current digit. Which value we use is selected using the output of the arbiter again but with another multiplexer. If we have 24 as the input, then the 20 would be selected and we would subtract that from the input. The remainder would be 4 and that would get passed to the next stage. The nice part about this design is that it doesn’t use any division. It uses comparisons and subtraction, which are relatively cheap when compared to division.

20 The Finite-State Machine
We need it to be able to display the stopwatch value on the seven-segment display. Lucid has a type specifically for FSMs, the fsm type. This type is similar to the dff type and in fact will instantiate DFFs. The difference is that instead of holding arbitrary binary values, the fsm type will hold one of many state constants. For our stopwatch example, we might want the following states: IDLE, RUNNING, PAUSED. To declare an FSM with these states, we could use the following:

21 The Finite-State Machine
When reset, the FSM will reset to the first state in the list—in this case, IDLE. If for some reason you want to reset to a different state, you can use the parameter INIT: FSMs have the same d and q ports that DFFs have and they act exactly the same. The q output is the current state, and the d input is the state to transition to. If you don’t assign d a state, the state will stay the same

22 The Stopwatch Although we could just use a counter as before, it would be much more useful if our stopwatch counted actual seconds. To do this, we need two counters: one that generates a tick at a regular interval, say every tenth of a second, and another that counts the ticks. The clock on the Mojo is 50 MHz, meaning we have 50,000,000 cycles per second. We need to generate a tick every 5,000,000 cycles for a tenth of a second. That means we need a counter that will count from 0 to 4,999,999, which takes exactly 5 million cycles. To count this high, we need the ceiling of log base 2 of 5,000,000, or 23 bits

23 The Stopwatch If we let the counter overflow naturally, it would count way too long, so we need to reset it whenever its value gets to 4,999,999. Each time we reset it, we can count this as a tick and increment the stopwatch counter. We can use these two counters with an FSM to control when they are running to create the stopwatch module:

24 The Stopwatch

25 The Stopwatch FSMs are generally used with case statements, as you want to do different things for each state. To access the different state constants, you need to prefix the state name with the FSM’s name and a period to separate them. For example, the IDLE state can be accessed with state.IDLE. This allows you to use the same state names for multiple FSMs if you want.

26 The Stopwatch Basically, the start_stop signal will get the FSM to transition from IDLE to RUNNING, and then it will toggle between RUNNING and PAUSED. The rst signal will always force the FSM back to IDLE regardless of the state it is currently in. Note that we don’t need to explicitly set state to IDLE when rst is 1 because we hooked rst to the reset input of the FSM, and IDLE is the first state in the list. We now need to hook this up in mojo_top. We need two buttons, one for the start_stop signal and one for reset. We don’t really need a separate reset signal because the button on the Mojo acts as the reset by default, but we can use one of the buttons on the IO Shield to reset just the stopwatch.

27 The Stopwatch Because we are using buttons, we need to condition them as before. We can use the Button Conditioner component for this. We also want the start_stop signal to be high for only one clock cycle when the start/stop button is pressed so that the FSM changes state only once. To do this, we can connect the Edge Detector component to the output of the Button Conditioner to detect the rising edge. Because we want both the global reset and the left button to reset the stopwatch, we can OR the signals together.

28 The Stopwatch First we need to instantiate everything:

29 The Stopwatch Notice that we didn’t put the stopwatch instance in the reset port connection block. This is because we are going to manually connect this later in the always block:


Download ppt "Seven-Segment LED Display & Finite-State Machines"

Similar presentations


Ads by Google