\title{Combinational-Circuit Building Blocks aka medium scale integrated circuit (MSI) in myHDL} \author{Steven K Armour} \maketitle
@book{brown_vranesic_2014, place={New York, NY}, edition={3}, title={Fundamentals of digital logic with Verilog design}, publisher={McGraw-Hill}, author={Brown, Stephen and Vranesic, Zvonko G}, year={2014} },
@book{lameres_2017, title={Introduction to logic circuits & logic design with Verilog}, publisher={springer}, author={LaMeres, Brock J}, year={2017} },
@misc{peeker_simple_mux, url={http://www.xess.com/static/media/pages/peeker_simple_mux.html}, journal={Xess.com}, year={2017} },
In [1]:
import numpy as np
import pandas as pd
from sympy import *
init_printing()
from myhdl import *
from myhdlpeek import *
import random
from sympy_myhdl_tools import *
pass
let $Z$ be its output $m_k$ the minterms of the controls to the mux and $I_k$ be the input feeds to the mux; then the expression for the mux in terms of boolean algebra becomes $$Z=\sum^{2^k-1}_{k=0} m_k \cdot I_k= \text{OR}(m_k \& I_k) $$
The above is Shannon's theorem
it can be written more sincintly as: $$f(x_1, x_2, ..., x_n)=\bar{x_1}f(0, x_2, ..., x_n)+x_1 f(x_1, x_2, ..., x_n)$$ and then each $f(0, x_2, ..., x_n)$ \& $f(x_1, x_2, ..., x_n)$ is broken down as the above till the maximum number of control statement and minim inputs are needed
In [2]:
def shannon_exspanson(f, term):
"""
f is not a full equation
"""
cof0=simplify(f.subs(term, 0)); cof1=simplify(f.subs(term, 1))
return ((~term & cof0 | (term & cof1))), cof0, cof1
In [3]:
sel, x_1in, x_2in=symbols('sel, x_1in, x_2in')
let $f(m_1, m_2, m_3)$ be the total set of minterms for a 3-bit then let $m_1$ be designated the select terms then by shannon's theorem states $$f(m_1, m_2, m_3)=\bar{m_1} \cdot f_1'(0, m_2, m_3)+m_1 \cdot f_1(1, m_2, m_3)$$ in other words we want select the two subset of the f where $m_1$ is 1 or 0 and call thouse two subsets $f_1'$, $f_1$
In [4]:
x_1in, x_2in, sel=symbols('x_1in, x_2in, sel')
In [5]:
ConversionTable=pd.DataFrame()
Terms=[bin(i, 3) for i in np.arange(0, 2**3)]
ConversionTable['sel']=[int(j[0]) for j in Terms]
ConversionTable['x_1in']=[int(j[1]) for j in Terms]
ConversionTable['x_2in']=[int(j[2]) for j in Terms]
#this is shannos theorm
ConversionTable['f']=list(ConversionTable.loc[ConversionTable['sel'] == 0]['x_1in'])+list(ConversionTable.loc[ConversionTable['sel'] == 1]['x_2in'])
ConversionTable.index.name='MinMaxTerm'
ConversionTable
Out[5]:
In [6]:
POS=list(ConversionTable.loc[ConversionTable['f'] == 0].index)
SOP=list(ConversionTable.loc[ConversionTable['f'] == 1].index)
f"POS: {POS}, SOP:{SOP}"
Out[6]:
In [7]:
f, _=POS_SOPformCalcater([sel, x_1in, x_2in], SOP, POS)
f
Out[7]:
In [8]:
a, b, c=shannon_exspanson(f, sel)
f,'= via shannaon', a
Out[8]:
In [9]:
m1bar_f0=~sel&x_1in; m1bar_f0
Out[9]:
In [10]:
f0Table=ConversionTable.loc[ConversionTable['sel'] == 0].copy()
f0Table['f0']=[m1bar_f0.subs({sel:i, x_1in:j}) for i, j in zip(f0Table['sel'], f0Table['x_1in'])]
f0Table
Out[10]:
In [11]:
m1_f1=sel&x_2in; m1_f1
Out[11]:
In [12]:
f1Table=ConversionTable.loc[ConversionTable['sel'] == 1].copy()
f1Table['f1']=[m1_f1.subs({sel:i, x_2in:j}) for i, j in zip(f1Table['sel'], f1Table['x_2in'])]
f1Table
Out[12]:
and since this is the lowest order mux this case use of shannon's theorem is kind of trivial
In [13]:
def mux21_gates(sel, x_1in, x_2in, f_out):
@always_comb
def logic():
f_out.next=(sel and x_2in) or (x_1in and not sel)
return logic
In [14]:
Peeker.clear()
sel, x_1in, x_2in, f_out=[Signal(bool(0)) for _ in range(4)]
Peeker(sel, 'sel'); Peeker(x_1in, 'x_1in'); Peeker(x_2in, 'x_2in')
Peeker(f_out, 'f_out')
DUT=mux21_gates(sel, x_1in, x_2in, f_out)
inputs=[sel, x_1in, x_2in]
sim=Simulation(DUT, Combo_TB(inputs), *Peeker.instances()).run()
Peeker.to_wavedrom(start_time=0, stop_time=2*2**len(inputs), tock=True,
title='MUX 2:1 gate type simulation',
caption=f'after clock cycle {2**len(inputs)-1} ->random input')
In [15]:
MakeDFfromPeeker(Peeker.to_wavejson(start_time=0, stop_time=2**len(inputs) -1))
Out[15]:
In [16]:
sel, x_1in, x_2in, f_out=[Signal(bool(0)) for _ in range(4)]
toVerilog(mux21_gates, sel, x_1in, x_2in, f_out)
#toVHDL(mux21_gates sel, x_1in, x_2in, f_out)
_=VerilogTextReader('mux21_gates')
The following shows the Xilinx's Vivado 2016.1 RTL generated schematic of our myHDL 2:1 MUX Gate level verilog code
however as will be shown doing gate implementation of MUXs is not sustainable in HDL code and this we will have to implement behavioral syntax as follows, thouse the caveat is that this only works for standard MUXs
In [17]:
def mux21_behavioral(sel, x_1in, x_2in, f_out):
@always_comb
def logic():
if sel:
f_out.next=x_1in
else:
f_out.next=x_2in
return logic
In [18]:
Peeker.clear()
sel, x_1in, x_2in, f_out=[Signal(bool(0)) for _ in range(4)]
Peeker(sel, 'sel'); Peeker(x_1in, 'x_1in'); Peeker(x_2in, 'x_2in')
Peeker(f_out, 'f_out')
DUT=mux21_behavioral(sel, x_1in, x_2in, f_out)
inputs=[sel, x_1in, x_2in]
sim=Simulation(DUT, Combo_TB(inputs), *Peeker.instances()).run()
Peeker.to_wavedrom(start_time=0, stop_time=2*2**len(inputs), tock=True,
title='MUX 2:1 behaviroal type simulation',
caption=f'after clock cycle {2**len(inputs)-1} ->random input')
In [19]:
MakeDFfromPeeker(Peeker.to_wavejson(start_time=0, stop_time=2**len(inputs) -1))
Out[19]:
In [20]:
sel, x_1in, x_2in, f_out=[Signal(bool(0)) for _ in range(4)]
toVerilog(mux21_behavioral, sel, x_1in, x_2in, f_out)
#toVHDL(mux21_behavioral sel, x_1in, x_2in, f_out)
_=VerilogTextReader('mux21_behavioral')
The following shows the Xilinx's Vivado 2016.1 RTL generated schematic of our myHDL behavioral level 2:1 MUX's verilog code
If you try to repeat the above using a 4:1 which has four input lines and needs two select lines you can become overwhelmed quickly instead it is easier to use the following diagram to than synthesis the gate level architecture
In [21]:
def MUX41_gates(sel_1, sel_2, x_1in, x_2in, x_3in, x_4in, f_out):
@always_comb
def logic():
f_out.next=((not sel_1) and (not sel_2) and x_1in) or ((not sel_1) and ( sel_2) and x_2in) or (( sel_1) and (not sel_2) and x_3in) or (( sel_1) and ( sel_2) and x_4in)
return logic
In [22]:
Peeker.clear()
sel_1, sel_2, x_1in, x_2in, x_3in, x_4in, f_out=[Signal(bool(0)) for _ in range(7)]
Peeker(sel_1, 'sel_1'); Peeker(sel_2, 'sel_2');
Peeker(x_1in, 'x_1in'); Peeker(x_2in, 'x_2in'); Peeker(x_3in, 'x_3in'); Peeker(x_4in, 'x_4in')
Peeker(f_out, 'f_out')
DUT=MUX41_gates(sel_1, sel_2, x_1in, x_2in, x_3in, x_4in, f_out)
inputs=[sel_1, sel_2, x_1in, x_2in, x_3in, x_4in]
sim=Simulation(DUT, Combo_TB(inputs), *Peeker.instances()).run()
Peeker.to_wavedrom(start_time=0, stop_time=2*2**len(inputs), tock=True,
title='MUX 4:1 gate type simulation',
caption=f'after clock cycle {2**len(inputs)-1} ->random input')
In [23]:
MakeDFfromPeeker(Peeker.to_wavejson(start_time=0, stop_time=2**len(inputs) -1))
Out[23]:
In [24]:
sel_1, sel_2, x_1in, x_2in, x_3in, x_4in, f_out=[Signal(bool(0)) for _ in range(7)]
toVerilog(MUX41_gates, sel_1, sel_2, x_1in, x_2in, x_3in, x_4in, f_out)
#toVHDL(MUX41_gates, sel_1, sel_2, x_1in, x_2in, x_3in, x_4in, f_out)
_=VerilogTextReader('MUX41_gates')
The following shows the Xilinx's Vivado 2016.1 RTL generated schematic of our myHDL 4:1 MUX Gate level verilog code
As one can clearly see this is not sustainable and thus 'if' Statements need to be used via behavioral logic modeling
In [25]:
def MUX41_behavioral(sel_1, sel_2, x_1in, x_2in, x_3in, x_4in, f_out):
@always_comb
def logic():
if (not sel_1) and (not sel_2):
f_out.next=x_1in
elif (not sel_1) and sel_2:
f_out.next=x_2in
elif sel_1 and (not sel_2):
f_out.next=x_3in
else:
f_out.next=x_4in
return logic
In [26]:
Peeker.clear()
sel_1, sel_2, x_1in, x_2in, x_3in, x_4in, f_out=[Signal(bool(0)) for _ in range(7)]
Peeker(sel_1, 'sel_1'); Peeker(sel_2, 'sel_2');
Peeker(x_1in, 'x_1in'); Peeker(x_2in, 'x_2in'); Peeker(x_3in, 'x_3in'); Peeker(x_4in, 'x_4in')
Peeker(f_out, 'f_out')
DUT=MUX41_behavioral(sel_1, sel_2, x_1in, x_2in, x_3in, x_4in, f_out)
inputs=[sel_1, sel_2, x_1in, x_2in, x_3in, x_4in]
sim=Simulation(DUT, Combo_TB(inputs), *Peeker.instances()).run()
Peeker.to_wavedrom(start_time=0, stop_time=2*2**len(inputs), tock=True,
title='MUX 4:1 behavioral type simulation',
caption=f'after clock cycle {2**len(inputs)-1} ->random input')
In [27]:
MakeDFfromPeeker(Peeker.to_wavejson(start_time=0, stop_time=2**len(inputs) -1))
Out[27]:
In [28]:
sel_1, sel_2, x_1in, x_2in, x_3in, x_4in, f_out=[Signal(bool(0)) for _ in range(7)]
toVerilog(MUX41_behavioral, sel_1, sel_2, x_1in, x_2in, x_3in, x_4in, f_out)
#toVHDL(MUX41_behavioral, sel_1, sel_2, x_1in, x_2in, x_3in, x_4in, f_out)
_=VerilogTextReader('MUX41_behavioral')
The following shows the Xilinx's Vivado 2016.1 RTL generated schematic of our myHDL behavioral level 4:1 MUX's verilog code
taking this a step further using bytes we can implement the behavioral using vector inputs instead of single bit inputs as follows
In [29]:
sel=intbv(1)[2:]; x_in=intbv(7)[4:]; f_out=bool(0)
In [30]:
for i in x_in:
print(i)
In [31]:
for i in range(4):
print(x_in[i])
In [32]:
def MUX41_behavioralVec(sel, x_in, f_out):
@always_comb
def logic():
f_out.next=x_in[sel]
return logic
In [33]:
Peeker.clear()
sel=Signal(intbv(0)[2:]); Peeker(sel, 'sel')
x_in=Signal(intbv(0)[4:]); Peeker(x_in, 'x_in')
f_out=Signal(bool(0)); Peeker(f_out, 'f_out')
DUT=MUX41_behavioralVec(sel, x_in, f_out)
def MUX41_behavioralVec_TB(sel, x_in):
selLen=len(sel); x_inLen=len(x_in)
for i in range(2**x_inLen):
x_in.next=i
for j in range(selLen):
sel.next=j
yield delay(1)
now()
In [34]:
im=Simulation(DUT, MUX41_behavioralVec_TB(sel, x_in), *Peeker.instances()).run()
Peeker.to_wavedrom(tock=True,
title='MUX 4:1 behavioral vectype simulation')
In [35]:
MakeDFfromPeeker(Peeker.to_wavejson())
Out[35]:
In [36]:
sel=Signal(intbv(0)[2:]); x_in=Signal(intbv(0)[4:]);
f_out=Signal(bool(0))
toVerilog(MUX41_behavioralVec,sel, x_in, f_out)
#toVHDL(MUX41_behavioralVec,sel, x_in, f_out)
_=VerilogTextReader('MUX41_behavioralVec')
The following shows the Xilinx's Vivado 2016.1 RTL generated schematic of our myHDL behavioral level 4:1 MUX using Bitvecters verilog code
while shannon's theorem did not prove very much useful in designing a 4:1 MUX it's true power lies converting boolean logic expression from and or gates to MUX's
using example 4.5 from Brown & Vranesic 3rd Ed
In [37]:
w1, w2, w3=symbols('w_1, w_2, w_3')
f=(~w1&~w3)|(w1&w2)|(w1&w3)
f
Out[37]:
In [38]:
s1=w1
fp, fp0, fp1=shannon_exspanson(f, s1)
fp, fp0, fp1
Out[38]:
In [39]:
s2=w2
fpp0, fpp00, fpp01=shannon_exspanson(fp0, s2)
fpp1, fpp10, fpp11=shannon_exspanson(fp1, s2)
fpp0, fpp00, fpp01, fpp1, fpp10, fpp11
Out[39]:
In [40]:
def Shannon21MUX(s1, s2, w_3in, f_out):
@always_comb
def logic():
if (not s1) and (not s2):
f_out.next=not w_3in
elif (not s1) and ( s2):
f_out.next=not w_3in
elif ( s1) and (not s2):
f_out.next= w_3in
else:
f_out.next=1
return logic
In [41]:
Peeker.clear()
s1, s2, w_3in, f_out=[Signal(bool(0)) for _ in range(4)]
Peeker(s1, 's1'); Peeker(s2, 's2');
Peeker(w_3in, 'w_3in')
Peeker(f_out, 'f_out')
DUT=Shannon21MUX(s1, s2, w_3in, f_out)
inputs=[s1, s2, w_3in, f_out]
sim=Simulation(DUT, Combo_TB(inputs), *Peeker.instances()).run()
Peeker.to_wavedrom(start_time=0, stop_time=2*2**len(inputs), tock=True,
title='Shannon 2:1 MUX gate type simulation',
caption=f'after clock cycle {2**len(inputs)-1} ->random input')
In [42]:
MakeDFfromPeeker(Peeker.to_wavejson(start_time=0, stop_time=2**len(inputs) -1))
Out[42]:
In [43]:
s1, s2, w_3in, f_out=[Signal(bool(0)) for _ in range(4)]
toVerilog(Shannon21MUX,s1, s2, w_3in, f_out)
#toVHDL(Shannon21MUX, s1, s2, w_3in, f_out)
_=VerilogTextReader('Shannon21MUX')
The following shows the Xilinx's Vivado 2016.1 RTL generated schematic of our myHDL 2:1 Mux expansion of $f$ using Shannon's Expansion Theorom