Some system register field encodings change based on the available and
in-use architecture features. In order to support these different
field encodings, introduce the Feat descriptor (Feat, ElseFeat,
EndFeat) for describing such sysregs.
The Feat descriptor can be used in the following way (Feat acts as
both an if and an else-if):
Sysreg EXAMPLE 0 1 2 3 4
Feat FEAT_A
Field 63:0 Foo
Feat FEAT_B
Field 63:1 Bar
Res0 0
ElseFeat
Field 63:0 Baz
EndFeat
EndSysreg
This will generate a single set of system register encodings (REG_,
SYS_, ...), and then generate three sets of field definitions for the
system register called EXAMPLE. The first set is prefixed by FEAT_A,
e.g. FEAT_A_EXAMPLE_Foo. The second set is prefixed by FEAT_B, e.g.,
FEAT_B_EXAMPLE_Bar. The third set is not given a prefix at all,
e.g. EXAMPLE_BAZ. For each set, a corresponding set of defines for
Res0, Res1, and Unkn is generated.
The intent for the final prefix-less ElseFeat is for describing
default or legacy field encodings. This ensure that new
feature-conditional encodings can be added to already-present sysregs
without affecting existing legacy code.
The Feat descriptor can be used within Sysreg or SysregFields
blocks. Field, Res0, Res1, Unkn, Rax, SignedEnum, Enum can all be used
within a Feat block. Fields and Mapping can not. Fields that vary with
features must be described as part of a SysregFields block,
instead. Mappings, which are just a code comment, make little sense in
this context, and have hence not been included.
There are no changes to the generated system register definitions as
part of this change.
Signed-off-by: Sascha Bischoff <sascha.bischoff@arm.com>
---
arch/arm64/tools/gen-sysreg.awk | 148 ++++++++++++++++++++++----------
1 file changed, 104 insertions(+), 44 deletions(-)
diff --git a/arch/arm64/tools/gen-sysreg.awk b/arch/arm64/tools/gen-sysreg.awk
index f2a1732cb1f63..c1bb6d5087a99 100755
--- a/arch/arm64/tools/gen-sysreg.awk
+++ b/arch/arm64/tools/gen-sysreg.awk
@@ -44,23 +44,35 @@ function expect_fields(nf) {
# 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
+function define(feat, name, val) {
+ printf "%-56s%s\n", "#define " feat 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)
+function define_field(feat, reg, field, msb, lsb) {
+ define(feat, reg "_" field, "GENMASK(" msb ", " lsb ")")
+ define(feat, reg "_" field "_MASK", "GENMASK(" msb ", " lsb ")")
+ define(feat, reg "_" field "_SHIFT", lsb)
+ define(feat, 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)
+function define_field_sign(feat, reg, field, sign) {
+ define(feat, reg "_" field "_SIGNED", sign)
}
+# Print the Res0, Res1, Unkn masks
+function define_resx_unkn(feat, reg, res0, res1, unkn) {
+ if (res0 != null)
+ define(feat, reg "_RES0", "(" res0 ")")
+ if (res1 != null)
+ define(feat, reg "_RES1", "(" res1 ")")
+ if (unkn != null)
+ define(feat, reg "_UNKN", "(" unkn ")")
+ if (res0 != null || res1 != null || unkn != null)
+ print ""
+ }
+
# Parse a "<msb>[:<lsb>]" string into the global variables @msb and @lsb
function parse_bitdef(reg, field, bitdef, _bits)
{
@@ -132,10 +144,7 @@ $1 == "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 ""
+ define_resx_unkn(feat, reg, res0, res1, unkn)
reg = null
res0 = null
@@ -162,14 +171,16 @@ $1 == "Sysreg" && block_current() == "Root" {
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 ")")
+ feat = null
+
+ define(feat, "REG_" reg, "S" op0 "_" op1 "_C" crn "_C" crm "_" op2)
+ define(feat, "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)
+ define(feat, "SYS_" reg "_Op0", op0)
+ define(feat, "SYS_" reg "_Op1", op1)
+ define(feat, "SYS_" reg "_CRn", crn)
+ define(feat, "SYS_" reg "_CRm", crm)
+ define(feat, "SYS_" reg "_Op2", op2)
print ""
@@ -183,14 +194,7 @@ $1 == "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 ""
+ define_resx_unkn(feat, reg, res0, res1, unkn)
reg = null
op0 = null
@@ -201,6 +205,7 @@ $1 == "EndSysreg" && block_current() == "Sysreg" {
res0 = null
res1 = null
unkn = null
+ feat = null
block_pop()
next
@@ -225,8 +230,7 @@ $1 == "EndSysreg" && block_current() == "Sysreg" {
next
}
-
-$1 == "Res0" && (block_current() == "Sysreg" || block_current() == "SysregFields") {
+$1 == "Res0" && (block_current() == "Sysreg" || block_current() == "SysregFields" || block_current() == "Feat") {
expect_fields(2)
parse_bitdef(reg, "RES0", $2)
field = "RES0_" msb "_" lsb
@@ -236,7 +240,7 @@ $1 == "Res0" && (block_current() == "Sysreg" || block_current() == "SysregFields
next
}
-$1 == "Res1" && (block_current() == "Sysreg" || block_current() == "SysregFields") {
+$1 == "Res1" && (block_current() == "Sysreg" || block_current() == "SysregFields" || block_current() == "Feat") {
expect_fields(2)
parse_bitdef(reg, "RES1", $2)
field = "RES1_" msb "_" lsb
@@ -246,7 +250,7 @@ $1 == "Res1" && (block_current() == "Sysreg" || block_current() == "SysregFields
next
}
-$1 == "Unkn" && (block_current() == "Sysreg" || block_current() == "SysregFields") {
+$1 == "Unkn" && (block_current() == "Sysreg" || block_current() == "SysregFields" || block_current() == "Feat") {
expect_fields(2)
parse_bitdef(reg, "UNKN", $2)
field = "UNKN_" msb "_" lsb
@@ -256,58 +260,58 @@ $1 == "Unkn" && (block_current() == "Sysreg" || block_current() == "SysregFields
next
}
-$1 == "Field" && (block_current() == "Sysreg" || block_current() == "SysregFields") {
+$1 == "Field" && (block_current() == "Sysreg" || block_current() == "SysregFields" || block_current() == "Feat") {
expect_fields(3)
field = $3
parse_bitdef(reg, field, $2)
- define_field(reg, field, msb, lsb)
+ define_field(feat, reg, field, msb, lsb)
print ""
next
}
-$1 == "Raz" && (block_current() == "Sysreg" || block_current() == "SysregFields") {
+$1 == "Raz" && (block_current() == "Sysreg" || block_current() == "SysregFields" || block_current() == "Feat") {
expect_fields(2)
parse_bitdef(reg, field, $2)
next
}
-$1 == "SignedEnum" && (block_current() == "Sysreg" || block_current() == "SysregFields") {
+$1 == "SignedEnum" && (block_current() == "Sysreg" || block_current() == "SysregFields" || block_current() == "Feat") {
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")
+ define_field(feat, reg, field, msb, lsb)
+ define_field_sign(feat, reg, field, "true")
next
}
-$1 == "UnsignedEnum" && (block_current() == "Sysreg" || block_current() == "SysregFields") {
+$1 == "UnsignedEnum" && (block_current() == "Sysreg" || block_current() == "SysregFields" || block_current() == "Feat") {
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")
+ define_field(feat, reg, field, msb, lsb)
+ define_field_sign(feat, reg, field, "false")
next
}
-$1 == "Enum" && (block_current() == "Sysreg" || block_current() == "SysregFields") {
+$1 == "Enum" && (block_current() == "Sysreg" || block_current() == "SysregFields" || block_current() == "Feat") {
block_push("Enum")
expect_fields(3)
field = $3
parse_bitdef(reg, field, $2)
- define_field(reg, field, msb, lsb)
+ define_field(feat, reg, field, msb, lsb)
next
}
@@ -329,7 +333,63 @@ $1 == "EndEnum" && block_current() == "Enum" {
val = $1
name = $2
- define(reg "_" field "_" name, "UL(" val ")")
+ define(feat, reg "_" field "_" name, "UL(" val ")")
+ next
+}
+
+$1 == "Feat" && (block_current() == "Sysreg" || block_current() == "SysregFields" || block_current() == "Feat") {
+ # Don't push a new block if we're already in a Feat
+ # block. This is to support constructs such as:
+ # Feat FEAT_A
+ # ...
+ # Feat FEAT_B
+ # ...
+ # ElseFeat
+ # ...
+ # EndFeat
+ if (block_current() != "Feat")
+ block_push("Feat")
+ else
+ define_resx_unkn(feat, reg, res0, res1, unkn)
+
+ expect_fields(2)
+ feat = $2 "_"
+
+ next_bit = 63
+
+ res0 = "UL(0)"
+ res1 = "UL(0)"
+ unkn = "UL(0)"
+
+ next
+}
+
+$1 == "ElseFeat" && block_current() == "Feat" {
+ expect_fields(1)
+
+ define_resx_unkn(feat, reg, res0, res1, unkn)
+
+ res0 = "UL(0)"
+ res1 = "UL(0)"
+ unkn = "UL(0)"
+ feat = null
+ next_bit = 63
+
+ next
+}
+
+$1 == "EndFeat" && block_current() == "Feat" {
+ expect_fields(1)
+
+ define_resx_unkn(feat, reg, res0, res1, unkn)
+
+ res0 = null
+ res1 = null
+ unkn = null
+ feat = null
+
+ block_pop()
+
next
}
--
2.34.1
On Tue, Oct 07, 2025 at 03:35:14PM +0000, Sascha Bischoff wrote:
> Some system register field encodings change based on the available and
> in-use architecture features. In order to support these different
> field encodings, introduce the Feat descriptor (Feat, ElseFeat,
> EndFeat) for describing such sysregs.
We have other system registers which change layouts based on things
other than features, ESR_ELx is probably the most entertaining of these
but there's others like PAR_EL1. Ideally we should probably do the
logic for generating the conditionals in a manner that's not tied to
features with a layer on top that generates standard naming for common
patterns like FEAT, but OTOH part of why that's not been done because
it's got a bunch of nasty issues so perhaps just doing the simpler case
is fine.
> The Feat descriptor can be used in the following way (Feat acts as
> both an if and an else-if):
> Sysreg EXAMPLE 0 1 2 3 4
> Feat FEAT_A
> Field 63:0 Foo
> Feat FEAT_B
This assumes that there will never be nesting of these conditions in the
architecture. I'm not sure I would want to assume that, even for plain
features though I can't think of any examples where it's an issue.
There are more serious issues with the implementation for practical
patterns with nesting (see below) which mean we might not want to deal
with that now but we should define the syntax for the file in a way that
will cope so I'd prefer not to have the implicit elses.
I'd also be inclined to say that something that's specificly for
features shouldn't repeat the FEAT_ so:
Feat A
instead, but that's purely a taste question.
> - define("REG_" reg, "S" op0 "_" op1 "_C" crn "_C" crm "_" op2)
> - define("SYS_" reg, "sys_reg(" op0 ", " op1 ", " crn ", " crm ", " op2 ")")
> + feat = null
> +
> + define(feat, "REG_" reg, "S" op0 "_" op1 "_C" crn "_C" crm "_" op2)
> + define(feat, "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)
> + define(feat, "SYS_" reg "_Op0", op0)
> + define(feat, "SYS_" reg "_Op1", op1)
> + define(feat, "SYS_" reg "_CRn", crn)
> + define(feat, "SYS_" reg "_CRm", crm)
> + define(feat, "SYS_" reg "_Op2", op2)
Possibly it's worth having a reg_define() or something given the number
of things needing updating here?
> @@ -201,6 +205,7 @@ $1 == "EndSysreg" && block_current() == "Sysreg" {
> res0 = null
> res1 = null
> unkn = null
> + feat = null
>
> block_pop()
> next
Probably worth complaining if we end a sysreg with a feature/prefix
defined.
> +$1 == "Feat" && (block_current() == "Sysreg" || block_current() == "SysregFields" || block_current() == "Feat") {
...
> + next_bit = 63
> +
> + res0 = "UL(0)"
> + res1 = "UL(0)"
> + unkn = "UL(0)"
This is only going to work if the whole register is in a FEAT_ block, so
you coudn't have:
Sysreg EXAMPLE 0 1 2 3 4
Res0 63
Feat FEAT_A
Field 62:1 Foo
Feat FEAT_B
Field 62:32 Foo
Field 31:1 Bar
EndFeat
Res0 0
EndSysreg
but then supporting partial registers does have entertaining
consequences for handling Res0 and Res1. If we're OK with that
restriction then the program should complain if someone tries to
define a smaller FEAT block.
On Tue, 2025-10-07 at 17:28 +0100, Mark Brown wrote:
> On Tue, Oct 07, 2025 at 03:35:14PM +0000, Sascha Bischoff wrote:
>
> > Some system register field encodings change based on the available
> > and
> > in-use architecture features. In order to support these different
> > field encodings, introduce the Feat descriptor (Feat, ElseFeat,
> > EndFeat) for describing such sysregs.
>
> We have other system registers which change layouts based on things
> other than features, ESR_ELx is probably the most entertaining of
> these
> but there's others like PAR_EL1. Ideally we should probably do the
> logic for generating the conditionals in a manner that's not tied to
> features with a layer on top that generates standard naming for
> common
> patterns like FEAT, but OTOH part of why that's not been done because
> it's got a bunch of nasty issues so perhaps just doing the simpler
> case
> is fine.
Hi Mark - thanks for your input!
I agree that there are plenty of cases where sysreg fields change based
on more than just architectural features. Whilst my intent for this
change wasn't strictly to cover all of those cases, it could arguably
be used for that. Effectively, all this change is doing is to add a
prefix to the field definitions so it can be used however works best.
It does feel as if Feat was potentially the wrong name to choose.
Something like Fieldset, Prefix, AltLayout, etc might be better. I'm
rather open to suggestions here - naming is hard!
>
> > The Feat descriptor can be used in the following way (Feat acts as
> > both an if and an else-if):
>
> > Sysreg EXAMPLE 0 1 2 3 4
> > Feat FEAT_A
> > Field 63:0 Foo
> > Feat FEAT_B
>
> This assumes that there will never be nesting of these conditions in
> the architecture.
My thinking was that for something like that one could do:
Feat FEAT_A_B
Or similar. Yes, it means that anything that is nested needs to be
flattened, but it does allow one to describe that situation. In the
end, all this effectively does is to add a prefix, and said prefix can
be anything. Double so if we move away from calling it Feat, and use
something more generic instead.
> I'm not sure I would want to assume that, even for plain
> features though I can't think of any examples where it's an issue.
> There are more serious issues with the implementation for practical
> patterns with nesting (see below) which mean we might not want to
> deal
> with that now but we should define the syntax for the file in a way
> that
> will cope so I'd prefer not to have the implicit elses.
I think we could do something more like this (sticking with calling it
Feat for now):
Sysreg EXAMPLE ...
Feat A
...
EndFeat
Feat B
..
EndFeat
Field ...
EndSysreg
This way each set of field layouts is explicitly wrapped in a block,
except for the "default" ones (if present/desired). It is a bit more
verbose, but removes as much of the implicit nature as possible, and
removes the need for the Else which is more in keeping with the rest of
the blocks. I'll do this for v2.
>
> I'd also be inclined to say that something that's specificly for
> features shouldn't repeat the FEAT_ so:
>
> Feat A
>
> instead, but that's purely a taste question.
Yup. I think the correct thing here does somewhat depend on how generic
this becomes.
>
> > - define("REG_" reg, "S" op0 "_" op1 "_C" crn "_C" crm "_"
> > op2)
> > - define("SYS_" reg, "sys_reg(" op0 ", " op1 ", " crn ", "
> > crm ", " op2 ")")
> > + feat = null
> > +
> > + define(feat, "REG_" reg, "S" op0 "_" op1 "_C" crn "_C" crm
> > "_" op2)
> > + define(feat, "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)
> > + define(feat, "SYS_" reg "_Op0", op0)
> > + define(feat, "SYS_" reg "_Op1", op1)
> > + define(feat, "SYS_" reg "_CRn", crn)
> > + define(feat, "SYS_" reg "_CRm", crm)
> > + define(feat, "SYS_" reg "_Op2", op2)
>
> Possibly it's worth having a reg_define() or something given the
> number of things needing updating here?
>
> > @@ -201,6 +205,7 @@ $1 == "EndSysreg" && block_current() ==
> > "Sysreg" {
> > res0 = null
> > res1 = null
> > unkn = null
> > + feat = null
> >
> > block_pop()
> > next
>
> Probably worth complaining if we end a sysreg with a feature/prefix
> defined.
This is already be caught as we hit the EndSysreg directive while still
in the Feat block:
Error at 4690: unhandled statement
Error at 4690: Missing terminator for Feat block
I can be more specific in catching that, if you like.
I have missed a check to catch someone defining fewer than 64-bits in a
Feat block, though - will be fixed in v2.
>
> > +$1 == "Feat" && (block_current() == "Sysreg" || block_current() ==
> > "SysregFields" || block_current() == "Feat") {
>
> ...
>
> > + next_bit = 63
> > +
> > + res0 = "UL(0)"
> > + res1 = "UL(0)"
> > + unkn = "UL(0)"
>
> This is only going to work if the whole register is in a FEAT_ block,
> so
> you coudn't have:
>
> Sysreg EXAMPLE 0 1 2 3 4
> Res0 63
> Feat FEAT_A
> Field 62:1 Foo
> Feat FEAT_B
> Field 62:32 Foo
> Field 31:1 Bar
> EndFeat
> Res0 0
> EndSysreg
>
> but then supporting partial registers does have entertaining
> consequences for handling Res0 and Res1. If we're OK with that
> restriction then the program should complain if someone tries to
> define a smaller FEAT block.
It was my intent to only support whole registers here. Anything else
gets insanely complex rather quickly, and much of the benefit gets lost
IMO. By only operating on whole Sysregs (or SysregFields) we are able
to re-use the existing logic to ensure that definitions are complete,
and the generator's state tracking remains simple. And, as you say, it
would break the ResX, Unkn side of things if we did anything else.
When I add the missing check I mentioned above, then it will complain
if fewer than 64 bits are in a defined block.
Thanks,
Sascha
On Thu, Oct 09, 2025 at 09:48:36AM +0000, Sascha Bischoff wrote: > On Tue, 2025-10-07 at 17:28 +0100, Mark Brown wrote: > > logic for generating the conditionals in a manner that's not tied to > > features with a layer on top that generates standard naming for > > common > > patterns like FEAT, but OTOH part of why that's not been done because > > it's got a bunch of nasty issues so perhaps just doing the simpler > > case > > is fine. > I agree that there are plenty of cases where sysreg fields change based > on more than just architectural features. Whilst my intent for this > change wasn't strictly to cover all of those cases, it could arguably > be used for that. Effectively, all this change is doing is to add a > prefix to the field definitions so it can be used however works best. Yeah, I don't know that we need to cover all the cases in the user visible syntax right now - my thought was more about the code. > It does feel as if Feat was potentially the wrong name to choose. > Something like Fieldset, Prefix, AltLayout, etc might be better. I'm > rather open to suggestions here - naming is hard! It is :) > > > The Feat descriptor can be used in the following way (Feat acts as > > > both an if and an else-if): > > > Sysreg EXAMPLE 0 1 2 3 4 > > > Feat FEAT_A > > > Field 63:0 Foo > > > Feat FEAT_B > > This assumes that there will never be nesting of these conditions in > > the architecture. > My thinking was that for something like that one could do: > Feat FEAT_A_B > Or similar. Yes, it means that anything that is nested needs to be > flattened, but it does allow one to describe that situation. In the > end, all this effectively does is to add a prefix, and said prefix can > be anything. Double so if we move away from calling it Feat, and use > something more generic instead. You could definitely do it that way, and for the limited implementation you're proposing it probably works as well. My thinking here is about what happens with the syntax when someone comes along and implements nesting of fatures - the existing syntax would be broken since you couldn't distinguish between an implicit else and nested FEAT_. > > Probably worth complaining if we end a sysreg with a feature/prefix > > defined. > This is already be caught as we hit the EndSysreg directive while still > in the Feat block: > Error at 4690: unhandled statement > Error at 4690: Missing terminator for Feat block > I can be more specific in catching that, if you like. No, it's fine so long sa there's a warning. > > This is only going to work if the whole register is in a FEAT_ block, > > so > > you coudn't have: ... > > but then supporting partial registers does have entertaining > > consequences for handling Res0 and Res1. If we're OK with that > > restriction then the program should complain if someone tries to > > define a smaller FEAT block. > It was my intent to only support whole registers here. Anything else > gets insanely complex rather quickly, and much of the benefit gets lost > IMO. By only operating on whole Sysregs (or SysregFields) we are able > to re-use the existing logic to ensure that definitions are complete, > and the generator's state tracking remains simple. And, as you say, it > would break the ResX, Unkn side of things if we did anything else. > When I add the missing check I mentioned above, then it will complain > if fewer than 64 bits are in a defined block. Yes, I think it's a reasonable simplification for the implementation and so long as the syntax in the file can cope with someone doing the more complicated cases it seems fine to leave them for later. It's not like it's a small bit of extra work it'd be easy to tack on, there's some thorny issues there.
© 2016 - 2026 Red Hat, Inc.