Here is the ABEL code:
// Defined inputs
CLOCK pin 1;
RESET pin 2;
// Defined outputs
Q3..Q0 pin 8,9,11,12 istype 'reg';
DATA = [Q3..Q0];
DATA.clk = CLOCK;
DATA.ar = !RESET;
DATA := DATA + 1;
The interesting part is the assignment of the clock line to each bit in the counter, the linking of the reset line, and the actual increment to the counter value.
None of this is very exciting, but it was nice to actually get a design I wrote (well, borrowed) working in real hardware.
ABEL is fine, but there are more modern (and more time-efficient) alternatives. The two (there may be more) competitors, as I mentioned in my previous post, are Verilog and VHDL. After playing with both, I have decided I like VHDL more. While it is more "wordy" then Verilog, there is a certain quality to the code which, at least to my untrained eye, seems to make it clearer then Verilog. I have no doubt it boils down to a preference thing. In any case, I have decided to stick to VHDL for the time being, and see what I can create with it. My eventual aim is to produce, using either the XC9572 or some more sophisticated CPLD or FPGA, a custom chip for my 6809 computer. But in the mean time, to help me learn VHDL I have been following various tutorials on the subject, and in the process coming up with some of my own designs.
One "beginner" practical use for VHDL I want to explore is to create a design which implements the IO glue, which I previously previously implemented in ABEL. This will be trivial to implement in VHDL. But in addition to the IO glue logic functions, I would like to replace the simple LED output latch with something easier to read. The output latch in my computer is used for debugging mostly, and as a simple output device. But reading the value from a set of LEDs is a little awkward, so I would like to replace it with a handy two digit seven segment display. I have several 7 segment LED modules sitting in my parts drawer for months and wasn't sure what to do with them. This seems like a good use.
But before implementing a full-on "IO glue + 7 segment output latch" HDL design, I thought it sensible to work up to it, by implementing something simpler that doesn't require integration with the 6809 computer.
So I have implemented the "tail end" of this idea: a two digit hexadecimal display driven from an 8 bit counter. To make things more interesting, the counter can go down as well as up.
First up is a description of the code running on the ATMega8. (This is overkill, and was the chip I had to hand. An ATTiny would have done the job just as well and taken less breadboard space). We need two signals, one for the counter and one to drive a multiplexer which can drive both digits using a single 7 segment decoder.
The code in the AVR is as follows:
DDRC = 0x3f; /* Outputs for bits 5 through 0. */
unsigned char x, c;
for (x = 0; x < 0x20; x++) /* 00000 -> 11111 */
PORTC = x; /* Set the value on the port */
for (c = 0; c < 0x80; c++) /* About 128ms worth */
PORTC ^= 0x20; /* Toggle bit 5. */
So for port C, pin 0 through 4 can be used for different clock rates, from about 256mS in period for pin 0 up to about 4096mS for pin 4. Pin 5 on the same port will toggle at a frequency of about 500Hz. This is used to drive the multiplexing display. It does seem quite crazy to use, what is essentially, a very small computer to generate a couple of square waves. We live in interesting times!
The block-level diagram for the circuit is as follows:
The yellow box represents the boundary of the CPLD. The names of the "ports" on the components, and the names of the wires entering the CPLD match the VHDL code shown later.
Each block is a separate circuit, or component in VHDL speak, with it's own inputs and outputs. The 8 bit counter - with clock, reset and direction inputs - is fed into a two by four bit multiplexer, with a 7-segment decoder driven from that. Outputs to drive the common side of the 7 segment display are tapped off the mux, which also needs a rapidly changing address input to switch between the two digits. It is also possible to disable the display (but not the counting) with the EN(able) input.
Starting from the left, the counter is implemented with the following VHDL:
-- An 8 bit up/down couner with reset
entity counter is
port ( CLK : in STD_LOGIC; -- Counter clock
RESET : in STD_LOGIC; -- Cunter reset
DIR : in STD_LOGIC; -- Cointer direction (0 for up)
X : out STD_LOGIC_VECTOR (7 downto 0)); -- And the counter output
architecture Behavioral of counter is
signal COUNT : STD_LOGIC_VECTOR (7 downto 0) := x"00";
process (CLK, RESET)
if (CLK'Event and CLK = '0') then
-- Up and down based on DIR (automatic wrapping)
if (DIR = '0') then
COUNT <= COUNT + '1';
COUNT <= COUNT - '1';
-- Sychronus reset
if (RESET = '1') then
COUNT <= x"00";
-- Copy the count to the output, because you cant do sum on outputs
X <= COUNT;
Quite simple stuff, I'm sure, to a seasoned HDL engineer, but for me this is new ground!
A process is a coding block that runs when one of the inputs in parenthesis changes. In this case, we are interested in the clock and reset inputs. On a rising clock edge, the direction input is examined and the counter value changed accordingly. Reset causes the count to return to the initial value, 0.
Next up is the 2 way, 4 bit Mux. This is a little interesting because the decoded value of the address is also output, so the correct digit can be lit up:
-- 2 way, 4 bit multiplexer with indicator outputs
entity twobyfourmux is
port ( A : in STD_LOGIC_VECTOR (3 downto 0); -- First input set
B : in STD_LOGIC_VECTOR (3 downto 0); -- Second input set
SEL : in STD_LOGIC; -- 0 for A, 1 for B
O : out STD_LOGIC_VECTOR (3 downto 0); -- Output
SEL_A : out STD_LOGIC; -- High for A
SEL_B : out STD_LOGIC); -- High for B
architecture Behavioral of twobyfourmux is
O <= A when (SEL = '0') else B; -- Standard Mux stuff
SEL_A <= '1' when (SEL = '0') else '0'; -- Set selector A
SEL_B <= '1' when (SEL = '1') else '0'; -- Set selector B
This is simple combinational logic, implemented with the "when" construct. The SEL_A and SEL_B outputs drive the common LED line for a digit, as the high level diagram shows.
Finally the 7-segment decoder:
-- A 7 segment (hex) decoder with enable input
entity sevensegdecoder is
port ( DATA : in STD_LOGIC_VECTOR (3 downto 0);
Q : out STD_LOGIC_VECTOR (6 downto 0);
EN : in STD_LOGIC); -- Active low
architecture Behavioral of sevensegdecoder is
process (DATA, EN)
Q <= "0000000";
if (EN = '0') then
case DATA is
when x"0" => Q <= "1111110";
when x"1" => Q <= "0110000";
when x"2" => Q <= "1101101";
when x"3" => Q <= "1111001";
when x"4" => Q <= "0110011";
when x"5" => Q <= "1011011";
when x"6" => Q <= "1011111";
when x"7" => Q <= "1110000";
when x"8" => Q <= "1111111";
when x"9" => Q <= "1111011";
when x"a" => Q <= "1110111";
when x"b" => Q <= "0011111";
when x"c" => Q <= "1001110";
when x"d" => Q <= "0111101";
when x"e" => Q <= "1001111";
when x"f" => Q <= "1000111";
when others => Q <= "0000001";
Again a process construct is used, with sensitivity on the input value and the enable line. The mapping of the output bits to LED segments is not really ideal, and was a mistake. The lowest bit is the "g" LED (the bar across the middle of the digit) and not the "a" segment. I noticed this on the initial testing. The cool part was that I could correct the error by remapping the pins on the CPLD, in software, instead of moving wires around on the breadboard.
All the parts have thus been described. The only remaining element is the outer most one, which wraps the 3 components. Here it is:
entity bytedisplay is
port ( CLK : in STD_LOGIC; -- Counter clock
DIR : in STD_LOGIC; -- Counter direction
RESET : in STD_LOGIC; -- Counter reset
DIGIT_ADDR : in STD_LOGIC; -- Which digit to show (Mux input)
DIGIT_A : out STD_LOGIC; -- Show digit A (from Mux)
DIGIT_B : out STD_LOGIC; -- Show digit B (from Mux)
EN : in STD_LOGIC; -- Decoder enable
SEGS : out STD_LOGIC_VECTOR (6 downto 0)); -- 7 segment output
architecture Behavioral of bytedisplay is
component counter is
port ( ... ));
component twobyfourmux is
port ( ... );
component sevensegdecoder is
port ( ... );
signal COUNT : STD_LOGIC_VECTOR(7 downto 0); -- 8 bit counter
signal MUX_OUT : STD_LOGIC_VECTOR(3 downto 0); -- 4 bits from Mux
signal NOT_SEGS : STD_LOGIC_VECTOR(6 downto 0); -- 7 outputs, high for light
counter1: counter port map (CLK, RESET, DIR, COUNT(7 downto 0));
mux1: twobyfourmux port map (COUNT(3 downto 0), COUNT(7 downto 4), DIGIT_ADDR, MUX_OUT, DIGIT_A, DIGIT_B);
decoder1: sevensegdecoder port map(MUX_OUT, NOT_SEGS, EN);
-- Invert outputs as we have common cathode display
SEGS <= not NOT_SEGS;
Each component interface must be repeated inside the behavioral description, which is a little bit annoying. It can probably be avoided if the components are published in a library (this is a guess). Anyway, you can see how the components are chained up. The count value is split in half and fed as two inputs into the multiplexer, which then has it's output fed into the 7 segment decoder. Finally, because I am using a common cathode display, the segment values are inverted. This is done outside of the decoder to make the decoder design useful with both types (inverting and non inverting) of display.
Note that the signals here are for the "internal wires" in the block diagram, above.
One thing that would be really cool is if the Xilinx ISE software could generate a schematic from the VHDL code. I think newer versions can, but the one I'm using (10.1, which was the last one to support ABEL) cannot. When I eventually upgrade I will be interested to see what the circuits which have been synthesized from my VHDL code actually look like.
One thing ISE gives you is a report on what "resources" inside the CPLD your design implements is using. A resource is a register bit, a pin, or a macrocell (which make up the programmable gates).
The current implementation is using the following resources:
- Macrocells Used: 17/72 (24%)
- Pterms Used: 89/360 (25%)
- Registers Used: 8/72 (12%)
- Pins Used: 14/34 (42%)
- Function Block Inputs Used: 23/144 (16%)
Here is a picture of the breadboard rig:
The AVR, with LED on the clock line, is at the top left. The box at the top is the JTAG programmer. Because I'm so pleased with this little device I've made, I have made a short video:
In the video, power is applied to the circuit, which starts the counter counting. After a while the counter direction button is pushed which sets the counter counting backwards. The reset action is also shown.
Some conclusions I have drawn from this little exersise:
- VHDL rocks. While I'm sure I will find new challenges with it, I'm amazed that the fairly sophisticated circuit I've implemented was coded up in a matter of a couple of days, whilst learning the language!
- These tutorials were invaluable and are a great way to get started in VHDL, and programmable logic in general.
- It's amazing to be able to make code changes, upload them into a piece of hardware, and then see the circuit change right there and then.
- The ISE tools are a bit basic really. They work, but that's all that can really be said about them. They aren't bad as such, but they are just not very good. I am still using versions that are about 5 years old though.
- It would be interesting to see how portable my VHDL design is. I'm guessing it could easily be implemented in another vendor's CPLD or FPGA, or even turned into an ASIC (boy that would be the most pointless ASIC ever).
- Running Windows 7 inside a VM on a host that only has 2GB RAM is quite painful.
So many fun things to be getting on with, but not nearly enough free time....