AI Models On Silicon (Series) — Part 1

Dipanshu
TheCyPhy
Published in
5 min readJun 29, 2020
Venus at the Edge — https://apod.nasa.gov/apod/ap120609.html

We discussed in Part 0 about the foundations for this project. We agreed on definitions and ideas, models, and approaches. We will now multiply two unsigned 8-bit numbers. First, we will discuss unsigned and signed numbers and some established approaches to multiply any two of any one kind of them.

What are unsigned numbers? What are signed numbers? Before answering these two question we should ask how do we represent positive and negative numbers with a collection of objects where each object can have only two states, namely, state up and state down or state 1 and state 0 or state + or state -. With computers it all about the interpretation of 0s and 1s, and their manipulations.

Representing Numbers

Theoretically, a computer is a system that manipulates states of itself or of another system according to some predefined instructions or instructions defined or given during runtime. The state of any part can have any discrete value or even nondiscrete values. But due to engineering and theoretical conclusions, to build a computer, we use states which are discrete and can have only two distinct values, namely 1’s and 0’s. These two-level states are generally implemented as voltage high level or low level, say, if we measure +3V to +1.8V on a wire then its a 1 else it’s a 0. We now know what 0’s and 1’s are and another fact that a computer is not a computer if there are not any instructions or standards on inferring the state values. So, if a computer works according to some specific instructions and it manipulates the states on which it can act on, and outputs some 0’s and 1’s then we must have some inference rules that can make sense of those 0’s and 1’s. The inference rules to figure out the output is simple if we know that the output can always be taken as numbers. Then the inference rule or rules are trivial, the output is just the base 2 representation of a number.

Unsigned And Signed Numbers

If you have learned the C programming language anywhere in your course or as a hobby then you probably have an idea about unsigned and signed numbers. Unlike many other scientific or technical terms where the term is named misleading according to what it means, the term unsigned and signed numbers conveys exactly what they mean. Signed numbers are those where a sign bit is present which signals whether the numbers represented by it are non-negative integers or integers. Typically a sign bit ‘1’ at the most significant position denotes an integer and ‘0’ there represents a non-negative integer. Unsigned numbers are simply the integers which are not signed (always non-negative). They can use all the available positions of the bit string to denote a value and have more range than signed numbers.

Multiplying Two Bit Strings

Numbers represented as bits, called bit strings, can be multiplied in the same manner and steps as we do with decimal numbers with only a single hand on a paper. Consider the example below where we are multiplying 10011101 (157) and 00100101 (37) which results in 0001011010110001 (5809) and notice, we are only interested in the lower 8 bits of the result.

So let’s implement the 8-bit multiplier in Verilog.

Implementation

Let’s take a look at the first implementation — directly multiplying the two numbers using the ‘*’ operator and putting all the burden on the chip. In Xilinx XC6SLX9, the multiplication is done by the on chip part called DSP48A1 Slice, a Digital Signal Processing part of the chip. The datasheet says that it contains an 18-bit input pre-adder, 18 x 18 bit two’s complement multiplier and a 48-bit sign extended adder / subtracter / accumulator. We won’t go into the details of either any DSP or the DSP48A1.

The code declares 24 pins from line 4 to line 27 where 16 pins are input pins, denoting two 8 bit operands for multiplication and 8 output pins, denoting the 8-bit multiplication output. The statements on line 30 and line 32 prepares the two input numbers a and b by correctly defining the significance of each input bit. Line 34 onwards, the output pins are prepared. A ‘variable’ c is declared and assigned the value a multiplied by b, then each bit of the result in ‘variable’ c is assigned to the output pins c0, c1, … Most of the keywords and expressions in the code are self-explanatory (assign, input, output, and bitwise arithmetic). The only unusual keyword here is the wire. It is aptly named because it connects different elements of the circuit just like a physical wire does. It can only be assigned and assigned only once. And just like that, you can use wires, assigns and logic operators to create any logic gate.

In the next implementation, we are going to directly implement all the stuff at the gate level, primarily using the Wallace Tree method.

Client-Side

To get the results of multiplication we have to have some client-side functionality, the connection between the FPGA and our computer. To get the result from the FPGA we have to sample our output Wires (c0, c1, …, c7). We are using an Arduino to sample it and using Python3 to communicate with the Arduino. A user-friendly client-side program would be helpful. The code of the Arduino and User Program is as follows.

Arduino
User Program

Please try the given code. Any improvement, comment, recommendation, and criticism will be highly appreciated.

In The Next Part

We will implement the Wallace Tree Multiplication method, extend the multiplication to 32 bits, and introduce the floating-point representation and its arithmetic. Stay tuned.

--

--