#
# trace_gen.py
#
#
# Packet format is defined in bp_me_nonsynth_pkg.vh
# {cmd, addr, uncached, data}
#
# Trace replay mechanism adds another 4 bits at start for internal TR command


class TraceGen:

  # constructor
  def __init__(self, addr_width_p, data_width_p):
    self.addr_width_p = addr_width_p
    self.data_width_p = data_width_p
    self.packet_len = addr_width_p + data_width_p + 1 + 4 + 4

  # print header
  def print_header(self):
    s = "// generated by trace_gen.py\n"
    s += "// packet_len = " + str(self.packet_len) + "\n"
    return s

  # create packet
  # tr_op: trace replay operation
  # opcode: cache opcode
  # addr: cache address
  # data: expected load data or data to store
  # uc: uncached operation
  def create_packet(self, tr_op, opcode, addr, data, uc=0):
    packet  = tr_op + "_"
    packet += opcode + "_"
    packet += format(addr, "0"+str(self.addr_width_p)+"b") + "_"
    packet += format(uc, "01b") + "_"
    packet += format(data, "0"+str(self.data_width_p)+"b")
    packet += "\n"
    return packet

  def get_store(self, size):
    if (size == 1):
      return "1000"
    elif (size == 2):
      return "1001"
    elif (size == 4):
      return "1010"
    elif (size == 8):
      return "1011"
    else:
      raise ValueError("unexpected size for store.")

  def get_load(self, signed, size):
    if (size == 8):
      return "0011"
    else:
      if (signed):
        if (size == 1):
          return "0000"
        elif (size == 2):
          return "0001"
        elif (size == 4):
          return "0010"
        else:
          raise ValueError("unexpected size for signed load.")
      else:
        if (size == 1):
          return "0100"
        elif (size == 2):
          return "0101"
        elif (size == 4):
          return "0110"
        else:
          raise ValueError("unexpected size for unsigned load.")

  # send load
  # signed: sign extend or not
  # size: load size in bytes
  # addr: load address
  def send_load(self, signed, size, addr, uc=0):
    tr_op = "0001"
    opcode = self.get_load(signed, size)
    return self.create_packet(tr_op, opcode, addr, 0, uc)

  # receive load
  # signed: sign extend or not
  # size: load size in bytes
  # addr: load address
  def recv_load(self, signed, size, addr, data, uc=0):
    tr_op = "0010"
    opcode = self.get_load(signed, size)
    return self.create_packet(tr_op, opcode, addr, data, uc)

  # send store
  # signed: sign extend or not
  # size: store size in bytes
  # addr: store address
  def send_store(self, size, addr, data, uc=0):
    tr_op = "0001"
    opcode = self.get_store(size)
    return self.create_packet(tr_op, opcode, addr, data, uc)

  # receive store
  # signed: sign extend or not
  # size: store size in bytes
  # addr: store address
  def recv_store(self, size, addr, uc=0):
    tr_op = "0010"
    opcode = self.get_store(size)
    return self.create_packet(tr_op, opcode, addr, 0, uc)

  # wait for a number of cycles
  # num_cycles: number of cycles to wait.
  def wait(self, num_cycles):
    s = "0110_" + format(num_cycles, "0" + str(self.packet_len-4) + "b") + "\n"
    s += "0101_" + (self.packet_len-4)*"0" + "\n"
    return s

  # finish trace
  def test_finish(self):
    s = "// FINISH\n"
    s += self.wait(8)
    s += "0100_" + (self.packet_len-4)*"0" + "\n"
    return s

  def test_done(self):
    s = "// DONE\n"
    s += self.wait(8)
    s += "0011_" + (self.packet_len-4)*"0" + "\n"
    return s

  # wait for a single cycle
  def nop(self):
    return "0000_" + "0"*(self.packet_len-4) + "\n"
