cpu/stm32/dist: add generator for kconfig cpu lines and models
This commit is contained in:
parent
a83927d031
commit
06c3361a15
17
cpu/stm32/dist/kconfig/Kconfig.lines.j2
vendored
Normal file
17
cpu/stm32/dist/kconfig/Kconfig.lines.j2
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
# Copyright (c) 2020 Inria
|
||||
#
|
||||
# This file is subject to the terms and conditions of the GNU Lesser
|
||||
# General Public License v2.1. See the file LICENSE in the top level
|
||||
# directory for more details.
|
||||
#
|
||||
|
||||
# This file was auto-generated from ST ProductsList.xlsx sheet using the
|
||||
# script in cpu/stm32/dist/kconfig/gen_kconfig.py
|
||||
# See cpu/stm32/dist/kconfig/README.md for details
|
||||
|
||||
# CPU lines
|
||||
{%- for line in lines %}
|
||||
config CPU_LINE_{{ line }}
|
||||
bool
|
||||
select CPU_FAM_{{ fam | upper }}
|
||||
{% endfor -%}
|
||||
23
cpu/stm32/dist/kconfig/Kconfig.models.j2
vendored
Normal file
23
cpu/stm32/dist/kconfig/Kconfig.models.j2
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
# Copyright (c) 2020 Inria
|
||||
#
|
||||
# This file is subject to the terms and conditions of the GNU Lesser
|
||||
# General Public License v2.1. See the file LICENSE in the top level
|
||||
# directory for more details.
|
||||
#
|
||||
|
||||
# This file was auto-generated from ST ProductsList.xlsx sheet using the
|
||||
# script in cpu/stm32/dist/kconfig/gen_kconfig.py
|
||||
# See cpu/stm32/dist/kconfig/README.md for details
|
||||
|
||||
# CPU models
|
||||
{%- for item in models %}
|
||||
config CPU_MODEL_{{ item.model }}
|
||||
bool
|
||||
select {{ item.line | upper }}
|
||||
{% endfor %}
|
||||
|
||||
# Configure CPU model
|
||||
config CPU_MODEL
|
||||
{%- for item in models %}
|
||||
default "{{ item.model | lower }}" if CPU_MODEL_{{ item.model }}
|
||||
{%- endfor %}
|
||||
78
cpu/stm32/dist/kconfig/README.md
vendored
Normal file
78
cpu/stm32/dist/kconfig/README.md
vendored
Normal file
@ -0,0 +1,78 @@
|
||||
## STM32 CPU lines and models Kconfig generator
|
||||
|
||||
The script `gen_kconfig.py` can be used to automatically generate the Kconfig
|
||||
files describing CPU lines and models, per families (f0, f1, etc) and from
|
||||
the ProductsList.xlsx sheet that are downloadable on the ST website.
|
||||
|
||||
### Prepare the data
|
||||
|
||||
The sheet are available from
|
||||
https://www.st.com/en/microcontrollers-microprocessors/stm32-32-bit-arm-cortex-mcus.html.
|
||||
Just select a CPU series (e.g family in RIOT) in the menu on the left and go in
|
||||
the product selector tab, then click the `Export` button to download the Excel
|
||||
sheet (the default filename is `ProductsList.xlsx`).
|
||||
|
||||
The available CPU lines are extracted from the
|
||||
`cpu/stm32/include/vendor/cmsis/<fam>/Include` directory. This means that the
|
||||
headers of a given family are already fetched here. This can be done with the
|
||||
following `make` commands:
|
||||
|
||||
```
|
||||
$ cd $RIOTBASE
|
||||
$ RIOTBASE=$(pwd) RIOTTOOLS=$(pwd)/dist/tools CPU_FAM=<cpu_fam> make -C cpu/stm32/include/vendor
|
||||
```
|
||||
|
||||
`<cpu_fam>` can be any family in `f0`, `f1`, etc
|
||||
|
||||
|
||||
### `gen_kconfig.py` dependencies
|
||||
|
||||
The script depends on `jinja2` templating engine to generate the Kconfig files
|
||||
and `xlrd` to load and parse the Excel sheets. The dependencies can be
|
||||
installed with `pip`:
|
||||
|
||||
```
|
||||
$ pip install -r ./cpu/stm32/dist/kconfig/requirements.txt
|
||||
```
|
||||
|
||||
### `gen_kconfig.py` usage
|
||||
|
||||
The script can be used to generate the `Kconfig.lines` and `Kconfig.models` of
|
||||
a given family as follows:
|
||||
|
||||
```
|
||||
$ cd $RIOTBASE
|
||||
$ ./cpu/stm32/dist/kconfig/gen_kconfig.py <cpu_fam> --sheets <path-to-sheet>/ProductsList.xlsx
|
||||
```
|
||||
|
||||
The `--sheets` option can take several files. This allows to handle the L4 case
|
||||
where the list of models is available in 2 separate sheets. So for L4 family,
|
||||
the command should be:
|
||||
|
||||
```
|
||||
$ cd $RIOTBASE
|
||||
$ ./cpu/stm32/dist/kconfig/gen_kconfig.py l4 --sheets <path-to-sheet>/L4ProductsList.xlsx <path-to-sheet>/L4+ProductsList.xlsx
|
||||
```
|
||||
|
||||
By default, if the `Kconfig.lines` and `Kconfig.models` files of a given family
|
||||
were not already created, they are created.
|
||||
If the `Kconfig.lines` and `Kconfig.models` files of a given family are already
|
||||
available in RIOT, by default the script will just dump the content to stdout.
|
||||
The files can still be overwritten by using the `--overwrite` flag.
|
||||
|
||||
Print the detailed usage with `--help`:
|
||||
|
||||
```
|
||||
$ ./cpu/stm32/dist/kconfig/gen_kconfig.py --help
|
||||
usage: gen_kconfig.py [-h] [--sheets SHEETS [SHEETS ...]] [--overwrite] [--quiet] cpu_fam
|
||||
|
||||
positional arguments:
|
||||
cpu_fam STM32 CPU Family
|
||||
|
||||
optional arguments:
|
||||
-h, --help show this help message and exit
|
||||
--sheets SHEETS [SHEETS ...], -s SHEETS [SHEETS ...]
|
||||
Excel sheet containing the list of products
|
||||
--overwrite, -o Overwrite any existing Kconfig file
|
||||
--quiet, -q Be quiet
|
||||
```
|
||||
154
cpu/stm32/dist/kconfig/gen_kconfig.py
vendored
Executable file
154
cpu/stm32/dist/kconfig/gen_kconfig.py
vendored
Executable file
@ -0,0 +1,154 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright (C) 2020 Inria
|
||||
#
|
||||
# This file is subject to the terms and conditions of the GNU Lesser
|
||||
# General Public License v2.1. See the file LICENSE in the top level
|
||||
# directory for more details.
|
||||
|
||||
import os
|
||||
import argparse
|
||||
|
||||
import xlrd
|
||||
from jinja2 import FileSystemLoader, Environment
|
||||
|
||||
|
||||
CURRENT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
RIOTBASE = os.getenv(
|
||||
"RIOTBASE", os.path.abspath(os.path.join(CURRENT_DIR, "../../../..")))
|
||||
STM32_KCONFIG_DIR = os.path.join(RIOTBASE, "cpu/stm32/kconfig")
|
||||
STM32_VENDOR_DIR = os.path.join(RIOTBASE, "cpu/stm32/include/vendor/cmsis")
|
||||
|
||||
|
||||
def parse_sheet(cpu_fam, sheets):
|
||||
"""Parse the Excel sheet and return a dict."""
|
||||
models = []
|
||||
for sheet in sheets:
|
||||
# Load the content of the xlsx sheet
|
||||
work_book = xlrd.open_workbook(sheet)
|
||||
sheet = work_book.sheet_by_name('ProductsList')
|
||||
|
||||
# Extract models from sheet
|
||||
for rownum in range(sheet.nrows):
|
||||
row = sheet.row_values(rownum)
|
||||
if not row[0].startswith("STM32"):
|
||||
continue
|
||||
models.append(row[0].replace("-", "_"))
|
||||
return sorted(models)
|
||||
|
||||
|
||||
def parse_cpu_lines(cpu_fam):
|
||||
"""Return the list of available CPU lines."""
|
||||
headers_dir = os.path.join(STM32_VENDOR_DIR, cpu_fam, "Include")
|
||||
cpu_lines = [
|
||||
header[:-2].upper() for header in os.listdir(headers_dir)
|
||||
if (
|
||||
header.startswith("stm32") and
|
||||
header != "stm32{}xx.h".format(cpu_fam)
|
||||
)
|
||||
]
|
||||
return sorted(cpu_lines)
|
||||
|
||||
|
||||
def _match(model, line):
|
||||
"""Return True if a cpu model matches a cpu line, False otherwise."""
|
||||
model = model.replace("_", "")
|
||||
family_model = model[6:9]
|
||||
family_model_letter1 = model[9]
|
||||
family_model_letter2 = model[10] if len(model) >= 11 else None
|
||||
family_model_letter3 = model[11] if len(model) == 12 else None
|
||||
|
||||
family_line = line[6:9]
|
||||
family_line_letter1 = line[9]
|
||||
family_line_letter2 = line[10] if len(line) >= 11 else None
|
||||
family_line_letter3 = line[11] if len(line) == 12 else None
|
||||
|
||||
if family_line_letter1 == "X" and family_line_letter2 == "X":
|
||||
letters_match = True
|
||||
elif family_line_letter1 == "X":
|
||||
if family_model_letter3 is not None or family_line_letter3 is not None:
|
||||
letters_match = (
|
||||
(family_line_letter2 == family_model_letter2) and
|
||||
(family_line_letter3 == family_model_letter3)
|
||||
)
|
||||
else:
|
||||
letters_match = family_line_letter2 == family_model_letter2
|
||||
elif family_line_letter2 == "X":
|
||||
letters_match = family_line_letter1 == family_model_letter1
|
||||
else:
|
||||
letters_match = False
|
||||
return family_model == family_line and letters_match
|
||||
|
||||
|
||||
def get_context(cpu_fam, models, lines):
|
||||
"""Return a dict where keys are the models and values are the lines/fam."""
|
||||
mapping = []
|
||||
for model in models:
|
||||
found_line = False
|
||||
for line in lines:
|
||||
if _match(model, line):
|
||||
mapping.append(
|
||||
{"model": model, "line": "CPU_LINE_{}".format(line)}
|
||||
)
|
||||
found_line = True
|
||||
# if a model has no matching line, just match it to the upper cpu
|
||||
# fam level
|
||||
if not found_line:
|
||||
mapping.append(
|
||||
{"model": model, "line": "CPU_FAM_{}".format(cpu_fam)}
|
||||
)
|
||||
return {"models": mapping, "lines": lines, "fam": cpu_fam}
|
||||
|
||||
|
||||
def generate_kconfig(kconfig, context, overwrite, verbose):
|
||||
"""Generic kconfig file generator."""
|
||||
loader = FileSystemLoader(searchpath=CURRENT_DIR)
|
||||
env = Environment(
|
||||
loader=loader, trim_blocks=False, lstrip_blocks=True,
|
||||
keep_trailing_newline=True
|
||||
)
|
||||
template_file = os.path.join("Kconfig.{}.j2".format(kconfig))
|
||||
env.globals.update(zip=zip)
|
||||
template = env.get_template(template_file)
|
||||
render = template.render(**context)
|
||||
|
||||
kconfig_file = os.path.join(
|
||||
STM32_KCONFIG_DIR, context["fam"], "Kconfig.{}".format(kconfig)
|
||||
)
|
||||
|
||||
if (not os.path.exists(kconfig_file) or
|
||||
(os.path.exists(kconfig_file) and
|
||||
overwrite is True and
|
||||
kconfig == "models")):
|
||||
with open(kconfig_file, "w") as f_dest:
|
||||
f_dest.write(render)
|
||||
if verbose:
|
||||
print("{}:".format(os.path.basename(kconfig_file)))
|
||||
print("-" * (len(os.path.basename(kconfig_file)) + 1) + "\n")
|
||||
print(render)
|
||||
|
||||
|
||||
def main(args):
|
||||
"""Main function."""
|
||||
models = parse_sheet(args.cpu_fam, args.sheets)
|
||||
lines = parse_cpu_lines(args.cpu_fam)
|
||||
context = get_context(args.cpu_fam, models, lines)
|
||||
if args.verbose:
|
||||
print("Generated kconfig files:\n")
|
||||
for kconfig in ["lines", "models"]:
|
||||
generate_kconfig(kconfig, context, args.overwrite, args.verbose)
|
||||
|
||||
|
||||
PARSER = argparse.ArgumentParser()
|
||||
PARSER.add_argument("cpu_fam",
|
||||
help="STM32 CPU Family")
|
||||
PARSER.add_argument("--sheets", "-s", nargs='+',
|
||||
help="Excel sheet containing the list of products")
|
||||
PARSER.add_argument("--overwrite", "-o", action="store_true",
|
||||
help="Overwrite any existing Kconfig file")
|
||||
PARSER.add_argument("--verbose", "-v", action="store_true",
|
||||
help="Print generated file content")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main(PARSER.parse_args())
|
||||
2
cpu/stm32/dist/kconfig/requirements.txt
vendored
Normal file
2
cpu/stm32/dist/kconfig/requirements.txt
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
xlrd
|
||||
jinja2
|
||||
Loading…
x
Reference in New Issue
Block a user