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 itertools

#https://github.com/jrjohansson/version_information
%load_ext version_information
%version_information myhdl, myhdlpeek, numpy, pandas, matplotlib, sympy, random, itertools

#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

BitVals=[0,1]

"""
Explanation: \title{Addition in myHDL (In Progress)}
\author{Steven K Armour}
\maketitle
Refrances
https://www.geeksforgeeks.org/digital-logic-carry-look-ahead-adder/
http://faculty.kfupm.edu.sa/COE/abouh/Lesson3_3.pdf
Libraries and Helper functions
End of explanation
"""


@block
def HalfAdder(x1, x2, s, c):
    """
    Half Adder
    Input:
        x1(bool): bit signal to be added
        x2(bool): bit signal to be added
    
    Output:
        s(bool): Half Adder Sum
        c(bool): Half Adder Carry

    """
    @always_comb
    def logic():
        s.next=x1 ^ x2
        c.next=x1 & x2
    
    return instances()

"""
Explanation: Half Adder
myHDL Code
End of explanation
"""


Peeker.clear()
x1=Signal(bool(0)); Peeker(x1, 'x1')
x2=Signal(bool(0)); Peeker(x2, 'x2')
s=Signal(bool(0)); Peeker(s, 's')
c=Signal(bool(0)); Peeker(c, 'c')

#generate test values
x1TVals=[i[0] for i in itertools.product(BitVals, repeat=len(BitVals))]
x2TVals=[i[1] for i in itertools.product(BitVals, repeat=len(BitVals))]

DUT=HalfAdder(x1, x2, s, c)


def HalfAdder_TB():
    """
    myHDL only Testbench for module "HalfAdder"
    """
    @instance
    def stimules():
        for i, j in zip(x1TVals, x2TVals):
            x1.next=i; x2.next=j
            yield delay(1)
        
        raise StopSimulation()
    
    return instances()

sim=Simulation(DUT, HalfAdder_TB(), *Peeker.instances()).run()

Peeker.to_wavedrom('x1', 'x2', 's', 'c')

HalfAdderData=Peeker.to_dataframe()
HalfAdderData=HalfAdderData[['x1', 'x2', 's', 'c']]
HalfAdderData

HalfAdderData['sCheck']=HalfAdderData.apply(lambda row: row['x1']^row['x2'], axis=1)
HalfAdderData['cCheck']=HalfAdderData.apply(lambda row: row['x1']&row['x2'], axis=1)
HalfAdderData

SumCheck=(HalfAdderData['s']==HalfAdderData['sCheck']).all()
CarryCheck=(HalfAdderData['c']==HalfAdderData['cCheck']).all()
print(f'Sum Result Check: {SumCheck}; Carry Result Check: {CarryCheck}')

"""
Explanation: myHDL Testing
End of explanation
"""


DUT.convert()
VerilogTextReader('HalfAdder');

"""
Explanation: Verilog Code
End of explanation
"""


#generate test values
x1TVals=[i[0] for i in itertools.product(BitVals, repeat=len(BitVals))]
x2TVals=[i[1] for i in itertools.product(BitVals, repeat=len(BitVals))]
#create single value representation of Test Inputs
x1TVal=intbv(int(''.join([str(i) for i in x1TVals]), 2))[len(x1TVals):]
x2TVal=intbv(int(''.join([str(i) for i in x2TVals]), 2))[len(x2TVals):]

print(f'x1Test: {x1TVals}, {x1TVal}, {[int(i) for i in x1TVal]}')
print(f'x2Test: {x2TVals}, {x2TVal}, {[int(i) for i in x2TVal]}')

@block
def HalfAdder_TBV():
    """
    myHDL -> Verilog Testbench for module "HalfAdder"
    """
    x1=Signal(bool(0))
    x2=Signal(bool(0))
    s=Signal(bool(0))
    c=Signal(bool(0))
    
    #test stimuli
    x1TVals=Signal(x1TVal)
    x2TVals=Signal(x2TVal)
    
    @always_comb
    def print_data():
        print(x1, x2, s, c)

    DUT=HalfAdder(x1, x2, s, c)

    @instance
    def stimules():
        for i in range(len(x1TVal)):
            x1.next=x1TVals[i]; x2.next=x2TVals[i]
            yield delay(1)
        
        raise StopSimulation()
    
    return instances()

