mirror of
https://github.com/MiSTer-devel/PokemonMini_MiSTer.git
synced 2026-04-19 03:04:53 +00:00
188 lines
6.5 KiB
Python
188 lines
6.5 KiB
Python
import os
|
|
|
|
# @warning: This is not a full-fledged verilog parser and might fail in certain
|
|
# cases. Use with caution.
|
|
def read_localparams(filepath):
|
|
text = open(filepath, 'r').read()
|
|
localparam_dict= {}
|
|
while True:
|
|
text = text.lstrip()
|
|
|
|
if len(text) == 0:
|
|
break
|
|
|
|
while text.startswith('//'):
|
|
newline_pos = text.find('\n')
|
|
if newline_pos == -1:
|
|
text = ""
|
|
break
|
|
text = text[newline_pos+1:]
|
|
text = text.lstrip()
|
|
|
|
if len(text) == 0:
|
|
break
|
|
|
|
if text.startswith('/*'):
|
|
endcomment_pos = text.find('*/')
|
|
if endcomment_pos == -1:
|
|
text = ""
|
|
break
|
|
text = text[endcomment_pos+1:]
|
|
text = text.lstrip()
|
|
|
|
endcommand_pos = text.find(';')
|
|
if endcommand_pos == -1:
|
|
text = ""
|
|
break
|
|
|
|
command = text[0:endcommand_pos]
|
|
# @todo: The current parsing does not allow for comments!
|
|
# @todo: Perhaps it's nice to be able to annotate the localparams to
|
|
# modify the number of bits/value, e.g. it would be nice to change
|
|
# MICRO_ALU_OP_NONE to be 7'd0 so that we do not have to define the alu
|
|
# size and flag updating explicitly.
|
|
if command.startswith('localparam'):
|
|
command = command.strip().replace('\n', '').replace(' ', '')
|
|
if command.find('[') != -1:
|
|
command = command[15:]
|
|
else:
|
|
command = command[10:]
|
|
|
|
if command.startswith('MICRO_'):
|
|
parts = command.split(',')
|
|
for part in parts:
|
|
name, value = part.split('=')
|
|
if "'" in value:
|
|
bits, value = value.split("'")
|
|
if value[0] == 'b':
|
|
value = value[1:]
|
|
elif value[0] == 'h':
|
|
value = bin(int(value[1:], base=16))[2:].zfill(int(bits))
|
|
elif value[0] == 'd':
|
|
value = bin(int(value[1:]))[2:].zfill(int(bits))
|
|
else:
|
|
print("Cannot read value in %s" % part)
|
|
else:
|
|
print('Not implemented %s' % command)
|
|
|
|
localparam_dict[name[6:]] = value
|
|
|
|
text = text[endcommand_pos+1:]
|
|
|
|
return localparam_dict
|
|
|
|
def num_string_to_binary_string(x):
|
|
|
|
if '\'' in x:
|
|
num_bits, value = x.split('\'')
|
|
num_bits = int(num_bits)
|
|
format, value = value[0], value[1:]
|
|
if format == 'd':
|
|
value = int(value)
|
|
elif format == 'h':
|
|
value = int(value, base=16)
|
|
elif format == 'b':
|
|
value = int(value, base=2)
|
|
else:
|
|
print("Couldn't decode x:", x)
|
|
return x
|
|
|
|
value = bin(value)[2:].zfill(num_bits)
|
|
|
|
return value
|
|
|
|
x = bin(int(x))[2:]
|
|
return x
|
|
|
|
if __name__ == '__main__':
|
|
microinstruction_width = 36
|
|
rom_bits = 11
|
|
|
|
root = os.path.abspath(os.path.join(os.path.dirname(__file__), '../'))
|
|
|
|
localparam_dict = read_localparams(os.path.join(root, 'rtl/s1c88.sv'))
|
|
|
|
lines = open(os.path.join(root, 'rom/microinstructions.txt'), 'r').readlines()
|
|
|
|
# @todo: Also check if MOV_DATA is called enough times given the
|
|
# instruction number of arguments.
|
|
done_exceptions = [
|
|
0xE0, 0xE1, 0xE2, 0xE3, 0x1F0, 0x1F1, 0x1F2, 0x1F3, 0x1F4, 0x1F5, 0x1F6, 0x1F7,
|
|
0x1F8, 0x1F9, 0x1FA, 0x1FB, 0x1FC, 0x1FD, 0x1FE, 0x1FF, 0xE8, 0xE9, 0xEA, 0xEB
|
|
]
|
|
|
|
addresses = [-1] * 768
|
|
rom = []
|
|
microinstruction_address = 0
|
|
num_opcodes_implemented = 0
|
|
default_address = 0
|
|
done_flags_in_instruction = 0
|
|
microinstruction_addresses = []
|
|
for line in lines:
|
|
line = line.strip()
|
|
|
|
comment_start = line.find('//')
|
|
if comment_start > -1:
|
|
line = line[:comment_start].strip()
|
|
|
|
if len(line) == 0:
|
|
continue
|
|
|
|
if line[0] == '#':
|
|
if done_flags_in_instruction > 1:
|
|
if opcode not in done_exceptions:
|
|
print('Warning: opcode 0x%x has multiple DONE!' % opcode)
|
|
done_flags_in_instruction = 0
|
|
if line[1:] == 'default':
|
|
default_address = microinstruction_address
|
|
for i in range(len(addresses)):
|
|
if addresses[i] == -1:
|
|
addresses[i] = default_address
|
|
else:
|
|
opcode = int(line[1:], base=16)
|
|
if opcode >= 0xCF00:
|
|
opcode = 0x200 | opcode & 0xFF
|
|
elif opcode >= 0xCE00:
|
|
opcode = 0x100 | opcode & 0xFF
|
|
|
|
if addresses[opcode] != default_address and addresses[opcode] != -1:
|
|
print('Warning: opcode 0x%x already implemented.' % opcode)
|
|
else:
|
|
num_opcodes_implemented += 1
|
|
addresses[opcode] = microinstruction_address
|
|
microinstruction_addresses.append(microinstruction_address)
|
|
else:
|
|
microcommands = line.split(' ')
|
|
microcommands = [x for x in microcommands if len(x) > 0]
|
|
if 'DONE' in microcommands:
|
|
done_flags_in_instruction += 1
|
|
microcommands = [
|
|
localparam_dict[x] if x in localparam_dict else
|
|
num_string_to_binary_string(x)
|
|
for x in microcommands]
|
|
|
|
command = ''.join(microcommands)
|
|
assert(len(command) == microinstruction_width)
|
|
rom.append(command)
|
|
microinstruction_address += 1
|
|
|
|
from collections import Counter
|
|
microprograms = [int(''.join(rom[x[0]:x[1]]),2) for x in zip(microinstruction_addresses[:-1], microinstruction_addresses[1:])]
|
|
duplicates = [i for i,(k,v) in enumerate(Counter(microprograms).items()) if v > 1]
|
|
if len(duplicates) > 0:
|
|
print("Warning: duplicates found! Check the following rom addresses:")
|
|
print([microinstruction_addresses[x] for x in duplicates])
|
|
|
|
print('%d microinstructions in rom.' % len(rom))
|
|
print('%d/608 opcodes implemented.' % num_opcodes_implemented)
|
|
|
|
rom = '\n'.join([hex(int(x, base=2))[2:] for x in rom])
|
|
addresses = '\n'.join([hex(int(bin(x)[2:].zfill(rom_bits)[:rom_bits], base=2))[2:] for x in addresses])
|
|
|
|
with open('translation_rom.mem', 'w') as fp:
|
|
fp.write(addresses)
|
|
|
|
with open('rom.mem', 'w') as fp:
|
|
fp.write(rom)
|
|
|