Purpose
With the foundations for FPGA utilization and decoding seven segment displays behind me in lab 1, I will now set out to create a true hexadecimal counter. Different values will be displayed on different digits of the display, counting up using the internal clock of the FPGA. I will also utilize Phase-Locked Loop (PLL) within Vivado's clocking wizard to manipulate the internal clocking rate of my FPGA.
Multiplexing The Displays
As I mentioned in lab 1, the layout of the four-digit seven-segment display presents an interesting problem when trying to display different values for different digits. Each of the digits utilize the same inputs for each of the seven segments, meaning if we were to power all four anodes, they would simply display the same value.
To counteract this, we can power the anode of one display and the segments it needs, then power the next anode with its corresponding segments. This would mean that only one display would ever show at a time. However, by switching the displays at a rate faster than the human eye can see, we can create the illusion that all of the displays are lit up at once. Accomplishing this would require a refresh rate on the display of 60 Hz, or 60 cycles per second.
The timing waveform below represents what this would look like in practice.
This timing diagram runs on repeat constantly. First, AN1 illuminates for 1/4th of the full refresh period. Then AN1 turns off and AN2 turns on. During this transition of which anode is powered, we can also change which segments are powered, meaning we can change the value of the digit displayed for one and not all. Again, so long as this refresh period is small enough that we can complete 60 full cycles each second, the display will appear as though it is displaying them all at the same time.
Clocking Wizard
So how fast do we need our clock to be to achieve a cycle of at least 60 Hz? Let's first take a look at the Cmod A7's 12 MHz clock. If we create a multiple bit counter similar to lab 1, we can choose values from this counter to leech the refresh rate from. In this lab, we will be utilizing bits 17 and 18 from a 38 bit binary counter. This allows us to calculate how fast the refresh rate would be.
12 MHz ÷ 2^(17) = 91.55 Hz
Since 91.55 Hz is greater than 60 Hz, we should be good right? It would be, except we're forgetting one more aspect. That would be the rate one display, and we have four. By dividing this result by 4, we get our true cycle count.
91.55 Hz ÷ 4 displays = 22.89 Hz
Now this value is definitely lower than 60 Hz, it becomes clear that the clock built into the Cmod A7 is not fast enough for our applications. To resolve this, we can utilize Vivado's built-in Clocking Wizard to simulate a clock based upon our initial clock. If we utilize a phase-locked loop (PLL) based clock regulator, Vivado will utilize a voltage controlled oscillator and frequency divider to multiply our input frequency and generate our desired frequency in phase with our original input.
Clocking wizard can be found in the IP Catalog of Vivado, available on the left task bar.
Inside the clocking wizard, there are only a handful of modifications we need to make to successfully simulate a clock.
In the image to the left, you will see the red box indicates the name of the port for the clock on your board, as indicated in your constraint file. I have named mine sysclk.
The blue box is where we will put the frequency of our input clock. The internal clock on the Cmod A7 is 12.000 MHz.
Now under the output clocks tab, we can rename the output port to however we would like it to be referred to. In this case, I called in clk_50MHz, as we are creating a 50 MHz clock.
In the blue box, you can indicate your desired output frequency, which the simulator will do its best to replicate based off the constraints it is working in.
Now that we have instantiated a simulated clock for our program, we need to define it within our sources to be accessible. Though there are multiple ways to do this, the easiest I could find is shown in the two snippets of code to the left.
The first is a declaration of the signal named after our output clock. Following this, we define a component of clk_wiz_0, which is the component name specified by default within the Clocking Wizard. This can be modified in the steps above.
The component has four ports; the input clock, which is referred to here as clk_in1, the output clock of clk_50MHz, a reset input, and a locked indicator. These last two are essential to define when utilizing the Clocking Wizard, even if you do not end up using them.
The reset will clear the clock, essentially pausing and restarting it. When the locked output is high, the clock is considered stable and usable by the circuitry. For obvious reasons, a clock cannot be both reset and locked simultaneously.
In the second snippet of code, which happens to fall within the architecture, we instantiate the clk_wiz_0 component. Here, we map the four aforementioned ports to their respective IOs. The Clocking Wizard's output is mapped to the signal we defined earlier, reset is mapped to our reset pin defined in our entity and constraint, locked is mapped to an output LED I referred to as lock_led, and lastly the input clock clk_in1 is mapped to our FPGA's clock which I called sysclk.
With all of this done, we now have a 50 Mhz clock, which means our full refresh rate should now be higher.
50 MHz ÷ 2^(17) = 381.47 Hz
381.47 Hz ÷ 4 displays = 95.37 Hz
Which works perfectly, as it is higher than 60 Hz!
Explanation of VHDL Source
The source code for lab 2 is very similar to lab 1, with the exception of the display multiplexing performed in 'hexcount.vhd' and 'counter.vhd'. We will take a look at these differences now.
Again, not many changes were made. The only difference between this counter program and the equivalent in part 2 of lab 1 is the signal mpx. This signal is mapped to the 17th and 18th bits within the clock counter.
As mentioned earlier, this gives us a refresh rate of 95.37 Hz. Essentially, 95.37 times each second the 17th and 18th bits will be updated and subsequently used to reflect which display is being illuminated.
Looking back to the top file, hexcount.vhd, the primary change was made towards the bottom of the file. The display port, of which the data port is mapped to, is represented by the values of signal S.
A few lines up, S is mapped to count, meaning the hexadecimal value represented is directly linked to the counting of our simulated clock.
As md changes, the data displayed is selected from a different range of bits within the counter.
Demonstration of Functional Counter
As usual, now that we fully understand the lab, let's see it in action!