TB=HalfAdder_TBV()
TB.convert(hdl="Verilog", initial_values=True)
VerilogTextReader('HalfAdder_TBV');

"""
Explanation: Verilog Testbench
End of explanation
"""


@block
def FullAdder(x1, x2, cin, s, c):
    """
    Full Adder 2bit+1 input from exspresion
    Input:
        x1(bool): bit signal to be added
        x2(bool): bit signal to be added
        cin(bool): carry in bit signal
    
    Output:
        s(bool): Full Adder Sum
        c(bool): Full Adder Carry

    """
    @always_comb
    def logic():
        s.next=x1^x2^cin
        c.next=(x1&x2) | (x1&cin) | (x2&cin)
    
    return instances()
    

"""
Explanation: Full Adder From Exspresion
myHDL Code
End of explanation
"""


Peeker.clear()
x1=Signal(bool(0)); Peeker(x1, 'x1')
x2=Signal(bool(0)); Peeker(x2, 'x2')
cin=Signal(bool(0)); Peeker(cin, 'cin')
s=Signal(bool(0)); Peeker(s, 's')
c=Signal(bool(0)); Peeker(c, 'c')

#generate test values
x1TVals=[i[0] for i in itertools.product(BitVals, repeat=len(BitVals)+1)]
x2TVals=[i[1] for i in itertools.product(BitVals, repeat=len(BitVals)+1)]
cinTVals=[i[2] for i in itertools.product(BitVals, repeat=len(BitVals)+1)]

DUT=FullAdder(x1, x2, cin, s, c)


def FullAdder_TB():
    """
    myHDL only Testbench for module "FullAdder"
    """
    @instance
    def stimules():
        for i, j, k in zip(x1TVals, x2TVals, cinTVals):
            x1.next=i; x2.next=j; cin.next=k
            yield delay(1)
        
        raise StopSimulation()
    
    return instances()

sim=Simulation(DUT, FullAdder_TB(), *Peeker.instances()).run()

Peeker.to_wavedrom('x1', 'x2', 'cin', 's', 'c')

FullAdderData=Peeker.to_dataframe()
FullAdderData=FullAdderData[['x1', 'x2', 'cin', 's', 'c']]
FullAdderData

FullAdderData['sCheck']=FullAdderData.apply(lambda row: row['x1']^row['x2']^row['cin'], axis=1)
FullAdderData['cCheck']=FullAdderData.apply(lambda row: (row['x1']&row['x2'])|(row['cin']*(row['x1']^row['x2'])), axis=1)
FullAdderData

SumCheck=(FullAdderData['s']==FullAdderData['sCheck']).all()
CarryCheck=(FullAdderData['c']==FullAdderData['cCheck']).all()
print(f'Sum Result Check: {SumCheck}; Carry Result Check: {CarryCheck}')

"""
Explanation: myHDL Testing
End of explanation
"""


DUT.convert()
VerilogTextReader('FullAdder');

"""
Explanation: Verilog Code
End of explanation
"""


#generate test values
x1TVals=[i[0] for i in itertools.product(BitVals, repeat=len(BitVals)+1)]
x2TVals=[i[1] for i in itertools.product(BitVals, repeat=len(BitVals)+1)]
cinTVals=[i[2] for i in itertools.product(BitVals, repeat=len(BitVals)+1)]

#create single value representation of Test Inputs
x1TVal=intbv(int(''.join([str(i) for i in x1TVals]), 2))[len(x1TVals):]
x2TVal=intbv(int(''.join([str(i) for i in x2TVals]), 2))[len(x2TVals):]
cinTVal=intbv(int(''.join([str(i) for i in cinTVals]), 2))[len(cinTVals):]

print(f'x1Test: {x1TVals}, {x1TVal}, {[int(i) for i in x1TVal]}')
print(f'x2Test: {x2TVals}, {x2TVal}, {[int(i) for i in x2TVal]}')
print(f'cinTest: {cinTVals}, {cinTVal}, {[int(i) for i in cinTVal]}')

