import numpy as np
import pandas as pd
from sympy import *
init_printing()

from myhdl import *
from myhdlpeek import *
import random

#python file of convince tools. Should be located with this notebook
from sympy_myhdl_tools import *

"""
Explanation: \title{Digital Flip-Flops with myHDL}
\author{Steven K Armour}
\maketitle
Refs
@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} }
Acknowledgments
Author of myHDL Jan Decaluwe and the author of the myHDL Peeker XESS Corp.
Draw.io
Xilinx
Python Libraries Utilized
End of explanation
"""


def DFFSyncCenter(D_in, Q_out, Qn_out, clk):
    @always(clk)
    def logic():
        Q_out.next=D_in
        Qn_out.next=not D_in
    return logic

Peeker.clear()
D_in, Q_out, Qn_out, clk=[Signal(bool(0)) for _ in range(4)]
Peeker(D_in, 'D_in'); Peeker(clk, 'clk')
Peeker(Q_out, 'Q_out'); Peeker(Qn_out, 'Qn_out')

DUT=DFFSyncCenter(D_in=D_in, Q_out=Q_out, Qn_out=Qn_out, clk=clk)

inputs=[D_in]
time=[0]

def DFFSync_TB(inputs=[]):
    
    #the # of inputs contorls everything
    Ninputs=len(inputs)
    tmax=2*2**Ninputs
    #genrate sequantil number of inputs for comparsion to known
    SequntialInputs=np.arange(2**Ninputs)
        
    
    @always(delay(1))
    def clkGen():
        clk.next = not clk

    @always(clk.negedge)
    def stimulus():
        time.append(time[-1]+1)
        #run sequantial
        try:
            #genrate binary bit repsersintion of current sequantl input
            NextSeqInput=np.binary_repr(SequntialInputs[time[-1]], width=Ninputs)

            #pass each bit into the inputs
            for i in range(Ninputs):
                inputs[i].next=bool(int(NextSeqInput[i]))

        #run the random to cheack for unexsected behavior
        except IndexError:
            NextRanInput=[random.randint(0,1) for i in range(Ninputs)]

            for i in range(Ninputs):
                inputs[i].next=NextRanInput[i]
            
        if time[-1]==tmax:
            raise StopSimulation
        
        
        
        

            

    return clkGen, stimulus

Sim=Simulation(DUT, DFFSync_TB(inputs), *Peeker.instances()).run()        
Peeker.to_wavedrom(start_time=0, tock=True)

MakeDFfromPeeker(Peeker.to_wavejson(start_time=0))

"""
Explanation: Flip-Flops vs Latches
Latches and Flip-Flops are both metastaple logic circuit tobologies in that once loaded with a state they hold that state information till that state is upset by a new state or a reset command. But the diffrance between the two is that Flip-Flops are clock controlled devices built upon Latches where as Latches are not clock dependent. And not only are Flip-Flops CLocked controlled but can be asynchronous or synchronous
D-FlipFlop
Sync
Center Clock
End of explanation
"""


toVerilog(DFFSyncCenter, D_in, Q_out, Qn_out, clk)
_=VerilogTextReader('DFFSyncCenter')

"""
Explanation: Sythinsis
End of explanation
"""


def DFFSyncPosEdge(D_in, Q_out, Qn_out, clk):
    @always(clk.posedge)
    def logic():
        Q_out.next=D_in
        Qn_out.next=not D_in
    return logic

Peeker.clear()
D_in, Q_out, Qn_out, clk=[Signal(bool(0)) for _ in range(4)]
Peeker(D_in, 'D_in'); Peeker(clk, 'clk')
Peeker(Q_out, 'Q_out'); Peeker(Qn_out, 'Qn_out')

DUT=DFFSyncPosEdge(D_in=D_in, Q_out=Q_out, Qn_out=Qn_out, clk=clk)

inputs=[D_in]
time=[0]

