
import string, sys, whrandom

class Trace:

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

  # -- method for addition of event
  def addEvent(self, t, value):
    if(t<self.lasttime):
      print "here is error", self.lasttime
    assert(t>=self.lasttime)
    if value!=self.lastvalue:
      if t==self.lasttime:
        self.events[-1] = (t, value)
      else:
        self.events.append((t, value))  # at t(nsec), append value. 
    self.lasttime = t                
    self.lastvalue = value

  # -- print out signal preperties.
  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)

  # -- For print out level (0 or 1)
  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

  # -- print out transition list 
  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:
      assert(dt>0.01)
      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')


  # -- writing down DISPLAY 
  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:

  # -- constructor
  def __init__(self):
    self.traces = {}
    self.tracelist = []
    self.clocks = []
    self.clkdict = {}
    self.nsec = 0.0
    self.rng = whrandom.whrandom()
    self.rng.seed(1,2,3)
    self.svtdata = []

  # -- generate random number (MAX imax)
  def iran(self, imax):
    return int(imax*self.rng.random())

  # -- time proceed to next dt
  def advTime(self, dt):
    self.nsec = self.nsec+dt

  # -- add clock
  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

  # -- set signal level to value from t during dt
  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)


  # -- writing down clock properties and making clock pattern
  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

  # -- add name to tracelist (name is pin name, type is i.e. INPUT, width is
  # -- number of bits.)
  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)

  # -- writing down creating time schematics to vwf-file.
  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.svtdata.append(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 vmeWrite(self, addr, data):
    self.force("data", data)
    self.force("addr", addr)
    self.force("vmewrite", 1, 40)
    self.force("vmeas", 1, 40)
    self.force("vmeds", 1, 40)
    self.force("vmeds", 0, 40)
    self.force("vmeas", 0, 40)

  def addTrack(self):
    words = []
    #
    # Word 0
    #
    phi = self.iran(8192)
    #for i in range(100):
    #  phi = self.iran(1044)
    #  if phi>1000:
    #    break	
    zin = self.iran(6)
    zout = zin
    if self.iran(5)==0:
      zout = zout ^ 1
    word = phi<<0 | zin<<13 | zout<<16 | 1<<20
    words.append(word)
    #
    # Word 1
    #
    dvx = self.iran(512)
    sdvx = self.iran(2)
    crv = self.iran(256)
    scrv = self.iran(2)
    word = dvx<<0 | sdvx<<9 | crv<<10 | scrv<<18
    words.append(word)
    #
    # Word 2
    #
    road = self.iran(0xffff)
    ##wedge = self.iran(12)    ##should be correlated to phi
    wed_diffflag = self.iran(2)
    edge= [342, 1024, 1707, 2405, 3102, 3805, 4488, 
          5170, 5803, 6496, 7168, 7851]
    if phi<=edge[0]:
	wedge = 0
	if wed_diffflag==1:
	   if phi>(edge[1]-20):
	      wedge = 1
    elif phi<=edge[1]:
	wedge = 1
	if wed_diffflag==1:
	   if phi<edge[0]+20:
	      wedge = 0
           if phi>edge[1]-20:
	      wedge = 2
    elif phi<edge[2]:
        wedge = 2
	if wed_diffflag==1:
	   if phi<edge[1]+20:
	      wedge = 1
           if phi>edge[2]-20:
	      wedge = 3
    elif phi<edge[3]:
        wedge = 3
	if wed_diffflag==1:
	   if phi<edge[2]+20:
	      wedge = 2
           if phi>edge[3]-20:
	      wedge = 4
    elif phi<edge[4]:
        wedge = 4
	if wed_diffflag==1:
	   if phi<edge[3]+20:
	      wedge = 3
           if phi>edge[4]-20:
	      wedge = 5
    elif phi<edge[5]:
        wedge = 5
	if wed_diffflag==1:
	   if phi<edge[4]+20:
	      wedge = 4
           if phi>edge[5]-20:
	      wedge = 6
    elif phi<edge[6]:
        wedge = 6
	if wed_diffflag==1:
	   if phi<edge[5]+20:
	      wedge = 5
           if phi>edge[6]-20:
	      wedge = 7
    elif phi<edge[7]:
        wedge = 7
	if wed_diffflag==1:
	   if phi<edge[6]+20:
	      wedge = 6
           if phi>edge[7]-20:
	      wedge = 5
    elif phi<edge[8]:
        wedge = 8
	if wed_diffflag==1:
	   if phi<edge[7]+20:
	      wedge = 7
           if phi>edge[8]-20:
	      wedge = 9
    elif phi<edge[9]:
        wedge = 9
	if wed_diffflag==1:
	   if phi<edge[8]+20:
	      wedge = 8
           if phi>edge[9]-20:
	      wedge = 10
    elif phi<edge[10]:
        wedge = 10
	if wed_diffflag==1:
	   if phi<edge[9]+20:
	      wedge = 9
           if phi>edge[10]-20:
	      wedge = 11
    elif phi<edge[11]:
        wedge = 11
	if wed_diffflag==1:
	   if phi<edge[10]+20:
	      wedge = 10
           if phi>edge[11]-20:
	      wedge = 0
    else:
	wedge = 0
    print "%d, %d"% (phi, wedge)
	 
    word = road<<0 | wedge<<17
    words.append(word)
    #
    # Word 3
    #
    x0 = self.iran(0x3ff)
    x1 = self.iran(0x3ff)
    word = x0<<0 | x1<<10
    words.append(word)
    #
    # Word 4
    #
    x2 = self.iran(0x3ff)
    x3 = self.iran(0x3ff)
    word = x2<<0 | x3<<10
    words.append(word)
    #
    # Word 5
    #
    x4 = self.iran(0x3ff)
    csq = self.iran(35)
    word = x4<<0 | csq<<10
    words.append(word)
    #
    # Word 6
    #
    xftnum = self.iran(288)
    xftnum = self.iran(3)  # make things more interesting
    fitqual = self.iran(15)  # may want to tune this later
    tfstat = fitqual<<0 | self.iran(0xff)<<3
    word = xftnum<<0 | tfstat<<9 | 1<<21
    words.append(word)
    #
    # Now add the track to the simulation input
    #
    for word in words:
      self.svtWord(word)

  def addEvent(self):
    ntracks = self.iran(10)
    for i in range(ntracks):
      self.addTrack()
    bc = self.iran(159)
    self.svtWord(0x600000 | bc)
    print "event: ntracks=%d bc=%02f"%(ntracks, bc)

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("ddb", "r", 23)
  s.addTrace("dvb", "r")
  s.addTrace("ddc", "r", 23)
  s.addTrace("dvc", "r")
  s.addTrace("ddd", "r", 23)
  s.addTrace("dvd", "r")
  s.addTrace("wcd", "r", 3)
  s.addTrace("dde", "r", 23)
  s.addTrace("dve", "r")
  s.addTrace("ddf", "r", 23)
  s.addTrace("dvf", "r")
  s.addTrace("ddg", "r", 23)
  s.addTrace("dvg", "r")
  s.addTrace("ddh", "r", 23)
  s.addTrace("dvh", "r")
  s.addTrace("ddi", "r", 23)
  s.addTrace("dvi", "r")
  s.addTrace("ddj", "r", 23)
  s.addTrace("dvj", "r")
  s.addTrace("ddk", "r", 23)
  s.addTrace("dvk", "r")
  s.addTrace("dout", "o", 23)
  s.addTrace("_dsout", "o")
  s.addTrace("fsm", "r")
  s.addTrace("outphold", "i")
  s.addTrace("framaddr", "o", 21)
  s.addTrace("framdata", "b", 16)
  s.addTrace("phifixf", "r", 11)
  s.addTrace("phifixg", "r", 13)
  s.addTrace("sincor", "r")
  s.addTrace("spare", "b", 30)
  s.addTrace("bpb", "r", 20)
  s.addTrace("ycosg", "r", 18)
  s.addTrace("xsing", "r", 18)
  s.addTrace("cosphie", "r", 9)
  s.addTrace("sinphie", "r", 9)
  s.addTrace("phib", "r", 13)
  s.addTrace("debug", "o", 16)
  s.addTrace("ipsub", "r", 18)
  s.addTrace("sinaddh", "r") 
  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("debugclk", "o")
  s.addTrace("sysclk", "i")
  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
  #
  s.force("spare", None)

  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(100)
  s.vmeWrite(2<<2, 1<<30)  # ctrl[62] => erase only 1st 32 words
  s.advTime(500)
  #
  # Send CDF and SVT backplane signals for H/R/R ...
  #
  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)
  #
  # ... and for a couple of L1 accepts
  #
  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)
  #
  # Send SVT data in
  #
  for i in range(5):
    s.addEvent()
  s.advTime(20000)
  #
  # Force some FRAM data values
  #
  s.force("framdata", 0x0000, t=0)
  s.force("framdata", 0x400, t=11475+110)
  s.force("framdata", 0x701, t=11710+110)
  s.force("framdata", 0x062, t=11940+110)
  s.force("framdata", 0x573, t=12175+110)
  s.force("framdata", 0x000, t=12410+110)    
  s.force("framdata", 0x011, t=14180+110)
  s.force("framdata", 0x721, t=14410+110)
  s.force("framdata", 0x6D4, t=14640+110)
  s.force("framdata", 0x6F8, t=14880+110)
  s.force("framdata", 0x011, t=15110+110)
  s.force("framdata", 0x012, t=16575+110)
  s.force("framdata", 0x291, t=16810+110)
  s.force("framdata", 0x283, t=17040+110)
  s.force("framdata", 0x031, t=17280+110)
  s.force("framdata", 0x2B1, t=17510+110)
  s.force("framdata", 0x2A0, t=17740+110)
  s.force("framdata", 0x531, t=17975+110)

  s.force("spare", 0x06FFDFFF, 55, t=11475+120)
  s.force("spare", None, t=11475+120+55)
  s.force("spare", 0x31745FFF, 55, t=11710+120)
  s.force("spare", None, t=11710+120+55)
  s.force("spare", 0x3EE5BFFF, 55, t=11940+120)
  s.force("spare", None, t=11940+120+55)
  s.force("spare", 0x155E2FFF, 55, t=12175+120)
  s.force("spare", None, t=12175+120+55)
  s.force("spare", 0x00DFFFFF, 55, t=12410+120)    
  s.force("spare", None, t=12410+120+55)
  s.force("spare", 0x05BFFFFF, 55, t=14180+120)    
  s.force("spare", None, t=14180+120+55)
  s.force("spare", 0x3F646FFF, 55, t=14410+120)
  s.force("spare", None, t=14410+120+55)
  s.force("spare", 0x0D1F5FFF, 55, t=14640+120)
  s.force("spare", None, t=14640+120+55)
  s.force("spare", 0x2D569FFF, 55, t=14880+120)
  s.force("spare", None, t=14880+120+55)
  s.force("spare", 0x049FEFFF, 55, t=15110+120)
  s.force("spare", None, t=15110+120+55)
  s.force("spare", 0x08FFAFFF, 55, t=16575+120)
  s.force("spare", None, t=16575+120+55)
  s.force("spare", 0x34F20FFF, 55, t=16810+120)
  s.force("spare", None, t=16810+120+55)
  s.force("spare", 0x3C8A4FFF, 55, t=17040+120)
  s.force("spare", None, t=17040+120+55)
  s.force("spare", 0x247A5FFF, 55, t=17280+120)
  s.force("spare", None, t=17280+120+55)
  s.force("spare", 0x1ADD0FFF, 55, t=17510+120)
  s.force("spare", None, t=17510+120+55)
  s.force("spare", 0x21BB3FFF, 55, t=17740+120)
  s.force("spare", None, t=17740+120+55)
  s.force("spare", 0x3FC21FFF, 55, t=17975+120)
  s.force("spare", None, t=17975+120+55)
  #
  # Do some VME I/O (not relevant for 'processor' design)
  #
  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)
  s.vmeRead((0x0<<buf)<<2)
  for i in range(6):
    s.vmeRead((1<<15 | buf<<10 | i)<<2)
  s.advTime(1.0)
  #
  # Write output vectors
  #
  s.writeVwf("gbq.vwf")
  #
  # Print out SVT input data
  #
  print "svtdata:",
  for x in s.svtdata:
    print "%6.6x"%(x),
  print ""
  fp = open("gbinput.dat", "w")
  for x in s.svtdata:
    fp.write("%6.6x\n"%(x))
  del fp

if __name__=="__main__":
  main()