@block
def FullAdder_TBV():
    """
    myHDL -> Verilog Testbench for module "FullAdder"
    """
    x1=Signal(bool(0))
    x2=Signal(bool(0))
    cin=Signal(bool(0))
    s=Signal(bool(0))
    c=Signal(bool(0))
    
    #test stimuli
    x1TVals=Signal(x1TVal)
    x2TVals=Signal(x2TVal)
    cinTVals=Signal(cinTVal)
    
    @always_comb
    def print_data():
        print(x1, x2, cin, s, c)

    DUT=FullAdder(x1, x2, cin, s, c)

    @instance
    def stimules():
        for i in range(len(x1TVal)):
            x1.next=x1TVals[i]; x2.next=x2TVals[i]; cin.next=cinTVals[i]
            yield delay(1)
        
        raise StopSimulation()
    
    return instances()

TB=FullAdder_TBV()
TB.convert(hdl="Verilog", initial_values=True)
VerilogTextReader('FullAdder_TBV');

"""
Explanation: Verilog Testbench
End of explanation
"""


@block
def FullAdderViaHAs(x1, x2, cin, s, c):
    """
    Full Adder 2bit+1 input from Half Adders
    Input:
        x1(bool): bit signal to be added
        x2(bool): bit signal to be added
        cin(bool): carry in bit signal
    
    Output:
        s(bool): Full Adder Sum
        c(bool): Full Adder Carry

    """
    #create ouput wires from first HA (HA1) to next HA and 
    #`CarryLogic`
    s_HA1HA2=Signal(bool(0)); c_HA1CL=Signal(bool(0))
    #create first HA and wire it
    HA1_x1x2=HalfAdder(x1=x1, x2=x2, s=s_HA1HA2, c=c_HA1CL)
    
    #create output wire for seconed HA (HA2) to `CarryLogic`
    c_HA2CL=Signal(bool(0))
    HA2_HA1cin=HalfAdder(x1=cin, x2=s_HA1HA2, s=s, c=c_HA2CL)

    @always_comb
    def CarryLogic():
        c.next= c_HA1CL|c_HA2CL
    
    return instances()
    

"""
Explanation: Full Adder via HalfAdders
myHDL Code
End of explanation
"""


Peeker.clear()
x1=Signal(bool(0)); Peeker(x1, 'x1')
x2=Signal(bool(0)); Peeker(x2, 'x2')
cin=Signal(bool(0)); Peeker(cin, 'cin')
s=Signal(bool(0)); Peeker(s, 's')
c=Signal(bool(0)); Peeker(c, 'c')

#generate test values
x1TVals=[i[0] for i in itertools.product(BitVals, repeat=len(BitVals)+1)]
x2TVals=[i[1] for i in itertools.product(BitVals, repeat=len(BitVals)+1)]
cinTVals=[i[2] for i in itertools.product(BitVals, repeat=len(BitVals)+1)]

DUT=FullAdderViaHAs(x1, x2, cin, s, c)


def FullAdderViaHAs_TB():
    """
    myHDL only Testbench for module "FullAdderViaHAs"
    """
    @instance
    def stimules():
        for i, j, k in zip(x1TVals, x2TVals, cinTVals):
            x1.next=i; x2.next=j; cin.next=k
            yield delay(1)
        
        raise StopSimulation()
    
    return instances()

sim=Simulation(DUT, FullAdder_TB(), *Peeker.instances()).run()

Peeker.to_wavedrom('x1', 'x2', 'cin', 's', 'c')

FullAdderHAData=Peeker.to_dataframe()
FullAdderHAData=FullAdderHAData[['x1', 'x2', 'cin', 's', 'c']]
FullAdderHAData

FullAdderHAData['sCheck']=FullAdderHAData.apply(lambda row: row['x1']^row['x2']^row['cin'], axis=1)
FullAdderHAData['cCheck']=FullAdderHAData.apply(lambda row: (row['x1']&row['x2'])|(row['cin']*(row['x1']^row['x2'])), axis=1)
FullAdderHAData

SumCheck=(FullAdderHAData['s']==FullAdderHAData['sCheck']).all()
CarryCheck=(FullAdderHAData['c']==FullAdderHAData['cCheck']).all()
print(f'Sum Result Check: {SumCheck}; Carry Result Check: {CarryCheck}')