def DFFSync_TB(inputs=[]):
    
    #the # of inputs contorls everything
    Ninputs=len(inputs)
    tmax=2*2**Ninputs
    #genrate sequantil number of inputs for comparsion to known
    SequntialInputs=np.arange(2**Ninputs)
        
    
    @always(delay(1))
    def clkGen():
        clk.next = not clk

    @always(clk.negedge)
    def stimulus():
        time.append(time[-1]+1)
        #run sequantial
        try:
            #genrate binary bit repsersintion of current sequantl input
            NextSeqInput=np.binary_repr(SequntialInputs[time[-1]], width=Ninputs)

            #pass each bit into the inputs
            for i in range(Ninputs):
                inputs[i].next=bool(int(NextSeqInput[i]))

        #run the random to cheack for unexsected behavior
        except IndexError:
            NextRanInput=[random.randint(0,1) for i in range(Ninputs)]

            for i in range(Ninputs):
                inputs[i].next=NextRanInput[i]
            
        if time[-1]==tmax:
            raise StopSimulation
        
        
        
        

            

    return clkGen, stimulus

Sim=Simulation(DUT, DFFSync_TB(inputs), *Peeker.instances()).run()        
Peeker.to_wavedrom(start_time=0, tock=True)

"""
Explanation: !? clock in not hooked up on the wiring in this code need to figure out why
suspect it is the @always(clk)
PosEdge
End of explanation
"""


toVerilog(DFFSyncPosEdge, D_in, Q_out, Qn_out, clk)
_=VerilogTextReader('DFFSyncPosEdge')

"""
Explanation: Sythinsis
End of explanation
"""


def DFFSyncNegEdge(D_in, Q_out, Qn_out, clk):
    @always(clk.negedge)
    def logic():
        Q_out.next=D_in
        Qn_out.next=not D_in
    return logic

Peeker.clear()
D_in, Q_out, Qn_out, clk=[Signal(bool(0)) for _ in range(4)]
Peeker(D_in, 'D_in'); Peeker(clk, 'clk')
Peeker(Q_out, 'Q_out'); Peeker(Qn_out, 'Qn_out')

DUT=DFFSyncNegEdge(D_in=D_in, Q_out=Q_out, Qn_out=Qn_out, clk=clk)

inputs=[D_in]
time=[0]

def DFFSync_TB(inputs=[]):
    
    #the # of inputs contorls everything
    Ninputs=len(inputs)
    tmax=2*2**Ninputs
    #genrate sequantil number of inputs for comparsion to known
    SequntialInputs=np.arange(2**Ninputs)
        
    
    @always(delay(1))
    def clkGen():
        clk.next = not clk

    @always(clk.posedge)
    def stimulus():
        time.append(time[-1]+1)
        #run sequantial
        try:
            #genrate binary bit repsersintion of current sequantl input
            NextSeqInput=np.binary_repr(SequntialInputs[time[-1]], width=Ninputs)

            #pass each bit into the inputs
            for i in range(Ninputs):
                inputs[i].next=bool(int(NextSeqInput[i]))

        #run the random to cheack for unexsected behavior
        except IndexError:
            NextRanInput=[random.randint(0,1) for i in range(Ninputs)]

            for i in range(Ninputs):
                inputs[i].next=NextRanInput[i]
            
        if time[-1]==tmax:
            raise StopSimulation
        
        
        
        

            

    return clkGen, stimulus

Sim=Simulation(DUT, DFFSync_TB(inputs), *Peeker.instances()).run()        
Peeker.to_wavedrom(start_time=0, tock=True)

toVerilog(DFFSyncNegEdge, D_in, Q_out, Qn_out, clk)
_=VerilogTextReader('DFFSyncNegEdge')

"""
Explanation: The following shows the Xilinx's Vivado 2016.1 RTL generated schematic of our myHDL Syncrones Postive Edge D Flip-Flop's verilog code
<img style="float: center;" src="DFFSyncPosBehavioralRTLSch.PNG">
NegEdge
End of explanation
"""


