
import string, sys

class Trace:

  def __init__(self, name, type, width):
    self.name = name
    self.type = type
    self.width = width
    self.lastvalue = 0
    self.lasttime = -0.1
    self.events = []

  def addEvent(self, t, value):
    assert(t>=self.lasttime)
    if value!=self.lastvalue:
      if t==self.lasttime:
        self.events[-1] = (t, value)
      else:
        self.events.append((t, value))
    self.lasttime = t
    self.lastvalue = value

  def writeSignal(self, fp, parent=""):
    t = self
    fp.write('SIGNAL("%s")\n'%(t.name))
    fp.write('{\n')
    fp.write('  VALUE_TYPE = NINE_LEVEL_BIT;\n')
    if t.width==1:
      fp.write('  SIGNAL_TYPE = SINGLE_BIT;\n')
    else:
      fp.write('  SIGNAL_TYPE = BUS;\n')
    fp.write('  WIDTH = %d;\n'%(t.width))
    if t.width==1:
      fp.write('  LSB_INDEX = -1;\n')
    else:
      fp.write('  LSB_INDEX = 0;\n')
    if t.type=='i' or t.type=='I':
      fp.write('  DIRECTION = INPUT;\n')
    elif t.type=='o' or t.type=='O':
      fp.write('  DIRECTION = OUTPUT;\n')
    elif t.type=='b' or t.type=='B':
      fp.write('  DIRECTION = BIDIR;\n')
    elif t.type=='r' or t.type=='R':
      fp.write('  DIRECTION = REGISTERED;\n')
    else:
      assert(0)
    fp.write('  PARENT = "%s";\n'%(parent))
    fp.write('}\n\n')
    if t.width>1:
      for i in range(t.width):
        tt = Trace("%s[%d]"%(t.name, t.width-1-i), t.type, 1)
        tt.writeSignal(fp, t.name)

  def levels(self, tend, bit=-1):
    l = []
    tlast = 0.0
    vlast = 0
    for t, value in self.events:
      if bit>=0 and value!=None:
        value = value>>bit & 1
      dt = t-tlast
      if t>0:
        l.append((dt, vlast))
      tlast = t
      vlast = value
    if tend>tlast:
      dt = tend-tlast
      l.append(dt, vlast)
    return l

  def writeTrans(self, fp, tend, bit=-1):
    t = self
    if t.width>1 and bit==-1:
      for i in range(t.width):
        t.writeTrans(fp, tend, t.width-1-i)
      return
    if t.width>1:
      name = "%s[%d]"%(t.name, bit)
    else:
      name = t.name
    levels = self.levels(tend, bit)
    bit = 0
    fp.write('TRANSITION_LIST("%s")\n'%(name))
    fp.write('{\n')
    fp.write('  NODE\n')
    fp.write('  {\n')
    fp.write('    REPEAT = 1;\n')
    for dt,v in levels:
      if v==None:
        fp.write("    LEVEL Z FOR %f;\n"%(dt))
      else:
        fp.write("    LEVEL %d FOR %f;\n"%(v, dt))
    fp.write('  }\n')
    fp.write('}\n\n')

  def writeDisplay(self, fp, index=0, bit=-1, parent=-1):
    t = self
    fp.write('DISPLAY_LINE\n')
    fp.write('{\n')
    fp.write('\tCHANNEL = "%s";\n'%(t.name))
    fp.write('\tEXPAND_STATUS = COLLAPSED;\n')
    fp.write('\tRADIX = Hexadecimal;\n')
    fp.write('\tTREE_INDEX = %d;\n'%(index))
    if t.width>1 and bit!=-1:
      fp.write('\tTREE_LEVEL = 1;\n')
      fp.write('\tPARENT = %d;\n'%(parent))
    elif t.width>1:
      fp.write('\tTREE_LEVEL = 0;\n')
      fp.write('\tCHILDREN = ')
      for i in range(t.width):
        fp.write('%d'%(index+1+i))
        if i==t.width-1:
          fp.write(';\n')
        else:
          fp.write(', ')
    else:
      fp.write('\tTREE_LEVEL = 0;\n')
    fp.write('}\n\n')
    if t.width>1 and bit==-1:
      for i in range(t.width):
        tt = Trace("%s[%d]"%(t.name, t.width-1-i), t.type, t.width)
        tt.writeDisplay(fp, index+1+i, i, index)

