013ecd4439
We'd like to support Enum/SignedEnum/UnsignedEnum blocks within SysregFields blocks, so that we can define enumerations for sets of registers. This isn't currently supported by gen-sysreg.awk due to the way we track the active block, which can't handle more than a single layer of nesting, which imposes an awkward requirement that when ending a block we know what the parent block is when calling change_block() Make this nicer by using a stack of active blocks, with block_push() to start a block, and block_pop() to end a block. Doing so means that we only need to check the active block at the start of parsing a line: for the start of a block we can check the parent is valid, and for the end of a block we check that the active block is valid. This structure makes the block parsing simpler and makes it easy to permit a block to live under several potential parents (e.g. by permitting Enum to start when the active block is Sysreg or SysregFields). It also permits further nesting, if we need that in future. To aid debugging, the stack of active blocks is reported for fatal errors, and an error is raised if the file is terminated without ending the active block. For clarity I've renamed the top-level element from "None" to "Root". The Fields element it intended only for use within Sysreg blocks, and does not make sense within SysregFields blocks, and so remains forbidden within a SysregFields block. I've verified using sha1sum that this patch does not change the current generated contents of <asm/sysreg-defs.h>. Signed-off-by: Mark Rutland <mark.rutland@arm.com> Cc: Anshuman Khandual <anshuman.khandual@arm.com> Cc: Catalin Marinas <catalin.marinas@arm.com> Cc: Marc Zyngier <maz@kernel.org> Cc: Mark Brown <broonie@kernel.org> Cc: Will Deacon <will@kernel.org> Reviewed-by: Anshuman Khandual <anshuman.khandual@arm.com> Reviewed-by: Mark Brown <broonie@kernel.org> Link: https://lore.kernel.org/r/20230306114836.2575432-1-mark.rutland@arm.com Signed-off-by: Will Deacon <will@kernel.org>
337 lines
6.5 KiB
Awk
Executable File
337 lines
6.5 KiB
Awk
Executable File
#!/bin/awk -f
|
|
# SPDX-License-Identifier: GPL-2.0
|
|
# gen-sysreg.awk: arm64 sysreg header generator
|
|
#
|
|
# Usage: awk -f gen-sysreg.awk sysregs.txt
|
|
|
|
function block_current() {
|
|
return __current_block[__current_block_depth];
|
|
}
|
|
|
|
# Log an error and terminate
|
|
function fatal(msg) {
|
|
print "Error at " NR ": " msg > "/dev/stderr"
|
|
|
|
printf "Current block nesting:"
|
|
|
|
for (i = 0; i <= __current_block_depth; i++) {
|
|
printf " " __current_block[i]
|
|
}
|
|
printf "\n"
|
|
|
|
exit 1
|
|
}
|
|
|
|
# Enter a new block, setting the active block to @block
|
|
function block_push(block) {
|
|
__current_block[++__current_block_depth] = block
|
|
}
|
|
|
|
# Exit a block, setting the active block to the parent block
|
|
function block_pop() {
|
|
if (__current_block_depth == 0)
|
|
fatal("error: block_pop() in root block")
|
|
|
|
__current_block_depth--;
|
|
}
|
|
|
|
# Sanity check the number of records for a field makes sense. If not, produce
|
|
# an error and terminate.
|
|
function expect_fields(nf) {
|
|
if (NF != nf)
|
|
fatal(NF " fields found where " nf " expected")
|
|
}
|
|
|
|
# Print a CPP macro definition, padded with spaces so that the macro bodies
|
|
# line up in a column
|
|
function define(name, val) {
|
|
printf "%-56s%s\n", "#define " name, val
|
|
}
|
|
|
|
# Print standard BITMASK/SHIFT/WIDTH CPP definitions for a field
|
|
function define_field(reg, field, msb, lsb) {
|
|
define(reg "_" field, "GENMASK(" msb ", " lsb ")")
|
|
define(reg "_" field "_MASK", "GENMASK(" msb ", " lsb ")")
|
|
define(reg "_" field "_SHIFT", lsb)
|
|
define(reg "_" field "_WIDTH", msb - lsb + 1)
|
|
}
|
|
|
|
# Print a field _SIGNED definition for a field
|
|
function define_field_sign(reg, field, sign) {
|
|
define(reg "_" field "_SIGNED", sign)
|
|
}
|
|
|
|
# Parse a "<msb>[:<lsb>]" string into the global variables @msb and @lsb
|
|
function parse_bitdef(reg, field, bitdef, _bits)
|
|
{
|
|
if (bitdef ~ /^[0-9]+$/) {
|
|
msb = bitdef
|
|
lsb = bitdef
|
|
} else if (split(bitdef, _bits, ":") == 2) {
|
|
msb = _bits[1]
|
|
lsb = _bits[2]
|
|
} else {
|
|
fatal("invalid bit-range definition '" bitdef "'")
|
|
}
|
|
|
|
|
|
if (msb != next_bit)
|
|
fatal(reg "." field " starts at " msb " not " next_bit)
|
|
if (63 < msb || msb < 0)
|
|
fatal(reg "." field " invalid high bit in '" bitdef "'")
|
|
if (63 < lsb || lsb < 0)
|
|
fatal(reg "." field " invalid low bit in '" bitdef "'")
|
|
if (msb < lsb)
|
|
fatal(reg "." field " invalid bit-range '" bitdef "'")
|
|
if (low > high)
|
|
fatal(reg "." field " has invalid range " high "-" low)
|
|
|
|
next_bit = lsb - 1
|
|
}
|
|
|
|
BEGIN {
|
|
print "#ifndef __ASM_SYSREG_DEFS_H"
|
|
print "#define __ASM_SYSREG_DEFS_H"
|
|
print ""
|
|
print "/* Generated file - do not edit */"
|
|
print ""
|
|
|
|
__current_block_depth = 0
|
|
__current_block[__current_block_depth] = "Root"
|
|
}
|
|
|
|
END {
|
|
if (__current_block_depth != 0)
|
|
fatal("Missing terminator for " block_current() " block")
|
|
|
|
print "#endif /* __ASM_SYSREG_DEFS_H */"
|
|
}
|
|
|
|
# skip blank lines and comment lines
|
|
/^$/ { next }
|
|
/^[\t ]*#/ { next }
|
|
|
|
/^SysregFields/ && block_current() == "Root" {
|
|
block_push("SysregFields")
|
|
|
|
expect_fields(2)
|
|
|
|
reg = $2
|
|
|
|
res0 = "UL(0)"
|
|
res1 = "UL(0)"
|
|
unkn = "UL(0)"
|
|
|
|
next_bit = 63
|
|
|
|
next
|
|
}
|
|
|
|
/^EndSysregFields/ && block_current() == "SysregFields" {
|
|
if (next_bit > 0)
|
|
fatal("Unspecified bits in " reg)
|
|
|
|
define(reg "_RES0", "(" res0 ")")
|
|
define(reg "_RES1", "(" res1 ")")
|
|
define(reg "_UNKN", "(" unkn ")")
|
|
print ""
|
|
|
|
reg = null
|
|
res0 = null
|
|
res1 = null
|
|
unkn = null
|
|
|
|
block_pop()
|
|
next
|
|
}
|
|
|
|
/^Sysreg/ && block_current() == "Root" {
|
|
block_push("Sysreg")
|
|
|
|
expect_fields(7)
|
|
|
|
reg = $2
|
|
op0 = $3
|
|
op1 = $4
|
|
crn = $5
|
|
crm = $6
|
|
op2 = $7
|
|
|
|
res0 = "UL(0)"
|
|
res1 = "UL(0)"
|
|
unkn = "UL(0)"
|
|
|
|
define("REG_" reg, "S" op0 "_" op1 "_C" crn "_C" crm "_" op2)
|
|
define("SYS_" reg, "sys_reg(" op0 ", " op1 ", " crn ", " crm ", " op2 ")")
|
|
|
|
define("SYS_" reg "_Op0", op0)
|
|
define("SYS_" reg "_Op1", op1)
|
|
define("SYS_" reg "_CRn", crn)
|
|
define("SYS_" reg "_CRm", crm)
|
|
define("SYS_" reg "_Op2", op2)
|
|
|
|
print ""
|
|
|
|
next_bit = 63
|
|
|
|
next
|
|
}
|
|
|
|
/^EndSysreg/ && block_current() == "Sysreg" {
|
|
if (next_bit > 0)
|
|
fatal("Unspecified bits in " reg)
|
|
|
|
if (res0 != null)
|
|
define(reg "_RES0", "(" res0 ")")
|
|
if (res1 != null)
|
|
define(reg "_RES1", "(" res1 ")")
|
|
if (unkn != null)
|
|
define(reg "_UNKN", "(" unkn ")")
|
|
if (res0 != null || res1 != null || unkn != null)
|
|
print ""
|
|
|
|
reg = null
|
|
op0 = null
|
|
op1 = null
|
|
crn = null
|
|
crm = null
|
|
op2 = null
|
|
res0 = null
|
|
res1 = null
|
|
unkn = null
|
|
|
|
block_pop()
|
|
next
|
|
}
|
|
|
|
# Currently this is effectivey a comment, in future we may want to emit
|
|
# defines for the fields.
|
|
/^Fields/ && block_current() == "Sysreg" {
|
|
expect_fields(2)
|
|
|
|
if (next_bit != 63)
|
|
fatal("Some fields already defined for " reg)
|
|
|
|
print "/* For " reg " fields see " $2 " */"
|
|
print ""
|
|
|
|
next_bit = 0
|
|
res0 = null
|
|
res1 = null
|
|
unkn = null
|
|
|
|
next
|
|
}
|
|
|
|
|
|
/^Res0/ && (block_current() == "Sysreg" || block_current() == "SysregFields") {
|
|
expect_fields(2)
|
|
parse_bitdef(reg, "RES0", $2)
|
|
field = "RES0_" msb "_" lsb
|
|
|
|
res0 = res0 " | GENMASK_ULL(" msb ", " lsb ")"
|
|
|
|
next
|
|
}
|
|
|
|
/^Res1/ && (block_current() == "Sysreg" || block_current() == "SysregFields") {
|
|
expect_fields(2)
|
|
parse_bitdef(reg, "RES1", $2)
|
|
field = "RES1_" msb "_" lsb
|
|
|
|
res1 = res1 " | GENMASK_ULL(" msb ", " lsb ")"
|
|
|
|
next
|
|
}
|
|
|
|
/^Unkn/ && (block_current() == "Sysreg" || block_current() == "SysregFields") {
|
|
expect_fields(2)
|
|
parse_bitdef(reg, "UNKN", $2)
|
|
field = "UNKN_" msb "_" lsb
|
|
|
|
unkn = unkn " | GENMASK_ULL(" msb ", " lsb ")"
|
|
|
|
next
|
|
}
|
|
|
|
/^Field/ && (block_current() == "Sysreg" || block_current() == "SysregFields") {
|
|
expect_fields(3)
|
|
field = $3
|
|
parse_bitdef(reg, field, $2)
|
|
|
|
define_field(reg, field, msb, lsb)
|
|
print ""
|
|
|
|
next
|
|
}
|
|
|
|
/^Raz/ && (block_current() == "Sysreg" || block_current() == "SysregFields") {
|
|
expect_fields(2)
|
|
parse_bitdef(reg, field, $2)
|
|
|
|
next
|
|
}
|
|
|
|
/^SignedEnum/ && (block_current() == "Sysreg" || block_current() == "SysregFields") {
|
|
block_push("Enum")
|
|
|
|
expect_fields(3)
|
|
field = $3
|
|
parse_bitdef(reg, field, $2)
|
|
|
|
define_field(reg, field, msb, lsb)
|
|
define_field_sign(reg, field, "true")
|
|
|
|
next
|
|
}
|
|
|
|
/^UnsignedEnum/ && (block_current() == "Sysreg" || block_current() == "SysregFields") {
|
|
block_push("Enum")
|
|
|
|
expect_fields(3)
|
|
field = $3
|
|
parse_bitdef(reg, field, $2)
|
|
|
|
define_field(reg, field, msb, lsb)
|
|
define_field_sign(reg, field, "false")
|
|
|
|
next
|
|
}
|
|
|
|
/^Enum/ && (block_current() == "Sysreg" || block_current() == "SysregFields") {
|
|
block_push("Enum")
|
|
|
|
expect_fields(3)
|
|
field = $3
|
|
parse_bitdef(reg, field, $2)
|
|
|
|
define_field(reg, field, msb, lsb)
|
|
|
|
next
|
|
}
|
|
|
|
/^EndEnum/ && block_current() == "Enum" {
|
|
|
|
field = null
|
|
msb = null
|
|
lsb = null
|
|
print ""
|
|
|
|
block_pop()
|
|
next
|
|
}
|
|
|
|
/0b[01]+/ && block_current() == "Enum" {
|
|
expect_fields(2)
|
|
val = $1
|
|
name = $2
|
|
|
|
define(reg "_" field "_" name, "UL(" val ")")
|
|
next
|
|
}
|
|
|
|
# Any lines not handled by previous rules are unexpected
|
|
{
|
|
fatal("unhandled statement")
|
|
}
|