litex/litex/soc/integration/export.py
Benjamin Herrenschmidt c78caeb998 csr: Fix definition(s) of CSR_BASE in generated headers
CSR_BASE is currently defined twice. Once in mem.h as the base
of the CSR region in the SoC address space, and once in csr.h
as the base address for all CSRs.

This fixes two issues with those definitions:

 - The mem.h one is unconditional which prevents an external
redefinition (which is useful under some circumstances such as
when using an address decoder outside of LiteX with a standalone
core).

 - The csr.h one is actually the origin of the first CSR region
rather than the origin of the CSR region in the SoC space. They
are usually the same ... unless you don't have CSR bank 0 in
which case the csr.h one becomes different. This causes conflicts
with the mem.h definition and breaks projects using a standalone
cores.

The first one is fixed by adding the #ifndef/#endif around the
definition of the memory regions, the second one by passing the
csr_base to use to get_csr_header()

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
2020-05-27 21:48:00 +02:00

445 lines
19 KiB
Python

# This file is Copyright (c) 2013-2014 Sebastien Bourdeauducq <sb@m-labs.hk>
# This file is Copyright (c) 2014-2019 Florent Kermarrec <florent@enjoy-digital.fr>
# This file is Copyright (c) 2018 Dolu1990 <charles.papon.90@gmail.com>
# This file is Copyright (c) 2019 Gabriel L. Somlo <gsomlo@gmail.com>
# This file is Copyright (c) 2018 Jean-François Nguyen <jf@lambdaconcept.fr>
# This file is Copyright (c) 2019 Antmicro <www.antmicro.com>
# This file is Copyright (c) 2013 Robert Jordens <jordens@gmail.com>
# This file is Copyright (c) 2018 Sean Cross <sean@xobs.io>
# This file is Copyright (c) 2018 Sergiusz Bazanski <q3k@q3k.org>
# This file is Copyright (c) 2018-2016 Tim 'mithro' Ansell <me@mith.ro>
# This file is Copyright (c) 2015 whitequark <whitequark@whitequark.org>
# This file is Copyright (c) 2018 William D. Jones <thor0505@comcast.net>
# This file is Copyright (c) 2020 Piotr Esden-Tempski <piotr@esden.net>
# License: BSD
import os
import json
import inspect
from shutil import which
from sysconfig import get_platform
from migen import *
from litex.soc.interconnect.csr import CSRStatus
from litex.build.tools import generated_banner
from litex.soc.doc.rst import reflow
from litex.soc.doc.module import gather_submodules, ModuleNotDocumented, DocumentedModule, DocumentedInterrupts
from litex.soc.doc.csr import DocumentedCSRRegion
from litex.soc.interconnect.csr import _CompoundCSR
# CPU files ----------------------------------------------------------------------------------------
def get_cpu_mak(cpu, compile_software):
# select between clang and gcc
clang = os.getenv("CLANG", "")
if clang != "":
clang = bool(int(clang))
else:
clang = None
if not hasattr(cpu, "clang_triple"):
if clang:
raise ValueError(cpu.name + "not supported with clang.")
else:
clang = False
else:
# Default to gcc unless told otherwise
if clang is None:
clang = False
assert isinstance(clang, bool)
if clang:
triple = cpu.clang_triple
flags = cpu.clang_flags
else:
triple = cpu.gcc_triple
flags = cpu.gcc_flags
# select triple when more than one
def select_triple(triple):
r = None
if not isinstance(triple, tuple):
triple = (triple,)
p = get_platform()
for i in range(len(triple)):
t = triple[i]
# use native toolchain if host and target platforms are the same
if t == 'riscv64-unknown-elf' and p == 'linux-riscv64':
r = '--native--'
break
if which(t+"-gcc"):
r = t
break
if r is None:
if not compile_software:
return "--not-found--"
msg = "Unable to find any of the cross compilation toolchains:\n"
for i in range(len(triple)):
msg += "- " + triple[i] + "\n"
raise OSError(msg)
return r
# return informations
return [
("TRIPLE", select_triple(triple)),
("CPU", cpu.name),
("CPUFLAGS", flags),
("CPUENDIANNESS", cpu.endianness),
("CLANG", str(int(clang))),
("CPU_DIRECTORY", os.path.dirname(inspect.getfile(cpu.__class__))),
]
def get_linker_output_format(cpu):
return "OUTPUT_FORMAT(\"" + cpu.linker_output_format + "\")\n"
def get_linker_regions(regions):
r = "MEMORY {\n"
for name, region in regions.items():
r += "\t{} : ORIGIN = 0x{:08x}, LENGTH = 0x{:08x}\n".format(name, region.origin, region.length)
r += "}\n"
return r
# C Export -----------------------------------------------------------------------------------------
def get_git_header():
from litex.build.tools import get_migen_git_revision, get_litex_git_revision
r = generated_banner("//")
r += "#ifndef __GENERATED_GIT_H\n#define __GENERATED_GIT_H\n\n"
r += "#define MIGEN_GIT_SHA1 \"{}\"\n".format(get_migen_git_revision())
r += "#define LITEX_GIT_SHA1 \"{}\"\n".format(get_litex_git_revision())
r += "#endif\n"
return r
def get_mem_header(regions):
r = generated_banner("//")
r += "#ifndef __GENERATED_MEM_H\n#define __GENERATED_MEM_H\n\n"
for name, region in regions.items():
r += "#ifndef {name}\n".format(name=name.upper())
r += "#define {name}_BASE 0x{base:08x}L\n#define {name}_SIZE 0x{size:08x}\n\n".format(
name=name.upper(), base=region.origin, size=region.length)
r += "#endif\n"
r += "#endif\n"
return r
def get_soc_header(constants, with_access_functions=True):
r = generated_banner("//")
r += "#ifndef __GENERATED_SOC_H\n#define __GENERATED_SOC_H\n"
for name, value in constants.items():
if value is None:
r += "#define "+name+"\n"
continue
if isinstance(value, str):
value = "\"" + value + "\""
ctype = "const char *"
else:
value = str(value)
ctype = "int"
r += "#define "+name+" "+value+"\n"
if with_access_functions:
r += "static inline "+ctype+" "+name.lower()+"_read(void) {\n"
r += "\treturn "+value+";\n}\n"
r += "\n#endif\n"
return r
def _get_rw_functions_c(reg_name, reg_base, nwords, busword, alignment, read_only, with_access_functions):
r = ""
addr_str = "CSR_{}_ADDR".format(reg_name.upper())
size_str = "CSR_{}_SIZE".format(reg_name.upper())
r += "#define {} (CSR_BASE + {}L)\n".format(addr_str, hex(reg_base))
r += "#define {} {}\n".format(size_str, nwords)
size = nwords*busword//8
if size > 8:
# downstream should select appropriate `csr_[rd|wr]_buf_uintX()` pair!
return r
elif size > 4:
ctype = "uint64_t"
elif size > 2:
ctype = "uint32_t"
elif size > 1:
ctype = "uint16_t"
else:
ctype = "uint8_t"
stride = alignment//8;
if with_access_functions:
r += "static inline {} {}_read(void) {{\n".format(ctype, reg_name)
if nwords > 1:
r += "\t{} r = csr_read_simple(CSR_BASE + {}L);\n".format(ctype, hex(reg_base))
for sub in range(1, nwords):
r += "\tr <<= {};\n".format(busword)
r += "\tr |= csr_read_simple(CSR_BASE + {}L);\n".format(hex(reg_base+sub*stride))
r += "\treturn r;\n}\n"
else:
r += "\treturn csr_read_simple(CSR_BASE + {}L);\n}}\n".format(hex(reg_base))
if not read_only:
r += "static inline void {}_write({} v) {{\n".format(reg_name, ctype)
for sub in range(nwords):
shift = (nwords-sub-1)*busword
if shift:
v_shift = "v >> {}".format(shift)
else:
v_shift = "v"
r += "\tcsr_write_simple({}, CSR_BASE + {}L);\n".format(v_shift, hex(reg_base+sub*stride))
r += "}\n"
return r
def get_csr_header(regions, constants, csr_base=None, with_access_functions=True):
alignment = constants.get("CONFIG_CSR_ALIGNMENT", 32)
r = generated_banner("//")
if with_access_functions: # FIXME
r += "#include <generated/soc.h>\n"
r += "#ifndef __GENERATED_CSR_H\n#define __GENERATED_CSR_H\n"
if with_access_functions:
r += "#include <stdint.h>\n"
r += "#include <system.h>\n"
r += "#ifndef CSR_ACCESSORS_DEFINED\n"
r += "#include <hw/common.h>\n"
r += "#endif /* ! CSR_ACCESSORS_DEFINED */\n"
csr_base = csr_base if csr_base is not None else regions[next(iter(regions))].origin
r += "#ifndef CSR_BASE\n"
r += "#define CSR_BASE {}L\n".format(hex(csr_base))
r += "#endif\n"
for name, region in regions.items():
origin = region.origin - csr_base
r += "\n/* "+name+" */\n"
r += "#define CSR_"+name.upper()+"_BASE (CSR_BASE + "+hex(origin)+"L)\n"
if not isinstance(region.obj, Memory):
for csr in region.obj:
nr = (csr.size + region.busword - 1)//region.busword
r += _get_rw_functions_c(name + "_" + csr.name, origin, nr, region.busword, alignment,
isinstance(csr, CSRStatus), with_access_functions)
origin += alignment//8*nr
if hasattr(csr, "fields"):
for field in csr.fields.fields:
r += "#define CSR_"+name.upper()+"_"+csr.name.upper()+"_"+field.name.upper()+"_OFFSET "+str(field.offset)+"\n"
r += "#define CSR_"+name.upper()+"_"+csr.name.upper()+"_"+field.name.upper()+"_SIZE "+str(field.size)+"\n"
r += "\n#endif\n"
return r
# JSON Export --------------------------------------------------------------------------------------
def get_csr_json(csr_regions={}, constants={}, mem_regions={}):
alignment = constants.get("CONFIG_CSR_ALIGNMENT", 32)
d = {
"csr_bases": {},
"csr_registers": {},
"constants": {},
"memories": {},
}
for name, region in csr_regions.items():
d["csr_bases"][name] = region.origin
region_origin = region.origin
if not isinstance(region.obj, Memory):
for csr in region.obj:
size = (csr.size + region.busword - 1)//region.busword
d["csr_registers"][name + "_" + csr.name] = {
"addr": region_origin,
"size": size,
"type": "ro" if isinstance(csr, CSRStatus) else "rw"
}
region_origin += alignment//8*size
for name, value in constants.items():
d["constants"][name.lower()] = value.lower() if isinstance(value, str) else value
for name, region in mem_regions.items():
d["memories"][name.lower()] = {
"base": region.origin,
"size": region.length,
"type": region.type,
}
return json.dumps(d, indent=4)
# CSV Export --------------------------------------------------------------------------------------
def get_csr_csv(csr_regions={}, constants={}, mem_regions={}):
d = json.loads(get_csr_json(csr_regions, constants, mem_regions))
r = generated_banner("#")
for name, value in d["csr_bases"].items():
r += "csr_base,{},0x{:08x},,\n".format(name, value)
for name in d["csr_registers"].keys():
r += "csr_register,{},0x{:08x},{},{}\n".format(name,
d["csr_registers"][name]["addr"],
d["csr_registers"][name]["size"],
d["csr_registers"][name]["type"])
for name, value in d["constants"].items():
r += "constant,{},{},,\n".format(name, value)
for name in d["memories"].keys():
r += "memory_region,{},0x{:08x},{:d},{:s}\n".format(name,
d["memories"][name]["base"],
d["memories"][name]["size"],
d["memories"][name]["type"],
)
return r
# SVD Export --------------------------------------------------------------------------------------
def get_csr_svd(soc, vendor="litex", name="soc", description=None):
def sub_csr_bit_range(busword, csr, offset):
nwords = (csr.size + busword - 1)//busword
i = nwords - offset - 1
nbits = min(csr.size - i*busword, busword) - 1
name = (csr.name + str(i) if nwords > 1 else csr.name).upper()
origin = i*busword
return (origin, nbits, name)
def print_svd_register(csr, csr_address, description, length, svd):
svd.append(' <register>')
svd.append(' <name>{}</name>'.format(csr.short_numbered_name))
if description is not None:
svd.append(' <description><![CDATA[{}]]></description>'.format(description))
svd.append(' <addressOffset>0x{:04x}</addressOffset>'.format(csr_address))
svd.append(' <resetValue>0x{:02x}</resetValue>'.format(csr.reset_value))
svd.append(' <size>{}</size>'.format(length))
svd.append(' <access>{}</access>'.format(csr.access))
csr_address = csr_address + 4
svd.append(' <fields>')
if hasattr(csr, "fields") and len(csr.fields) > 0:
for field in csr.fields:
svd.append(' <field>')
svd.append(' <name>{}</name>'.format(field.name))
svd.append(' <msb>{}</msb>'.format(field.offset +
field.size - 1))
svd.append(' <bitRange>[{}:{}]</bitRange>'.format(
field.offset + field.size - 1, field.offset))
svd.append(' <lsb>{}</lsb>'.format(field.offset))
svd.append(' <description><![CDATA[{}]]></description>'.format(
reflow(field.description)))
svd.append(' </field>')
else:
field_size = csr.size
field_name = csr.short_name.lower()
# Strip off "ev_" from eventmanager fields
if field_name == "ev_enable":
field_name = "enable"
elif field_name == "ev_pending":
field_name = "pending"
elif field_name == "ev_status":
field_name = "status"
svd.append(' <field>')
svd.append(' <name>{}</name>'.format(field_name))
svd.append(' <msb>{}</msb>'.format(field_size - 1))
svd.append(' <bitRange>[{}:{}]</bitRange>'.format(field_size - 1, 0))
svd.append(' <lsb>{}</lsb>'.format(0))
svd.append(' </field>')
svd.append(' </fields>')
svd.append(' </register>')
interrupts = {}
for csr, irq in sorted(soc.irq.locs.items()):
interrupts[csr] = irq
documented_regions = []
for name, region in soc.csr.regions.items():
documented_regions.append(DocumentedCSRRegion(
name = name,
region = region,
csr_data_width = soc.csr.data_width)
)
svd = []
svd.append('<?xml version="1.0" encoding="utf-8"?>')
svd.append('')
svd.append('<device schemaVersion="1.1" xmlns:xs="http://www.w3.org/2001/XMLSchema-instance" xs:noNamespaceSchemaLocation="CMSIS-SVD.xsd" >')
svd.append(' <vendor>{}</vendor>'.format(vendor))
svd.append(' <name>{}</name>'.format(name.upper()))
if description is not None:
svd.append(' <description><![CDATA[{}]]></description>'.format(reflow(description)))
svd.append('')
svd.append(' <addressUnitBits>8</addressUnitBits>')
svd.append(' <width>32</width>')
svd.append(' <size>32</size>')
svd.append(' <access>read-write</access>')
svd.append(' <resetValue>0x00000000</resetValue>')
svd.append(' <resetMask>0xFFFFFFFF</resetMask>')
svd.append('')
svd.append(' <peripherals>')
for region in documented_regions:
csr_address = 0
svd.append(' <peripheral>')
svd.append(' <name>{}</name>'.format(region.name.upper()))
svd.append(' <baseAddress>0x{:08X}</baseAddress>'.format(region.origin))
svd.append(' <groupName>{}</groupName>'.format(region.name.upper()))
if len(region.sections) > 0:
svd.append(' <description><![CDATA[{}]]></description>'.format(
reflow(region.sections[0].body())))
svd.append(' <registers>')
for csr in region.csrs:
description = None
if hasattr(csr, "description"):
description = csr.description
if isinstance(csr, _CompoundCSR) and len(csr.simple_csrs) > 1:
is_first = True
for i in range(len(csr.simple_csrs)):
(start, length, name) = sub_csr_bit_range(
region.busword, csr, i)
if length > 0:
bits_str = "Bits {}-{} of `{}`.".format(
start, start+length, csr.name)
else:
bits_str = "Bit {} of `{}`.".format(
start, csr.name)
if is_first:
if description is not None:
print_svd_register(
csr.simple_csrs[i], csr_address, bits_str + " " + description, length, svd)
else:
print_svd_register(
csr.simple_csrs[i], csr_address, bits_str, length, svd)
is_first = False
else:
print_svd_register(
csr.simple_csrs[i], csr_address, bits_str, length, svd)
csr_address = csr_address + 4
else:
length = ((csr.size + region.busword - 1) //
region.busword) * region.busword
print_svd_register(
csr, csr_address, description, length, svd)
csr_address = csr_address + 4
svd.append(' </registers>')
svd.append(' <addressBlock>')
svd.append(' <offset>0</offset>')
svd.append(' <size>0x{:x}</size>'.format(csr_address))
svd.append(' <usage>registers</usage>')
svd.append(' </addressBlock>')
if region.name in interrupts:
svd.append(' <interrupt>')
svd.append(' <name>{}</name>'.format(region.name))
svd.append(' <value>{}</value>'.format(interrupts[region.name]))
svd.append(' </interrupt>')
svd.append(' </peripheral>')
svd.append(' </peripherals>')
svd.append('</device>')
return "\n".join(svd)
# Memory.x Export ----------------------------------------------------------------------------------
def get_memory_x(soc):
r = get_linker_regions(soc.mem_regions)
r += '\n'
r += 'REGION_ALIAS("REGION_TEXT", spiflash);\n'
r += 'REGION_ALIAS("REGION_RODATA", spiflash);\n'
r += 'REGION_ALIAS("REGION_DATA", sram);\n'
r += 'REGION_ALIAS("REGION_BSS", sram);\n'
r += 'REGION_ALIAS("REGION_HEAP", sram);\n'
r += 'REGION_ALIAS("REGION_STACK", sram);\n\n'
r += '/* CPU reset location. */\n'
r += '_stext = {:#08x};\n'.format(soc.cpu.reset_address)
return r