text: '''
  Rummage through input file and construct a JSON file representing
  most of the read address space.  Depends on stylized Verilog representation
  of the first stage of the (scalar) read data multiplexer, using reg_bank_n
  pipeline registers.  Attempts to deduce signed-ness and bit width for each
  entry by peeking at wire declarations in the Verilog.
  '''
  import re
  from sys import stderr, argv
  
  trantab = {"[": "_", "]": ""}
  
  wire_info = {}
  addr_found = {}
  name_found = {}
  fail = 0
  
  # Bugs:
  #   brittle to variations in Verilog code formatting
  #   Hard-coded filename
  
  
  def reg_trans(s, trandict):
      for k, v in trandict.items():
          s = s.replace(k, v)
      return s
  
  
  def ponder_int(s):
      try:
          r = int(s)
      except Exception:
          r = 31
          m4 = re.search(r'(\w+)-(\d+)', s)
          if m4:
              p = m4.group(1)
              o = int(m4.group(2))
              if p in param_db:
                  pv = param_db[p]
                  # stderr.write('p, pv, o = %s, %d, %d\n' % (p, pv, o))
                  return pv - o
              else:
                  stderr.write('ERROR: parameter %s not found?\n' % p)
          else:
              stderr.write("ERROR: Couldn't" + ' understand "%s", using 31\n' %
                           s)
      return r
  
  
  def rprint(g, line, alias, address_offset):
      global fail
      addr = int(g(2) + g(1), 16) + address_offset
      # Given g(2) that might have the form m_accum[1], construct
      # m_accum_1 for the JSON name, and
      # m_accum as the name with which to look up the wire properties.
      name = reg_trans(g(3), trantab)  # Apply regname replacements
      if alias is not None:
          name = alias
      wname = g(3).split('[')[0]
      # print addr, name, wname, g(1), g(2), g(3), line
      if name in name_found:
          stderr.write('ERROR: Duplicate name "%s"\n' % name)
          fail = 1
      name_found[name] = True
      if addr in addr_found:
          stderr.write('ERROR: Duplicate address "0x%x"\n' % addr)
          fail = 1
      addr_found[addr] = True
      # default width and signed-ness should only apply to strange
      # cases where the width is parameterized
      wid = 32
      sign = "unsigned"
      if wname in wire_info:
          # print a, n, info[nn]
          sign, wid = wire_info[wname].split(':')
          wid = ponder_int(wid) + 1
      else:
          stderr.write('WARNING: Taking default sign and wid for "%s"\n' % wname)
      s = '''    "%s": {
          "access": "r",
          "addr_width": 0,
          "sign": "%s",
          "base_addr": %d,
          "data_width": %d
      }''' % (name, sign, addr, wid)
      return s
  
  
  def memorize(g):
      # print g(1), g(2), g(3)
      sign = "signed" if g(1) else "unsigned"
      wire_info[g(3)] = sign + ":" + g(2)
  
  
  f = open(argv[1], "r")
  print("{")
  sl = []
  param_db = {}
  address_offset = 0
  bank_state = None  # keep track of current block
  line_no = 0
  for line in f.read().split('\n'):
      line_no += 1
      ehead = 'ERROR:%s:%d:' % (argv[1], line_no)  # just in case
      if "reverse_json_offset" in line:
          m1 = re.search(r"\s*//\s*reverse_json_offset\s*:\s*(\d+)\s*", line)
          if m1:
              address_offset = int(m1.group(1))
      if "4'h" in line and ": reg_bank_" in line:
          m1 = re.search(r"4'h(\w):\s*reg_bank_(\w)\s*<=\s*(\S+);", line)
          if m1:
              alias = None
              m1a = re.search(r";\s*//\s*alias:\s*(\w+)", line)
              if m1a:
                  # stderr.write('INFO: alias "%s"\n' % m1a.group(1))
                  alias = m1a.group(1)
              tbank = m1.group(2)
              if bank_state == "=armed=":
                  bank_state = tbank
              if bank_state != tbank:
                  stderr.write(ehead + ' bank %s assignment found in bank %s stanza\n' % (tbank, bank_state))
                  fail = 1
              sl += [rprint(m1.group, line, alias, address_offset)]
          else:
              stderr.write("WARNING: Surprising regexp failure: %s\n" % line)
      if "default" in line and ": reg_bank_" in line:
          m1 = re.search(r"default:\s*reg_bank_(\w)\s*<=\s*32'h", line)
          if m1:
              # stderr.write('INFO: default %s\n' % line)
              tbank = m1.group(1)
              if bank_state != tbank:
                  stderr.write(ehead + ' bank %s assignment found in bank %s stanza\n' % (tbank, bank_state))
                  fail = 1
      if "wire" in line:
          m2 = re.search(r"wire\s+(signed)?\s*\[([^:]+):0\]\s*(\w+)", line)
          if m2:
              memorize(m2.group)
      if "reg " in line:
          m2 = re.search(r"reg\s+(signed)?\s*\[([^:]+):0\]\s*(\w+)", line)
          if m2:
              memorize(m2.group)
      if any(x in line for x in ["parameter ", "localparam "]):
          m3 = re.search(r"(?:parameter|localparam)\s+(\w+)\s*=\s*(\d+);", line)
          if m3:
              p, v = m3.group(1), int(m3.group(2))
              param_db[p] = v
              # stderr.write('INFO: found parameter "%s" with value %d\n' % (p, v))
      if "localparam " in line:
          m3 = re.search(r"localparam\s+(\w+)\s*=\s*(\d+);", line)
          if m3:
              p, v = m3.group(1), int(m3.group(2))
              param_db[p] = v
              # stderr.write('INFO: found localparam "%s" with value %d\n' % (p, v))
      if "endcase" in line:
          bank_state = None
      if "case" in line and "addr" in line:
          m4 = re.search(r"case\s*\(\w*addr", line)
          if m4:
              bank_state = "=armed="
  print(",\n".join(sl))
  print("}")
  exit(fail)
  