Implementing a Digital Design Project

Implementing a Digital Design Project

In digital systems, I teach about a bunch of different hardware solutions that can be used to implement a project. I thought it would be fun (and not to mention, a learning experience for me) to come up with both a combinational logic project and a sequential logic project and implement each one using a couple different types of technology. Then I could compare and contrast the results for each one.

Types of hardware

Discrete logic chips

Using discrete logic chips is the same method that I teach in digital systems class. In particular, my class uses the 7400 series of TTL integrated circuits to accomplish our course objectives. This method might be the most approachable to students as it’s similar to what they’ve already learned in class. The chips are not terribly expensive, datasheets are easy to find and follow, and a lot can be accomplished with discrete components. It’s also relatively simple to use simulation tools to test and verify a design before building it.

The difficulties with using discrete components are the same as the difficulties encountered in lab classes. The chips can take up a lot of space, there might be a lot of wiring involved, debugging can be tricky, and once a design is built, it is difficult or impossible to change.

ROM (Read-Only Memory)

ROM acts like a giant truth table of values that comes together to implement a digital function or functions. It can take up much less space than using individual logic chips. Paired with flip-flops, they can also be used to implement sequential circuits. UV-EPROM chips can be erased with exposure to high-intensity UV radiation and rewritten many times, making them a great platform for prototyping a design. OTP (one-time programmable) EPROM chips can be programmed once the design is complete, leading to a single-chip implementation of a project.

The major drawback to using ROM is in the requirement to use an external programmer to program them (although if you do a search online, there are many articles for building your own… I have not tried it!). At times it can be onerous to generate the HEX file for programming them. I use Xcode to program (in C) a file that automatically generates a HEX file based on the criteria that I am interested in, as it reduces user error in typing in values to the programmer software directly. However this is not an option for people who do not know how to write code.

GAL / PAL (Generic / Programmable Array Logic)

GAL and PAL are SPLD (simple programmable logic devices) that I believe combine the best of both worlds between discrete logic and ROM. They are capable of executing both combinational and sequential logic by using a programmable AND array connected to fixed OR gates. On the output of each OR gate is something called an output logic macrocell which can be used to connect to a flip-flop for a sequential circuit, or create an active-LOW output signal, or a bunch of diverse things depending on the type of PAL device that is used. Similarly to ROM, a single chip is all that is needed to carry out a program. Similarly to discrete logic, a PAL is relatively quick in its parallel execution of logic functions (as opposed to a microcontroller, which requires a certain number of clock cycles to get things done).

The difference between PAL (programmable array logic) and GAL (generic array logic) is that PAL is generally one-time writeable, whereas GAL can be erased and rewritten. I compare the difference to that between PROM (one time) and EPROM (can be erased and rewritten).

The drawbacks to using PAL devices are related to how obsolete the technology is. While you can still find and purchase PAL chips, it is difficult to find software to create a fuse map to program them (although thankfully, Microchip offers a free download of WinCUPL on their website), and an external programmer (such as the type used for a ROM) is required to burn the fuses on the actual chip. WinCUPL is actually simpler to use than other software I’ve used; if you have a minimum SOP implementation for a function, it is somewhat trivial to use WinCUPL. It doesn’t require you to know how to use a hardware description language such as that used in FPGAs (described below).

While GAL chips can be reprogrammed, their erase/rewrite cycles are limited to about 100 due to the damaging nature of the fuse burn process. (This is compared to about 100,000 erase/rewrites that you get in flash memory; you could essentially create a device to reprogram a microcontroller several times a day, every day, for a hundred years, and still not get close to that limit.) So if you’re going to be doing a lot of prototyping, you do need to have an idea of how many times your chip has been rewritten.

And the number one biggest drawback to using PAL devices is in the number of I/O pins that they have. The 16V8 devices have 16 pins, 8 of which can be used as output pins. The 22V10 devices have 22 pins, 10 of which can be used as output pins. Any intermediate variables that may be required to implement a logic function will use one of those limited pins. Therefore, careful planning needs to be undertaken in any designs using a PAL.

FPGA (Field Programmable Gate Array)

An FPGA is a very complicated integrated circuit that can be programmed in many diverse ways. They are much more complicated than PAL/GAL devices in that the logic blocks used in the interior are much more complex than a simple output macrocell. Some FPGAs contain memory blocks, a clock signal, and even possibly other peripheral features such as analog-to-digital conversion.

A development board for an FPGA typically costs $60-$100 (depending on the features), and is not something that can be easily integrated into a project as-is. Using the chip directly would require a programmer that can handle surface-mount or ball-grid devices, and the ability to solder something like that onto a protoboard or PCB.

An additional consideration for an FPGA is that you need to know a hardware description language like VHDL or Verilog in order to program one. You also need design software (which may come at additional expense) to program the FPGA. However, because an FPGA can be reprogrammed, it makes reconfiguring a design much simpler than it would be with a static implementation (such as with individual logic gates).

So why would anybody use an FPGA? They are fast, powerful, and a really great platform for open hardware projects. I would categorize it as overkill for a simple digital design project, however.

ASIC (Application-Specific Integrated Circuit)

An ASIC is essentially a digital logic chip that does exactly what a circuit designer wants it to do. It is designed for a specific purpose (hence the name ASIC), rather than a general-purpose IC which is built to be used in many diverse situations. An ASIC generally has to be fabricated in a cleanroom and can cost a lot of money (and by a lot of money, I mean tens of thousands, hundreds of thousands, or even millions of dollars) to develop and create. This is a good option for something that will eventually be used in mass-produced consumer products, but is not a good option for implementing a simple digital design project.

Microcontroller

While a microcontroller is not something I teach in digital systems (you have to take, wait for it, microcontrollers!) it is the logical next step in digital design. Simple microcontrollers have become very approachable technology in the past 20 years. Platforms like the Arduino have helped make simple 8-bit microcontrollers much more accessible to hobbyists, and while I don’t agree with everything Arduino does, the openness more than makes up for the drawbacks. A microcontroller is, at its heart, a very complicated finite state machine that is capable of executing instructions one after the other. This enables the user to write simple programs to achieve in software what would otherwise be realizable in hardware.

Microcontrollers are small, compact, and powerful. Many of them contain a built-in clock, so there isn’t a need to connect a 555 timer or function generator. There can be dozens of peripheral features from analog-to-digital conversion to timer units to serial communications protocols. They are relatively inexpensive (the ATmega328P, which is the microcontroller used on the Arduino, can be purchased for less than $3), and even a full development board such as an Arduino Uno will run you only about $25. The bidirectional nature of the I/O pins means that you are not as limited as you are in a ROM or PAL implementation. The ATmega328P has approximately 20 pins, and any of them can be used as either inputs or outputs.

The drawbacks of microcontroller implementations are that they require knowledge of a programming language (such as C) to program. Although a simple digital design project will typically not require time-critical processes to occur, an implementation with discrete components or something like an FPGA can be much faster than a microcontroller, which requires a certain number of clock cycles to execute each instruction.

Using different devices to solve digital design projects

Now that I’ve explained each of the major types of ways that a project can be implemented, I’m going to explain my process of using them to create a digital project that was purely combinational (AND / OR / NOT gates) and a project that was sequential (required flip-flops and a clock signal to execute). I have to say that this was a fascinating use of my time, and required me to finally learn how to use the PAL chips that I’ve had stored in a bin for a couple of years. Any student or hobbyist looking to expand their skills should consider doing something like this to help them really appreciate the benefits, drawbacks, and limitations of each type of technology.

Combinational logic project: adding ones in a binary number

This project was inspired by my previous student and tutor Max. The goal is to have an 8-bit DIP switch, and the project will sum up (or count) the number of ones in the input number. So if the binary number on the DIP switch is 11011001, the display would show the number 5. I made a simulation in CircuitVerse of how this works. You can play around with it and see what happens.

Photograph of each of the three implementations. On the left is the discrete logic implementation, in the center is the EPROM implementation, and on the right is the microcontroller implementation.
Discrete logic componentsEPROMMicrocontroller
Power Consumption590 – 980 mW190 – 840 mW170 – 450 mW
Sizemediumsmallsmall
Cost$23.50$14.50$14.15
Funmost funleast funmedium fun
Other7448 chips mean no external resistors are required for the 7-segment displayNo separate decoder is required for the 7-segment display, a programmer is required to burn the chipInternal pull-ups mean that no external pull-down resistors are required, no separate decoder is required for the 7-segment display, a programmer is required to burn the chip
Table comparing and contrasting the different features of the combinational project implementation using three different technologies

Because this device requires adders, the implementation using discrete logic components required four 74LS283 chips: two acting as one-bit adders, one acting as a two-bit adder, and one acting as a three-bit adder. I could have implemented the one-bit adders using AND / OR / XOR gates, but that would have increased the footprint size. Then, I needed to use a BCD to 7-segment decoder chip to display the number on a 7-segment display. I decided to use the 74LS48 because of the internal resistance on the chip; external current-limiting resistors are not required. All of the chips are capable of sourcing enough current to illuminate the display, and I had a common-cathode display to use, so everything worked out fine.

Photograph of the discrete logic implementation (left), and overlaid with a flowchart (right).

The EPROM just required me to program a truth table onto a chip (27C256). This was the least fun because while I enjoyed writing the C code that created the HEX file for me, everything else about this was just wiring. It’s not necessary to write C code to create a HEX file, but typing 256 memory locations in by hand would be tedious.

Cartoon indicating that a ROM is basically just one giant truth table burned onto a chip.

I first did a prototype using a UV erasable ROM; once that worked I programmed an OTP ROM to solder onto the prototype board. The good thing about the EPROM I used is that there are 8 output pins, which means I could do all of the 7-segment decoding on-chip; I didn’t need to use an external decoder. However, I did need to use external current-limiting resistors for each segment on the display. Clever positioning of the DIP switch and EPROM chip allowed me to bypass using 8 wires to connect the two devices together.

Photograph of EPROM implementation (left), and overlaid with a flowchart (right).

Both the discrete logic and EPROM consumed the most power when all of the DIP switches are in their ON position; this is due to the 330 Ohm pull-down resistors pulling a lot of current in that circumstance. (About 15 mA per switch, so about 120 mA total, leading to about 600 mW.) The pull-down resistors that I used were bussed; they look like very narrow yellow rectangles in the photographs. I could have used slightly higher pull-downs, but using TTL components limits how large the pull-downs can be for the devices to continue to operate correctly. I probably could have played around with larger pull-downs on the EPROM as it is more CMOS compatible than the 7400 chips in my discrete logic implementation. The microcontroller is able to greatly reduce the amount of power consumed because of the large value of the internal pull-ups (somewhere around 30 kOhm each).

The microcontroller required just a few lines of code to program. I decided to make the programming as challenging as possible and use interrupts. I was also able to do all of the segment decoding on-chip. I used eight input pins (one for each on the DIP switch) and seven output pins (one for each segment on the display), so the ATmega328P was a good option. I started by using the Arduino Uno with everything on a breadboard, and once I got the design right, burned the program onto a chip to solder onto my protoboard. If I were to do this again I might write the code in Assembly just to make this more of a challenge.

volatile unsigned char inputC = 0;
volatile unsigned char inputB = 0;
volatile unsigned char totalInput = 0;

void setup() {
  // configure internal pull-ups on input pins
  PORTC = 0x0F;
  PORTB = 0x0F;

  // configure PORTD as output pins
  DDRD = 0xFE;

  cli();
  // configure pin-change interrupts on input pins
  PCICR = 0x03;
  PCMSK0 = 0x0F;
  PCMSK1 = 0x0F;
  sei();

  // obtain initial data from the DIP switch in case any are in a set state
  inputB = ~PINB & 0x0F;
  inputC = (~PINC & 0x0F) << 4;
  totalInput = inputB | inputC;
}

void loop() {
  // these are the numeral encodings for the 7-segment display
  unsigned char numberArray[] = {0xFC, 0x60, 0xDA, 0xF2, 0x66, 0xB6, 0xBE, 0xE0, 0xFE};
  unsigned char sumOfOnes = 0;

  // count the number of ones in the input stream
  for (unsigned char j = 0; j < 8; j++) {
    if (totalInput & (1 << j)) {
      sumOfOnes ++;
    }
  }

  // write data to the 7-segment display
  PORTD = numberArray[sumOfOnes];
}

ISR (PCINT0_vect) {
  inputB = ~PINB & 0x0F;
  totalInput = inputB | inputC;
}

ISR (PCINT1_vect) {
  inputC = (~PINC & 0x0F) << 4;
  totalInput = inputB | inputC;
}
Photograph of microcontroller implementation (left), and overlaid with a flowchart (right).

I did not ultimately chose to implement this design onto a PAL chip. While I believe it would be possible to do this on a 16V8 chip (there are only eight inputs and seven outputs required for this project, after all), coming up with minimum SOP expressions for each segment would not be an enjoyable task for me. I could reduce the agony by having the PAL output a BCD number, but I would still have to come up with four SOP expressions of eight variables. More importantly, I don’t believe that the PAL implementation would be any better than the ROM, other than the size of the chip is physically smaller.

Sequential logic project: seconds counter (0-59)

This project simply uses a 1 Hz clock to count between 0-59, and then back to 0 again. This could be useful as a countdown timer, and doesn’t really need to be limited to just 0-59. This could also be connected to a second counter to effectively act as a clock. (Although the accuracy of some of the clocks would be questionable, and without reformulating things to accept input values, it would be hard to program it to tell the right time!)

Photograph of each of the three implementations. On the left is the discrete logic implementation, in the center is the PAL implementation, and on the right is the microcontroller implementation.
Discrete logic componentsPALMicrocontroller
Power Consumption420 – 600 mW420 – 475 mW110 – 200 mW
Sizemediummediumsmall
Cost$29.50$27.50$14.70
Funmedium funmedium funmedium fun
Accuracynot verynot verypretty good
Other7448 chips mean no external resistors are required for the 7-segment display7448 chips mean no external resistors are required for the 7-segment display, a programmer is requiredNo decoder is necessary for the 7-segment display, no external clock or crystal is necessary (although I did use an external crystal), a programmer is required
Table comparing and contrasting the different features of the sequential project implementation using three different technologies

For the discrete logic implementation, there are a lot of different possibilities I could have chosen. I decided to use two 74LS190 (BCD counter) chips to save me from having to build my own counters (which I could have designed as either synchronous or ripple). I chose this approach because the footprint size would have been very large if I would have built my own counter. I looked into using the 74LS390 (dual BCD counter) chip, as it would have been a great candidate for reducing the footprint down even more. However, its lack of asynchronous clearing or ripple carry out pins made it seemingly impossible to daisy-chain two together. Because the counter only counts to 59, I needed a way to asynchronously clear the most significant BCD value after 5. This was easily accomplished using a NAND gate on counter outputs QB and QC going into the asynchronous clear pin (noting that it does add a very short glitch, but it is not possible to see that glitch with the human eye). The two BCD values were then funneled into 74LS48 chips before connecting to the dual 7-segment display. I called this “medium” fun because it wasn’t necessarily boring, but wan’t a huge challenge. Most of my design went into determining how to make the footprint as small as possible by considering different types of adder chips. I was ultimately disappointed in my options.

Photograph of discrete logic implementation (left), and overlaid with a flowchart (right).

The PAL implementation was the first time I really used a PAL. I had tinkered with them just a little bit but never in pursuit of a goal. The fun part of this was learning how to make things work, but it was frustrating finding software, and one of my programmers started on fire when I plugged it into my USB port, so this part was a lot of cascading errors that are funny in hindsight. Because I had so little experience using PALs, I could have had this entire project done months ago if not for just this one implementation, but I was determined to see it through. I’m glad I did.

Photograph of PAL implementation (left), and overlaid with a flowchart (right).

Because the heart of a PAL is a programmable AND gate connected to an OR gate (connected to a flip-flop), I found it was just easiest to implement it with minimum SOP expressions. Because I was using the PAL as a synchronous device, I specified the equations for each flip-flop input (which is depicted with the .d part of each equation, because the PAL device I used has D flip-flops in their macrocells). In WinCUPL, ! means NOT, & means AND, and # means OR.

Q0.d = !Q0;

Q1.d = !Q3 & !Q1 & Q0
	# Q1 & !Q0;

Q2.d = Q2 & !Q1
	# Q2 & !Q0
	# !Q2 & Q1 & Q0;

Q3.d = Q2 & Q1 & Q0
	# Q3 & !Q1 & !Q0;

Q4.d = !Q6 & !Q4 & Q3 & Q0
	# !Q6 & Q4 & !Q3
	# !Q6 & Q4 & !Q0
	# !Q5 & !Q4 & Q3 & Q0
	# !Q5 & Q4 & !Q3
	# !Q5 & Q4 & !Q0;

Q5.d = !Q6 & !Q5 & Q4 & Q3 & Q0
	# !Q6 & Q5 & !Q4
	# !Q6 & Q5 & !Q3
	# !Q6 & Q5 & !Q0;

