Merge pull request #1496 from MateuszKarlic/json2renode-update
json2renode: Multicore configuration support
This commit is contained in:
commit
7157b4c5e8
|
@ -1,6 +1,6 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Copyright (c) 2019-2021 Antmicro <www.antmicro.com>
|
||||
Copyright (c) 2019-2022 Antmicro <www.antmicro.com>
|
||||
|
||||
Renode platform definition (repl) and script (resc) generator for LiteX SoC.
|
||||
|
||||
|
@ -20,6 +20,14 @@ import argparse
|
|||
# and should not be generated automatically
|
||||
non_generated_mem_regions = ['ethmac', 'csr']
|
||||
|
||||
def get_soc_interrupt_parent(csr):
|
||||
'''
|
||||
We assume that for multi-core setups plic will be used hence cpu0 is enough
|
||||
'''
|
||||
if 'plic' in csr['memories']:
|
||||
return 'plic'
|
||||
else:
|
||||
return 'cpu0'
|
||||
|
||||
def get_descriptor(csr, name, size=None):
|
||||
res = { 'base': csr['csr_bases'][name], 'constants': {} }
|
||||
|
@ -110,7 +118,8 @@ ethmac: Network.LiteX_Ethernet{} @ {{
|
|||
|
||||
interrupt_name = '{}_interrupt'.format(name)
|
||||
if interrupt_name in csr['constants']:
|
||||
result += ' -> cpu@{}\n'.format(
|
||||
result += ' -> {}@{}\n'.format(
|
||||
get_soc_interrupt_parent(csr),
|
||||
csr['constants'][interrupt_name])
|
||||
|
||||
result += """
|
||||
|
@ -204,8 +213,54 @@ def get_cpu_type(csr):
|
|||
|
||||
return (kind, variant)
|
||||
|
||||
def get_cpu_count(csr):
|
||||
return csr['constants']['config_cpu_count']
|
||||
|
||||
def generate_cpu(csr, time_provider):
|
||||
vexriscv_common_kind = {
|
||||
'name': 'VexRiscv',
|
||||
'variants': {
|
||||
'linux': {
|
||||
'properties': ['cpuType: "rv32ima"', 'privilegeArchitecture: PrivilegeArchitecture.Priv1_10'],
|
||||
},
|
||||
'i': {
|
||||
'properties': ['cpuType: "rv32i"'],
|
||||
},
|
||||
'im': {
|
||||
'properties': ['cpuType: "rv32im"'],
|
||||
},
|
||||
'ima': {
|
||||
'properties': ['cpuType: "rv32ima"'],
|
||||
},
|
||||
'imac': {
|
||||
'properties': ['cpuType: "rv32imac"'],
|
||||
},
|
||||
'others': {
|
||||
'properties': ['cpuType: "rv32im"'],
|
||||
}
|
||||
},
|
||||
'supports_time_provider': True,
|
||||
}
|
||||
cpu_kinds = {
|
||||
'vexriscv': vexriscv_common_kind,
|
||||
'vexriscv_smp': vexriscv_common_kind,
|
||||
'picorv32': {
|
||||
'name': 'PicoRV32',
|
||||
'properties': ['cpuType: "rv32imc"'],
|
||||
},
|
||||
'minerva': {
|
||||
'name': 'Minerva',
|
||||
},
|
||||
'ibex': {
|
||||
'name': 'IbexRiscV32',
|
||||
},
|
||||
'cv32e40p': {
|
||||
'name': 'CV32E40P',
|
||||
'supports_time_provider': True,
|
||||
'properties': ['cpuType: "rv32imc"'],
|
||||
}
|
||||
}
|
||||
|
||||
def generate_cpu(csr, time_provider, number_of_cores):
|
||||
""" Generates definition of a CPU.
|
||||
|
||||
Returns:
|
||||
|
@ -213,63 +268,39 @@ def generate_cpu(csr, time_provider):
|
|||
"""
|
||||
kind, variant = get_cpu_type(csr)
|
||||
|
||||
if kind == 'vexriscv' or kind == 'vexriscv_smp':
|
||||
result = """
|
||||
cpu: CPU.VexRiscv @ sysbus
|
||||
"""
|
||||
if variant == 'linux':
|
||||
result += """
|
||||
cpuType: "rv32ima"
|
||||
privilegeArchitecture: PrivilegeArchitecture.Priv1_10
|
||||
"""
|
||||
elif variant in ["i", "im", "ima", "imac"]:
|
||||
result += """
|
||||
cpuType: "rv32{}"
|
||||
""".format(variant)
|
||||
else:
|
||||
result += """
|
||||
cpuType: "rv32im"
|
||||
"""
|
||||
if time_provider:
|
||||
result += """
|
||||
timeProvider: {}
|
||||
""".format(time_provider)
|
||||
try:
|
||||
cpu = cpu_kinds[kind]
|
||||
|
||||
return result
|
||||
elif kind == 'picorv32':
|
||||
return """
|
||||
cpu: CPU.PicoRV32 @ sysbus
|
||||
cpuType: "rv32imc"
|
||||
"""
|
||||
elif kind == 'minerva':
|
||||
return """
|
||||
cpu: CPU.Minerva @ sysbus
|
||||
"""
|
||||
elif kind == 'ibex':
|
||||
return """
|
||||
cpu: CPU.IbexRiscV32 @ sysbus
|
||||
"""
|
||||
elif kind == 'cv32e40p':
|
||||
result = """
|
||||
cpu: CPU.CV32E40P @ sysbus
|
||||
"""
|
||||
if variant == 'standard':
|
||||
result += """
|
||||
cpuType: "rv32imc"
|
||||
"""
|
||||
else:
|
||||
result += """
|
||||
cpuType: "rv32imc"
|
||||
"""
|
||||
if time_provider:
|
||||
result += """
|
||||
timeProvider: {}
|
||||
""".format(time_provider)
|
||||
cpu_string = f'{cpu["name"]} @ sysbus\n'
|
||||
|
||||
def unpack_properties(prop_list):
|
||||
return ''.join([f' {prop}\n' for prop in prop_list])
|
||||
|
||||
return result
|
||||
else:
|
||||
raise Exception('Unsupported cpu type: {}'.format(kind))
|
||||
if 'properties' in cpu:
|
||||
cpu_string += unpack_properties(cpu["properties"])
|
||||
|
||||
if 'variants' in cpu:
|
||||
variant = cpu['variants'].get(variant)
|
||||
|
||||
if variant is None:
|
||||
variant = cpu['variants'].get('others', [])
|
||||
|
||||
if 'properties' in variant:
|
||||
cpu_string += unpack_properties(variant["properties"])
|
||||
|
||||
except KeyError:
|
||||
raise Exception(f'Unsupported cpu type: {kind}')
|
||||
|
||||
result = ''
|
||||
for cpu_id in range(0, number_of_cores):
|
||||
result += f"""
|
||||
cpu{cpu_id}: CPU.{cpu_string.strip()}
|
||||
hartId: {cpu_id}
|
||||
"""
|
||||
if cpu.get('supports_time_provider', False):
|
||||
result += f' timeProvider: {time_provider}\n'
|
||||
|
||||
return result
|
||||
|
||||
def generate_peripheral(csr, name, **kwargs):
|
||||
""" Generates definition of a peripheral.
|
||||
|
@ -298,7 +329,7 @@ def generate_peripheral(csr, name, **kwargs):
|
|||
for constant, val in peripheral['constants'].items():
|
||||
if 'ignored_constants' not in kwargs or constant not in kwargs['ignored_constants']:
|
||||
if constant == 'interrupt':
|
||||
result += ' -> cpu@{}\n'.format(val)
|
||||
result += ' -> {}@{}\n'.format(get_soc_interrupt_parent(csr), val)
|
||||
else:
|
||||
result += ' {}: {}\n'.format(constant, val)
|
||||
|
||||
|
@ -413,31 +444,37 @@ mmc_controller: SD.LiteSDCard{} @ {{
|
|||
return result
|
||||
|
||||
|
||||
def generate_clint(clint, frequency):
|
||||
def generate_clint(clint, frequency, number_of_cores):
|
||||
# TODO: this is configuration for VexRiscv - add support for other CPU types
|
||||
result = """
|
||||
clint: IRQControllers.CoreLevelInterruptor @ {}
|
||||
frequency: {}
|
||||
[0, 1] -> cpu@[101, 100]
|
||||
numberOfTargets: {}
|
||||
""".format(generate_sysbus_registration(clint,
|
||||
skip_braces=True,
|
||||
skip_size=True),
|
||||
frequency)
|
||||
frequency, number_of_cores)
|
||||
|
||||
for cpu_id in range(0, number_of_cores):
|
||||
result += f" [{2 * cpu_id}, {2 * cpu_id + 1}] -> cpu{cpu_id}@[101, 100]\n"
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def generate_plic(plic):
|
||||
def generate_plic(plic, number_of_cores):
|
||||
# TODO: this is configuration for linux-on-litex-vexriscv - add support for other CPU types
|
||||
result = """
|
||||
plic: IRQControllers.PlatformLevelInterruptController @ {}
|
||||
[0, 1] -> cpu@[11, 9]
|
||||
numberOfSources: 31
|
||||
numberOfContexts: 2
|
||||
numberOfContexts: {}
|
||||
prioritiesEnabled: false
|
||||
""".format(generate_sysbus_registration(plic,
|
||||
skip_braces=True,
|
||||
skip_size=True))
|
||||
skip_size=True),
|
||||
2 * number_of_cores)
|
||||
|
||||
for cpu_id in range(0, number_of_cores):
|
||||
result += f" [{2 * cpu_id}, {2 * cpu_id + 1}] -> cpu{cpu_id}@[11, 9]\n"
|
||||
|
||||
return result
|
||||
|
||||
|
@ -489,6 +526,67 @@ def get_clock_frequency(csr):
|
|||
# has different names
|
||||
return csr['constants']['config_clock_frequency' if 'config_clock_frequency' in csr['constants'] else 'system_clock_frequency']
|
||||
|
||||
def generate_gpio_port(csr, name, **kwargs):
|
||||
peripheral = get_descriptor(csr, name)
|
||||
|
||||
ints = ''
|
||||
if 'interrupt' in kwargs:
|
||||
ints += f' IRQ -> {get_soc_interrupt_parent(csr)}@{kwargs["interrupt"]}'
|
||||
if 'connections' in kwargs:
|
||||
for irq, target in zip(kwargs['connections'], kwargs['connections_targets']):
|
||||
ints += f' {irq} -> {target}\n'
|
||||
|
||||
result = """
|
||||
gpio_{}: GPIOPort.LiteX_GPIO @ {}
|
||||
type: {}
|
||||
enableIrq: {}
|
||||
{}
|
||||
""".format(name,
|
||||
generate_sysbus_registration(peripheral, skip_braces=True),
|
||||
kwargs['type'],
|
||||
'true' if kwargs['type'] != 'Type.Out' else 'false',
|
||||
ints)
|
||||
|
||||
return result
|
||||
|
||||
def generate_switches(csr, name, **kwargs):
|
||||
interrupt_name = '{}_interrupt'.format(name)
|
||||
result = generate_gpio_port(csr, name, **{'type': 'Type.In', 'interrupt': csr['constants'][interrupt_name], **kwargs})
|
||||
|
||||
# Litex puts 4 by default into dts (litex_json2dts)
|
||||
count = int(csr['constants'].get(f'{name}_ngpio', 4))
|
||||
|
||||
for i in range(0, count):
|
||||
result += """
|
||||
{name}_{i}: Miscellaneous.Button @ {periph} {i}
|
||||
-> {periph}@{i}
|
||||
""".format(name=name, periph=f"gpio_{name}", i=i)
|
||||
|
||||
return result
|
||||
|
||||
def generate_leds(csr, name, **kwargs):
|
||||
# Litex puts 4 by default into dts (litex_json2dts)
|
||||
count = int(csr['constants'].get(f'{name}_ngpio', 4))
|
||||
|
||||
result = generate_gpio_port(csr, name,
|
||||
**{'type': 'Type.Out',
|
||||
'connections': [*range(0, count)],
|
||||
'connections_targets': [f'leds_{i}@0' for i in range(0, count)],
|
||||
**kwargs})
|
||||
|
||||
for i in range(0, count):
|
||||
result += """
|
||||
{name}_{i}: Miscellaneous.LED @ {periph} {i}
|
||||
""".format(name=name, periph=f"gpio_{name}", i=i)
|
||||
|
||||
return result
|
||||
|
||||
def handled_peripheral(csr, name, **kwargs):
|
||||
"""
|
||||
Use this to silence warnings about unsupported peripherals, that are in reality emulated by other peripheral
|
||||
e. g. mmc, ethmac
|
||||
"""
|
||||
return ''
|
||||
|
||||
peripherals_handlers = {
|
||||
'uart': {
|
||||
|
@ -521,7 +619,7 @@ peripherals_handlers = {
|
|||
},
|
||||
'interrupts': {
|
||||
# IRQ #100 in Renode's VexRiscv model is mapped to Machine Timer Interrupt
|
||||
'IRQ': lambda: 'cpu@100'
|
||||
'IRQ': lambda: 'cpu0@100'
|
||||
}
|
||||
},
|
||||
'ddrphy': {
|
||||
|
@ -559,12 +657,37 @@ peripherals_handlers = {
|
|||
'handler': generate_video_framebuffer,
|
||||
},
|
||||
'video_framebuffer_vtg': {
|
||||
'handler': lambda *args, **kwargs: "", # This is handled by generate_video_framebuffer
|
||||
}
|
||||
'handler': handled_peripheral, # This is handled by generate_video_framebuffer
|
||||
},
|
||||
'leds': {
|
||||
'handler': generate_leds,
|
||||
},
|
||||
'switches': {
|
||||
'handler': generate_switches,
|
||||
},
|
||||
'mmcm': {
|
||||
'handler': generate_peripheral,
|
||||
'model': 'Miscellaneous.LiteX_MMCM',
|
||||
'model_CSR32': 'Miscellaneous.LiteX_MMCM_CSR32',
|
||||
'ignored_constants': ['lock_timeout', 'drdy_timeout'],
|
||||
},
|
||||
'ethphy': {
|
||||
'handler': handled_peripheral # by generate_ethmac
|
||||
},
|
||||
# handled by generate_mmc
|
||||
'sdblock2mem': {
|
||||
'handler': handled_peripheral
|
||||
},
|
||||
'sdmem2block': {
|
||||
'handler': handled_peripheral
|
||||
},
|
||||
'sdcore': {
|
||||
'handler': handled_peripheral
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def genereate_etherbone_bridge(name, address, port):
|
||||
def generate_etherbone_bridge(name, address, port):
|
||||
# FIXME: for now the width is fixed to 0x800
|
||||
return """
|
||||
{}: EtherboneBridge @ sysbus <{}, +0x800>
|
||||
|
@ -572,7 +695,7 @@ def genereate_etherbone_bridge(name, address, port):
|
|||
""".format(name, hex(address), port)
|
||||
|
||||
|
||||
def generate_repl(csr, etherbone_peripherals, autoalign):
|
||||
def generate_repl(csr, etherbone_peripherals, autoalign, number_of_cores):
|
||||
""" Generates platform definition.
|
||||
|
||||
Args:
|
||||
|
@ -592,7 +715,6 @@ def generate_repl(csr, etherbone_peripherals, autoalign):
|
|||
"""
|
||||
result = ""
|
||||
|
||||
|
||||
# RISC-V CPU in Renode requires memory region size
|
||||
# to be a multiple of 4KB - this is a known limitation
|
||||
# (not a bug) and there are no plans to handle smaller
|
||||
|
@ -611,16 +733,16 @@ def generate_repl(csr, etherbone_peripherals, autoalign):
|
|||
|
||||
time_provider = None
|
||||
if 'clint' in csr['memories']:
|
||||
result += generate_clint(csr['memories']['clint'], csr['constants']['config_clock_frequency'])
|
||||
result += generate_clint(csr['memories']['clint'], csr['constants']['config_clock_frequency'], number_of_cores)
|
||||
time_provider = 'clint'
|
||||
|
||||
if 'plic' in csr['memories']:
|
||||
result += generate_plic(csr['memories']['plic'])
|
||||
result += generate_plic(csr['memories']['plic'], number_of_cores)
|
||||
|
||||
if not time_provider and 'cpu' in csr['csr_bases']:
|
||||
time_provider = 'cpu_timer'
|
||||
|
||||
result += generate_cpu(csr, time_provider)
|
||||
result += generate_cpu(csr, time_provider, number_of_cores)
|
||||
|
||||
for name, address in csr['csr_bases'].items():
|
||||
if name not in peripherals_handlers:
|
||||
|
@ -631,7 +753,7 @@ def generate_repl(csr, etherbone_peripherals, autoalign):
|
|||
if name in etherbone_peripherals:
|
||||
# generate an etherbone bridge for the peripheral
|
||||
port = etherbone_peripherals[name]
|
||||
result += genereate_etherbone_bridge(name, address, port)
|
||||
result += generate_etherbone_bridge(name, address, port)
|
||||
pass
|
||||
else:
|
||||
# generate an actual model of the peripheral
|
||||
|
@ -674,6 +796,8 @@ def filter_memory_regions(raw_regions, alignment=None, autoalign=[]):
|
|||
size_mismatch = r['size'] % alignment
|
||||
address_mismatch = r['base'] % alignment
|
||||
|
||||
def print_autoalign_hint():
|
||||
print('Hint: use "--auto-align {}" to force automatic alignement of the region'.format(r['name']))
|
||||
if address_mismatch != 0:
|
||||
if r['name'] in autoalign:
|
||||
r['original_address'] = r['base']
|
||||
|
@ -681,6 +805,7 @@ def filter_memory_regions(raw_regions, alignment=None, autoalign=[]):
|
|||
print('Re-aligning `{}` memory region base address from {} to {} due to limitations in Renode'.format(r['name'], hex(r['original_address']), hex(r['base'])))
|
||||
else:
|
||||
print('Error: `{}` memory region base address ({}) is not aligned to {}. This configuration cannot be currently simulated in Renode'.format(r['name'], hex(r['size']), hex(alignment)))
|
||||
print_autoalign_hint()
|
||||
sys.exit(1)
|
||||
|
||||
if size_mismatch != 0:
|
||||
|
@ -690,6 +815,7 @@ def filter_memory_regions(raw_regions, alignment=None, autoalign=[]):
|
|||
print('Extending `{}` memory region size from {} to {} due to limitations in Renode'.format(r['name'], hex(r['original_size']), hex(r['size'])))
|
||||
else:
|
||||
print('Error: `{}` memory region size ({}) is not aligned to {}. This configuration cannot be currently simulated in Renode'.format(r['name'], hex(r['size']), hex(alignment)))
|
||||
print_autoalign_hint()
|
||||
sys.exit(1)
|
||||
|
||||
if r['size'] == 0:
|
||||
|
@ -730,7 +856,7 @@ def find_memory_region(memory_regions, address):
|
|||
return None
|
||||
|
||||
|
||||
def generate_resc(csr, args, flash_binaries={}, tftp_binaries={}):
|
||||
def generate_resc(csr, number_of_cores, args, flash_binaries={}, tftp_binaries={}):
|
||||
""" Generates platform definition.
|
||||
|
||||
Args:
|
||||
|
@ -756,14 +882,15 @@ showAnalyzer sysbus.uart
|
|||
showAnalyzer sysbus.uart Antmicro.Renode.Analyzers.LoggingUartAnalyzer
|
||||
""".format(cpu_type, args.repl)
|
||||
|
||||
rom_base = csr['memories']['rom']['base'] if 'rom' in csr['memories'] else None
|
||||
if rom_base is not None and args.bios_binary:
|
||||
opensbi_base = csr['memories']['opensbi']['base'] if 'opensbi' in csr['memories'] else None
|
||||
if opensbi_base is not None and args.bios_binary:
|
||||
# load LiteX BIOS to ROM
|
||||
result += """
|
||||
sysbus LoadBinary @{} {}
|
||||
cpu PC {}
|
||||
""".format(args.bios_binary, rom_base, rom_base)
|
||||
""".format(args.bios_binary, hex(opensbi_base))
|
||||
|
||||
for cpu_id in range(0, number_of_cores):
|
||||
result += f"cpu{cpu_id} PC {hex(opensbi_base)}\n"
|
||||
|
||||
if args.tftp_ip:
|
||||
result += """
|
||||
|
@ -792,16 +919,16 @@ connector Connect ethmac switch
|
|||
connector Connect host.tap switch
|
||||
""".format(args.configure_network)
|
||||
elif flash_binaries:
|
||||
if 'flash_boot_address' not in csr['constants']:
|
||||
if 'spiflash' not in csr['memories']:
|
||||
print('Warning! There is no flash memory to load binaries to')
|
||||
else:
|
||||
# load binaries to spiflash to boot from there
|
||||
|
||||
for offset in flash_binaries:
|
||||
path = flash_binaries[offset]
|
||||
flash_boot_address = int(csr['constants']['flash_boot_address'], 0) + offset
|
||||
flash_boot_address = int(csr['memories']['spiflash']['base']) + offset
|
||||
|
||||
firmware_data = open(path, 'rb').read()
|
||||
firmware_data = open(os.path.expanduser(path), 'rb').read()
|
||||
crc32 = zlib.crc32(firmware_data)
|
||||
|
||||
result += 'sysbus WriteDoubleWord {} {}\n'.format(hex(flash_boot_address), hex(len(firmware_data)))
|
||||
|
@ -950,8 +1077,10 @@ def main():
|
|||
|
||||
etherbone_peripherals = check_etherbone_peripherals(args.etherbone_peripherals)
|
||||
|
||||
number_of_cores = get_cpu_count(csr)
|
||||
|
||||
if args.repl:
|
||||
print_or_save(args.repl, generate_repl(csr, etherbone_peripherals, args.autoalign_memor_regions))
|
||||
print_or_save(args.repl, generate_repl(csr, etherbone_peripherals, args.autoalign_memor_regions, number_of_cores))
|
||||
|
||||
if args.resc:
|
||||
if not args.repl:
|
||||
|
@ -961,7 +1090,7 @@ def main():
|
|||
flash_binaries = parse_flash_binaries(csr, args)
|
||||
tftp_binaries = check_tftp_binaries(args)
|
||||
|
||||
print_or_save(args.resc, generate_resc(csr, args,
|
||||
print_or_save(args.resc, generate_resc(csr, number_of_cores, args,
|
||||
flash_binaries,
|
||||
tftp_binaries))
|
||||
|
||||
|
|
Loading…
Reference in New Issue