1#!/usr/bin/env python3
2# Copyright (c) 2022 Intel corporation
3# SPDX-License-Identifier: Apache-2.0
4import argparse
5import re
6
7# Scratch register allocator.  Zephyr uses multiple Xtensa SRs as
8# scratch space for various special purposes.  Unfortunately the
9# configurable architecture means that not all registers will be the
10# same on every device.  This script parses a pre-cooked ("gcc -E
11# -dM") core-isa.h file for the current architecture and assigns
12# registers to usages.
13
14def parse_args():
15    parser = argparse.ArgumentParser(allow_abbrev=False)
16
17    parser.add_argument("--coherence", action="store_true",
18                        help="Enable scratch registers for CONFIG_KERNEL_COHERENCE")
19    parser.add_argument("--mmu", action="store_true",
20                        help="Enable scratch registers for MMU usage")
21    parser.add_argument("--syscall-scratch", action="store_true",
22                        help="Enable scratch registers for syscalls if needed")
23    parser.add_argument("coreisa",
24                        help="Path to preprocessed core-isa.h")
25    parser.add_argument("outfile",
26                        help="Output file")
27
28    return parser.parse_args()
29
30args = parse_args()
31
32NEEDED = ["A0SAVE", "CPU"]
33if args.mmu:
34    NEEDED += ["DBLEXC", "DEPC_SAVE", "EXCCAUSE_SAVE"]
35if args.coherence:
36    NEEDED += ["FLUSH"]
37
38coreisa = args.coreisa
39outfile = args.outfile
40
41syms = {}
42
43def get(s):
44    return syms[s] if s in syms else 0
45
46with open(coreisa) as infile:
47    for line in infile.readlines():
48        m = re.match(r"^#define\s+([^ ]+)\s*(.*)", line.rstrip())
49        if m:
50            syms[m.group(1)] = m.group(2)
51
52# Use MISC registers first if available, that's what they're for
53regs = [ f"MISC{n}" for n in range(0, int(get("XCHAL_NUM_MISC_REGS"))) ]
54
55if args.syscall_scratch:
56    # If there is no THREADPTR, we need to use syscall for
57    # arch_is_user_context() where the code needs a scratch
58    # register.
59    have_threadptr = int(get("XCHAL_HAVE_THREADPTR"))
60    if have_threadptr == 0:
61        NEEDED.append("SYSCALL_SCRATCH")
62
63# Next come EXCSAVE. Also record our highest non-debug interrupt level.
64maxint = 0
65for il in range(1, 1 + int(get("XCHAL_NUM_INTLEVELS"))):
66    regs.append(f"EXCSAVE{il}")
67    if il != int(get("XCHAL_DEBUGLEVEL")):
68        maxint = max(il, maxint)
69
70# Find the highest priority software interrupt.  We'll use that for
71# arch_irq_offload().
72irqoff_level = -1
73irqoff_int = -1
74for sym, val in syms.items():
75    if val == "XTHAL_INTTYPE_SOFTWARE":
76        m = re.match(r"XCHAL_INT(\d+)_TYPE", sym)
77        if m:
78            intnum = int(m.group(1))
79            levelsym = f"XCHAL_INT{intnum}_LEVEL"
80            if levelsym in syms:
81                intlevel = int(syms[levelsym])
82                if intlevel > irqoff_level:
83                    irqoff_int = intnum
84                    irqoff_level = intlevel
85
86# Now emit our output header with the assignments we chose
87with open(outfile, "w") as f:
88    f.write("/* Generated File, see gen_zsr.py */\n")
89    f.write("#ifndef ZEPHYR_ZSR_H\n")
90    f.write("#define ZEPHYR_ZSR_H\n")
91    for i, need in enumerate(NEEDED):
92        f.write(f"# define ZSR_{need} {regs[i]}\n")
93        f.write(f"# define ZSR_{need}_STR \"{regs[i]}\"\n")
94
95    # Emit any remaining registers as generics
96    for i in range(len(NEEDED), len(regs)):
97        f.write(f"# define ZSR_EXTRA{i - len(NEEDED)} {regs[i]}\n")
98        f.write(f"# define ZSR_EXTRA{i - len(NEEDED)}_STR \"{regs[i]}\"\n")
99
100    # Also, our highest level EPC/EPS registers
101    f.write(f"# define ZSR_RFI_LEVEL {maxint}\n")
102    f.write(f"# define ZSR_EPC EPC{maxint}\n")
103    f.write(f"# define ZSR_EPS EPS{maxint}\n")
104
105    # And the irq offset interrupt
106    if irqoff_int >= 0:
107        f.write(f"# define ZSR_IRQ_OFFLOAD_INT {irqoff_int}\n")
108
109    f.write("#endif\n")
110