\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 [ ]: