#!/usr/bin/python

import math
import sys
import tokenize

namelist = []
match = {}
mask = {}
arguments = {}

arglut = {}
arglut['rd'] = (11,7)
arglut['rs1'] = (19,15)
arglut['rs2'] = (24,20)
arglut['rs3'] = (31,27)
arglut['aqrl'] = (26,25)
arglut['pred'] = (27,24)
arglut['succ'] = (23,20)
arglut['rm'] = (14,12)
arglut['imm20'] = (31,12)
arglut['jimm20'] = (31,12)
arglut['imm12'] = (31,20)
arglut['imm12hi'] = (31,25)
arglut['bimm12hi'] = (31,25)
arglut['imm12lo'] = (11,7)
arglut['bimm12lo'] = (11,7)
arglut['shamt'] = (25,20)
arglut['shamtw'] = (24,20)
arglut['vseglen'] = (31,29)

arglut['crd'] = (9,5)
arglut['crs2'] = (9,5)
arglut['crs1'] = (14,10)
arglut['crds'] = (15,13)
arglut['crs2s'] = (15,13)
arglut['crs2bs'] = (7,5)
arglut['crs1s'] = (12,10)
arglut['cimm6'] = (15,10)
arglut['cimm10'] = (14,5)
arglut['cimm5'] = (9,5)

opcode_base = 0
opcode_size = 7
funct_base = 12
funct_size = 3

def binary(n, digits=0):
  rep = bin(n)[2:]
  return rep if digits == 0 else ('0' * (digits - len(rep))) + rep

def make_disasm_table(match,mask):
  print '/* Automatically generated by parse-opcodes */'
  for name,match in match.iteritems():
    name2 = name.upper().replace('.','_')
    print '#define MATCH_%s %s' % (name2, hex(match))
    print '#define  MASK_%s %s' % (name2, hex(mask[name]))

def make_isasim(match, mask):
  for name in match.iterkeys():
    name2 = name.replace('.','_')
    print 'DECLARE_INSN(%s, 0x%x, 0x%x)' % (name2, match[name], mask[name])

def yank(num,start,len):
  return (num >> start) & ((1 << len) - 1)

def str_arg(arg0,name,match,arguments):
  if arg0 in arguments:
    return name or arg0
  else:
    start = arglut[arg0][1]
    len = arglut[arg0][0] - arglut[arg0][1] + 1
    return binary(yank(match,start,len),len)

def str_inst(name,arguments):
  ret = name.upper() + ' '
  if 'imm12hi' in arguments and 'imm12lo' in arguments:
    arguments.remove('imm12hi')
    arguments.remove('imm12lo')
    arguments.append('imm')
  if 'bimm12hi' in arguments and 'bimm12lo' in arguments:
    arguments.remove('bimm12hi')
    arguments.remove('bimm12lo')
    arguments.append('imm')
  if 'imm12' in arguments:
    arguments.remove('imm12')
    arguments.append('imm')
  if 'imm20' in arguments:
    arguments.remove('imm20')
    arguments.append('imm')
  if 'jimm20' in arguments:
    arguments.remove('jimm20')
    arguments.append('imm')
  if 'shamtw' in arguments:
    arguments.remove('shamtw')
    arguments.append('shamt')
  if 'aqrl' in arguments:
    arguments.remove('aqrl')
  if 'pred' in arguments:
    arguments.remove('pred')
  if 'succ' in arguments:
    arguments.remove('succ')
  for idx in range(len(arguments)):
    ret = ret + arguments[idx]
    if idx != len(arguments)-1:
      ret = ret + ','
  return ret

def print_unimp_type(name,match,arguments):
  print """
&
\\multicolumn{10}{|c|}{%s} & %s \\\\
\\cline{2-11}
  """ % \
  ( \
    '0'*32, \
    'UNIMP' \
  )

def print_u_type(name,match,arguments):
  print """
&
\\multicolumn{8}{|c|}{%s} &
\\multicolumn{1}{c|}{%s} &
\\multicolumn{1}{c|}{%s} & %s \\\\
\\cline{2-11}
  """ % \
  ( \
    str_arg('imm20','imm[31:12]',match,arguments), \
    str_arg('rd','',match,arguments), \
    binary(yank(match,opcode_base,opcode_size),opcode_size), \
    str_inst(name,arguments) \
  )

def print_uj_type(name,match,arguments):
  print """
&
\\multicolumn{8}{|c|}{%s} &
\\multicolumn{1}{c|}{%s} &
\\multicolumn{1}{c|}{%s} & %s \\\\
\\cline{2-11}
  """ % \
  ( \
    str_arg('jimm20','imm[20$\\vert$10:1$\\vert$11$\\vert$19:12]',match,arguments), \
    str_arg('rd','',match,arguments), \
    binary(yank(match,opcode_base,opcode_size),opcode_size), \
    str_inst(name,arguments) \
  )

def print_s_type(name,match,arguments):
  print """
&
\\multicolumn{4}{|c|}{%s} &
\\multicolumn{2}{c|}{%s} &
\\multicolumn{1}{c|}{%s} &
\\multicolumn{1}{c|}{%s} &
\\multicolumn{1}{c|}{%s} &
\\multicolumn{1}{c|}{%s} & %s \\\\
\\cline{2-11}
  """ % \
  ( \
    str_arg('imm12hi','imm[11:5]',match,arguments), \
    str_arg('rs2','',match,arguments), \
    str_arg('rs1','',match,arguments), \
    binary(yank(match,funct_base,funct_size),funct_size), \
    str_arg('imm12lo','imm[4:0]',match,arguments), \
    binary(yank(match,opcode_base,opcode_size),opcode_size), \
    str_inst(name,arguments) \
  )

def print_sb_type(name,match,arguments):
  print """
&
\\multicolumn{4}{|c|}{%s} &
\\multicolumn{2}{c|}{%s} &
\\multicolumn{1}{c|}{%s} &
\\multicolumn{1}{c|}{%s} &
\\multicolumn{1}{c|}{%s} &
\\multicolumn{1}{c|}{%s} & %s \\\\
\\cline{2-11}
  """ % \
  ( \
    str_arg('bimm12hi','imm[12$\\vert$10:5]',match,arguments), \
    str_arg('rs2','',match,arguments), \
    str_arg('rs1','',match,arguments), \
    binary(yank(match,funct_base,funct_size),funct_size), \
    str_arg('bimm12lo','imm[4:1$\\vert$11]',match,arguments), \
    binary(yank(match,opcode_base,opcode_size),opcode_size), \
    str_inst(name,arguments) \
  )

def print_i_type(name,match,arguments):
  print """
&
\\multicolumn{6}{|c|}{%s} &
\\multicolumn{1}{c|}{%s} &
\\multicolumn{1}{c|}{%s} &
\\multicolumn{1}{c|}{%s} &
\\multicolumn{1}{c|}{%s} & %s \\\\
\\cline{2-11}
  """ % \
  ( \
    str_arg('imm12','imm[11:0]',match,arguments), \
    str_arg('rs1','',match,arguments), \
    binary(yank(match,funct_base,funct_size),funct_size), \
    str_arg('rd','',match,arguments), \
    binary(yank(match,opcode_base,opcode_size),opcode_size), \
    str_inst(name,arguments) \
  )

def print_ish_type(name,match,arguments):
  print """
&
\\multicolumn{3}{|c|}{%s} &
\\multicolumn{3}{c|}{%s} &
\\multicolumn{1}{c|}{%s} &
\\multicolumn{1}{c|}{%s} &
\\multicolumn{1}{c|}{%s} &
\\multicolumn{1}{c|}{%s} & %s \\\\
\\cline{2-11}
  """ % \
  ( \
    binary(yank(match,26,6),6), \
    str_arg('shamt','shamt',match,arguments), \
    str_arg('rs1','',match,arguments), \
    binary(yank(match,funct_base,funct_size),funct_size), \
    str_arg('rd','',match,arguments), \
    binary(yank(match,opcode_base,opcode_size),opcode_size), \
    str_inst(name,arguments) \
  )

def print_ishw_type(name,match,arguments):
  print """
&
\\multicolumn{4}{|c|}{%s} &
\\multicolumn{2}{c|}{%s} &
\\multicolumn{1}{c|}{%s} &
\\multicolumn{1}{c|}{%s} &
\\multicolumn{1}{c|}{%s} &
\\multicolumn{1}{c|}{%s} & %s \\\\
\\cline{2-11}
  """ % \
  ( \
    binary(yank(match,25,7),7), \
    str_arg('shamtw','shamt',match,arguments), \
    str_arg('rs1','',match,arguments), \
    binary(yank(match,funct_base,funct_size),funct_size), \
    str_arg('rd','',match,arguments), \
    binary(yank(match,opcode_base,opcode_size),opcode_size), \
    str_inst(name,arguments) \
  )

def print_r_type(name,match,arguments):
  print """
&
\\multicolumn{4}{|c|}{%s} &
\\multicolumn{2}{c|}{%s} &
\\multicolumn{1}{c|}{%s} &
\\multicolumn{1}{c|}{%s} &
\\multicolumn{1}{c|}{%s} &
\\multicolumn{1}{c|}{%s} & %s \\\\
\\cline{2-11}
  """ % \
  ( \
    binary(yank(match,25,7),7), \
    str_arg('rs2','',match,arguments), \
    str_arg('rs1','',match,arguments), \
    str_arg('rm','',match,arguments), \
    str_arg('rd','',match,arguments), \
    binary(yank(match,opcode_base,opcode_size),opcode_size), \
    str_inst(name,arguments) \
  )

def print_r4_type(name,match,arguments):
  print """
&
\\multicolumn{2}{|c|}{%s} &
\\multicolumn{2}{c|}{%s} &
\\multicolumn{2}{c|}{%s} &
\\multicolumn{1}{c|}{%s} &
\\multicolumn{1}{c|}{%s} &
\\multicolumn{1}{c|}{%s} &
\\multicolumn{1}{c|}{%s} & %s \\\\
\\cline{2-11}
  """ % \
  ( \
    str_arg('rs3','',match,arguments), \
    binary(yank(match,25,2),2), \
    str_arg('rs2','',match,arguments), \
    str_arg('rs1','',match,arguments), \
    str_arg('rm','',match,arguments), \
    str_arg('rd','',match,arguments), \
    binary(yank(match,opcode_base,opcode_size),opcode_size), \
    str_inst(name,arguments) \
  )

def print_amo_type(name,match,arguments):
  print """
&
\\multicolumn{2}{|c|}{%s} &
\\multicolumn{1}{c|}{aq} &
\\multicolumn{1}{c|}{rl} &
\\multicolumn{2}{c|}{%s} &
\\multicolumn{1}{c|}{%s} &
\\multicolumn{1}{c|}{%s} &
\\multicolumn{1}{c|}{%s} &
\\multicolumn{1}{c|}{%s} & %s \\\\
\\cline{2-11}
  """ % \
  ( \
    binary(yank(match,27,5),5), \
    str_arg('rs2','',match,arguments), \
    str_arg('rs1','',match,arguments), \
    binary(yank(match,funct_base,funct_size),funct_size), \
    str_arg('rd','',match,arguments), \
    binary(yank(match,opcode_base,opcode_size),opcode_size), \
    str_inst(name,arguments) \
  )

def print_fence_type(name,match,arguments):
  print """
&
\\multicolumn{2}{|c|}{%s} &
\\multicolumn{3}{c|}{%s} &
\\multicolumn{1}{c|}{%s} &
\\multicolumn{1}{c|}{%s} &
\\multicolumn{1}{c|}{%s} &
\\multicolumn{1}{c|}{%s} &
\\multicolumn{1}{c|}{%s} & %s \\\\
\\cline{2-11}
  """ % \
  ( \
    binary(yank(match,28,4),4), \
    str_arg('pred','pred',match,arguments), \
    str_arg('succ','',match,arguments), \
    str_arg('rs1','',match,arguments), \
    binary(yank(match,funct_base,funct_size),funct_size), \
    str_arg('rd','',match,arguments), \
    binary(yank(match,opcode_base,opcode_size),opcode_size), \
    str_inst(name,arguments) \
  )

