diff --git a/schemas/param-common-schema.json b/schemas/param-common-schema.json index 0a6d9af..0e335a0 100644 --- a/schemas/param-common-schema.json +++ b/schemas/param-common-schema.json @@ -77,10 +77,53 @@ "type": "string", "enum": ["WARL", "WLRL"] }, - "csrType": { - "type": "array", - "items": { "type": "integer" }, - "minItems": 1 + "csrEnum": { + "type": "object", + "properties": { + "legal": { + "type": "array", + "items": { "$ref": "#/multiBaseValue" }, + "minItems": 1, + "description": "Array of one or more legal values (decimal integer, 0x hex, or 0b binary)" + }, + "illegal-write-ignore": { + "const": true, + "description": "Must be true; indicates illegal writes are silently ignored" + }, + "illegal-write-return": { + "$ref": "#/multiBaseValue", + "description": "If specified, illegal writes return this value (decimal integer, 0x hex, or 0b binary)" + } + }, + "required": ["legal"], + "oneOf": [ + { "required": ["legal", "illegal-write-ignore"] }, + { "required": ["legal", "illegal-write-return"] } + ] + }, + "csrEnumOutput": { + "type": "object", + "properties": { + "legal": { + "type": "array", + "items": { "type": "integer" }, + "minItems": 1, + "description": "Array of one or more legal integers (converted to decimal)" + }, + "illegal-write-ignore": { + "const": true, + "description": "Must be true; indicates illegal writes are silently ignored" + }, + "illegal-write-return": { + "type": "integer", + "description": "Value returned on illegal writes (converted to decimal)" + } + }, + "required": ["legal"], + "oneOf": [ + { "required": ["legal", "illegal-write-ignore"] }, + { "required": ["legal", "illegal-write-return"] } + ] }, "multiBaseValue": { "oneOf": [ diff --git a/schemas/param-defs-schema.json b/schemas/param-defs-schema.json index 463fc45..97979e2 100644 --- a/schemas/param-defs-schema.json +++ b/schemas/param-defs-schema.json @@ -148,9 +148,9 @@ "$ref": "param-common-schema.json#/csrNameArray", "description": "Multiple CSR field names" }, - "type": { - "$ref": "param-common-schema.json#/csrType", - "description": "List of possible legal values" + "enum": { + "$ref": "param-common-schema.json#/csrEnum", + "description": "Enum definition specifying legal write values and associated behavior (including handling of illegal writes)" }, "width": { "$ref": "param-common-schema.json#/paramName", @@ -186,11 +186,13 @@ } }, { - "oneOf": [ - { "required": ["type"] }, - { "required": ["width"] }, - { "required": ["ro-mask"] } - ] + "not": { + "anyOf": [ + { "required": ["enum", "width"] }, + { "required": ["enum", "ro-mask"] }, + { "required": ["width", "ro-mask"] } + ] + } }, { "if": { diff --git a/schemas/params-schema.json b/schemas/params-schema.json index 3083417..d4021b8 100644 --- a/schemas/params-schema.json +++ b/schemas/params-schema.json @@ -134,8 +134,9 @@ "$ref": "param-common-schema.json#/csrCategory", "description": "CSR category" }, - "type": { - "$ref": "param-common-schema.json#/csrType" + "enum": { + "$ref": "param-common-schema.json#/csrEnumOutput", + "description": "CSR legal values and illegal-write behavior" }, "width": { "$ref": "param-common-schema.json#/paramName", @@ -173,35 +174,13 @@ } }, { - "oneOf": [ - { - "required": ["type"], - "not": { - "anyOf": [ - { "required": ["width"] }, - { "required": ["ro-mask"] } - ] - } - }, - { - "required": ["width"], - "not": { - "anyOf": [ - { "required": ["type"] }, - { "required": ["ro-mask"] } - ] - } - }, - { - "required": ["ro-mask"], - "not": { - "anyOf": [ - { "required": ["type"] }, - { "required": ["width"] } - ] - } - } - ] + "not": { + "anyOf": [ + { "required": ["enum", "width"] }, + { "required": ["enum", "ro-mask"] }, + { "required": ["width", "ro-mask"] } + ] + } } ], "additionalProperties": false diff --git a/tests/norm-rule/expected/test-ch2-norm-tags.json b/tests/norm-rule/expected/test-ch2-norm-tags.json index 4d910a9..c3ba5fc 100644 --- a/tests/norm-rule/expected/test-ch2-norm-tags.json +++ b/tests/norm-rule/expected/test-ch2-norm-tags.json @@ -17,10 +17,12 @@ "norm:mock-csr-x": "Here’s a normative rule for mock CSR \"X\".", "norm:mock-csr-field-y": "Here’s a normative rule for mock CSR field \"Y\".", "norm:mock-ext-dep-A-on-B": "Here’s a normative rule for mock extension dependency of extension A on extension B.", - "norm:foo_abc_warl_legal_list": "The architecturally-defined values for the 4-bit foo.ABC CSR field are 0, 3, or 10.\nAn implementation can support any subset of these values.", - "norm:foo_def_warl_width_uint4to8": "The foo.DEF CSR field width ranges from 4 to 8 bits as specified by the UINT_4TO8 parameter value.", + "norm:foo_abc_warl_enum": "The architecturally-defined values for the 4-bit foo.ABC\nCSR field are 0, 0b11, or 0xffff_ffff. Returns 0x7fff if written with an illegal value.", + "norm:foo_def_warl_enum": "The architecturally-defined values for the 8-bit foo.DEF\nCSR field are -1 or 0xf0. Ignores writes with an illegal value.", + "norm:foo_ghi_warl_width_uint4to8": "The foo.GHI CSR field width ranges from 4 to 8 bits as specified by the UINT_4TO8 parameter value.", "norm:zort_xyz_wlrl_readonly_zero": "An implementation may treat each bit of the zort.XYZ CSR as\nread-only or read-write. Read-only bits can be 0 or 1.", - "norm:bar_warl_readonly_value": "An implementation may treat each bit of the bar CSR as\nread-only or read-write. Read-only bits are 0." + "norm:bar_warl_readonly_value": "An implementation may treat each bit of the bar CSR as\nread-only or read-write. Read-only bits are 0.", + "norm:qux_qqq_wlrl_no_selector": "The qux.QQQ CSR field has implementation-defined behavior\nwithout an enum, width, or read-only mask selector." }, "sections": { "title": "", @@ -66,10 +68,12 @@ "id": "_chapter_2_3_csr_field_types", "children": [], "tags": [ - "norm:foo_abc_warl_legal_list", - "norm:foo_def_warl_width_uint4to8", + "norm:foo_abc_warl_enum", + "norm:foo_def_warl_enum", + "norm:foo_ghi_warl_width_uint4to8", "norm:zort_xyz_wlrl_readonly_zero", - "norm:bar_warl_readonly_value" + "norm:bar_warl_readonly_value", + "norm:qux_qqq_wlrl_no_selector" ] } ], diff --git a/tests/norm-rule/expected/test-norm-rules.html b/tests/norm-rule/expected/test-norm-rules.html index a872342..0430ee0 100644 --- a/tests/norm-rule/expected/test-norm-rules.html +++ b/tests/norm-rule/expected/test-norm-rules.html @@ -155,7 +155,7 @@