class Sim:

  def __init__(self):
    self.traces = {}
    self.tracelist = []
    self.clocks = []
    self.clkdict = {}
    self.nsec = 0.0

  def advTime(self, dt):
    self.nsec = self.nsec+dt

  def addClock(self, net, t0, tf, period):
    clk = (net, t0, tf, period)
    self.clocks.append(clk)
    self.clkdict[net] = clk

  def clockNext(self, net, t=None):
    if t==None:
      t = self.nsec
    clk = self.clkdict[net]
    t0 = clk[1]
    dt = clk[3]
    next = t+dt-(t-t0)%dt
    print "clknext: net=%s t=%.1f t0=%.1f dt=%.1f next=%.1f"% \
          (net, t, t0, dt, next)
    return next

  def force(self, net, value, dt=None, t=None):
    trace = self.traces[net]
    if t==None:
      t = self.nsec
    trace.addEvent(t, value)
    if dt!=None:
      self.advTime(dt)

  def flushClocks(self):
    for net, t0, tf, period in self.clocks:
      if tf==None:
        tf = self.nsec
      t = t0
      print "clock '%s' start %.2f end %.2f freq %.3f MHz"% \
            (net, t0, tf, 1000.0/period)
      while t<tf:
        self.force(net, 1, 0, t)
        self.force(net, 0, 0, t+period/2.)
        t = t+period

  def addTrace(self, name, type, width=1):
    if name in self.traces.keys():
      print "duplicate key:", name
      assert(0)
    self.traces[name] = Trace(name, type, width)
    self.tracelist.append(name)

  def writeVwf(self, fnam):
    self.flushClocks()
    fp = open(fnam, "w")
    fp.write('''HEADER
{
  VERSION = 1;
  TIME_UNIT = ns;
  PRINT_OPTIONS = "Print_options_version 6\\
range_start 0ps\n''')
    fp.write("range_end %fns\n"%(self.nsec))
    fp.write('''width 1\\
names_percentage 25\\
comments 1\\
grid_lines 1\\
time_bars 1\\
name_every_page 0\\
expand_groups 0\\
print_all 1";\n''')
    fp.write("  SIMULATION_TIME = %f;\n"%(self.nsec))
    fp.write('''  GRID_PHASE = 0.0;
  GRID_PERIOD = 10.0;
  GRID_DUTY_CYCLE = 50;
}

''')
    names = self.tracelist
    for t in names:
      self.traces[t].writeSignal(fp)
    for t in names:
      self.traces[t].writeTrans(fp, self.nsec)
    index = 0
    for t in names:
      t = self.traces[t]
      t.writeDisplay(fp, index)
      if t.width==1:
        index = index+1
      else:
        index = index+t.width+1
    fp.write('''
TIME_BAR
{
\tTIME = 0;
\tMASTER = TRUE;
}
;
    ''')

  def l1a(self, buf):
    self.advTime(self.clockNext("cdfclk")-self.nsec)
    self.advTime(132/2)
    self.force("_cdfl2b0", (buf&1)==0)
    self.force("_cdfl2b1", (buf&2)==0)
    self.force("_cdfl1a", 0)
    self.advTime(132)
    self.force("_cdfl2b0", 1)
    self.force("_cdfl2b1", 1)
    self.force("_cdfl1a", 1)

  def svtWord(self, d):
    self.force("_fifoef", 3, t=self.nsec-1000.0/30)
    self.force("din", d, 1000.0/30)
    self.force("_fifoef", 0, t=self.nsec-1000.0/30)

  def vmeRead(self, addr):
    self.force("data", None)
    self.force("addr", addr)
    self.force("vmewrite", 0, 40)
    self.force("vmeas", 1, 40)
    self.force("vmeds", 1, 40)
    self.force("vmeds", 0, 40)
    self.force("vmeas", 0, 40)

def main():
  s = Sim()
  #
  # Create traces
  #
  s.addTrace("_fifoef", "i", 2)
  s.addTrace("_fifofull", "i")
  s.addTrace("_fifoaf", "i")
  s.addTrace("fiforclk", "o")
  s.addTrace("_fiforena", "o")
  s.addTrace("_fiforeset", "o")
  s.addTrace("inphold", "o")
  s.addTrace("din", "i", 23)
  s.addTrace("_inpds", "i")
  s.addTrace("clock", "i")
  s.addTrace("dda", "r", 23)
  s.addTrace("dva", "r")
  s.addTrace("dout", "o", 23)
  s.addTrace("_dsout", "o")
  s.addTrace("outphold", "i")
  s.addTrace("framaddr", "o", 21)
  s.addTrace("framdata", "b", 16)
  s.addTrace("_framoe", "o")
  s.addTrace("_framwe", "o")
  s.addTrace("_framrp", "o")
  s.addTrace("_framwp", "o")
  s.addTrace("framsts", "i")
  s.addTrace("addr", "i", 24)
  s.addTrace("data", "b", 32)
  s.addTrace("vmewrite", "i")
  s.addTrace("vmeas", "i")
  s.addTrace("vmeds", "i")
  s.addTrace("cdfclk", "i")
  s.addTrace("_cdfbc", "i")
  s.addTrace("_cdfb0", "i")
  s.addTrace("_cdfl1a", "i")
  s.addTrace("_cdfl1r", "i")
  s.addTrace("_cdfl2b0", "i")
  s.addTrace("_cdfl2b1", "i")
  s.addTrace("_cdfhalt", "i")
  s.addTrace("_cdfrecover", "i")
  s.addTrace("_cdfl2a", "i")
  s.addTrace("cdfl1a", "r")
  s.addTrace("cdfl2b0", "r")
  s.addTrace("cdfl2b1", "r")
  s.addTrace("_svtinit", "i")
  s.addTrace("_svtfreeze", "i")
  s.addTrace("_svtspare", "i")
  s.addTrace("_svterror", "o")
  s.addTrace("_cdferror", "o")
  s.addTrace("vmejtagtck", "i")
  s.addTrace("vmejtagtdo", "i")
  s.addTrace("vmejtagtms", "i")
  s.addTrace("vmejtagtdi", "i")
  s.addTrace("id", "i", 2)
  s.addTrace("dip", "i", 3)
  s.addTrace("debug", "o", 16)
  s.addTrace("debugclk", "o")
  s.addTrace("sysclk", "i")
  s.addTrace("spare", "i", 30)
  s.addTrace("ctrlspare", "i", 3)
  s.addTrace("l2data", "i", 34)
  s.addTrace("dsinled", "o")
  s.addTrace("holdinled", "o")
  s.addTrace("dsoutled", "o")
  s.addTrace("holdoutled", "o")
  s.addTrace("runled", "o")
  s.addTrace("errorled", "o")
  s.addTrace("_dataready", "r")
  s.addTrace("_outputhold", "r")
  s.addTrace("_outputhold0", "r")
  #
  # Make input patterns
  #
  for net in [ \
      "_cdfb0", "_cdfbc", "_cdfhalt", "_cdfl1a", "_cdfl1r", "_cdfl2a", \
      "_cdfl2b0", "_cdfl2b1", "_cdfrecover", "_fifoaf", "_fifofull", \
      "_inpds", "_svtfreeze", "_svtinit", "_svtspare"]:
    s.force(net, 1, t=0)
  s.addClock("clock", 100.0, None, 1000.0/30)
  s.addClock("cdfclk", 100.0, None, 132.)
  s.advTime(500)
  s.force("_cdfrecover", 0, 100)
  s.force("_svtinit", 0, 200)
  s.force("_cdfrecover", 1, 100)
  s.force("_svtinit", 1, 200)
  s.advTime(s.clockNext("cdfclk")-s.nsec)
  s.advTime(132/2)
  s.force("_cdfb0", 0, 132)
  s.force("_cdfb0", 1)
  s.advTime(50*132)
  s.l1a(0)
  s.advTime(4*132)
  s.l1a(3)
  s.advTime(s.clockNext("clock")-s.nsec)
  s.advTime(500.0/30)
  for i in range(10):
    s.svtWord(0x200000+i)
  s.svtWord(0x600000)
  for i in range(5):
    s.svtWord(0x203000+i)
  s.svtWord(0x780000)
  s.advTime(2000)
  buf = 0
  s.vmeRead((0x10<<buf)<<2)
  s.vmeRead((0x20<<buf)<<2)
  s.vmeRead((0x30<<buf)<<2)
  for i in range(11):
    s.vmeRead((1<<15 | buf<<10 | i)<<2)
  buf = 3
  s.vmeRead((0x10<<buf)<<2)
  s.vmeRead((0x20<<buf)<<2)
  s.vmeRead((0x30<<buf)<<2)
  for i in range(6):
    s.vmeRead((1<<15 | buf<<10 | i)<<2)
  #
  # Write output vectors
  #
  s.writeVwf("gbq.vwf")

if __name__=="__main__":
  main()