Q6.d = !Q6 & Q5 & Q4 & Q3 & Q0
	# Q6 & !Q5 & !Q4
	# Q6 & !Q5 & !Q3
	# Q6 & !Q5 & !Q0;

At the end of the day, I had two BCD values which meant I still needed to use 74LS48 chips to decode the outputs to the 7-segment displays. Because there were two displays, a PAL doesn’t have enough output pins for each of the segments… it also would have been much more annoying to come up with SOP expressions for each segment rather than for the BCD values!

I needed a clock signal for both the discrete logic and PAL implementations. I haven’t found anything better than a 555 timer (for now), although I suppose I could have used a microcontroller to create a clock (that seemed like cheating!). I made sure to put a trimpot in for one of the resistances so I could get the frequency as close to 1 Hz as possible. However, these two implementations are just not going to be that accurate. One additional consideration with the PAL implementation was that I noticed that the output would consistently skip numbers. I put a bypass capacitor on the power pin on the PAL which didn’t help, so I decided to condition the clock signal by placing it into a 74LS14 chip to make use of the Schmitt trigger inputs. Thankfully it solved the problem, but also added to the footprint of the implementation.

Finally, I used an ATmega328P to implement this with a microcontroller. Because it already has a built-in clock and has three timer/counter units, this seemed like a no-brainer. However, I decided to do something different. I had a bunch of 32,768 kHz crystals to use with real-time clocks, so I decided to use that with asynchronous timer/counter 2 to create a clock signal as close to 1 Hz as I could get. This makes the microcontroller implementation by far the most accurate as far as time-keeping goes. I used an interrupt to increment the number (from 0-59) every second. The decoding for the 7-segment display also took place in software. This made the footprint really small as I didn’t need to include an external clock or external decoding hardware. I decided to multiplex the display so that I only had to use seven output pins rather than fourteen (essentially I write one numeral to the tens place and then write a second numeral to the ones place, and switch between the two so quickly that the human eye can’t tell that both aren’t being written simultaneously).

Photograph of microcontroller implementation (left), and overlaid with a flowchart (right).

For some reason, I programmed this microcontroller using AtmelStudio rather than the Arduino IDE. I looked through my notebook and can’t see any compelling reason why.

#define F_CPU 8000000UL

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>

volatile unsigned char x = 0;
volatile unsigned char numArray[10] = {0x01, 0x4F, 0x12, 0x06, 0x4C, 0x24, 0x60, 0x0F, 0x00, 0x0C};

int main(void)
{
    // configure output pins
	DDRB = 0x03;
	DDRD = 0x7F;
	// configure TCNT 2 in asynchronous mode
	cli();
	TIMSK2 = 0;
	ASSR |= (1<<AS2);
	_delay_ms(1000);
	TCNT2 = 0;
	TCCR2A = 0x00;
	TCCR2B = 0x05;
	TIMSK2 = 0x01;
	// configure TCNT 1 
	TCCR1A = 0x00;
	TCCR1B = 0x0A;
	TIMSK1 = 0x02;
	OCR1A = 625;
	sei();
    while (1) 
    {
    }
}

ISR(TIMER2_OVF_vect) {
	x++;
	if (x==60) x=0;
}

ISR(TIMER1_COMPA_vect) {
	static unsigned char z = 0;
	unsigned char tensPlace = x / 10;
	unsigned char onesPlace = x % 10;
	PORTB &= 0xFC;
	PORTB |= (1<<z);
	if (z) PORTD = numArray[tensPlace];
	else PORTD = numArray[onesPlace];
	z ^= 1;
}

Why just medium fun for each implementation? Each one was fun in its own way, in the challenges that I encountered trying to optimize the project as much as possible. But I don’t think any one of them stands out as being the *most* fun.

I could have implemented this with a ROM and flip-flops, but I don’t think it would have provided any benefits that weren’t realized by the other implementation.

Conclusion

Getting down to brass tacks, I don’t think either of these projects is something that somebody would be super excited to build, except for the learning aspect of it. Most projects that you might think of are probably going to quickly become too complicated to implement with anything but a microcontroller (or maybe an FPGA). Thinking about the projects I’ve built and am currently building, none of them use anything but a microcontroller. However, I still think it’s worth exploring all of these technologies for, as I mentioned earlier, really getting to understand them and their limitations.

Update: video

In March 2021, I finally had time to make a video showcasing the two projects I built. Think of it as a companion to this blog post. Enjoy!