Implementation-Defined Behaviors

-

94 Normative Rules: Includes 31 Implementation-Defined Behaviors (26 No Category, 4 WARL, 1 WLRL)

+

96 Normative Rules: Includes 33 Implementation-Defined Behaviors (26 No Category, 5 WARL, 2 WLRL)

@@ -821,7 +821,7 @@

94 Normative Rules: Includes 31 Implementation-D

- + @@ -1048,7 +1048,7 @@

94 Normative Rules: Includes 31 Implementation-D

- + @@ -1065,11 +1065,32 @@

94 Normative Rules: Includes 31 Implementation-D

- - + + - + + + + + + + + + + + + + + + + + + + + + + @@ -1078,8 +1099,8 @@

94 Normative Rules: Includes 31 Implementation-D

- - + + @@ -1123,6 +1144,27 @@

94 Normative Rules: Includes 31 Implementation-D

+ + + + + + + + + + + + + + + + + + + + +
Chapter Two: 20 Normative Rules: Includes 15 Implementation-Defined Behaviors (11 No Category, 3 WARL, 1 WLRL)Chapter Two: 22 Normative Rules: Includes 17 Implementation-Defined Behaviors (11 No Category, 4 WARL, 2 WLRL)
norm:mock-ext-dep-A-on-B
FOO_ABC_WARL_ENUM csr_field Kind
Implementation-defined behavior category
The architecturally-defined values for the 4-bit foo.ABC CSR field are 0, 3, or 10.
An implementation can support any subset of these values.
norm:foo_abc_warl_legal_listThe architecturally-defined values for the 4-bit foo.ABC
CSR field are 0, 0b11, or 0xffff_ffff. Returns 0x7fff if written with an illegal value.
norm:foo_abc_warl_enum
FOO_DEF_WARL_WIDTH_UINT4TO8FOO_DEF_WARL_ENUMcsr_fieldKind
foo.DEFInstance
Implementation-defined behaviorImplementation-defined behavior
WARLImplementation-defined behavior category
The architecturally-defined values for the 8-bit foo.DEF
CSR field are -1 or 0xf0. Ignores writes with an illegal value.
norm:foo_def_warl_enum
FOO_GHI_WARL_WIDTH_UINT4TO8 Implementation-defined behavior Implementation-defined behavior
Implementation-defined behavior category
The foo.DEF CSR field width ranges from 4 to 8 bits as specified by the UINT_4TO8 parameter value.norm:foo_def_warl_width_uint4to8The foo.GHI CSR field width ranges from 4 to 8 bits as specified by the UINT_4TO8 parameter value.norm:foo_ghi_warl_width_uint4to8
BAR_WARL_READONLY_VALUEAn implementation may treat each bit of the zort.XYZ CSR as
read-only or read-write. Read-only bits can be 0 or 1.
norm:zort_xyz_wlrl_readonly_zero
QUX_QQQ_WLRL_NO_SELECTORcsr_fieldKind
qux.QQQInstance
Implementation-defined behaviorImplementation-defined behavior
WLRLImplementation-defined behavior category
The qux.QQQ CSR field has implementation-defined behavior
without an enum, width, or read-only mask selector.
norm:qux_qqq_wlrl_no_selector
@@ -1479,7 +1521,7 @@

94 Normative Rules: Includes 31 Implementation-D
- + @@ -1503,7 +1545,7 @@

94 Normative Rules: Includes 31 Implementation-D

- + @@ -1512,13 +1554,26 @@

94 Normative Rules: Includes 31 Implementation-D

- - + + + + + + + + + + + + + + + - - - + + + @@ -1539,7 +1594,7 @@

94 Normative Rules: Includes 31 Implementation-D

WARL Category (A-Z): All 4 Implementation-Defined BehaviorsWARL Category (A-Z): All 5 Implementation-Defined Behaviors
norm:bar_warl_readonly_value
FOO_ABC_WARL_LEGAL_LISTFOO_ABC_WARL_ENUM csr_field Kind
Instance
The architecturally-defined values for the 4-bit foo.ABC CSR field are 0, 3, or 10.
An implementation can support any subset of these values.
norm:foo_abc_warl_legal_listThe architecturally-defined values for the 4-bit foo.ABC
CSR field are 0, 0b11, or 0xffff_ffff. Returns 0x7fff if written with an illegal value.
norm:foo_abc_warl_enum
FOO_DEF_WARL_ENUMcsr_fieldKind
foo.DEFInstance
The architecturally-defined values for the 8-bit foo.DEF
CSR field are -1 or 0xf0. Ignores writes with an illegal value.
norm:foo_def_warl_enum
FOO_DEF_WARL_WIDTH_UINT4TO8The foo.DEF CSR field width ranges from 4 to 8 bits as specified by the UINT_4TO8 parameter value.norm:foo_def_warl_width_uint4to8FOO_GHI_WARL_WIDTH_UINT4TO8The foo.GHI CSR field width ranges from 4 to 8 bits as specified by the UINT_4TO8 parameter value.norm:foo_ghi_warl_width_uint4to8
SATP_ASID_WARL_ASIDLEN
- + @@ -1549,6 +1604,19 @@

94 Normative Rules: Includes 31 Implementation-D

+ + + + + + + + + + + + + @@ -1800,7 +1868,7 @@

94 Normative Rules: Includes 31 Implementation-D

WLRL Category (A-Z): All 1 Implementation-Defined BehaviorsWLRL Category (A-Z): All 2 Implementation-Defined Behaviors
NameInformationInformation Source
QUX_QQQ_WLRL_NO_SELECTORcsr_fieldKind
qux.QQQInstance
The qux.QQQ CSR field has implementation-defined behavior
without an enum, width, or read-only mask selector.
norm:qux_qqq_wlrl_no_selector
ZORT_XYZ_WLRL_READONLY_ZERO csr_field
- + @@ -1946,7 +2014,7 @@

94 Normative Rules: Includes 31 Implementation-D

- + @@ -1959,17 +2027,34 @@

94 Normative Rules: Includes 31 Implementation-D

- - + + + + + + + + + + + - - - + + + + + + + + + + + @@ -2005,6 +2090,23 @@

94 Normative Rules: Includes 31 Implementation-D

+ + + + + + + + + + + + + + + + +
Chapter Two: All 15 Implementation-Defined BehaviorsChapter Two: All 17 Implementation-Defined Behaviors
norm:mock-ext-dep-A-on-B
FOO_ABC_WARL_LEGAL_LISTFOO_ABC_WARL_ENUM csr_field Kind
Implementation-defined behavior category
The architecturally-defined values for the 4-bit foo.ABC CSR field are 0, 3, or 10.
An implementation can support any subset of these values.
norm:foo_abc_warl_legal_listThe architecturally-defined values for the 4-bit foo.ABC
CSR field are 0, 0b11, or 0xffff_ffff. Returns 0x7fff if written with an illegal value.
norm:foo_abc_warl_enum
FOO_DEF_WARL_ENUMcsr_fieldKind
foo.DEFInstance
FOO_DEF_WARL_WIDTH_UINT4TO8 WARL Implementation-defined behavior category
The foo.DEF CSR field width ranges from 4 to 8 bits as specified by the UINT_4TO8 parameter value.norm:foo_def_warl_width_uint4to8The architecturally-defined values for the 8-bit foo.DEF
CSR field are -1 or 0xf0. Ignores writes with an illegal value.
norm:foo_def_warl_enum
FOO_GHI_WARL_WIDTH_UINT4TO8WARLImplementation-defined behavior category
The foo.GHI CSR field width ranges from 4 to 8 bits as specified by the UINT_4TO8 parameter value.norm:foo_ghi_warl_width_uint4to8
BAR_WARL_READONLY_VALUEAn implementation may treat each bit of the zort.XYZ CSR as
read-only or read-write. Read-only bits can be 0 or 1.
norm:zort_xyz_wlrl_readonly_zero
QUX_QQQ_WLRL_NO_SELECTORcsr_fieldKind
qux.QQQInstance
WLRLImplementation-defined behavior category
The qux.QQQ CSR field has implementation-defined behavior
without an enum, width, or read-only mask selector.
norm:qux_qqq_wlrl_no_selector
diff --git a/tests/norm-rule/expected/test-norm-rules.json b/tests/norm-rule/expected/test-norm-rules.json index 60f034f..6249d6f 100644 --- a/tests/norm-rule/expected/test-norm-rules.json +++ b/tests/norm-rule/expected/test-norm-rules.json @@ -1528,7 +1528,7 @@ ] }, { - "name": "FOO_ABC_WARL_LEGAL_LIST", + "name": "FOO_ABC_WARL_ENUM", "def_filename": "tests/norm-rule/test-ch2.yaml", "chapter_name": "Two", "kind": "csr_field", @@ -1539,25 +1539,45 @@ "impl-def-category": "WARL", "tags": [ { - "name": "norm:foo_abc_warl_legal_list", + "name": "norm:foo_abc_warl_enum", "context": false, - "text": "The architecturally-defined values for the 4-bit foo.ABC CSR field are 0, 3, or 10.\nAn implementation can support any subset of these values.", + "text": "The architecturally-defined values for the 4-bit foo.ABC\nCSR field are 0, 0b11, or 0xffff_ffff. Returns 0x7fff if written with an illegal value.", "tag_filename": "/build/test-ch2-norm-tags.json", "stds_doc_url": "test-ch2.html" } ] }, { - "name": "FOO_DEF_WARL_WIDTH_UINT4TO8", + "name": "FOO_DEF_WARL_ENUM", + "def_filename": "tests/norm-rule/test-ch2.yaml", + "chapter_name": "Two", + "kind": "csr_field", + "impl-def-behavior": true, + "instances": [ + "foo.DEF" + ], + "impl-def-category": "WARL", + "tags": [ + { + "name": "norm:foo_def_warl_enum", + "context": false, + "text": "The architecturally-defined values for the 8-bit foo.DEF\nCSR field are -1 or 0xf0. Ignores writes with an illegal value.", + "tag_filename": "/build/test-ch2-norm-tags.json", + "stds_doc_url": "test-ch2.html" + } + ] + }, + { + "name": "FOO_GHI_WARL_WIDTH_UINT4TO8", "def_filename": "tests/norm-rule/test-ch2.yaml", "chapter_name": "Two", "impl-def-behavior": true, "impl-def-category": "WARL", "tags": [ { - "name": "norm:foo_def_warl_width_uint4to8", + "name": "norm:foo_ghi_warl_width_uint4to8", "context": false, - "text": "The foo.DEF CSR field width ranges from 4 to 8 bits as specified by the UINT_4TO8 parameter value.", + "text": "The foo.GHI CSR field width ranges from 4 to 8 bits as specified by the UINT_4TO8 parameter value.", "tag_filename": "/build/test-ch2-norm-tags.json", "stds_doc_url": "test-ch2.html" } @@ -1602,6 +1622,26 @@ "stds_doc_url": "test-ch2.html" } ] + }, + { + "name": "QUX_QQQ_WLRL_NO_SELECTOR", + "def_filename": "tests/norm-rule/test-ch2.yaml", + "chapter_name": "Two", + "kind": "csr_field", + "impl-def-behavior": true, + "instances": [ + "qux.QQQ" + ], + "impl-def-category": "WLRL", + "tags": [ + { + "name": "norm:qux_qqq_wlrl_no_selector", + "context": false, + "text": "The qux.QQQ CSR field has implementation-defined behavior\nwithout an enum, width, or read-only mask selector.", + "tag_filename": "/build/test-ch2-norm-tags.json", + "stds_doc_url": "test-ch2.html" + } + ] } ] } \ No newline at end of file diff --git a/tests/norm-rule/test-ch2.adoc b/tests/norm-rule/test-ch2.adoc index 510ebaa..0f7f699 100644 --- a/tests/norm-rule/test-ch2.adoc +++ b/tests/norm-rule/test-ch2.adoc @@ -82,13 +82,19 @@ Here's a normative rule for mock extension dependency of extension A on extensio === Chapter 2.3 - CSR Field Types -[#norm:foo_abc_warl_legal_list]#The architecturally-defined values for the 4-bit `foo.ABC` CSR field are 0, 3, or 10. -An implementation can support any subset of these values.# +[#norm:foo_abc_warl_enum]#The architecturally-defined values for the 4-bit `foo.ABC` +CSR field are 0, 0b11, or 0xffff_ffff. Returns 0x7fff if written with an illegal value.# -[#norm:foo_def_warl_width_uint4to8]#The `foo.DEF` CSR field width ranges from 4 to 8 bits as specified by the UINT_4TO8 parameter value.# +[#norm:foo_def_warl_enum]#The architecturally-defined values for the 8-bit `foo.DEF` +CSR field are -1 or 0xf0. Ignores writes with an illegal value.# + +[#norm:foo_ghi_warl_width_uint4to8]#The `foo.GHI` CSR field width ranges from 4 to 8 bits as specified by the UINT_4TO8 parameter value.# [#norm:zort_xyz_wlrl_readonly_zero]#An implementation may treat each bit of the `zort.XYZ` CSR as read-only or read-write. Read-only bits can be 0 or 1.# [#norm:bar_warl_readonly_value]#An implementation may treat each bit of the `bar` CSR as read-only or read-write. Read-only bits are 0.# + +[#norm:qux_qqq_wlrl_no_selector]#The `qux.QQQ` CSR field has implementation-defined behavior +without an enum, width, or read-only mask selector.# diff --git a/tests/norm-rule/test-ch2.yaml b/tests/norm-rule/test-ch2.yaml index cbc9c64..3b80474 100644 --- a/tests/norm-rule/test-ch2.yaml +++ b/tests/norm-rule/test-ch2.yaml @@ -71,14 +71,20 @@ normative_rule_definitions: instance: MockExtDepAOnB # CSR field types - - name: FOO_ABC_WARL_LEGAL_LIST - tag: "norm:foo_abc_warl_legal_list" + - name: FOO_ABC_WARL_ENUM + tag: "norm:foo_abc_warl_enum" impl-def-behavior: true impl-def-category: WARL kind: csr_field instance: foo.ABC - - name: FOO_DEF_WARL_WIDTH_UINT4TO8 - tag: "norm:foo_def_warl_width_uint4to8" + - name: FOO_DEF_WARL_ENUM + tag: "norm:foo_def_warl_enum" + impl-def-behavior: true + impl-def-category: WARL + kind: csr_field + instance: foo.DEF + - name: FOO_GHI_WARL_WIDTH_UINT4TO8 + tag: "norm:foo_ghi_warl_width_uint4to8" impl-def-behavior: true impl-def-category: WARL - name: BAR_WARL_READONLY_VALUE @@ -93,3 +99,9 @@ normative_rule_definitions: impl-def-category: WLRL kind: csr_field instance: zort.XYZ + - name: QUX_QQQ_WLRL_NO_SELECTOR + tag: "norm:qux_qqq_wlrl_no_selector" + impl-def-behavior: true + impl-def-category: WLRL + kind: csr_field + instance: qux.QQQ diff --git a/tests/params/expected/test-params.html b/tests/params/expected/test-params.html index 71fa6c6..9e70863 100644 --- a/tests/params/expected/test-params.html +++ b/tests/params/expected/test-params.html @@ -53,13 +53,13 @@

