#! /usr/local/bin/python

#
# sim.py
# begun August 1998 by WJA for SVT Hit Finder board
# adapted September 2000 by WJA for COT Flash ADC board
# adapted May 2001 by WJA for SVT GhostBuster board
# adapted November 2001 by WJA for Level 2 Magical Mystery board
#
# Produces Quicksim force files to test virtual board in CAD system;
# analyzes Quicksim trace output in "log" format
#

import sys, os, math, string, whrandom
from types import *

class Qsim:
  #
  # Makes a quicksim force file
  #

  def __init__(self, outfile="qsim.ff"):
    self.cleanedup = 0
    self.outfile = outfile
    self.fp = open(self.outfile, "w")
    self.nsec = 500.0
    self.clocks = []
    self.clkdict = {}
    self.rng = whrandom.whrandom()
    self.rng.seed(3, 2, 1)
    self.eoftext = ""
    self.vmeaddr = "vmeaddress(24:0)"
    self.vmewe = "vmewrite"
    self.vmeclk = "vmeclock"
    self.vmeas = "vmeas"
    self.vmeds = None
    self.vmedata = "vmedata(15:0)"

  def __del__(self):
    self.cleanup()

  def forceinit(self, net, state):
    self.force(net, state, when=0, ftype="fixed", duration=250)
    self.force(net, state, when=250)

  def cleanup(self):
    if self.cleanedup==1:
      return
    self.cleanedup = 1
    print "Qsim.cleanup()"
    for clock in self.clocks:
      net, tstart, tend, period, type = clock
      if tend<=0:
	tend = self.nsec
      t = tstart
      print "clock '%s' start %.2f end %.2f freq %.3f MHz"% \
	    (net, tstart, tend, 1000.0/period)
      while t<tend:
	self.fp.write("force %s 1 %f -abs -%s\n"%(net, t, type))
	self.fp.write("force %s 0 %f -abs -%s\n"%(net, t+period/2., type))
	t = t+period
      self.fp.write("set clock period %f\n"%(period,))
      self.fp.write("force %s 1 %f -abs -%s -r\n"%(net, t, type))
      self.fp.write("force %s 0 %f -abs -%s -r\n"%(net, t+period/2., type))
    for i in range(100):
      self.fp.write("run %f\n"%(self.nsec/100,))
    self.fp.write(self.eoftext)
    self.fp.close()

  def vmeread(self, addr, when=None, slot=5):
    assert((addr&3)==0)
    addr = addr/4
    if when==None:
      when = self.nsec
    ns = when
    dt = 40.0
    self.force("vme_interface/A(31:27)", slot, when=ns)
    self.force(self.vmeaddr, addr, when=ns)
    if self.vmewe!=None:
      self.force(self.vmewe, 0, when=ns)
    self.force("vme_interface/P1_DS0*", 0, when=ns)
    self.force("vme_interface/P1_DS1*", 0, when=ns)
    self.force("vme_interface/P1_AS*", 0, when=ns)
    self.force("vme_interface/P1_vme_write*", 1, when=ns)
    self.force("vme_interface/P1_DS0*", 1, when=ns+9*dt)
    self.force("vme_interface/P1_DS1*", 1, when=ns+9*dt)
    self.force("vme_interface/P1_AS*", 1, when=ns+9*dt)
    if self.vmeds!=None:
      self.force(self.vmeds, 1, when=ns)
      self.force(self.vmeds, 0, when=ns+9*dt)
    if self.vmeas!=None:
      self.force(self.vmeas, 0, when=ns)
      self.force(self.vmeas, 1, when=ns+2*dt)
      self.force(self.vmeas, 0, when=ns+9*dt)
    if self.vmeclk!=None:
      self.force(self.vmeclk, 0, when=ns)
      self.force(self.vmeclk, 1, when=ns+4*dt)
      self.force(self.vmeclk, 0, when=ns+9*dt)
    self.advtime(10*dt)
    self.force("vme_interface/A(31:27)", 0, when=ns+9.5*dt)

  def vmewrite(self, addr, data, when=None, dt1=None, slot=5):
    assert((addr&3)==0)
    addr = addr/4
    if when==None:
      when = self.nsec
    ns = when
    if dt1==None:
      dt = 40.0
    else:
      dt = dt1
    self.force("vme_interface/A(31:27)", slot, when=ns)
    self.force(self.vmeaddr, addr, when=ns)
    self.force(self.vmedata, data, when=ns, duration=9*dt)
    if self.vmewe!=None:
      self.force(self.vmewe, 1, when=ns)
    self.force("vme_interface/P1_DS0*", 0, when=ns)
    self.force("vme_interface/P1_DS1*", 0, when=ns)
    self.force("vme_interface/P1_AS*", 0, when=ns)
    self.force("vme_interface/P1_vme_write*", 0, when=ns)
    self.force("vme_interface/P1_DS0*", 1, when=ns+9*dt)
    self.force("vme_interface/P1_DS1*", 1, when=ns+9*dt)
    self.force("vme_interface/P1_AS*", 1, when=ns+9*dt)
    if self.vmeds!=None:
      self.force(self.vmeds, 1, when=ns)
      self.force(self.vmeds, 0, when=ns+9*dt)
    if self.vmeas!=None:
      self.force(self.vmeas, 0, when=ns)
      self.force(self.vmeas, 1, when=ns+2*dt)
      self.force(self.vmeas, 0, when=ns+9*dt)
    if self.vmeclk!=None:
      self.force(self.vmeclk, 0, when=ns)
      self.force(self.vmeclk, 1, when=ns+4*dt)
      self.force(self.vmeclk, 0, when=ns+6*dt)
    self.advtime(10*dt)
    self.force("vme_interface/A(31:27)", 0, when=ns+9.5*dt)

  def addeoftext(self, text):
    self.eoftext = self.eoftext+text

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

  def addclk(self, net, t0, tf, dt, ftype="wired"):
    clk = (net, t0, tf, dt, ftype)
    self.clocks.append(clk)
    self.clkdict[net] = clk

  def clknext(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 clkdt(self, net):
    clk = self.clkdict[net]
    dt = clk[3]
    return dt

  def force(self, net, state, dt=0, ftype="wired", when=None, duration=None):
    if when==None:
      when = self.nsec
    if duration==None:
      if type(state)!=StringType:
        self.fp.write("force %s %x %f -abs -%s\n"%(net, state, when, ftype))
      else:
        self.fp.write("force %s %s %f -abs -%s\n"%(net, state, when, ftype))
    else:
      if type(state)!=StringType:
        self.fp.write("force %s %x %f %f -abs -%s\n"% \
                      (net, state, when, when+duration, ftype))
      else:
        self.fp.write("force %s %s %f %f -abs -%s\n"% \
                      (net, state, when, when+duration, ftype))
    if dt>0:
      self.advtime(dt)

  def unforce(self, net):
    self.fp.write("force %s Xr %f %f -abs\n"%(net, self.nsec, self.nsec+0.1))

class Wdb:
  #
  # Builds a database out of quicksim "results" force file
  #

  def __init__(self):
    self.netdict = {}
    self.aliasdict = {}

  def alias(self, namein, nameout):
    self.aliasdict[namein] = nameout

  def trace(self, net):
    t = []
    if not self.netdict.has_key(net):
      return None
    for event in self.netdict[net]:
      time = event[0]
      state = event[1]
      if type(state) is ListType:
	x = 0L
	for i in range(len(state)):
	  if state[i]==None:
	    x = None
	    break
	  elif state[i]==1:
	    x = x | (1L<<i)
	state = x
      t.append((time, state))
    return t
	
  def addbusevent(self, net, bit, state, time):
    if not self.netdict.has_key(net):
      self.netdict[net] = [(0.0, [])]
    netentry = self.netdict[net]
    lastevent = netentry[-1]
    lasttime = lastevent[0]
    if time<lasttime:
      raise ValueError, "net=%s time=%f lasttime=%f"%(net, time, lasttime)
    busstate = lastevent[1][:]
    while len(busstate)<=bit:
      busstate.append(0)
    busstate[bit] = state
    if time==lasttime:
      netentry[-1] = (time, busstate)
    else:
      netentry.append((time, busstate))

  def addevent(self, net, state, time):
    if self.aliasdict.has_key(net):
      net = self.aliasdict[net]
    bus, bit = busbit(net)
    if bit!=None:
      self.addbusevent(bus, bit, state, time)
    if not self.netdict.has_key(net):
      self.netdict[net] = [(0.0, 0)]
    netentry = self.netdict[net]
    lastevent = netentry[-1]
    lasttime = lastevent[0]
    if time<lasttime:
      raise ValueError, "net=%s time=%f lasttime=%f"%(net, time, lasttime)
    laststate = lastevent[1]
    if time==lasttime:
      netentry[-1] = (time, state)
    else:
      netentry.append((time, state))

def bytemap(b):
  lo, hi = b & 0xf, b>>4 & 0xf
  lo = (lo + hi%4) & 0xf
  return hi<<4 | lo

def mmbtest():
  # on-board oscillator
  freq = 30.0
  dt = 1000.0/freq
  # quicksim force file
  sim = Qsim("qsim_mmb.ff")
  # VME net names
  sim.vmeaddr = "vme_interface/A(26:2)"
  sim.vmedata = "vme_interface/D(31:0)"
  sim.vmewe = None
  sim.vmeas = None
  sim.vmeclk = None
  # quicksim setup
  sim.fp.write("""\

    reset state -discard
    delete trace -all
    delete force -all
    add synonym -replace P3 /i$7894
    add synonym -replace P3Buf /i$8909
    add synonym -replace P2 /VMEbus_P2
    add synonym -replace P2Buf /P2BUFFER
    add synonym -replace DOut /i$8707
    add synonym -replace DIn /i$9112
    add synonym -replace Proc /i$8706

    add trace Clock
    add trace DIn/din_data(22:0)
    add trace Din/n$3785
    add trace DataIn_Ctrl(3:0)
    add trace DataOut(22:0)
    add trace _DsOut
    add trace OutpHold
    add trace Proc/debug(15:0)
    add trace Proc/debugclk
    add trace P2_Signals(10:0)
    add trace CDF_Signals(6:0)
    add trace p2buf/p2_cdf_b0_
    add trace p2buf/p2_cdf_l1a_
    add trace p2buf/p2_cdf_l2b0
    add trace p2buf/p2_cdf_l2b1_
    add trace p2buf/p2_cdf_halt_
    add trace p2buf/p2_cdf_recover_
    add trace BuffCtrl(2:0)
    add trace P3_MBData(127:96)
    add trace P3_MBData(95:64)
    add trace P3_MBData(63:32)
    add trace P3_MBData(31:0)
    add trace P3_MBAddr(31:0)
    add trace P3_MBArb(3:0)
    add trace P3_MBCtrl(12:0)
    add trace p3buf/p3_ddone*
    add trace p3buf/rcv__ddone
    add trace p3buf/drv__ddone
    add trace p3buf/p3_ev_loaded0
    add trace p3buf/p3_ev_loaded1
    add trace p3buf/p3_ev_loaded2
    add trace p3buf/p3_ev_loaded3
    add trace p3buf/p3_done_out
    add trace p3buf/p3_mod_done0
    add trace proc/_MbReset
    add trace p3buf/p3_mbreset*
    add trace proc/_Start_Load
    add trace p3buf/p3_start_load*
    add trace proc/Buffer0
    add trace p3buf/p3_buffer0
    add trace proc/Buffer1
    add trace p3buf/p3_buffer1
    add trace proc/MbRd
    add trace p3buf/p3_mbrd
    add trace proc/_Dstrobe
    add trace p3buf/p3_dstrobe*
    add trace proc/mbdata(127:96)
    add trace proc/mbdata(95:64)
    add trace proc/mbdata(63:32)
    add trace proc/mbdata(31:0)
    add trace proc/mbaddr(31:0)
    add trace proc/MbMaster
    add trace proc/MbWrite
    add trace proc/MbControl
    add trace proc/rcv_boss
    add trace proc/rcv_bossreq
    add trace proc/rcv_bossgrin
    add trace proc/rcv_bossgrout
    add trace proc/drv_boss
    add trace proc/drv_bossreq
    add trace proc/drv_bossgrin
    add trace proc/drv_bossgrout
    
    \n""")

  #
  # initialize everything in sight
  #
  sim.forceinit("p2buf/p2_cdf_bc_", 1)
  sim.forceinit("p2buf/p2_cdf_b0_", 1)
  sim.forceinit("p2buf/p2_cdf_l1a_", 1)
  sim.forceinit("p2buf/p2_cdf_l2b0", 1)  # sic
  sim.forceinit("p2buf/p2_cdf_l2b1_", 1)
  sim.forceinit("p2buf/p2_cdf_halt_", 1)
  sim.forceinit("p2buf/p2_cdf_recover_", 1)
  sim.forceinit("p2buf/cdf_clk", 0)
  sim.forceinit("proc/rcv_boss", 0)
  sim.forceinit("proc/rcv_bossreq", 0)
  sim.forceinit("proc/rcv_bossgrin", 0)
  sim.forceinit("proc/rcv_bossgrout", 0)
  sim.forceinit("p3buf/p3_ddone*", 1)
  sim.forceinit("p3buf/p3_mbrd", 0)
  sim.forceinit("p3buf/p3_dstrobe*", 1)
  sim.forceinit("p3buf/p3_mbreset*", 1)
  sim.forceinit("p3buf/p3_start_load*", 1)
  sim.forceinit("p3buf/p3_buffer0", 0)
  sim.forceinit("p3buf/p3_buffer1", 0)
  sim.forceinit("p3buf/p3_ev_loaded0", 0)
  sim.forceinit("p3buf/p3_ev_loaded1", 0)
  sim.forceinit("p3buf/p3_ev_loaded2", 0)
  sim.forceinit("p3buf/p3_ev_loaded3", 0)
  sim.forceinit("p3buf/p3_done_out", 0)
  sim.forceinit("p3buf/p3_mod_done0", 0)
  #
  # Reset FIFO models (hmmm....)
  #
  l = [ "6184" ]
  for n in l:
    rsnet = "/din/i$"+n+"/RS"
    rennet = "/din/i$"+n+"/REN"
    wennet = "/din/i$"+n+"/WEN"
    rclknet = "/din/i$"+n+"/RCLK"
    wclknet = "/din/i$"+n+"/WCLK"
    #dnet = "/din/i$"+n+"/D(17:0)"
    #sim.force(rennet, 1, when=0, ftype="fixed", duration=750)
    sim.force(wennet, 1, when=0, ftype="fixed", duration=250)
    sim.force(rclknet, 0, when=0, ftype="fixed", duration=250)
    sim.force(wclknet, 0, when=0, ftype="fixed", duration=250)
    #sim.force(dnet, 0, when=0, ftype="fixed", duration=250)
    sim.force(rsnet, 0, when=0, ftype="fixed", duration=100)
    sim.force(rsnet, 0, when=100, ftype="fixed", duration=100)
    sim.force(rsnet, 1, when=200, ftype="fixed", duration=50)
  sim.force("/din/din_data(22:0)", 0, when=0, ftype="fixed", duration=250)
  sim.force("/proc/_fifoef0", 1, when=550, duration=250, ftype="fixed")
  sim.force("/proc/_fifoef1", 1, when=550, duration=250, ftype="fixed")
  #
  # input data
  #
  sim.force("din/n$3785", 1, ftype="fixed", when=0)
  sim.force("din/din_data(22:0)", 0, when=0)
  sim.force("proc/outphold", 0, ftype="fixed", when=0)
  #
  sim.fp.write("run 200\n")
  sim.addeoftext("""\
    //save log $HOME/results.log results -replace
    //$system("$HOME/bin/scp $HOME/results.log cdf1:fadc");\n""")
  #
  # start clocks
  #
  offset = 0.0
  sim.addclk("clock", 250+offset, 0, 33.0)
  sim.addclk("p2buf/cdf_clk", 500+offset, 0, 132.0)
  #
  #
  #
  t = sim.clknext("p2buf/cdf_clk")
  sim.advtime(t-sim.nsec)
  sim.force("p2buf/p2_cdf_recover_", 0)
  sim.advtime(132)
  sim.force("p2buf/p2_cdf_recover_", 1)
  #
  #
  #
  sim.advtime(66)
  sim.force("p3buf/p3_ddone*", 0)
  sim.advtime(66)
  sim.force("p3buf/p3_ddone*", 1)
  sim.advtime(66)
  sim.force("p3buf/rcv_boss", 1)
  sim.advtime(66)
  sim.force("p3buf/rcv_boss", 0)
  sim.advtime(66)
  sim.force("p3buf/rcv_bossreq", 1)
  sim.advtime(66)
  sim.force("p3buf/rcv_bossreq", 0)
  sim.advtime(66)
  sim.force("p3buf/rcv_bossgrin", 1)
  sim.advtime(66)
  sim.force("p3buf/rcv_bossgrin", 0)
  sim.advtime(66)
  sim.force("p3buf/rcv_bossgrout", 1)
  sim.advtime(66)
  sim.force("p3buf/rcv_bossgrout", 0)
  #
  #
  #
  net = "p3buf/p3_mbrd"; x=0
  net = "p3buf/p3_dstrobe*"; x=1
  net = "p3buf/p3_mbreset*"; x=1
  net = "p3buf/p3_start_load*"; x=1
  net = "p3buf/p3_buffer0"; x=0
  net = "p3buf/p3_buffer1"; x=0
  net = "p3buf/p3_ev_loaded0"; x=0
  net = "p3buf/p3_ev_loaded1"; x=0
  net = "p3buf/p3_ev_loaded2"; x=0
  net = "p3buf/p3_ev_loaded3"; x=0
  net = "p3buf/p3_done_out"; x=0
  net = "p3buf/p3_mod_done0"; x=0
  sim.force(net, x, when=6300)
  sim.force(net, 1-x, when=6432)
  sim.force(net, x, when=6564)
  #
  #
  #
  net = "p2buf/p2_cdf_b0_"; x=1
  net = "p2buf/p2_cdf_l2b0"; x=1
  net = "p2buf/p2_cdf_l2b1_"; x=1
  net = "p2buf/p2_cdf_halt_"; x=1
  net = "p2buf/p2_cdf_recover_"; x=1
  net = "p2buf/p2_cdf_l1a_"; x=1
  sim.force(net, x, when=6926)
  sim.force(net, 1-x, when=6926+132)
  sim.force(net, x, when=6926+2*132)
  #
  # First try capturing MBus data with MMB
  #
  t = sim.clknext("p2buf/cdf_clk")
  sim.advtime(t-sim.nsec)
  sim.advtime(16*33)
  for i in range(8):
    sim.force("p3_mbaddr(31:0)", 0x12345678*(i%2))
    sim.advtime(4*33)
  for i in range(8):
    sim.force("p3_mbdata(31:0)", 0x11112222*(i%2))
    sim.force("p3_mbdata(63:32)", 0x33334444*(i%2))
    sim.force("p3_mbdata(95:64)", 0x22221111*(i%2))
    sim.force("p3_mbdata(127:96)", 0x44443333*(i%2))
    sim.force("p3_mbaddr(31:0)", 0x12345678*(i%2))
    sim.advtime(32*33)
  #
  # Issue a L1A to buffer 1
  #
  t = sim.clknext("p2buf/cdf_clk")
  sim.force("p2buf/p2_cdf_l2b0", 0, when=t+66)
  sim.force("p2buf/p2_cdf_l2b1_", 1, when=t+66)
  sim.force("p2buf/p2_cdf_l1a_", 0, when=t+66)
  sim.force("p2buf/p2_cdf_l1a_", 1, when=t+66+132)
  #
  # Put out some data: first slowly
  #
  sim.advtime(50+0*132)
  n = 10
  n = 0
  for i in range(n):
    sim.force("din/din_data(22:0)", 0x600000+i, ftype="fixed")
    sim.force("din/n$3785", 0, ftype="fixed")
    sim.advtime(100)
    sim.force("din/n$3785", 1, ftype="fixed")
    sim.advtime(100)
  #
  # ... then with some junk interspersed
  #
  d = [ 0x600000,
        0x3fffff,
        0x000fff, 0x000000, 0x3fffff, 0x3fffff, 0x000002,
        0x3fffff, 0x000003, 0x000004, 0x000005, 0x3fffff, 0x3fffff,
        0x3fffff, 0x3fffff, 0x2000a6,
        0x000555, 0x000000, 0x000002, 0x000003, 0x000004, 0x000005,
        0x200056,
        0x600099, 0x6000aa ]
  for i in range(len(d)):
    sim.force("din/din_data(22:0)", d[i], ftype="fixed")
    sim.force("din/n$3785", 0, ftype="fixed")
    sim.advtime(14)
    sim.force("din/n$3785", 1, ftype="fixed")
    sim.advtime(14)
  #
  # ... then quickly
  #
  n = 10
  n = 0
  for i in range(n):
    sim.force("din/din_data(22:0)", 0x200000+i, ftype="fixed")
    sim.force("din/n$3785", 0, ftype="fixed")
    sim.advtime(14)
    sim.force("din/n$3785", 1, ftype="fixed")
    sim.advtime(14)
  #
  # Skip ahead a bit
  #
  sim.advtime(16*10)
  # done
  sim.cleanup()
  print "scp qsim_mmb.ff impulse.uchicago.edu:"
  os.system("scp qsim_mmb.ff impulse.uchicago.edu:")

def busbit(net):
  net = string.split(net, "(")
  if len(net)>1:
    bus = net[0]
    bit = string.split(net[1], ")")
    bit = string.atoi(bit[0])
  else:
    bus = net[0]
    bit = None
  return bus, bit

def main():
  usage = "Usage: %s mmb|results"%(sys.argv[0],)
  if len(sys.argv)<2:
    print usage
    sys.exit(-1)
  elif sys.argv[1]=="mmb":
    mmbtest()
  elif sys.argv[1]=="results":
    hfresultstest()
  else:
    print usage
    sys.exit(-1)

if __name__=="__main__":
  main()
