\title{Digital Arithmetic Cells with myHDL} \author{Steven K Armour} \maketitle
In [29]:
from myhdl import *
from myhdlpeek import Peeker
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline
from sympy import *
init_printing()
import random
#https://github.com/jrjohansson/version_information
%load_ext version_information
%version_information myhdl, myhdlpeek, numpy, pandas, matplotlib, sympy, random
Out[29]:
In [2]:
#helper functions to read in the .v and .vhd generated files into python
def VerilogTextReader(loc, printresult=True):
with open(f'{loc}.v', 'r') as vText:
VerilogText=vText.read()
if printresult:
print(f'***Verilog modual from {loc}.v***\n\n', VerilogText)
return VerilogText
def VHDLTextReader(loc, printresult=True):
with open(f'{loc}.vhd', 'r') as vText:
VerilogText=vText.read()
if printresult:
print(f'***VHDL modual from {loc}.vhd***\n\n', VerilogText)
return VerilogText
In [3]:
ConversionTable=pd.DataFrame()
ConversionTable['Decimal']=np.arange(0, 21)
ConversionTable['Binary']=[bin(i, 3) for i in np.arange(0, 21)]
ConversionTable['hex']=[hex(i) for i in np.arange(0, 21)]
ConversionTable['oct']=[oct(i) for i in np.arange(0, 21)]
ConversionTable
Out[3]:
In [4]:
binarySum=lambda a, b, bits=2: np.binary_repr(a+b, bits)
In [5]:
for i in [[0,0], [0,1], [1,0], [1,1]]:
print(f'{i[0]} + {i[1]} yields {binarySum(*i)}_2, {int(binarySum(*i), 2)}_10')
Notice that when we add $1_2+1_2$ we get instead of just a single bit we also get a new bit in the next binary column which we call the carry bit. This yields the following truth table for what is called the Two Bit or Half adder
In [6]:
TwoBitAdderTT=pd.DataFrame()
TwoBitAdderTT['x2']=[0,0,1,1]
TwoBitAdderTT['x1']=[0,1,0,1]
TwoBitAdderTT['Sum']=[0,1,1,0]
TwoBitAdderTT['Carry']=[0,0,0,1]
TwoBitAdderTT
Out[6]:
Looking at this truth table we can surmise the following
In [7]:
x1, x2=symbols('x_1, x_2')
Sum, Carry=symbols(r'\text{Sum}, \text{Carry}')
HASumDef=x1^x2; HACarryDef=x1 & x2
HASumEq=Eq(Sum, HASumDef); HACarryDef=Eq(Carry, HACarryDef)
HASumEq, HACarryDef
Out[7]:
We can thus generate the following myHDL
In [8]:
@block
def HalfAdder(x1, x2, Sum, Carry):
"""
Half adder in myHDL
I/O:
x1 (bool): x1 input
x2 (bool): x2 input
Sum (bool): the sum Half Adder ouput
Carry (bool): the carry Half Adder ouput
"""
@always_comb
def logic():
Sum.next=x1^x2
Carry.next=x1 & x2
return logic
In [9]:
Peeker.clear()
x1=Signal(bool(0)); Peeker(x1, 'x1')
x2=Signal(bool(0)); Peeker(x2, 'x2')
Sum=Signal(bool(0)); Peeker(Sum, 'Sum')
Carry=Signal(bool(0)); Peeker(Carry, 'Carry')
DUT=HalfAdder(x1, x2, Sum, Carry)
def HalfAdder_TB():
"""
Half Adder Testbench for use in python only
"""
@instance
def Stimules():
for _, row in TwoBitAdderTT.iterrows():
x1.next=row['x1']
x2.next=row['x2']
yield delay(1)
raise StopSimulation()
return instances()
sim = Simulation(DUT, HalfAdder_TB(), *Peeker.instances()).run()
In [10]:
Peeker.to_wavedrom('x2 x1 | Sum Carry', title='Half Adder Wave Form', tock=True)
In [11]:
HARes=Peeker.to_dataframe()
HARes=HARes[['x2', 'x1','Sum', 'Carry']]
HARes
Out[11]:
Wich, we can then confirm via these results to the expected results via
In [12]:
TwoBitAdderTT==HARes
Out[12]:
In [13]:
DUT.convert()
VerilogTextReader('HalfAdder');
HalfAdder RTL
In [26]:
@block
def HalfAdder_TBV():
"""
Half Adder Testbench for use in Verilog
"""
x1 = Signal(bool(0))
x2 = Signal(bool(0))
Sum = Signal(bool(0))
Carry = Signal(bool(0))
DUT = HalfAdder(x1, x2, Sum, Carry)
testx1=Signal(intbv(int("".join([str(i) for i in TwoBitAdderTT['x1'].tolist()]), 2))[4:])
testx2=Signal(intbv(int("".join([str(i) for i in TwoBitAdderTT['x2'].tolist()]), 2))[4:])
@instance
def Stimulus():
for i in range(len(testx1)):
x1.next = testx1[i]
x2.next = testx2[i]
yield delay(1)
raise StopSimulation()
@always_comb
def print_data():
print(x1, x2, Sum, Carry)
return instances()
# create instaince of TB
TB = HalfAdder_TBV()
# convert to verilog with reintilzed values
TB.convert(hdl="verilog", initial_values=True)
VerilogTextReader('HalfAdder_TBV');
Where the above warning can be ignored since we are using testx1 and testx2 as stores for the binay set of inputs to apply to x1 and x2
In [17]:
ThreeBitAdderTT=pd.DataFrame()
ThreeBitAdderTT['c1']=[0,0,0,0,1,1,1,1]
ThreeBitAdderTT['x2']=[0,0,1,1,0,0,1,1]
ThreeBitAdderTT['x1']=[0,1,0,1,0,1,0,1]
ThreeBitAdderTT['Sum']=[0,1,1,0,1,0,0,1]
ThreeBitAdderTT['Carry']=[0,0,0,1,0,1,1,1]
ThreeBitAdderTT
Out[17]:
! need a way to make K-Maps in python
whereupon making and reducing the maps from the three-bit adder truth table we get the following equations
In [18]:
c1, x1, x2=symbols('c_1,x_1, x_2')
Sum, Carry=symbols(r'\text{Sum}, \text{Carry}')
FASumDef=x1^x2^c1; FACarryDef=x1&x2 | x1&c1 | x2&c1
FASumEq=Eq(Sum, FASumDef); FACarryDef=Eq(Carry, FACarryDef)
FASumEq, FACarryDef
Out[18]:
yielding the following next myHDL module
In [19]:
@block
def FullAdder(x1, x2, c1, Sum, Carry):
"""
Full adder in myHDL
I/O:
x1 (bool): x1 input
x2 (bool): x2 input
c1 (bool): carry input
Sum (bool): the sum Full Adder ouput
Carry (bool): the carry Full Adder ouput
Note!:
There something wrong on the HDL side at the moment
"""
@always_comb
def logic():
Sum.next=x1 ^ x2 ^c1
Carry.next=(x1 & x2) | (x1 & c1) | (x2 & c1)
return logic
In [21]:
Peeker.clear()
x1=Signal(bool(0)); Peeker(x1, 'x1')
x2=Signal(bool(0)); Peeker(x2, 'x2')
c1=Signal(bool(0)); Peeker(c1, 'c1')
Sum=Signal(bool(0)); Peeker(Sum, 'Sum')
Carry=Signal(bool(0)); Peeker(Carry, 'Carry')
DUT=FullAdder(x1, x2, c1, Sum, Carry)
def FullAdder_TB():
@instance
def Stimules():
for _, row in ThreeBitAdderTT.iterrows():
x1.next=row['x1']
x2.next=row['x2']
c1.next=row['c1']
yield delay(1)
raise StopSimulation()
return instances()
sim = Simulation(DUT, HalfAdder_TB(), *Peeker.instances()).run()
In [22]:
Peeker.to_wavedrom('c1 x2 x1 | Sum Carry', title='Full Adder Wave Form', tock=True)
In [23]:
FARes=Peeker.to_dataframe()
FARes=FARes[['c1, x2','x1','Sum', 'Carry']]
FARes
In [24]:
ThreeBitAdderTT==FARes
In [25]:
DUT.convert()
VerilogTextReader('FullAdder');
FullAdder RTL
In [27]:
@block
def FullAdder_TBV():
"""
Full Adder Testbench for use in Verilog
"""
x1=Signal(bool(0))
x2=Signal(bool(0))
c1=Signal(bool(0))
Sum=Signal(bool(0))
Carry=Signal(bool(0))
DUT = FullAdder(x1, x2, c1, Sum, Carry)
testx1=Signal(intbv(int("".join([str(i) for i in ThreeBitAdderTT['x1'].tolist()]), 2))[4:])
testx2=Signal(intbv(int("".join([str(i) for i in ThreeBitAdderTT['x2'].tolist()]), 2))[4:])
testc1=Signal(intbv(int("".join([str(i) for i in ThreeBitAdderTT['c1'].tolist()]), 2))[4:])
@instance
def Stimulus():
for i in range(len(testx1)):
x1.next=testx1[i]
x2.next=testx2[i]
c1.next=testc1[i]
yield delay(1)
raise StopSimulation()
@always_comb
def print_data():
print(x1, x2, c1, Sum, Carry)
return instances()
# create instaince of TB
TB = FullAdder_TBV()
# convert to verilog with reintilzed values
TB.convert(hdl="verilog", initial_values=True)
VerilogTextReader('FullAdder_TBV');
Where the above warning can be ignored since we are using testx1, testx2, and testc1 as stores for the binay set of inputs to apply to x1, x2, and c1
In [ ]: