\title{myHDL Two Word Complex Multiplier} \author{Steven K Armour} \maketitle
This notebook/Program is a walkthrough on how to design and construct a two number complex Multiplier unit in myHDL based on the example of Guenter Dannoritzer
Guenter Dannoritzer, Complex Math, http://old.myhdl.org/doku.php/projects:cplx_math [3-29-18]
myHDL, [myhdlpeek by xesscorp] (using the experimental pandas branch)(https://github.com/xesscorp/myhdlpeek), draw.io
In [1]:
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
In [2]:
A, B=symbols('A, B')
a, b, c, d=symbols('a, b, c, d', real=True)
In [3]:
Multi=A*B; Multi
Out[3]:
In [4]:
simplify(Multi.subs({A:1+1j, B:1+1j}))
Out[4]:
In [5]:
Multi=Multi.subs({A:(a+1j*b), B:(c+1j*d)})
Multi
Out[5]:
In [6]:
Multi=expand(Multi); Multi
Out[6]:
In [7]:
re(Multi), im(Multi)
Out[7]:
To encode the real number word and imaginary word into a single word we need to not treat the signal as a number but as a word and then contract the word into a signal word based on a well-posed contraction rule.
The contraction rule for creating a multiplication is that both the real and imaginary part of the whole number is a 2's complement number(word) that we then contract via the first word as $Re$ part and the second word is the $Im$ part such that we then have
$$Re(\text{word})+Im(\text{word})j\Rightarrow Re(\text{word})Im(\text{word})=\mathbb{Z}(\text{word} ) $$There is an ongoing issue with the concat function in myHDL that is ongoing [Issue Reopened 3-29-18] https://github.com/myhdl/myhdl/issues/128#issuecomment-377370353
When that issue is resolved this notebook will be updated accordingly. But for the moment, an attempt to develop this algorithm is shown and the problem with concat is highlighted so this notebook/program can be used as a benchmark for getting concat working properly.
In [8]:
BitWidth=32
WordSize=BitWidth
In [9]:
ReWordLen=WordSize//2; ImWordLen=WordSize//2
print(f'The Re WordLen is {ReWordLen} bits and the Im WordLen is {ImWordLen} bits')
In [10]:
ReMax=(2**(ReWordLen-1)-1); ReMin=-2**(ReWordLen-1)
ImMax=(2**(ImWordLen-1)-1); ImMin=-2**(ImWordLen-1)
ReMax, ReMin, ImMax, ImMin
Out[10]:
Test Tragets
In [11]:
R=(2**ReWordLen)//4; I=(2**ImWordLen)//8
print(f'Re: {R}, Im: {I}')
In [12]:
RN=intbv(R, min=ReMin, max=ReMax); RN
RNBin=''.join([str(int(i)) for i in RN])
bin(R, ReWordLen), RNBin, bin(R, ReWordLen)==RNBin
Out[12]:
In [13]:
IN=intbv(I, min=ImMin, max=ImMax); IN
INBin=''.join([str(int(i)) for i in IN])
bin(I, ImWordLen), INBin, bin(I, ImWordLen)==INBin
Out[13]:
In [14]:
AN=concat(RN, IN)
ANBin=''.join([str(int(i)) for i in AN])
bin(AN, WordSize), ANBin, bin(AN, WordSize)==ANBin
Out[14]:
So now we can then Split AN to get back RN and IN
In [15]:
RNBack=AN[:ReWordLen]; INBack=AN[ImWordLen:]
RNBack, RN, RNBack==RN,INBack, IN, INBack==IN
Out[15]:
But since myHDL implements 2's complement, we need to test for negative numbers where the leading bit is the signed signal
In [16]:
TestNegNum=-26
print(f"""Target: {TestNegNum}
Absolote Bin: {bin(abs(TestNegNum), 8)},
Signed Bin: {bin(TestNegNum, 8)}""")
In [17]:
TestNegNumBV=intbv(TestNegNum)[8:]
TestNegNumBV, TestNegNumBV.signed()
Out[17]:
In [18]:
R=-R; I=-I
print(f'Re: {R}, Im: {I}')
In [19]:
RN=intbv(R, min=ReMin, max=ReMax); RN
RNBin=''.join([str(int(i)) for i in RN])
RN.signed(), bin(R, ReWordLen), RNBin, bin(R, ReWordLen)==RNBin
Out[19]:
In [20]:
IN=intbv(I, min=ImMin, max=ImMax); IN
INBin=''.join([str(int(i)) for i in IN])
IN.signed(), bin(I, ImWordLen), INBin, bin(I, ImWordLen)==INBin
Out[20]:
In [21]:
AN=concat(RN, IN).signed()
ANBin=''.join([str(int(i)) for i in AN])
bin(AN, WordSize), ANBin, bin(AN, WordSize)==ANBin
AN
In [22]:
RNBack=AN[:ReWordLen]; INBack=AN[ImWordLen:]
RNBack.signed(), RN.signed(), RNBack.signed()==RN.signed(), INBack.signed(), IN.signed(), INBack.signed()==IN.signed()
Out[22]:
Here we prototype the algorithm using myHDL types without explicit HDL type code that will be developed in the final algorithm.
In [23]:
ReMax=(2**(ReWordLen-1)-1); ReMin=-2**(ReWordLen-1)
ReMax, ReMin
Out[23]:
In [24]:
ImMax=(2**(ImWordLen-1)-1); ImMin=-2**(ImWordLen-1)
ImMax, ImMin
Out[24]:
Create a Test A number
In [25]:
AVal=43-78j; AVal
Out[25]:
In [26]:
a=intbv(int(np.real(AVal)), min=ReMin, max=ReMax); a.signed()
Out[26]:
In [27]:
b=intbv(int(np.imag(AVal)), min=ImMin, max=ImMax); b.signed()
Out[27]:
In [28]:
A=concat(a, b); A, A.signed()
Out[28]:
In [29]:
a=intbv(A[:ReWordLen].signed(), min=ReMin, max=ReMax)
b=intbv(A[ImWordLen:].signed(), min=ImMin, max=ReMax)
a, b,
Out[29]:
Perform the same action as above but on $B$
In [30]:
BVal=1+123j; BVal
Out[30]:
In [31]:
c=intbv(int(np.real(BVal)), min=ReMin, max=ReMax); c
Out[31]:
In [32]:
d=intbv(int(np.imag(BVal)), min=ImMin, max=ImMax); d
Out[32]:
In [33]:
B=concat(c, d); B, B.signed()
Out[33]:
In [34]:
c=intbv(B[:ReWordLen].signed(), min=ReMin, max=ReMax)
d=intbv(B[ImWordLen:].signed(), min=ImMin, max=ImMax)
c, d
Out[34]:
In [35]:
ac=intbv(a*c, min=ReMin, max=ReMax)
np.real(AVal)*np.real(BVal), ac, ac.signed()
Out[35]:
In [36]:
bd=intbv(b.signed()*d.signed(), min=ImMin, max=ImMax)
np.imag(AVal)*np.imag(BVal), bd, bd.signed()
Out[36]:
In [37]:
ad=intbv(a.signed()*d.signed(), min=min(ReMin, ImMin), max=max(ReMax, ImMax))
np.real(AVal)*np.imag(BVal), ad, ad.signed()
Out[37]:
In [38]:
bc=intbv(b.signed()*c.signed(), min=min(ReMin, ImMin), max=max(ReMax, ImMax))
np.imag(AVal)*np.real(BVal), bc, bc.signed()
Out[38]:
In [39]:
Re=intbv(ac-bd, min=min(ac.min, bd.min), max=max(ac.max, bd.max))
np.real(AVal)*np.real(BVal)-np.imag(AVal)*np.imag(BVal), Re.signed()
Out[39]:
In [40]:
Im=intbv(ad+bc, min=min(ad.min, bc.min), max=max(ad.max, bc.max))
np.real(AVal)*np.imag(BVal)+np.imag(AVal)*np.real(BVal), Im, Im.signed()
Out[40]:
In [41]:
AVal*BVal
Out[41]:
In [42]:
Result=concat(Re, Im)
bin(Re, Re._nrbits)+bin(Im, Im._nrbits), ''.join([str(int(i)) for i in Result])
Out[42]:
In [43]:
@block
def CompMulti(A, B, C, ReWordLen=ReWordLen, ImWordLen=ImWordLen):
"""
Module to implement the complex multiplication of two complex numbers
Inputs:
A: A 2's `concat` of the input complex number where
the input word is a ` concat` according to ReIm
B: A 2's `concat` of the input complex number where
input word is a `concat` according to ReIm
Outputs:
C: A 2's `concat` of the output complex number product
where output world is a `concat` according to ReIm
Conversion Parameters;
ReWordLen: The word len of the real part of `concat` word
ImWordLen: The word len of the imag part of `concat` word
"""
#calc the min max of the based on ReWord and ImWord length
ReMax=(2**(ReWordLen-1)-1); ReMin=-2**(ReWordLen-1)
ImMax=(2**(ImWordLen-1)-1); ImMin=-2**(ImWordLen-1)
#create the regeistors to hold the procuct results
ac=Signal(intbv(0, min=ReMin, max=ReMax))
bd=Signal(intbv(0, min=ImMin, max=ImMax))
ad=Signal(intbv(0, min=min(ReMin, ImMin), max=max(ReMax, ImMax)))
bc=Signal(intbv(0, min=min(ReMin, ImMin), max=max(ReMax, ImMax)))
@always_comb
def SepMulti():
"""
The combinational logic to separate input words into components and
find multiplication products
"""
ac.next=A[:ReWordLen] *B[:ReWordLen]
bd.next=A[ImWordLen:]*B[ImWordLen:]
ad.next=A[:ReWordLen]*B[ImWordLen:]
bc.next=A[ImWordLen:]*B[:ReWordLen]
#will fix when Concat is working properly
#@always_comb
#def AddSupConcat():
# """
# Perfrom the real sup, im addtion, and concat based on
# ReIm to final output
# """
#
# C.next=concat(ac-bd, ad+bc)
return instances()
Generate random number within word size bounds for testing
In [44]:
#calc Bounds
Max=(2**(WordSize-1)-1); Min=-2**(WordSize-1)
ReMax=(2**(ReWordLen-1)-1); ReMin=-2**(ReWordLen-1)
ImMax=(2**(ImWordLen-1)-1); ImMin=-2**(ImWordLen-1)
#Create Testing Data
Refrance=pd.DataFrame(columns=['a', 'b', 'c', 'd'])
#Generate Testing data and bind values to DF
for i in range(20):
a=random.sample(range(0, ReMax), 1)[0]
b=random.sample(range(ImMin, ImMax), 1)[0]
c=random.sample(range(0, ReMax), 1)[0]
d=random.sample(range(ImMin, ImMax), 1)[0]
Refrance.loc[Refrance.shape[0]]=[a, b, c, d]
#force the stored value in DF to be ints
Refrance=Refrance.astype(int)
Refrance
Out[44]:
In [45]:
#calc exspected results
RefranceResults=Refrance.copy()
RefranceResults['A']=RefranceResults['a']+1j*RefranceResults['b']
RefranceResults['B']=RefranceResults['c']+1j*RefranceResults['d']
RefranceResults['C']=RefranceResults['A']*RefranceResults['B']
RefranceResults
Out[45]:
In [46]:
Peeker.clear()
Max=(2**(WordSize-1)-1); Min=-2**(WordSize-1)
ReMax=(2**(ReWordLen-1)-1); ReMin=-2**(ReWordLen-1)
ImMax=(2**(ImWordLen-1)-1); ImMin=-2**(ImWordLen-1)
A=Signal(intbv(0, min=Min, max=Max)); Peeker(A, 'A')
B=Signal(intbv(0, min=Min, max=Max)); Peeker(B, 'B')
C=Signal(intbv(0, min=Min, max=Max)); Peeker(C, 'C')
a=Signal(intbv(0, min=ReMin, max=ReMax)); Peeker(a, 'a')
b=Signal(intbv(0, min=ImMin, max=ImMax)); Peeker(b, 'b')
c=Signal(intbv(0, min=ReMin, max=ReMax)); Peeker(c, 'c')
d=Signal(intbv(0, min=ImMin, max=ImMax)); Peeker(d, 'd')
DUT=CompMulti(A, B, C, ReWordLen=ReWordLen, ImWordLen=ImWordLen)
def CompMulti_TB():
@instance
def Test():
for _, j in Refrance.iterrows():
a.next, b.next, c.next, d.next =j
print(a, b, c, d)
#A.next=concat(a, b); B.next=concat(c, d)
yield delay(1)
raise StopSimulation
return instances()
In [47]:
sim = Simulation(DUT, CompMulti_TB(), *Peeker.instances()).run()
Peeker.to_wavedrom()
In [48]:
@block
def CompMulti(A, B, C, ReWordLen=ReWordLen, ImWordLen=ImWordLen):
"""
Module to implent the complex multibtion of two conplex numbers
Inputs:
A: A 2's conplimint concat of the input complex number where
input workd is a concat according to ReIm
B: A 2's conplimint concat of the input complex number where
input workd is a concat according to ReIm
Outputs:
C: A 2's conplimint concat of the output complex number product
where ouput wordd is a concat according to ReIm
Conversion Parmters;
ReWordLen: The word len of the real part of concat word
ImWordLen: The word len of the imag part of concat word
"""
#calc the min max of the based on ReWord and ImWord length
ReMax=(2**(ReWordLen-1)-1); ReMin=-2**(ReWordLen-1)
ImMax=(2**(ImWordLen-1)-1); ImMin=-2**(ImWordLen-1)
#create the regeistors to hold the procuct results
ac=Signal(intbv(0, min=ReMin, max=ReMax))
bd=Signal(intbv(0, min=ImMin, max=ImMax))
ad=Signal(intbv(0, min=min(ReMin, ImMin), max=max(ReMax, ImMax)))
bc=Signal(intbv(0, min=min(ReMin, ImMin), max=max(ReMax, ImMax)))
@always_comb
def SepMulti():
"""
Compintion logic to sep input words into componets and
find multibilication products
"""
ac.next=A[:ReWordLen] *B[:ReWordLen]
bd.next=A[ImWordLen:]*B[ImWordLen:]
ad.next=A[:ReWordLen]*B[ImWordLen:]
bc.next=A[ImWordLen:]*B[:ReWordLen]
@always_comb
def AddSupConcat():
"""
Perfrom the real sup, im addtion, and concat based on
ReIm to final output
"""
C.next=concat(ac-bd, ad+bc)
return instances()
In [49]:
#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 [50]:
A=Signal(intbv(0, min=Min, max=Max))
B=Signal(intbv(0, min=Min, max=Max))
C=Signal(intbv(0, min=Min, max=Max))
In [51]:
DUT=CompMulti(A, B, C, ReWordLen=ReWordLen, ImWordLen=ImWordLen)
In [52]:
DUT.convert()
In [53]:
_=VerilogTextReader('CompMulti', True)
In [54]:
DUT.convert(hdl='VHDL')
In [55]:
_=VHDLTextReader('CompMulti', True)