diff --git a/arch/csr/mhartid.yaml b/arch/csr/mhartid.yaml index 2b41e1c95..1e5425801 100644 --- a/arch/csr/mhartid.yaml +++ b/arch/csr/mhartid.yaml @@ -8,7 +8,7 @@ address: 0xf14 priv_mode: M length: MXLEN description: Reports the unique hart-specific ID in the system. -definedBy: I +definedBy: Sm fields: ID: location_rv32: 31-0 @@ -17,4 +17,4 @@ fields: description: hart-specific ID. reset_value: UNDEFINED_LEGAL sw_read(): | - return hartid(); + return hartid(); \ No newline at end of file diff --git a/arch/csr/mie.yaml b/arch/csr/mie.yaml index b663443ca..2a51b862d 100644 --- a/arch/csr/mie.yaml +++ b/arch/csr/mie.yaml @@ -7,147 +7,9 @@ long_name: Machine Interrupt Enable address: 0x304 priv_mode: M length: MXLEN -definedBy: I -description: | - Per-type interrupt enables. - - For a detailed description of interrupt handling, see <%= link_to(:section, 'sec:interrupts') %>. - - The `mie` register is an - MXLEN-bit read/write register containing interrupt enable bits. - Interrupt cause number _i_ (as reported in CSR `mcause`) corresponds with - bit _i_ in - `mie`. Bits 15:0 are allocated to standard interrupt causes only, while - bits 16 and above are designated for platform use. - - NOTE: Interrupts designated for platform use may be designated for custom use - at the platform's discretion. - - An interrupt _i_ will trap to M-mode (causing the privilege mode to - change to M-mode) if all of the following are true: - - * either the current privilege mode is M and the MIE bit in the - `mstatus` register is set, or the current privilege mode has less - privilege than M-mode; - * bit _i_ is set in both `mip` and `mie`; - * if register `mideleg` exists, bit _i_ is not set in `mideleg`. - - These conditions for an interrupt trap to occur must be evaluated in a - bounded amount of time from when an interrupt becomes, or ceases to be, - pending in `mip`, and must also be evaluated immediately following the - execution of an __x__RET instruction or an explicit write to a CSR on - which these interrupt trap conditions expressly depend (including `mip`, - `mie`, `mstatus`, and `mideleg`). - - Interrupts to M-mode take priority over any interrupts to lower - privilege modes. - - A bit in `mie` must be writable if the corresponding interrupt can ever - become pending. Bits of `mie` that are not writable must be read-only - zero. - - [NOTE] - ==== - The machine-level interrupt registers handle a few root interrupt - sources which are assigned a fixed service priority for simplicity, - while separate external interrupt controllers can implement a more - complex prioritization scheme over a much larger set of interrupts that - are then muxed into the machine-level interrupt sources. - - ''' - - The non-maskable interrupt is not made visible via the `mip` register as - its presence is implicitly known when executing the NMI trap handler. - ==== - - If supervisor mode is implemented, bits `mip`.SEIP and `mie`.SEIE are - the interrupt-pending and interrupt-enable bits for supervisor-level - external interrupts. SEIP is writable in `mip`, and may be written by - M-mode software to indicate to S-mode that an external interrupt is - pending. Additionally, the platform-level interrupt controller may - generate supervisor-level external interrupts. Supervisor-level external - interrupts are made pending based on the logical-OR of the - software-writable SEIP bit and the signal from the external interrupt - controller. When `mip` is read with a CSR instruction, the value of the - SEIP bit returned in the `rd` destination register is the logical-OR of - the software-writable bit and the interrupt signal from the interrupt - controller, but the signal from the interrupt controller is not used to - calculate the value written to SEIP. Only the software-writable SEIP bit - participates in the read-modify-write sequence of a CSRRS or CSRRC - instruction. - - [NOTE] - ==== - For example, if we name the software-writable SEIP bit `B` and the - signal from the external interrupt controller `E`, then if - `csrrs t0, mip, t1` is executed, `t0[9]` is written with `B || E`, then - `B` is written with `B || t1[9]`. If `csrrw t0, mip, t1` is executed, - then `t0[9]` is written with `B || E`, and `B` is simply written with - `t1[9]`. In neither case does `B` depend upon `E`. - - The SEIP field behavior is designed to allow a higher privilege layer to - mimic external interrupts cleanly, without losing any real external - interrupts. The behavior of the CSR instructions is slightly modified - from regular CSR accesses as a result. - ==== - - If supervisor mode is implemented, bits `mip`.STIP and `mie`.STIE are - the interrupt-pending and interrupt-enable bits for supervisor-level - timer interrupts. STIP is writable in `mip`, and may be written by - M-mode software to deliver timer interrupts to S-mode. - - If supervisor mode is implemented, bits `mip`.SSIP and `mie`.SSIE are - the interrupt-pending and interrupt-enable bits for supervisor-level - software interrupts. SSIP is writable in `mip` and may also be set to 1 - by a platform-specific interrupt controller. - - <%- if ext?(:Sscofpmf) -%> - Bits `mip`.LCOFIP and `mie`.LCOFIE - are the interrupt-pending and interrupt-enable bits for local counter-overflow - interrupts. - LCOFIP is read-write in `mip` and reflects the occurrence of a local - counter-overflow overflow interrupt request resulting from any of the - `mhpmevent__n__`.OF bits being set. - If the Sscofpmf extension is not implemented, `mip`.LCOFIP and `mie`.LCOFIE are - read-only zeros. - <%- end -%> - - Multiple simultaneous interrupts destined for M-mode are handled in the - following decreasing priority order: MEI, MSI, MTI, SEI, SSI, STI, LCOFI. - - [NOTE] - ==== - The machine-level interrupt fixed-priority ordering rules were developed - with the following rationale. - - Interrupts for higher privilege modes must be serviced before interrupts - for lower privilege modes to support preemption. - - The platform-specific machine-level interrupt sources in bits 16 and - above have platform-specific priority, but are typically chosen to have - the highest service priority to support very fast local vectored - interrupts. - - External interrupts are handled before internal (timer/software) - interrupts as external interrupts are usually generated by devices that - might require low interrupt service times. - - Software interrupts are handled before internal timer interrupts, - because internal timer interrupts are usually intended for time slicing, - where time precision is less important, whereas software interrupts are - used for inter-processor messaging. Software interrupts can be avoided - when high-precision timing is required, or high-precision timer - interrupts can be routed via a different interrupt path. Software - interrupts are located in the lowest four bits of `mip` as these are - often written by software, and this position allows the use of a single - CSR instruction with a five-bit immediate. - ==== - - Restricted views of the `mip` and `mie` registers appear as the `sip` - and `sie` registers for supervisor level. If an interrupt is delegated - to S-mode by setting a bit in the `mideleg` register, it becomes visible - in the `sip` register and is maskable using the `sie` register. - Otherwise, the corresponding bits in `sip` and `sie` are read-only zero. +definedBy: Sm +description: + $copy: "mip.yaml#/description" fields: SSIE: location: 1 diff --git a/arch/csr/mip.yaml b/arch/csr/mip.yaml index 161abbf6c..b1e77a627 100644 --- a/arch/csr/mip.yaml +++ b/arch/csr/mip.yaml @@ -6,7 +6,153 @@ name: mip long_name: Machine Interrupt Pending address: 0x344 priv_mode: M -description: Machine Interrupt Pending bits + +# Description is shared with mie CSR (it copies it from here). +description: | + The `mie` and `mip` CSRs are MXLEN-bit read/write registers used when + the CLINT or PLIC interrupt controllers are present. + Note that the CLINT refers to an interrupt controller + used by some RISC-V implementations but isn't a ratified + RISC-V International standard. + + The `mip` CSR contains information on pending interrupts, while `mie` is the corresponding + CSR containing interrupt enable bits. + Interrupt cause number _i_ (as reported in the `mcause` CSR) + corresponds to bit _i_ in both `mip` and `mie`. + Bits 15:0 are allocated to standard interrupt causes only, while + bits 16 and above are designated for platform use. + + NOTE: Interrupts designated for platform use may be designated for custom use + at the platform's discretion. + + An interrupt _i_ will trap to M-mode (causing the privilege mode to + change to M-mode) if all of the following are true: + + * either the current privilege mode is M and the MIE bit in the `mstatus` register is + set, or the current privilege mode has less privilege than M-mode; + * bit _i_ is set in both `mip` and `mie` + * if register `mideleg` exists, bit _i_ is not set in `mideleg`. + + These conditions for an interrupt trap to occur must be evaluated in a + bounded amount of time from when an interrupt becomes, or ceases to be, + pending in `mip`, and must also be evaluated immediately following the + execution of an __x__RET instruction or an explicit write to a CSR on + which these interrupt trap conditions expressly depend (including `mip`, + `mie`, `mstatus`, and `mideleg`). + + Interrupts to M-mode take priority over any interrupts to lower + privilege modes. + + Each individual bit in register `mip` may be writable or may be + read-only. When bit _i_ in `mip` is writable, a pending interrupt _i_ + can be cleared by writing 0 to this bit. If interrupt _i_ can become + pending but bit _i_ in `mip` is read-only, the implementation must + provide some other mechanism for clearing the pending interrupt. + + A bit in `mie` must be writable if the corresponding interrupt can ever + become pending. Bits of `mie` that are not writable must be read-only + zero. + + [NOTE] + ==== + The machine-level interrupt registers handle a few root interrupt + sources which are assigned a fixed service priority for simplicity, + while separate external interrupt controllers can implement a more + complex prioritization scheme over a much larger set of interrupts that + are then muxed into the machine-level interrupt sources. + + ''' + + The non-maskable interrupt is not made visible via the `mip` register as + its presence is implicitly known when executing the NMI trap handler. + ==== + + If supervisor mode is implemented, bits `mip`.SEIP and `mie`.SEIE are + the interrupt-pending and interrupt-enable bits for supervisor-level + external interrupts. SEIP is writable in `mip`, and may be written by + M-mode software to indicate to S-mode that an external interrupt is + pending. Additionally, the platform-level interrupt controller may + generate supervisor-level external interrupts. Supervisor-level external + interrupts are made pending based on the logical-OR of the + software-writable SEIP bit and the signal from the external interrupt + controller. When `mip` is read with a CSR instruction, the value of the + SEIP bit returned in the `rd` destination register is the logical-OR of + the software-writable bit and the interrupt signal from the interrupt + controller, but the signal from the interrupt controller is not used to + calculate the value written to SEIP. Only the software-writable SEIP bit + participates in the read-modify-write sequence of a CSRRS or CSRRC + instruction. + + [NOTE] + ==== + For example, if we name the software-writable SEIP bit `B` and the + signal from the external interrupt controller `E`, then if + `csrrs t0, mip, t1` is executed, `t0[9]` is written with `B || E`, then + `B` is written with `B || t1[9]`. If `csrrw t0, mip, t1` is executed, + then `t0[9]` is written with `B || E`, and `B` is simply written with + `t1[9]`. In neither case does `B` depend upon `E`. + + The SEIP field behavior is designed to allow a higher privilege layer to + mimic external interrupts cleanly, without losing any real external + interrupts. The behavior of the CSR instructions is slightly modified + from regular CSR accesses as a result. + ==== + + If supervisor mode is implemented, bits `mip`.STIP and `mie`.STIE are + the interrupt-pending and interrupt-enable bits for supervisor-level + timer interrupts. STIP is writable in `mip`, and may be written by + M-mode software to deliver timer interrupts to S-mode. + + If supervisor mode is implemented, bits `mip`.SSIP and `mie`.SSIE are + the interrupt-pending and interrupt-enable bits for supervisor-level + software interrupts. SSIP is writable in `mip` and may also be set to 1 + by a platform-specific interrupt controller. + + <% if ext?(:Sscofpmf) -%> + bits `mip`.LCOFIP and `mie`.LCOFIE + are the interrupt-pending and interrupt-enable bits for local counter-overflow + interrupts. + LCOFIP is read-write in `mip` and reflects the occurrence of a local + counter-overflow overflow interrupt request resulting from any of the + `mhpmevent__n__`.OF bits being set. + <% end -%> + + Multiple simultaneous interrupts destined for M-mode are handled in the + following decreasing priority order: MEI, MSI, MTI, SEI, SSI, STI, LCOFI. + + [NOTE] + ==== + The machine-level interrupt fixed-priority ordering rules were developed + with the following rationale. + + Interrupts for higher privilege modes must be serviced before interrupts + for lower privilege modes to support preemption. + + The platform-specific machine-level interrupt sources in bits 16 and + above have platform-specific priority, but are typically chosen to have + the highest service priority to support very fast local vectored + interrupts. + + External interrupts are handled before internal (timer/software) + interrupts as external interrupts are usually generated by devices that + might require low interrupt service times. + + Software interrupts are handled before internal timer interrupts, + because internal timer interrupts are usually intended for time slicing, + where time precision is less important, whereas software interrupts are + used for inter-processor messaging. Software interrupts can be avoided + when high-precision timing is required, or high-precision timer + interrupts can be routed via a different interrupt path. Software + interrupts are located in the lowest four bits of `mip` as these are + often written by software, and this position allows the use of a single + CSR instruction with a five-bit immediate. + ==== + + Restricted views of the `mip` and `mie` registers appear as the `sip` + and `sie` registers for supervisor level. If an interrupt is delegated + to S-mode by setting a bit in the `mideleg` register, it becomes visible + in the `sip` register and is maskable using the `sie` register. + Otherwise, the corresponding bits in `sip` and `sie` are read-only zero. length: MXLEN definedBy: Sm fields: @@ -256,4 +402,4 @@ fields: <%- end -%> type: RW-H reset_value: 0 - definedBy: Sscofpmf + definedBy: Sscofpmf \ No newline at end of file diff --git a/backends/certificate_doc/templates/certificate.adoc.erb b/backends/certificate_doc/templates/certificate.adoc.erb index 443133da4..cf1c1215c 100644 --- a/backends/certificate_doc/templates/certificate.adoc.erb +++ b/backends/certificate_doc/templates/certificate.adoc.erb @@ -559,7 +559,7 @@ h| Privilege Mode | <%= csr.priv_mode %> |=== -==== <%= cert_model.name %> Format +==== Format <% unless csr.dynamic_length?(arch_def) || csr.implemented_fields(arch_def).any? { |f| f.dynamic_location?(arch_def) } -%> <%# CSR has a known static length, so there is only one format to display -%> .<%= csr.name %> format @@ -587,7 +587,7 @@ This CSR format changes dynamically with XLEN. <% end # unless dynamic length -%> -==== <%= cert_model.name %> Field Summary +==== Field Summary // use @ as a separator since IDL code can contain | [%autowidth,separator=@,float="center",align="center",cols="^,<,<,<",options="header",role="stretch"] @@ -627,7 +627,7 @@ a@ <%- end -%> |=== -==== <%= cert_model.name %> Fields +==== Fields <%- if csr.implemented_fields(arch_def).empty? -%> This CSR has no fields. However, it must still exist (not cause an `Illegal Instruction` trap) and always return zero on a read. diff --git a/lib/test/test_yaml_loader.rb b/lib/test/test_yaml_loader.rb index 132a644e9..562d93d41 100644 --- a/lib/test/test_yaml_loader.rb +++ b/lib/test/test_yaml_loader.rb @@ -208,7 +208,7 @@ def test_that_double_inherits_doesnt_delete_keys assert_equal({ "key1" => "value1", "key2" => "value2", "key3" => "value3_new", "key4" => "value4", "key5" => "value5", "key6" => "value6_new" }, doc["child"]) end - def test_inheritss_in_the_same_document + def test_inherits_in_the_same_document yaml = <<~YAML $defs: target1: A string @@ -225,7 +225,6 @@ def test_inheritss_in_the_same_document obj3: a: Should take precedence $inherits: "#/$defs/target2" - YAML f = Tempfile.new("yml") @@ -238,7 +237,7 @@ def test_inheritss_in_the_same_document assert_equal({ "a" => "Should take precedence" }, doc["obj3"]) end - def test_inheritss_in_the_different_document + def test_inherits_in_the_different_document yaml1 = <<~YAML $defs: target1: A string @@ -274,7 +273,7 @@ def test_inheritss_in_the_different_document assert_equal({ "a" => "Should take precedence" }, doc["obj3"]) end - def test_multi_inheritss_in_the_same_document + def test_multi_inherits_in_the_same_document yaml = <<~YAML $defs: target1: @@ -297,7 +296,7 @@ def test_multi_inheritss_in_the_same_document assert_equal({ "a" => "hash", "b" => "nice" }, doc["obj1"]) end - def test_that_invalid_inheritss_raise + def test_that_invalid_inherits_raise yaml = <<~YAML $defs: target1: @@ -337,4 +336,96 @@ def test_that_invalid_refs_raise assert_raises(YamlLoader::DereferenceError) { YamlLoader.load(f.path) } end + def test_copy_in_the_same_document + yaml = <<~YAML + $defs: + target1: A string + target2: + a: hash + target3: Another string + + obj1: + target10: abc + target11: + $copy: "#/$defs/target1" + target12: def + target13: + $copy: "#/$defs/target3" + + YAML + + f = Tempfile.new("yml") + f.write(yaml) + f.flush + + doc = YamlLoader.load(f.path) + assert_equal({ + "target10" => "abc", + "target11" => "A string", + "target12" => "def", + "target13" => "Another string" + }, doc["obj1"]) + end + + def test_copy_in_the_different_document + yaml1 = <<~YAML + $defs: + target1: A string + target2: + a: hash + target3: Another string + YAML + + f1 = Tempfile.new("yml") + f1.write(yaml1) + f1.flush + f1_path = Pathname.new(f1.path) + + yaml2 = <<~YAML + obj1: + target10: abc + target11: + $copy: "#{f1_path.basename}#/$defs/target1" + target12: def + target13: + $copy: "#{f1_path.basename}#/$defs/target3" + YAML + + f2 = Tempfile.new("yml") + f2.write(yaml2) + f2.flush + + doc = YamlLoader.load(f2.path) + assert_equal({ + "target10" => "abc", + "target11" => "A string", + "target12" => "def", + "target13" => "Another string" + }, doc["obj1"]) + end + + def test_multi_inherits_in_the_same_document + yaml = <<~YAML + $defs: + target1: + b: nice + target2: + a: hash + + obj1: + $inherits: + - "#/$defs/target1" + - "#/$defs/target2" + + YAML + + f = Tempfile.new("yml") + f.write(yaml) + f.flush + + doc = YamlLoader.load(f.path) + assert_equal({ "a" => "hash", "b" => "nice" }, doc["obj1"]) + end + + end diff --git a/lib/yaml_loader.rb b/lib/yaml_loader.rb index 513ec0a36..06f994865 100644 --- a/lib/yaml_loader.rb +++ b/lib/yaml_loader.rb @@ -136,28 +136,66 @@ def self.expand(filename, obj, yaml_opts = {}) end final_obj - + elsif obj.keys.include?("$copy") + self.get_copy_target_obj(filename, obj["$copy"], yaml_opts) else - obj_keys = obj.keys - obj_keys.each do |key| - value = obj[key] - + # Go through each hash entry. + obj.each do |key, value| obj[key] = expand(filename, value, yaml_opts) end obj end - obj_keys = new_obj.keys - if obj_keys.include? "$remove" - remove_keys = obj["$remove"].is_a?(Array) ? obj["$remove"] : [obj["$remove"]] - remove_keys.each do |key| - new_obj.delete(key) + if new_obj.is_a?(Hash) + obj_keys = new_obj.keys + if obj_keys.include? "$remove" + remove_keys = obj["$remove"].is_a?(Array) ? obj["$remove"] : [obj["$remove"]] + remove_keys.each do |key| + new_obj.delete(key) + end end + new_obj.delete("$remove") end - new_obj.delete("$remove") + new_obj end + # @param filename [String,Pathname] path to the YAML file + # @param copy_target [String] + # @param yaml_opts [Hash] options to pass to YAML.load_file + # @return [Object] + def self.get_copy_target_obj(filename, copy_target, yaml_opts) + relative_path = copy_target.split("#")[0] + if relative_path.empty? + # this is a reference in the same document + obj_doc = YAML.load_file(filename, **yaml_opts) + obj_path = copy_target.split("#")[1].split("/")[1..] + target_obj = obj_doc.dig(*obj_path) + raise DereferenceError, "$copy: #{obj_path} referenced to same file cannot be found in file #{filename}" if target_obj.nil? + + ref = expand(filename, target_obj, yaml_opts) + if ref.nil? + raise DereferenceError, "$copy: JSON Path #{obj_path} referenced to same file does not exist in file #{filename}" + end + + ref + else + target_filename = File.realpath(File.join(filename.dirname, relative_path)) + + obj_doc = YamlLoader.load(target_filename, yaml_opts) + obj_path = copy_target.split("#")[1].split("/")[1..] + target_obj = obj_doc.dig(*obj_path) + raise DereferenceError, "$copy: #{obj_path} referenced from file #{filename} cannot be found in file #{target_filename}" if target_obj.nil? + + ref = expand(target_filename, target_obj, yaml_opts) + if ref.nil? + raise DereferenceError, "$copy: JSON Path #{obj_path} referenced from file #{filename} does not exist in file #{target_filename}" + end + + ref + end + end + # load a YAML file and expand any $ref/$inherits references # @param filename [String,Pathname] path to the YAML file # @param yaml_opts [Hash] options to pass to YAML.load_file diff --git a/schemas/csr_schema.json b/schemas/csr_schema.json index ae0e26d18..16ef9686c 100644 --- a/schemas/csr_schema.json +++ b/schemas/csr_schema.json @@ -193,8 +193,23 @@ "description": "Descriptive name for the CSR" }, "description": { - "type": "string", - "description": "A full Asciidoc description of the CSR, intended to be used as documentation." + "oneOf": [ + { + "type": "string", + "description": "A full Asciidoc description of the CSR, intended to be used as documentation." + }, + { + "type": "object", + "description": "A full Asciidoc description of the CSR, intended to be used as documentation.", + "properties": { + "$copy" : { + "type": "string", + "format": "uri-reference" + } + }, + "additionalProperties": false + } + ] }, "definedBy": { "$ref": "schema_defs.json#/$defs/requires_entry",