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