def print_header(*types):
  print """
\\newpage

\\begin{table}[p]
\\begin{small}
\\begin{center}
\\begin{tabular}{p{0in}p{0.4in}p{0.05in}p{0.05in}p{0.05in}p{0.05in}p{0.4in}p{0.6in}p{0.4in}p{0.6in}p{0.7in}l}
& & & & & & & & & & \\\\
                      &
\\multicolumn{1}{l}{\\instbit{31}} &
\\multicolumn{1}{r}{\\instbit{27}} &
\\instbit{26} &
\\instbit{25} &
\\multicolumn{1}{l}{\\instbit{24}} &
\\multicolumn{1}{r}{\\instbit{20}} &
\\instbitrange{19}{15} &
\\instbitrange{14}{12} &
\\instbitrange{11}{7} &
\\instbitrange{6}{0} \\\\
\\cline{2-11}
"""
  if 'r' in types:
    print """
&
\\multicolumn{4}{|c|}{funct7} &
\\multicolumn{2}{c|}{rs2} &
\\multicolumn{1}{c|}{rs1} &
\\multicolumn{1}{c|}{funct3} &
\\multicolumn{1}{c|}{rd} &
\\multicolumn{1}{c|}{opcode} & R-type \\\\
\\cline{2-11}
"""
  if 'r4' in types:
    print """
&
\\multicolumn{2}{|c|}{rs3} &
\\multicolumn{2}{c|}{funct2} &
\\multicolumn{2}{c|}{rs2} &
\\multicolumn{1}{c|}{rs1} &
\\multicolumn{1}{c|}{funct3} &
\\multicolumn{1}{c|}{rd} &
\\multicolumn{1}{c|}{opcode} & R4-type \\\\
\\cline{2-11}
  """
  if 'i' in types:
    print """
&
\\multicolumn{6}{|c|}{imm[11:0]} &
\\multicolumn{1}{c|}{rs1} &
\\multicolumn{1}{c|}{funct3} &
\\multicolumn{1}{c|}{rd} &
\\multicolumn{1}{c|}{opcode} & I-type \\\\
\\cline{2-11}
"""
  if 's' in types:
    print """
&
\\multicolumn{4}{|c|}{imm[11:5]} &
\\multicolumn{2}{c|}{rs2} &
\\multicolumn{1}{c|}{rs1} &
\\multicolumn{1}{c|}{funct3} &
\\multicolumn{1}{c|}{imm[4:0]} &
\\multicolumn{1}{c|}{opcode} & S-type \\\\
\\cline{2-11}
"""
  if 'sb' in types:
    print """
&
\\multicolumn{4}{|c|}{imm[12$\\vert$10:5]} &
\\multicolumn{2}{c|}{rs2} &
\\multicolumn{1}{c|}{rs1} &
\\multicolumn{1}{c|}{funct3} &
\\multicolumn{1}{c|}{imm[4:1$\\vert$11]} &
\\multicolumn{1}{c|}{opcode} & SB-type \\\\
\\cline{2-11}
"""
  if 'u' in types:
    print """
&
\\multicolumn{8}{|c|}{imm[31:12]} &
\\multicolumn{1}{c|}{rd} &
\\multicolumn{1}{c|}{opcode} & U-type \\\\
\\cline{2-11}
"""
  if 'uj' in types:
    print """
&
\\multicolumn{8}{|c|}{imm[20$\\vert$10:1$\\vert$11$\\vert$19:12]} &
\\multicolumn{1}{c|}{rd} &
\\multicolumn{1}{c|}{opcode} & UJ-type \\\\
\\cline{2-11}
"""

def print_subtitle(title):
  print """
&
\\multicolumn{10}{c}{} & \\\\
&
\\multicolumn{10}{c}{\\bf %s} & \\\\
\\cline{2-11}
  """ % title

def print_footer(caption):
  print """
\\end{tabular}
\\end{center}
\\end{small}
%s
\\label{instr-table}
\\end{table}
  """ % (caption and '\\caption{Instruction listing for RISC-V}' or '')

def print_inst(n):
  if n == 'fence' or n == 'fence.i':
    print_fence_type(n, match[n], arguments[n])
  elif 'aqrl' in arguments[n]:
    print_amo_type(n, match[n], arguments[n])
  elif 'shamt' in arguments[n]:
    print_ish_type(n, match[n], arguments[n])
  elif 'shamtw' in arguments[n]:
    print_ishw_type(n, match[n], arguments[n])
  elif 'imm20' in arguments[n]:
    print_u_type(n, match[n], arguments[n])
  elif 'jimm20' in arguments[n]:
    print_uj_type(n, match[n], arguments[n])
  elif 'imm12' in arguments[n]:
    print_i_type(n, match[n], arguments[n])
  elif 'imm12hi' in arguments[n]:
    print_s_type(n, match[n], arguments[n])
  elif 'bimm12hi' in arguments[n]:
    print_sb_type(n, match[n], arguments[n])
  elif 'rs3' in arguments[n]:
    print_r4_type(n, match[n], arguments[n])
  else:
    print_r_type(n, match[n], arguments[n])

def print_insts(*names):
  for n in names:
    print_inst(n)

def make_latex_table():
  print_header('r','i','s','sb','u','uj')
  print_subtitle('RV32I Base Instruction Set')
  print_insts('lui', 'auipc')
  print_insts('jal', 'jalr', 'beq', 'bne', 'blt', 'bge', 'bltu', 'bgeu')
  print_insts('lb', 'lh', 'lw', 'lbu', 'lhu', 'sb', 'sh', 'sw')
  print_insts('addi', 'slti', 'sltiu', 'xori', 'ori', 'andi', 'slli', 'srli', 'srai')
  print_insts('add', 'sub', 'sll', 'slt', 'sltu', 'xor', 'srl', 'sra', 'or', 'and')
  print_insts('fence', 'fence.i')
  print_insts('syscall', 'break', 'rdcycle', 'rdtime', 'rdinstret')
  print_footer(0)

  print_header('r','a','i','s')
  print_subtitle('RV64I Base Instruction Set (in addition to RV32I)')
  print_insts('lwu', 'ld', 'sd')
  print_insts('addiw', 'slliw', 'srliw', 'sraiw')
  print_insts('addw', 'subw', 'sllw', 'srlw', 'sraw')
  print_subtitle('RV32M Standard Extension')
  print_insts('mul', 'mulh', 'mulhsu', 'mulhu')
  print_insts('div', 'divu', 'rem', 'remu')
  print_subtitle('RV64M Standard Extension (in addition to RV32M)')
  print_insts('mulw', 'divw', 'divuw', 'remw', 'remuw')
  print_subtitle('RV32A Standard Extension')
  print_insts('lr.w', 'sc.w')
  print_insts('amoswap.w')
  print_insts('amoadd.w', 'amoxor.w', 'amoand.w', 'amoor.w')
  print_insts('amomin.w', 'amomax.w', 'amominu.w', 'amomaxu.w')
  print_footer(0)

  print_header('r','r4','i','s')
  print_subtitle('RV64A Standard Extension (in addition to RV32A)')
  print_insts('lr.d', 'sc.d')
  print_insts('amoswap.d')
  print_insts('amoadd.d', 'amoxor.d', 'amoand.d', 'amoor.d')
  print_insts('amomin.d', 'amomax.d', 'amominu.d', 'amomaxu.d')
  print_subtitle('RV32F Standard Extension')
  print_insts('flw', 'fsw')
  print_insts('fadd.s', 'fsub.s', 'fmul.s', 'fdiv.s', 'fsqrt.s', 'fmin.s', 'fmax.s')
  print_insts('fmadd.s', 'fmsub.s', 'fnmsub.s', 'fnmadd.s')
  print_insts('fsgnj.s', 'fsgnjn.s', 'fsgnjx.s')
  print_insts('fcvt.s.w', 'fcvt.s.wu', 'fmv.s.x')
  print_insts('fcvt.w.s', 'fcvt.wu.s', 'fmv.x.s')
  print_insts('feq.s', 'flt.s', 'fle.s')
  print_insts('fssr', 'frsr')
  print_footer(0)

  print_header('r','r4','i','s')
  print_subtitle('RV64F Standard Extension (in addition to RV32F)')
  print_insts('fcvt.s.l', 'fcvt.s.lu')
  print_insts('fcvt.l.s', 'fcvt.lu.s')
  print_subtitle('RV32D Standard Extension')
  print_insts('fld', 'fsd')
  print_insts('fadd.d', 'fsub.d', 'fmul.d', 'fdiv.d', 'fsqrt.d', 'fmin.d', 'fmax.d')
  print_insts('fmadd.d', 'fmsub.d', 'fnmsub.d', 'fnmadd.d')
  print_insts('fsgnj.d', 'fsgnjn.d', 'fsgnjx.d')
  print_insts('fcvt.d.w', 'fcvt.d.wu')
  print_insts('fcvt.w.d', 'fcvt.wu.d')
  print_insts('feq.d', 'flt.d', 'fle.d')
  print_subtitle('RV64D Standard Extension (in addition to RV32D)')
  print_insts('fcvt.d.l', 'fcvt.d.lu', 'fmv.d.x')
  print_insts('fcvt.l.d', 'fcvt.lu.d', 'fmv.x.d')
  print_insts('fcvt.s.d', 'fcvt.d.s')
  print_footer(1)

def print_chisel_insn(name):
  s = "  def %-18s = Bits(\"b" % name.replace('.', '_').upper()
  for i in range(31, -1, -1):
    if yank(mask[name], i, 1):
      s = '%s%d' % (s, yank(match[name], i, 1))
    else:
      s = s + '?'
  print s + "\")"

def make_chisel():
  print '  /* Automatically generated by parse-opcodes */'
  for name in namelist:
    print_chisel_insn(name)

for line in sys.stdin:
  line = line.partition('#')
  tokens = line[0].split()

  if len(tokens) == 0:
    continue
  assert len(tokens) >= 2

  name = tokens[0]
  mymatch = 0
  mymask = 0
  cover = 0

  if not name in arguments.keys():
    arguments[name] = []

  for token in tokens[1:]:
    if len(token.split('=')) == 2:
      tokens = token.split('=')
      if len(tokens[0].split('..')) == 2:
        tmp = tokens[0].split('..')
        hi = int(tmp[0])
        lo = int(tmp[1])
        if hi <= lo:
          sys.exit("%s: bad range %d..%d" % (name,hi,lo))
      else:
        hi = lo = int(tokens[0])

      if tokens[1] != 'ignore':
        val = int(tokens[1], 0)
        if val >= (1 << (hi-lo+1)):
          sys.exit("%s: bad value %d for range %d..%d" % (name,val,hi,lo))
        mymatch = mymatch | (val << lo)
        mymask = mymask | ((1<<(hi+1))-(1<<lo))

      if cover & ((1<<(hi+1))-(1<<lo)):
        sys.exit("%s: overspecified" % name)
      cover = cover | ((1<<(hi+1))-(1<<lo))

    elif token in arglut:
      if cover & ((1<<(arglut[token][0]+1))-(1<<arglut[token][1])):
        sys.exit("%s: overspecified" % name)
      cover = cover | ((1<<(arglut[token][0]+1))-(1<<arglut[token][1]))
      arguments[name].append(token)

    else:
      sys.exit("%s: unknown token %s" % (name,token))

  if not (cover == 0xFFFFFFFF or cover == 0xFFFF):
    sys.exit("%s: not all bits are covered" % name)

  for name2,match2 in match.iteritems():
    if (match2 & mymask) == mymatch:
      sys.exit("%s and %s overlap" % (name,name2))

  mask[name] = mymask
  match[name] = mymatch
  namelist.append(name)

if sys.argv[1] == '-tex':
  make_latex_table()
elif sys.argv[1] == '-chisel':
  make_chisel()
elif sys.argv[1] == '-disasm':
  make_disasm_table(match,mask)
elif sys.argv[1] == '-isasim':
  make_isasim(match,mask)
else:
  assert 0