Parameters by Chapter

WARL/WLRL by Chapter

-

42 Parameters, 5 WARL/WLRL CSRs

+

42 Parameters, 7 WARL/WLRL CSRs

@@ -398,7 +398,7 @@

42 Parameters, 5 WARL/WLRL CSRs

- + @@ -408,7 +408,7 @@

42 Parameters, 5 WARL/WLRL CSRs

satp.MODEwidth:ASIDLENwidth: ASIDLEN FLD:satp.ASID The satp.ASID CSR field width is specified by the ASIDLEN parameter value.
- + @@ -421,15 +421,21 @@

42 Parameters, 5 WARL/WLRL CSRs

- + - + - + + + + + + + - + @@ -443,7 +449,7 @@

42 Parameters, 5 WARL/WLRL CSRs

Chapter Two WARL: 3 CSRsChapter Two WARL: 4 CSRs
foo.ABC[0, 3, 10]Enum
legal: [0, 0b11, 0xffff_ffff]
Illegal write returns: 0x7fff
FLD:foo.ABCThe architecturally-defined values for the 4-bit foo.ABC CSR field are 0, 3, or 10.
An implementation can support any subset of these values.
The architecturally-defined values for the 4-bit foo.ABC
CSR field are 0, 0b11, or 0xffff_ffff. Returns 0x7fff if written with an illegal value.
foo.DEFwidth:UINT_4TO8Enum
legal: [-1, 0xf0]
Ignores illegal writes
FLD:foo.DEFThe architecturally-defined values for the 8-bit foo.DEF
CSR field are -1 or 0xf0. Ignores writes with an illegal value.
foo.GHIwidth: UINT_4TO8 CHAP:TwoThe foo.DEF CSR field width ranges from 4 to 8 bits as specified by the UINT_4TO8 parameter value.The foo.GHI CSR field width ranges from 4 to 8 bits as specified by the UINT_4TO8 parameter value.
bar
- + @@ -460,6 +466,12 @@

