TITLE: The Road not Traveled : Full Spec Display
AUTHOR: Chuck McManis
LAST UPDATE: 10-Mar-2001

VHDL is a language that you can write very high level things in, and very low level things in. In fact you can practically write it like you were designing with gates. But that needs a bit of clarification.

VHDL is a “high level” architecture and behavior specification language, just like C is a “high level” programming language. But experienced C programmers often have an idea of the kinds of assembly code the compiler is going to generate, and they write their code so as not to trip up on situations where the compiler creates code that does more work than it has to. VHDL coders do the same thing, they get experience using a synthesis tool and how it infers logic from their code and they write (or re-write) their code to avoid common pit falls. One of the best books I’ve seen which talks about this in an actionable way is Essential VHDL - RTL Synthesis Done Right by Sundar Rajan.

As you saw in the Looper you can instantiate a bit of VHDL like a mini “part” inside your design, and then wire those parts together with a structural description. That is what I did and ended up discovering the path to making a better HEX display.

Breaking up the Problem

As I mention in my article on the hex display, I was astonished at how many CLBs (chunks of logic) the design used when synthesized as hardware. I realized with my exploration of various Karnaugh maps that I could, if I chose to, create a series of small “devices” which drove each segment.

An example of a some code that would drive one segment based on the state of the various pins is shown below.

With this you define the behavior of one segment which even the most poorly written inference engine manages to compile into a single CLB. Define the state of the output for any of 16 possible state inputs. Similarly for segment 1, as follows.

And so on and so one for each of the seven segments. Now for the test and blank signals we can create another device which is basically a force on / force off / don’t change multiplexor for a signal like so:

That takes a TST input, a BLNK input, and a DATA input and then picks which one to send out Q based on some simple logic.

Wiring Them up in VHDL

Once we have a number of components like this we can wire them up like we did with the 10Hz Clock and the LED display with a bit of structural VHDL coding. In the case of our hex display that looks like this:

example.vhd : Structural construction of HEX display
architecture structural of hex5_display is
    component TBSEL port (TST : in std_logic;
                                 BLNK : in std_logic;
                                 DATA : in std_logic;
                                 Q : out std_logic);
    end component;

    component GEN_S0 port (DATA : in std_logic_vector(3 downto 0);
                                  SEG : out std_logic);
    end component;

    component GEN_S1 port (DATA : in std_logic_vector(3 downto 0);
                                  SEG : out std_logic);
    end component;

    component GEN_S2 port (DATA : in std_logic_vector(3 downto 0);
                                  SEG : out std_logic);
    end component;

    component GEN_S3 port (DATA : in std_logic_vector(3 downto 0);
                                  SEG : out std_logic);
    end component;

    component GEN_S4 port (DATA : in std_logic_vector(3 downto 0);
                                  SEG : out std_logic);
    end component;

    component GEN_S5 port (DATA : in std_logic_vector(3 downto 0);
                                  SEG : out std_logic);
    end component;

    component GEN_S6 port (DATA : in std_logic_vector(3 downto 0);
                                  SEG : out std_logic);
    end component;

    signal leds : std_logic_vector(6 downto 0);
begin
    U1: GEN_S0 port map (data => data, seg => leds(0));
    U2: GEN_S1 port map (data => data, seg => leds(1));
    U3: GEN_S2 port map (data => data, seg => leds(2));
    U4: GEN_S3 port map (data => data, seg => leds(3));
    U5: GEN_S4 port map (data => data, seg => leds(4));
    U6: GEN_S5 port map (data => data, seg => leds(5));
    U7: GEN_S6 port map (data => data, seg => leds(6));
    U8: TBSEL port map (TST => test, BLNK => blank, DATA=> leds(0), Q => segs(0));
    U9: TBSEL port map (TST => test, BLNK => blank, DATA=> leds(1), Q => segs(1));
    U10: TBSEL port map (TST => test, BLNK => blank, DATA=> leds(2), Q => segs(2));
    U11: TBSEL port map (TST => test, BLNK => blank, DATA=> leds(3), Q => segs(3));
    U12: TBSEL port map (TST => test, BLNK => blank, DATA=> leds(4), Q => segs(4));
    U13: TBSEL port map (TST => test, BLNK => blank, DATA=> leds(5), Q => segs(5));
    U14: TBSEL port map (TST => test, BLNK => blank, DATA=> leds(6), Q => segs(6));
end structural;

And then we can send that pile of stuff to the synthesizer.

Not surprisingly it does not disappoint and wires up the display in a nearly minimal set of CLBs. The beauty of VHDL is that you can then take this overly specified VHDL file and make it a component. Now you can instantiate one, two, or even a dozen hex displays and they won’t take up too much room. I found this very analagous to writing a program subroutine in assembler and then linking to it from other C code. Its messy but its servicable.

Conclusions

The exercise was quite useful for me. I got the most gain out of seeing how various choices in compilation affected (or didn’t affect) the resulting inferred logic. I don’t doubt that if I decided to do lots and lots of VHDL design these things would become internalized and I would naturally avoid bad HDL code. However, as a novice I have to remain vigilant.

Doing this version of the display eventually led me to the best solution which carefully avoids inferring latches or needless multiplexers for wide data paths. When looking at the architecture of the basic building block of an FPGA it is useful to know what “basic” capabilities each unit has. If every CLB has a register bit for example you don’t have to worry if you only infer one register because you get that for “free”, but if you infer two, you end up burning twice the number of CLBs as a second rank gets pulled into service just for their register bits.

This leads to a sort of symbiotic relationship between coding style and target family. The closest analog I can think of in the software world is high level languages versus underlying instruction set. If your language assumes a stack for example, and you don’t have a CPU that has a stack and have to simulate it, well you can still write in that high level language but the code generated will be less efficient than if it was written in a language that didn’t presume a stack.

–Chuck