#!/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")
}