[PATCH 1/3] arm64/sysreg: Support feature-specific fields with 'Feat' descriptor

Sascha Bischoff posted 3 patches 4 months ago
[PATCH 1/3] arm64/sysreg: Support feature-specific fields with 'Feat' descriptor
Posted by Sascha Bischoff 4 months ago
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
Re: [PATCH 1/3] arm64/sysreg: Support feature-specific fields with 'Feat' descriptor
Posted by Mark Brown 4 months ago
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.
Re: [PATCH 1/3] arm64/sysreg: Support feature-specific fields with 'Feat' descriptor
Posted by Sascha Bischoff 4 months ago
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
Re: [PATCH 1/3] arm64/sysreg: Support feature-specific fields with 'Feat' descriptor
Posted by Mark Brown 4 months ago
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.