Doing Math in FPGAs, Part 2 (BCD)

Binary coded decimal is not used as much as it once was, but we can still find it hither and yon -- mostly in applications where the data will be directly driving a display.

This leaves BCD subtraction, which is a little more interesting. Of course, multiplication is just successive addition, so we can get there from here. In fact, we can get to division from here, too, but we still need subtraction. When working in binary, we often use the one's complement or two's complement formats to represent negative numbers. This allows us to perform subtractions by simply adding the two numbers. (Subtracting is just taking the negative of the number to be subtracted and then adding them, right?) This is true in decimal, too, only we use either nine's or ten's complement representations.

Wait, what are nine's and ten's complements? To be honest, I don't remember learning this in school. It works like this -- consider the following subtraction problem.

Another way to present this problem would be as follows.

And this is the same as performing the following actions.

Let's take this step by step: First, let's perform the following subtraction.

Simply put, 9,722 is the nine's complement of -277. This does not solve our subtraction problem in and of itself, since we still need to perform a subtraction to obtain the nine's complement. Fortunately, there will never be a borrow from a next-higher-power column. (I'll leave it to you to figure it out.) We can use a lookup table or something similar to derive each individual digit.

The nine's complement of -277 is 9,722 (the same as performing the subtraction). Now we add 1 to generate the tens's complement.

Next, we add the ten's complement of -277 to 4,337.

We still need to subtract 10,000 from our result, but we can easily achieve this by throwing away (ignoring) the most significant digit. Our result is 4,060. If the leftmost digit is 5 or greater, we know the number is negative. To convert to the proper BCD representation (for display), we just use our lookup table to get the nine's complement. Reversing the numbers, we get the following (with a negative sign, which we might need to monitor separately, depending on our implementation).

Because the first digit is greater than or equal to 5, we know this is a negative number. Taking the nine's complement of 5,940 gives 4,060, so our answer is -4,060. Problem solved.

I've expended a lot of hot air explaining something you probably already knew. At least we now have the tools to perform decimal math in BCD using addition, subtraction, multiplication, and division. I'll leave it to you to implement this in hardware. Granted, this is pretty inefficient in terms of hardware and memory (something like 40% more hardware), and it is considerably slower and more klunky. However, we gain exact representation of our digits, and there is no issue with rounding/truncation. (We'll talk about this in another installment, I think.) This could be useful, but your mileage may vary. As always, you need to take a close look at your problem and figure out the best solution.

In my next column, I think I'll talk about some simple floating-point stuff. Until then, if you have any questions, please post them below as comments.

The concerns about precision seem seem exaggerated even in financial calculations. There are rounding methods such that insignifican amounts are discarded.

I've seen the repeated additions and other mathematical operations rapidly create some ugly mistakes. I once had a CS class that focused specifically on this, and ways to work within the constraints of the hardware to overcome these errors. It can take a fair amount of massaging!

Hi, Tom: Things are too quiet here. Altera has 9 bit multipliers and it takes 4 to do 32 bits.

The concerns about precision seem seem exaggerated even in financial calculations. There are rounding methods such that insignifican amounts are discarded.

Given all the complexity of floating point HW, Why not convert the decimal to Hex/Binary and do the algorithm as if doing decimal with pencil and paper then output the whole number part and fractional part converted from binary to decimal?

The version I saw was in a DDS [Numerically Controlled Oscillator]. 32 bits make 4,000,000,000 instead of 4,294,967,296. So a 200MHz oscillator gave a frequency strep of precisely 0.05 Hz instead of 0.046566128731 Hz [ie. subject to oscillator drift, not rounding error]

I think there is a typo in the article on its 2nd page - instead of "Taking the nine's complement of 5,940 gives .." I think there should be "Taking the 109's complement of 5,940 gives..."

The nine's complement of 5940 gives 4059, to get ten's complement we need to add 1, which finally gives 4060. Also the difference to the first computation 4337 - 277 is that we need to add the +1 to the result (use 109's complement) because the result is negative.

I primarily hope to not prove the old addage about letting people think yu a fool, vice opening your mouth and proving you're one... But then, I freely admit that I am, indeed, an idiot, so just keep that in mind, okay?

Is multiply by 1000 done with 1000 additions and divide by 1000 done with 1000 subtractions?

Well, no. I was kind of waving my hands and glossing over dirty details. Certainly one way to do it is to do repeated additions or subtractoins (you can do division via addition, as well). When you get down into the guts of the computing machinery, though, we can go about this in different ways - look at the way we do these operations by hand, and we can do things similarly in the computer.

In BCD it's a little harder, but we can do the following type of thing, where each digit has its own combinational cloud to do the multiplication:

So let's talk about pure binary as an example. For multiplication, you can actually synthesize a 2N-bit combinational cloud to do your multiplications. Lacking that, you can build a state machine that's pretty easy, as binary is a special case. From the same Wikipedia page:

1011 (this is 11 in decimal)
x 1110 (this is 14 in decimal)
======
0000 (this is 1011 x 0)
1011 (this is 1011 x 2, shifted one position to the left)
1011 (this is 1011 x 4, shifted two positions to the left)
+ 1011 (this is 1011 x 8, shifted three positions to the left)
=========
10011010 (this is 154 in decimal)

So, you can see we can do a multiply in (potentially) a lot less operations than just adding them all up.

I mentioned combinationall clouds, so let's touch on that a bit. For all possible N-bit multiplications, a truth table can be made, and a set of discrete logic implemented to perform that multiplication. I will point you to this paper for a quick overview - they show how to implement a 4-bit combinational multiplier and its complexity. The modern synthesis tools appear to be able to create a multiplier of at least 32 bits with no issue (I've not tried anything bigger), and modern FPGAs have dedicated multiplier circuits in them (or so it would seem), but I don't know how many bits wide they are.