42 Parameters, 5 WARL/WLRL CSRs

+ + + + + +
Chapter Two WLRL: 1 CSRChapter Two WLRL: 2 CSRs
FLD:zort.XYZ An implementation may treat each bit of the zort.XYZ CSR as
read-only or read-write. Read-only bits can be 0 or 1.
qux.QQQWLRLFLD:qux.QQQThe qux.QQQ CSR field has implementation-defined behavior
without an enum, width, or read-only mask selector.
diff --git a/tests/params/expected/test-params.json b/tests/params/expected/test-params.json index 07fe4b1..4707c64 100644 --- a/tests/params/expected/test-params.json +++ b/tests/params/expected/test-params.json @@ -1533,14 +1533,17 @@ "def_filename": "test-ch2.yaml", "chapter_name": "Two", "category": "WARL", - "type": [ - 0, - 3, - 10 - ], + "enum": { + "legal": [ + 0, + 3, + 4294967295 + ], + "illegal-write-return": 32767 + }, "impl-defs": [ { - "name": "FOO_ABC_WARL_LEGAL_LIST", + "name": "FOO_ABC_WARL_ENUM", "def_filename": "tests/norm-rule/test-ch2.yaml", "chapter_name": "Two", "kind": "csr_field", @@ -1551,9 +1554,9 @@ "impl-def-category": "WARL", "tags": [ { - "name": "norm:foo_abc_warl_legal_list", + "name": "norm:foo_abc_warl_enum", "context": false, - "text": "The architecturally-defined values for the 4-bit foo.ABC CSR field are 0, 3, or 10.\nAn implementation can support any subset of these values.", + "text": "The architecturally-defined values for the 4-bit foo.ABC\nCSR field are 0, 0b11, or 0xffff_ffff. Returns 0x7fff if written with an illegal value.", "tag_filename": "/build/test-ch2-norm-tags.json", "stds_doc_url": "test-ch2.html" } @@ -1567,19 +1570,55 @@ "def_filename": "test-ch2.yaml", "chapter_name": "Two", "category": "WARL", + "enum": { + "legal": [ + -1, + 240 + ], + "illegal-write-ignore": true + }, + "impl-defs": [ + { + "name": "FOO_DEF_WARL_ENUM", + "def_filename": "tests/norm-rule/test-ch2.yaml", + "chapter_name": "Two", + "kind": "csr_field", + "impl-def-behavior": true, + "instances": [ + "foo.DEF" + ], + "impl-def-category": "WARL", + "tags": [ + { + "name": "norm:foo_def_warl_enum", + "context": false, + "text": "The architecturally-defined values for the 8-bit foo.DEF\nCSR field are -1 or 0xf0. Ignores writes with an illegal value.", + "tag_filename": "/build/test-ch2-norm-tags.json", + "stds_doc_url": "test-ch2.html" + } + ] + } + ] + }, + { + "reg-name": "foo", + "field-name": "GHI", + "def_filename": "test-ch2.yaml", + "chapter_name": "Two", + "category": "WARL", "width": "UINT_4TO8", "impl-defs": [ { - "name": "FOO_DEF_WARL_WIDTH_UINT4TO8", + "name": "FOO_GHI_WARL_WIDTH_UINT4TO8", "def_filename": "tests/norm-rule/test-ch2.yaml", "chapter_name": "Two", "impl-def-behavior": true, "impl-def-category": "WARL", "tags": [ { - "name": "norm:foo_def_warl_width_uint4to8", + "name": "norm:foo_ghi_warl_width_uint4to8", "context": false, - "text": "The foo.DEF CSR field width ranges from 4 to 8 bits as specified by the UINT_4TO8 parameter value.", + "text": "The foo.GHI CSR field width ranges from 4 to 8 bits as specified by the UINT_4TO8 parameter value.", "tag_filename": "/build/test-ch2-norm-tags.json", "stds_doc_url": "test-ch2.html" } @@ -1646,6 +1685,35 @@ ] } ] + }, + { + "reg-name": "qux", + "field-name": "QQQ", + "def_filename": "test-ch2.yaml", + "chapter_name": "Two", + "category": "WLRL", + "impl-defs": [ + { + "name": "QUX_QQQ_WLRL_NO_SELECTOR", + "def_filename": "tests/norm-rule/test-ch2.yaml", + "chapter_name": "Two", + "kind": "csr_field", + "impl-def-behavior": true, + "instances": [ + "qux.QQQ" + ], + "impl-def-category": "WLRL", + "tags": [ + { + "name": "norm:qux_qqq_wlrl_no_selector", + "context": false, + "text": "The qux.QQQ CSR field has implementation-defined behavior\nwithout an enum, width, or read-only mask selector.", + "tag_filename": "/build/test-ch2-norm-tags.json", + "stds_doc_url": "test-ch2.html" + } + ] + } + ] } ] } diff --git a/tests/params/test-ch2.yaml b/tests/params/test-ch2.yaml index 60367fe..34a988c 100644 --- a/tests/params/test-ch2.yaml +++ b/tests/params/test-ch2.yaml @@ -50,17 +50,31 @@ parameter_definitions: note: This is a Mock extension dependency parameter (A depends B). csr_definitions: - # Case 1: CSR field with a list of possible legal values. Some values may be mandatory and some optional - # but that isn't tracked here. + # Case 1a: Enum with list of potentially legal values. + # Some values may be mandatory and some optional for an implementation but that isn't tracked here. + # Returns 0x7fff if written with an illegal value. + # Legal values use a mix of decimal, binary (0b11), and hex (0xffff_ffff) to test multibase support. - reg-name: foo field-name: ABC - impl-def: FOO_ABC_WARL_LEGAL_LIST - type: [0, 3, 10] + impl-def: FOO_ABC_WARL_ENUM + enum: + legal: [0, 0b11, 0xffff_ffff] + illegal-write-return: 0x7fff - # Case 2: CSR field with width specified by a named parameter's value. + # Case 1b: Enum with list of potentially legal values. + # Ignores writes of illegal values (i.e. the write has no effect). + # Legal values use decimal and hex (0xf0) to further test multibase support. - reg-name: foo field-name: DEF - impl-def: FOO_DEF_WARL_WIDTH_UINT4TO8 + impl-def: FOO_DEF_WARL_ENUM + enum: + legal: [-1, 0xf0] + illegal-write-ignore: true + + # Case 2: CSR field with width specified by a named parameter's value. + - reg-name: foo + field-name: GHI + impl-def: FOO_GHI_WARL_WIDTH_UINT4TO8 width: UINT_4TO8 # Case 3a: CSR with some bits that are read-only and some bits that are read-write. @@ -76,3 +90,8 @@ csr_definitions: field-name: XYZ impl-def: ZORT_XYZ_WLRL_READONLY_ZERO ro-mask: 0b1111 + + # Case 4: CSR field with no selector property (enum/width/ro-mask all omitted). + - reg-name: qux + field-name: QQQ + impl-def: QUX_QQQ_WLRL_NO_SELECTOR diff --git a/tests/shared_utils/test_shared_utils.py b/tests/shared_utils/test_shared_utils.py index 8381f06..481f94f 100644 --- a/tests/shared_utils/test_shared_utils.py +++ b/tests/shared_utils/test_shared_utils.py @@ -191,6 +191,20 @@ def test_infer_param_type_string_success_cases(): ) == "[1, 2, 3]" ) + assert ( + shared_utils.infer_param_type_string( + { + "reg-name": "foo", + "field-name": "ABC", + "enum": { + "legal": [0, 3, 10], + "illegal-write-return": 0, + }, + }, + lambda msg: (_ for _ in ()).throw(AssertionError(msg)), + ) + == "[0, 3, 10]" + ) # Range and array wrapping. assert ( @@ -265,7 +279,7 @@ def test_infer_param_type_string_all_fatal_cases(): ), ( {"name": "P"}, - "has neither a valid type nor a valid range", + "has neither a valid type, enum, nor a valid range", ), ( {"name": "P", "type": "boolean", "array": [0]}, diff --git a/tools/README.md b/tools/README.md index 4e2dd9e..c469724 100644 --- a/tools/README.md +++ b/tools/README.md @@ -186,8 +186,8 @@ Examples: CSR Definition Encoding: - Use `csr_definitions` entries for CSRs, with `reg-name` (single CSR) or `reg-names` (multiple CSRs). -- A CSR entry must include exactly one selector property: - - `type` +- CSR selector properties are optional. If provided, use at most one of: + - `enum` - `width` - `ro-mask` - `ro-value` is optional, but if present then `ro-mask` is required. @@ -198,8 +198,11 @@ CSR Definition Encoding: - The category is mapped into CSR category (`WARL`/`WLRL`) for output grouping. CSR selector properties: -- `type`: List of legal integer values for the CSR (or CSR field). - - Format: `[v0, v1, ...]` where each value is an integer. +- `enum`: Object specifying legal values and illegal-write behavior. + - `legal`: Array of one or more integers representing legal write values (required). + - `illegal-write-ignore`: Boolean specifying whether illegal writes are silently ignored (mutually exclusive with `illegal-write-return`). + - `illegal-write-return`: Integer value to return on illegal writes (mutually exclusive with `illegal-write-ignore`). + - Must specify `legal` and exactly one of `illegal-write-ignore` or `illegal-write-return`. - `width`: Name of a parameter that defines CSR width. - Format: `width: `. - `ro-mask`/`ro-value`: Bit-mask model for read-only bits. @@ -209,16 +212,19 @@ CSR selector properties: - Both accept decimal integer, hex string (`0x...`), or binary string (`0b...`). Notes on output behavior: +- JSON output preserves the structure of the authored `enum` object in the generated `csrs` entry, but normalizes any multibase numeric values (for example hex `0x...` or binary `0b...`) to plain integers. - JSON output stores `ro-mask`/`ro-value` as integers. -- HTML output preserves the original authored literal text for `ro-mask`/`ro-value` when available (for example `0xF0F0` or `0b1100`). +- HTML output uses internal underscore-prefixed fields to preserve the original authored literal text for `enum` values and `ro-mask`/`ro-value` when available (for example `0xF0F0` or `0b1100`). Examples: ```yaml -# Type-based CSR field (potentially legal enumerated values) +# Enum: Legal CSR field values plus illegal-write behavior - reg-name: mtvec field-name: MODE impl-def: MTVEC_MODE_WARL - type: [0, 1] + enum: + legal: [0, 1] + illegal-write-ignore: true # Width-based CSR field (width references an existing parameter) - reg-name: satp diff --git a/tools/create_params.py b/tools/create_params.py index aa16f26..5926aa6 100644 --- a/tools/create_params.py +++ b/tools/create_params.py @@ -110,7 +110,7 @@ def load_yaml_file(pathname: str) -> Dict[str, Any]: return load_yaml_object(pathname, fatal) -def load_csr_literal_texts(pathname: str) -> List[Dict[str, str]]: +def load_csr_literal_texts(pathname: str) -> List[Dict[str, Any]]: """Load raw scalar token text for CSR definitions from YAML source.""" yaml_module: Any = None try: @@ -148,10 +148,10 @@ def load_csr_literal_texts(pathname: str) -> List[Dict[str, str]]: if not isinstance(csr_items, list): return [] - wanted_keys = {"ro-mask", "ro-value"} - literal_rows: List[Dict[str, str]] = [] + wanted_scalar_keys = {"ro-mask", "ro-value"} + literal_rows: List[Dict[str, Any]] = [] for item_node in csr_items: - row: Dict[str, str] = {} + row: Dict[str, Any] = {} item_pairs = getattr(item_node, "value", None) if isinstance(item_pairs, list): for pair in item_pairs: @@ -159,9 +159,41 @@ def load_csr_literal_texts(pathname: str) -> List[Dict[str, str]]: continue key_node, val_node = pair key_text = getattr(key_node, "value", None) - val_text = getattr(val_node, "value", None) - if isinstance(key_text, str) and key_text in wanted_keys and isinstance(val_text, str): - row[key_text] = val_text + if not isinstance(key_text, str): + continue + + if key_text in wanted_scalar_keys: + val_text = getattr(val_node, "value", None) + if isinstance(val_text, str): + row[key_text] = val_text + + elif key_text == "enum": + # Navigate into the nested enum mapping to capture raw texts. + enum_pairs = getattr(val_node, "value", None) + if isinstance(enum_pairs, list): + for enum_pair in enum_pairs: + if not isinstance(enum_pair, tuple) or len(enum_pair) != 2: + continue + enum_key_node, enum_val_node = enum_pair + enum_key = getattr(enum_key_node, "value", None) + if not isinstance(enum_key, str): + continue + + if enum_key == "legal": + legal_item_nodes = getattr(enum_val_node, "value", None) + if isinstance(legal_item_nodes, list): + texts = [ + t for node in legal_item_nodes + if isinstance(t := getattr(node, "value", None), str) + ] + if texts: + row["_enum-legal-texts"] = texts + + elif enum_key == "illegal-write-return": + t = getattr(enum_val_node, "value", None) + if isinstance(t, str): + row["_enum-illegal-write-return-text"] = t + literal_rows.append(row) return literal_rows @@ -555,16 +587,16 @@ def parse_multibase_int(value: Any, label: str, csr_name: str) -> int: ) return 0 - has_type = "type" in entry + has_enum = "enum" in entry has_width = "width" in entry has_read_only_mask = "ro-mask" in entry has_read_only_value = "ro-value" in entry - selector_count = int(has_type) + int(has_width) + int(has_read_only_mask) - if selector_count != 1: + selector_count = int(has_enum) + int(has_width) + int(has_read_only_mask) + if selector_count > 1: fatal( - f"Found CSR entry in {def_filename} that must define exactly one of " - "'type', 'width', or 'ro-mask'" + f"Found CSR entry in {def_filename} that cannot define more than one of " + "'enum', 'width', or 'ro-mask'" ) if has_read_only_value and not has_read_only_mask: fatal( @@ -598,26 +630,65 @@ def parse_multibase_int(value: Any, label: str, csr_name: str) -> int: representative_name = names[0] - csr_type_input: Optional[List[int]] = None csr_width_input: Optional[str] = None csr_read_only_mask: Optional[int] = None csr_read_only_value: Optional[int] = None csr_read_only_mask_text: Optional[str] = None csr_read_only_value_text: Optional[str] = None - - if has_type: - raw_type = entry.get("type") - if not isinstance(raw_type, list) or not raw_type: - fatal(f"CSR {representative_name} in {def_filename} has invalid or empty type") - assert isinstance(raw_type, list) - csr_type_input = [] - for value in raw_type: + csr_legal_enum: Optional[List[int]] = None + csr_illegal_write_ignore: Optional[bool] = None + csr_illegal_write_return: Optional[int] = None + + if has_enum: + raw_enum = entry.get("enum") + if not isinstance(raw_enum, dict): + fatal(f"CSR {representative_name} in {def_filename} has invalid enum (must be an object)") + + # Parse legal values. YAML auto-converts hex/binary literals to integers; + # original text preservation is handled separately via load_csr_literal_texts. + raw_legal = raw_enum.get("legal") + if not isinstance(raw_legal, list) or not raw_legal: + fatal(f"CSR {representative_name} in {def_filename} has invalid or empty enum.legal") + csr_legal_enum = [] + for value in raw_legal: if not isinstance(value, int) or isinstance(value, bool): fatal( f"CSR {representative_name} in {def_filename} has non-integer " - f"value in type: {value!r}" + f"value in enum.legal: {value!r}" + ) + csr_legal_enum.append(value) + + # Parse illegal-write behavior (must be one of the two) + raw_illegal_ignore = raw_enum.get("illegal-write-ignore") + raw_illegal_return = raw_enum.get("illegal-write-return") + + if raw_illegal_ignore is None and raw_illegal_return is None: + fatal(f"CSR {representative_name} in {def_filename} must specify either 'illegal-write-ignore' or 'illegal-write-return' in enum") + + if raw_illegal_ignore is not None and raw_illegal_return is not None: + fatal(f"CSR {representative_name} in {def_filename} must specify only one of 'illegal-write-ignore' or 'illegal-write-return' in enum") + + if raw_illegal_ignore is not None: + if not isinstance(raw_illegal_ignore, bool): + fatal(f"CSR {representative_name} in {def_filename} has non-boolean illegal-write-ignore: {raw_illegal_ignore!r}") + if raw_illegal_ignore is not True: + fatal( + f"CSR {representative_name} in {def_filename} must set 'illegal-write-ignore' to true " + f"when present (got {raw_illegal_ignore!r})" ) - csr_type_input.append(value) + csr_illegal_write_ignore = True + + if raw_illegal_return is not None: + if not isinstance(raw_illegal_return, (str, int)) or isinstance(raw_illegal_return, bool): + fatal( + f"CSR {representative_name} in {def_filename} has invalid illegal-write-return " + f"{raw_illegal_return!r}; expected integer, hex string, or binary string" + ) + csr_illegal_write_return = parse_multibase_int( + raw_illegal_return, + "illegal-write-return", + representative_name, + ) if has_width: raw_width = entry.get("width") @@ -738,8 +809,21 @@ def csr_id(field_name: Optional[str] = None) -> str: "category": csr_category, } - if csr_type_input is not None: - out_entry["type"] = list(csr_type_input) + if csr_legal_enum is not None: + enum_dict = {"legal": list(csr_legal_enum)} + if csr_illegal_write_ignore is not None: + enum_dict["illegal-write-ignore"] = csr_illegal_write_ignore + if csr_illegal_write_return is not None: + enum_dict["illegal-write-return"] = csr_illegal_write_return + out_entry["enum"] = enum_dict + # Preserve original literal texts from YAML AST (stripped before JSON output, used by HTML). + if isinstance(literal_texts, dict): + legal_texts = literal_texts.get("_enum-legal-texts") + if isinstance(legal_texts, list) and legal_texts: + out_entry["_enum-legal-texts"] = legal_texts + return_text = literal_texts.get("_enum-illegal-write-return-text") + if isinstance(return_text, str): + out_entry["_enum-illegal-write-return-text"] = return_text if csr_width_input is not None: out_entry["width"] = csr_width_input @@ -1334,7 +1418,7 @@ def html_csr_table_row(f, csr: Dict[str, Any], chapter_name: Optional[str]): csr_parts: List[str] = [] width_value = csr.get("width") if isinstance(width_value, str): - csr_parts.append(f"width:{width_value}") + csr_parts.append(f"width: {width_value}") if "ro-mask" in csr: mask_text_obj = csr.get("_ro-mask-text") @@ -1354,11 +1438,46 @@ def html_csr_table_row(f, csr: Dict[str, Any], chapter_name: Optional[str]): if csr_parts: type_display = "
".join(csr_parts) - elif "type" in csr: - type_display = infer_param_type_string( - csr, - fatal, - ) + elif "enum" in csr: + enum_value = csr.get("enum") + if not isinstance(enum_value, dict): + fatal(f"CSR {name} has invalid enum output; expected object") + return + + legal_values = enum_value.get("legal") + if not isinstance(legal_values, list) or not legal_values: + fatal(f"CSR {name} has invalid enum.legal output; expected non-empty list") + return + if not all(isinstance(value, int) and not isinstance(value, bool) for value in legal_values): + fatal(f"CSR {name} has invalid enum.legal output; expected integers") + return + + legal_texts = csr.get("_enum-legal-texts") + if isinstance(legal_texts, list) and len(legal_texts) == len(legal_values): + legal_display = "[" + ", ".join(legal_texts) + "]" + else: + legal_display = str(legal_values) + + enum_lines = ["Enum", f"legal: {legal_display}"] + if enum_value.get("illegal-write-ignore") is True: + enum_lines.append("Ignores illegal writes") + elif "illegal-write-return" in enum_value: + illegal_write_return = enum_value.get("illegal-write-return") + if not isinstance(illegal_write_return, int) or isinstance(illegal_write_return, bool): + fatal(f"CSR {name} has invalid illegal-write-return output; expected integer") + return + illegal_return_text = csr.get("_enum-illegal-write-return-text") + if isinstance(illegal_return_text, str): + enum_lines.append(f"Illegal write returns: {illegal_return_text}") + else: + enum_lines.append(f"Illegal write returns: {illegal_write_return}") + else: + fatal( + f"CSR {name} enum output must include illegal-write-ignore or illegal-write-return" + ) + return + + type_display = "
".join(enum_lines) else: category = csr.get("category") if isinstance(category, str) and category: diff --git a/tools/shared_utils.py b/tools/shared_utils.py index f2428bd..664bf3e 100644 --- a/tools/shared_utils.py +++ b/tools/shared_utils.py @@ -209,6 +209,7 @@ def infer_param_type_string( scalar type/range forms and optional array bounds. """ param_type = param.get("type") + param_enum = param.get("enum") param_range = param.get("range") param_array = param.get("array") param_width = param.get("width") @@ -245,6 +246,19 @@ def infer_param_type_string( "or all integers" ) + elif isinstance(param_enum, dict): + enum_legal = param_enum.get("legal") + if not isinstance(enum_legal, list) or not enum_legal: + fatal( + f"Parameter {param_name!r} has invalid enum object; expected non-empty legal list" + ) + if not all(isinstance(v, int) and not isinstance(v, bool) for v in enum_legal): + fatal( + f"Parameter {param_name!r} has invalid enum.legal array; expected all integers" + ) + enum_values = ", ".join(map(str, enum_legal)) + scalar_type = f"[{enum_values}]" + elif isinstance(param_type, str): if param_type in {"boolean", "bit", "byte", "hword", "word", "dword"}: scalar_type = param_type @@ -296,7 +310,7 @@ def infer_param_type_string( else: fatal( - f"Parameter {param_name!r} has neither a valid type nor a valid range" + f"Parameter {param_name!r} has neither a valid type, enum, nor a valid range" ) if isinstance(param_array, list):