(FullAdderData==FullAdderHAData).all()

"""
Explanation: myHDL Testing
End of explanation
"""


DUT.convert()
VerilogTextReader('FullAdderViaHAs');

"""
Explanation: Verilog Code
End of explanation
"""


#generate test values
x1TVals=[i[0] for i in itertools.product(BitVals, repeat=len(BitVals)+1)]
x2TVals=[i[1] for i in itertools.product(BitVals, repeat=len(BitVals)+1)]
cinTVals=[i[2] for i in itertools.product(BitVals, repeat=len(BitVals)+1)]

#create single value representation of Test Inputs
x1TVal=intbv(int(''.join([str(i) for i in x1TVals]), 2))[len(x1TVals):]
x2TVal=intbv(int(''.join([str(i) for i in x2TVals]), 2))[len(x2TVals):]
cinTVal=intbv(int(''.join([str(i) for i in cinTVals]), 2))[len(cinTVals):]

print(f'x1Test: {x1TVals}, {x1TVal}, {[int(i) for i in x1TVal]}')
print(f'x2Test: {x2TVals}, {x2TVal}, {[int(i) for i in x2TVal]}')
print(f'cinTest: {cinTVals}, {cinTVal}, {[int(i) for i in cinTVal]}')

@block
def FullAdderViaHAs_TBV():
    """
    myHDL -> Verilog Testbench for module "FullAdderViaHAs"
    """
    x1=Signal(bool(0))
    x2=Signal(bool(0))
    cin=Signal(bool(0))
    s=Signal(bool(0))
    c=Signal(bool(0))
    
    #test stimuli
    x1TVals=Signal(x1TVal)
    x2TVals=Signal(x2TVal)
    cinTVals=Signal(cinTVal)
    
    @always_comb
    def print_data():
        print(x1, x2, cin, s, c)

    DUT=FullAdderViaHAs(x1, x2, cin, s, c)

    @instance
    def stimules():
        for i in range(len(x1TVal)):
            x1.next=x1TVals[i]; x2.next=x2TVals[i]; cin.next=cinTVals[i]
            yield delay(1)
        
        raise StopSimulation()
    
    return instances()

TB=FullAdderViaHAs_TBV()
TB.convert(hdl="Verilog", initial_values=True)
VerilogTextReader('FullAdderViaHAs_TBV');

"""
Explanation: Verilog Testbench
End of explanation
"""


@block
def RippleCarry4Bit(X1, X2, cin, S, c):
    """
    4 Bit Ripple Carray Adder
    Input:
    Output:
    """
    
    S_i = [Signal(bool(0)) for _ in range(len(S))]   # Sum bit for each stage.
    
    #create intercontect wire between FA0 and FA1 and intialze FA0 and wire up
    c_FA0FA1=Signal(bool(0))
    FA0=FullAdder(x1=X1(0), x2=X2(0), cin=cin, s=S_i[0], c=c_FA0FA1)
    
    c_FA1FA2=Signal(bool(0))
    FA1=FullAdder(x1=X1(1), x2=X2(1), cin=c_FA0FA1, s=S_i[1], c=c_FA1FA2)
    
    c_FA2FA3=Signal(bool(0))
    FA2=FullAdder(x1=X1(2), x2=X2(2), cin=c_FA1FA2, s=S_i[2], c=c_FA2FA3)
    
    FA3=FullAdder(x1=X1(3), x2=X2(3), cin=c_FA2FA3, s=S_i[3], c=c)

    #concat bus wires to single bitvec wire
    @always_comb
    def ConCatSum():
        S.next=concat(S_i[3], S_i[2], S_i[1], S_i[0])

    
    return instances()

"""
Explanation: 4 Bit Ripple Carry
myHDL Code
End of explanation
"""


BitSize=4
np.random.seed(12)
X1TVals=np.random.randint(0, 2**BitSize, 2**BitSize)
X2TVals=np.random.randint(0, 2**BitSize, 2**BitSize)
cinTVals=np.random.randint(0,2,2**BitSize)

print(X1TVals)
print(X2TVals)
print(cinTVals)

Peeker.clear()
X1=Signal(intbv(0)[BitSize:]); Peeker(X1, 'X1')
X2=Signal(intbv(0)[BitSize:]); Peeker(X2, 'X2')
cin=Signal(bool(0)); Peeker(cin, 'cin')
S=Signal(intbv(0)[BitSize:]); Peeker(S, 'S')
c=Signal(bool(0)); Peeker(c, 'c')

DUT = RippleCarry4Bit(X1=X1, X2=X2, cin=cin, S=S, c=c)

def RippleCarry4Bit_TB():
    @instance
    def stimules():
        for i in range(len(X1TVals)):
            X1.next=int(X1TVals[i])
            X2.next=int(X2TVals[i])
            cin.next=int(cinTVals[i])
            yield delay(1)
        
        raise StopSimulation()

    return instances()
    
    
sim=Simulation(DUT, RippleCarry4Bit_TB(), *Peeker.instances()).run()

Peeker.to_wavedrom('cin', 'X1', 'X2', 'S', 'c')

RC4BData=Peeker.to_dataframe()
RC4BData=RC4BData[['cin', 'X1', 'X2', 'S', 'c']]
RC4BData

def RC4B_SCheck(cin, X1, X2):
    S=cin+X1+X2
    if S<16:
        return S
    else:
        return S-16
def RC4B_cCheck(cin, X1, X2):
    S=cin+X1+X2
    if S<16:
        return 0
    else:
        return 1

RC4BData['SCheck']=RC4BData.apply(lambda row: RC4B_SCheck(row['cin'], row['X1'], row['X2']), axis=1)
RC4BData['cCheck']=RC4BData.apply(lambda row: RC4B_cCheck(row['cin'], row['X1'], row['X2']), axis=1)
RC4BData

SumCheck=(RC4BData['S']==RC4BData['SCheck']).all()
CarryCheck=(RC4BData['c']==RC4BData['cCheck']).all()
print(f'Sum Result Check: {SumCheck}; Carry Result Check: {CarryCheck}')

DUT.convert()
VerilogTextReader('RippleCarry4Bit');

"""
Explanation: myHDL Testing
End of explanation
"""


X1TVal=[intbv(int(i))[BitSize:] for i in X1TVals]
X2TVal=[intbv(int(i))[BitSize:] for i in X2TVals]
cinTVal=intbv(int(''.join([str(i) for i in cinTVals]), 2))[len(cinTVals):]

print(X1TVal)
print(X2TVal)
print(cinTVal)

@block
def RippleCarry4Bit_TBV():
    """
    myHDL -> Verilog Testbench for module "FullAdderViaHAs"
    """
    X1=Signal(intbv(0)[BitSize:])
    X2=Signal(intbv(0)[BitSize:])
    cin=Signal(bool(0))
    S=Signal(intbv(0)[BitSize:])
    c=Signal(bool(0))

    
    #test stimuli
    X1TVals=[Signal(i) for i in X1TVal]
    X2TVals=[Signal(i) for i in X2TVal]
    cinTVals=Signal(cinTVal)
    
    @always_comb
    def print_data():
        print(X1, X2, cin, S, c)

    DUT=RippleCarry4Bit(X1=X1, X2=X2, cin=cin, S=S, c=c)

    @instance
    def stimules():
        for i in range(len(X1TVals)):
            X1.next=X1TVals[i]
            X2.next=X2TVals[i]
            cin.next=cinTVals[i]
            yield delay(1)
        
        raise StopSimulation()

    return instances()


TB=RippleCarry4Bit_TBV()
TB.convert(hdl="Verilog", initial_values=True)
VerilogTextReader('RippleCarry4Bit_TBV');

"""
Explanation: Verilog Testbench
Conversion Issue
Values are not beeing set to X1TVals and X1TVals in RippleCarry4Bit_TBV thus the running the simultion results in High Z values for each time stamp
End of explanation
"""


@block
def RippleCarryNBit(X1, X2, cin, S, c):
    """
    N Bit Ripple Carray Adder
    Input:
    Output:
    """
    
    S_i = [Signal(bool(0)) for _ in range(len(S))]   # Sum bit for each stage.
    c_FAiFAj=[Signal(bool(0)) for _ in range(len(S)-1)]
    
    FAStages=[]
    
    for i in range(len(S)):
        if i==0:
            FAStages.append(FullAdder(x1=X1(i), x2=X2(i), cin=cin, s=S_i[i], c=c_FAiFAj[i]))
        elif i<(len(S)-1):
            FAStages.append(FullAdder(x1=X1(i), x2=X2(i), cin=c_FAiFAj[i-1], s=S_i[i], c=c_FAiFAj[i]))
        else:
            FAStages.append(FullAdder(x1=X1(i), x2=X2(i), cin=c_FAiFAj[i-1], s=S_i[i], c=c))



    #concat bus wires to single bitvec wire
    @always_comb
    def ConCatSum():
        S.next=concat(*reversed(S_i))

    
    return instances()

"""
Explanation: N Bit Ripple Carray Adder
End of explanation
"""


BitSize=16
np.random.seed(12)
X1TVals=np.random.randint(0, 2**BitSize, 2**4)
X2TVals=np.random.randint(0, 2**BitSize, 2**4)
cinTVals=np.random.randint(0,2,2**4)

print(X1TVals)
print(X2TVals)
print(cinTVals)

Peeker.clear()
X1=Signal(intbv(0)[BitSize:]); Peeker(X1, 'X1')
X2=Signal(intbv(0)[BitSize:]); Peeker(X2, 'X2')
cin=Signal(bool(0)); Peeker(cin, 'cin')
S=Signal(intbv(0)[BitSize:]); Peeker(S, 'S')
c=Signal(bool(0)); Peeker(c, 'c')

DUT = RippleCarryNBit(X1=X1, X2=X2, cin=cin, S=S, c=c)

def RippleCarryNBit_TB():
    @instance
    def stimules():
        for i in range(len(X1TVals)):
            X1.next=int(X1TVals[i])
            X2.next=int(X2TVals[i])
            cin.next=int(cinTVals[i])
            yield delay(1)
        
        raise StopSimulation()

    return instances()
    
    
sim=Simulation(DUT, RippleCarryNBit_TB(), *Peeker.instances()).run()

Peeker.to_wavedrom('cin', 'X1', 'X2', 'S', 'c')

RCNBData=Peeker.to_dataframe()
RCNBData=RCNBData[['cin', 'X1', 'X2', 'S', 'c']]
RCNBData

def RCNB_SCheck(cin, X1, X2):
    S=cin+X1+X2
    if S<2**BitSize:
        return S
    else:
        return S-(2**BitSize)
def RCNB_cCheck(cin, X1, X2):
    S=cin+X1+X2
    if S<2**BitSize:
        return 0
    else:
        return 1

RCNBData['SCheck']=RCNBData.apply(lambda row: RCNB_SCheck(row['cin'], row['X1'], row['X2']), axis=1)
RCNBData['cCheck']=RCNBData.apply(lambda row: RCNB_cCheck(row['cin'], row['X1'], row['X2']), axis=1)
RCNBData

SumCheck=(RCNBData['S']==RCNBData['SCheck']).all()
CarryCheck=(RCNBData['c']==RCNBData['cCheck']).all()
print(f'Sum Result Check: {SumCheck}; Carry Result Check: {CarryCheck}')

"""
Explanation: myHDL Testing
End of explanation
"""


#DUT.convert()
#VerilogTextReader('RippleCarryNBit');

"""
Explanation: Verilog Code
Conversion Error
there is a conversion error in that the converter does not know how to translate as least the * unpacking operator in line 27  of RippleCarryNBit
S.next=ConcatSignal(*reversed(S_i))
There is also a high liklyhood that the converter also does not know how to handle the reversed operator
End of explanation
"""


@block
def CLAS1(x1, x2, p, g):
    """
    Carry Look Ahead Adder Stage One
    Input:
        x1(bool): x1 input
        x2(bool): x2 input
    
    Output:
        p(bool):carry propagate
        g(bool):carry generate
    
    """
    @always_comb
    def logic():
        p.next=x1^x2
        g.next=x1&x2
    return instances()

"""
Explanation: myHDL Testbench (ToDo)
To be done when Testbench conversion is improved
Carry Look Ahead 4 Bit
https://www.geeksforgeeks.org/digital-logic-carry-look-ahead-adder/
http://faculty.kfupm.edu.sa/COE/abouh/Lesson3_3.pdf
Stage One
$$p_i=A_i \oplus B_i$$
$$G_i=A_i \cdot B_i$$
myHDL Code
End of explanation
"""


Peeker.clear()
x1=Signal(bool(0)); Peeker(x1, 'x1')
x2=Signal(bool(0)); Peeker(x2, 'x2')
p=Signal(bool(0)); Peeker(p, 'p')
g=Signal(bool(0)); Peeker(g, 'g')

#generate test values
x1TVals=[i[0] for i in itertools.product(BitVals, repeat=len(BitVals))]
x2TVals=[i[1] for i in itertools.product(BitVals, repeat=len(BitVals))]

DUT=CLAS1(x1, x2, p, g)


def CLAS1_TB():
    """
    myHDL only Testbench for module "CL"
    """
    @instance
    def stimules():
        for i, j in zip(x1TVals, x2TVals):
            x1.next=i; x2.next=j
            yield delay(1)
        
        raise StopSimulation()
    
    return instances()

sim=Simulation(DUT, CLAS1_TB(), *Peeker.instances()).run()

Peeker.to_wavedrom('x1', 'x2', 'p', 'g')

CLAS1Data=Peeker.to_dataframe()
CLAS1Data=CLAS1Data[['x1', 'x2', 'p', 'g']]
CLAS1Data

CLAS1Data['pCheck']=CLAS1Data.apply(lambda row: row['x1']^row['x2'], axis=1)
CLAS1Data['gCheck']=CLAS1Data.apply(lambda row: row['x1']&row['x2'], axis=1)
CLAS1Data

pCheck=(CLAS1Data['p']==CLAS1Data['pCheck']).all()
gCheck=(CLAS1Data['g']==CLAS1Data['gCheck']).all()
print(f'p Result Check: {pCheck}; g Result Check: {gCheck}')

"""
Explanation: myHDL Testing
End of explanation
"""


DUT.convert()
VerilogTextReader('CLAS1');

"""
Explanation: Verilog Code
End of explanation
"""


#generate test values
x1TVals=[i[0] for i in itertools.product(BitVals, repeat=len(BitVals))]
x2TVals=[i[1] for i in itertools.product(BitVals, repeat=len(BitVals))]
#create single value representation of Test Inputs
x1TVal=intbv(int(''.join([str(i) for i in x1TVals]), 2))[len(x1TVals):]
x2TVal=intbv(int(''.join([str(i) for i in x2TVals]), 2))[len(x2TVals):]

print(f'x1Test: {x1TVals}, {x1TVal}, {[int(i) for i in x1TVal]}')
print(f'x2Test: {x2TVals}, {x2TVal}, {[int(i) for i in x2TVal]}')

@block
def CLAS1_TBV():
    """
    myHDL -> Verilog Testbench for module "CLAS1"
    """
    x1=Signal(bool(0))
    x2=Signal(bool(0))
    p=Signal(bool(0))
    g=Signal(bool(0))
    
    #test stimuli
    x1TVals=Signal(x1TVal)
    x2TVals=Signal(x2TVal)
    
    @always_comb
    def print_data():
        print(x1, x2, p, g)

    DUT=CLAS1(x1, x2, p, g)

    @instance
    def stimules():
        for i in range(len(x1TVal)):
            x1.next=x1TVals[i]; x2.next=x2TVals[i]
            yield delay(1)
        
        raise StopSimulation()
    
    return instances()

TB=CLAS1_TBV()
TB.convert(hdl="Verilog", initial_values=True)
VerilogTextReader('CLAS1_TBV');

"""
Explanation: Verilog Testbench
End of explanation
"""


@block
def CLAS3(p, c, s):
    """
    Carrry Look Ahead Adder Stage 3
    Input:
        p(bool):carry propagate
        c(bool): carry

    Output:
        s(bool): sum
    """
    @always_comb
    def logic():
        s.next=p^c
    return instances()

"""
Explanation: Stage Three
$$S_i=P_i \oplus C_i $$
myHDL Code
End of explanation
"""


Peeker.clear()
p=Signal(bool(0)); Peeker(p, 'p')
c=Signal(bool(0)); Peeker(c, 'c')
s=Signal(bool(0)); Peeker(s, 's')

#generate test values
pTVals=[i[0] for i in itertools.product(BitVals, repeat=len(BitVals))]
cTVals=[i[1] for i in itertools.product(BitVals, repeat=len(BitVals))]

DUT=CLAS3(p, c, s)


def CLAS3_TB():
    """
    myHDL only Testbench for module "CLAS3"
    """
    @instance
    def stimules():
        for i, j in zip(pTVals, cTVals):
            p.next=i; c.next=j
            yield delay(1)
        
        raise StopSimulation()
    
    return instances()

sim=Simulation(DUT, CLAS3_TB(), *Peeker.instances()).run()

Peeker.to_wavedrom('p', 'c', 's')

CLAS3Data=Peeker.to_dataframe()
CLAS3Data=CLAS3Data[['p', 'c', 's']]
CLAS3Data

CLAS3Data['sCheck']=CLAS3Data.apply(lambda row: row['p']^row['c'], axis=1)
CLAS3Data

sCheck=(CLAS3Data['s']==CLAS3Data['sCheck']).all()
print(f's Result Check: {sCheck}')

"""
Explanation: myHDL Testing
End of explanation
"""


DUT.convert()
VerilogTextReader('CLAS3');

"""
Explanation: Verilog Code
End of explanation
"""


#generate test values
pTVals=[i[0] for i in itertools.product(BitVals, repeat=len(BitVals))]
cTVals=[i[1] for i in itertools.product(BitVals, repeat=len(BitVals))]
#create single value representation of Test Inputs
pTVal=intbv(int(''.join([str(i) for i in pTVals]), 2))[len(pTVals):]
cTVal=intbv(int(''.join([str(i) for i in cTVals]), 2))[len(cTVals):]

print(f'pTest: {pTVals}, {pTVal}, {[int(i) for i in pTVal]}')
print(f'cTest: {cTVals}, {cTVal}, {[int(i) for i in cTVal]}')

@block
def CLAS3_TBV():
    """
    myHDL -> Verilog Testbench for module "CLAS3"
    """
    p=Signal(bool(0))
    c=Signal(bool(0))
    s=Signal(bool(0))
    
    #test stimuli
    pTVals=Signal(pTVal)
    cTVals=Signal(cTVal)
    
    @always_comb
    def print_data():
        print(p, c, s)

    DUT=CLAS3(p, c, s)

    @instance
    def stimules():
        for i in range(len(pTVal)):
            p.next=pTVals[i]; c.next=cTVals[i]
            yield delay(1)
        
        raise StopSimulation()
    
    return instances()

TB=CLAS3_TBV()
TB.convert(hdl="Verilog", initial_values=True)
VerilogTextReader('CLAS3_TBV');

"""
Explanation: Verilog Testbench
End of explanation
"""


def CLAS2B4_TestFunc(P, G, cin):
    P=[int(i) for i in reversed(bin(P,4))]
    G=[int(i) for i in reversed(bin(G,4))]
    C=[]
    for i in range(4):
        if i==0:
            C.append(int(G[i]) | (P[i]&cin))
        else:
            C.append(int(G[i]) | (P[i] &C[i-1]))
        
    C=int(''.join(str(i) for i in C[::-1]),2)
    return C
CLAS2B4_TestFunc(8,6,1)

"""
Explanation: Stage Two
$$C_i+1=G_i+P_i\cdot C_i$$
And is thus a recursion formula, so for a exsample a 4 bit  Carray Lock Ahead Adder would have the following carry exspersions
$$C_1=G_0+P_0\cdot C_{in}$$
$$C_2=G_1+P_1\cdot C_1=G_1+P_1G_0+P_1P_0C_{in}$$
$$C_3=G_2+P_2\cdot C_2=G_2+P_2G_1+P_2P_1G_0+P_2P_1P_0C_{in}$$
$$C_4=G_3+P_3\cdot C_3=G_3+P_3G_2+P_3P_2G_1+P_3P_2P_1G_0+P_3P_2P_1P_0C_{in}$$
myHDL Code (ToDo)
myHDL Testing (ToDo)
End of explanation
"""


