From 8ae1a430d2d0d2bfc9db3f6bf8a6ed9278527346 Mon Sep 17 00:00:00 2001 From: Derek Hower Date: Wed, 20 Nov 2024 19:46:17 -0800 Subject: [PATCH 1/3] Recursive requirements --- arch/ext/Zca.yaml | 17 +++ backends/arch_gen/lib/arch_gen.rb | 4 +- .../templates/certificate.adoc.erb | 25 ++-- backends/cfg_html_doc/templates/ext.adoc.erb | 25 ++-- backends/ext_pdf_doc/tasks.rake | 4 +- .../ext_pdf_doc/templates/ext_pdf.adoc.erb | 83 ++++++----- backends/manual/templates/ext.adoc.erb | 27 ++-- backends/manual/templates/param_list.adoc.erb | 2 +- .../profile_doc/templates/profile.adoc.erb | 31 ++--- lib/arch_def.rb | 2 +- lib/arch_obj_models/csr.rb | 2 - lib/arch_obj_models/extension.rb | 102 +++++++++++--- lib/arch_obj_models/manual.rb | 2 +- lib/arch_obj_models/obj.rb | 47 +++++++ lib/arch_obj_models/portfolio.rb | 4 +- lib/resolver.rb | 20 +++ lib/yaml_resolver.py | 131 ++++++++++++++++++ schemas/schema_defs.json | 2 +- 18 files changed, 413 insertions(+), 117 deletions(-) create mode 100644 arch/ext/Zca.yaml create mode 100644 lib/resolver.rb create mode 100644 lib/yaml_resolver.py diff --git a/arch/ext/Zca.yaml b/arch/ext/Zca.yaml new file mode 100644 index 000000000..f3c3de73f --- /dev/null +++ b/arch/ext/Zca.yaml @@ -0,0 +1,17 @@ +# yaml-language-server: $schema=../../schemas/ext_schema.json + +Zca: + long_name: Common compressed instructions + description: | + TODO + type: unprivileged + company: + name: RISC-V International + url: https://riscv.org + versions: + - version: "1.0.0" + state: ratified + ratification_date: 2021-06 + repositories: + - url: https://github.com/riscv/riscv-bitmanip + branch: main diff --git a/backends/arch_gen/lib/arch_gen.rb b/backends/arch_gen/lib/arch_gen.rb index 6018fd65c..7f424fd6f 100644 --- a/backends/arch_gen/lib/arch_gen.rb +++ b/backends/arch_gen/lib/arch_gen.rb @@ -639,7 +639,7 @@ def maybe_add_csr(csr_name, extra_env = {}) arch_def_mock.define_singleton_method(:possible_xlens) do pos_xlen_local end - impl_ext = @cfg_impl_ext.map { |e| ExtensionVersion.new(e[0], e[1]) } + impl_ext = @cfg_impl_ext.map { |e| ExtensionVersion.new(e[0], e[1], nil) } arch_def_mock.define_singleton_method(:implemented_extensions) do impl_ext end @@ -893,7 +893,7 @@ def maybe_add_inst(inst_name, extra_env = {}) arch_def_mock = Object.new arch_def_mock.define_singleton_method(:fully_configured?) { true } arch_def_mock.define_singleton_method(:possible_xlens) { possible_xlens } - impl_ext = @cfg_impl_ext.map { |e| ExtensionVersion.new(e[0], e[1]) } + impl_ext = @cfg_impl_ext.map { |e| ExtensionVersion.new(e[0], e[1], nil) } arch_def_mock.define_singleton_method(:implemented_extensions) do impl_ext end diff --git a/backends/certificate_doc/templates/certificate.adoc.erb b/backends/certificate_doc/templates/certificate.adoc.erb index cf1c1215c..ef87f9632 100644 --- a/backends/certificate_doc/templates/certificate.adoc.erb +++ b/backends/certificate_doc/templates/certificate.adoc.erb @@ -297,31 +297,30 @@ Requirement <%= req.name %> only apply when <%= req.when_pretty %>. *Version Requirement*: <%= ext_req.version_requirement %> + -<% ext.versions.each do |v| -%> -<%= v["version"] %>:: +<% ext_db.versions.each do |v| -%> +<%= v.version %>:: State::: - <%= v["state"] %> - <% if v["state"] == "ratified" -%> + <%= v.state %> + <% if v.state == "ratified" -%> Ratification date::: - <%= v["ratification_date"] %> + <%= v.ratification_date %> <% end # if %> - <% if v.key?("changes") -%> + <% if v.changes.size > 0 -%> Changes::: - <% v["changes"].each do |c| -%> + <% v.changes.each do |c| -%> * <%= c %> <% end -%> <% end -%> - <% if v.key?("url") -%> + <% unless v.url.nil? -%> Ratification document::: - <%= v["url"] %> + <%= v.url %> <% end -%> - <% if v.key?("implies") -%> + <% if v.implications.size > 0 -%> Implies::: - <% implications = v["implies"][0].is_a?(Array) ? v["implies"] : [v["implies"]] -%> - <% implications.each do |i| -%> - * `<%= i[0] %>` version <%= i[1] %> + <% v.implications.each do |i| -%> + * `<%= i.name %>` version <%= i.version %> <% end -%> <% end -%> <% end -%> diff --git a/backends/cfg_html_doc/templates/ext.adoc.erb b/backends/cfg_html_doc/templates/ext.adoc.erb index b3844b233..1808e4941 100644 --- a/backends/cfg_html_doc/templates/ext.adoc.erb +++ b/backends/cfg_html_doc/templates/ext.adoc.erb @@ -7,23 +7,26 @@ Implemented Version:: <%= ext_version.version %> == Versions <%- ext.versions.each do |v| -%> -<%- implemented = arch_def.implemented_extensions.include?(ExtensionVersion.new(ext.name, v["version"])) -%> -<%= v["version"] %>:: +<%- implemented = arch_def.implemented_extensions.include?(v) -%> +<%= v.version %>:: Ratification date::: - <%= v["ratification_date"] %> - <%- if v.key?("changes") -%> + <%= v.ratification_date %> + <%- unless v.changes.empty? -%> Changes::: - <%= v["changes"] %> + + <% v.changes.each do |c| -%> + * <%= c %> + <% end -%> + <%- end -%> - <%- if v.key?("url") -%> + <%- unless v.url.nil? -%> Ratification document::: - <%= v["url"] %> + <%= v.url %> <%- end -%> - <%- if v.key?("implies") -%> + <%- unless v.implications.empty? -%> Implies::: - <%- implications = v["implies"][0].is_a?(Array) ? v["implies"] : [v["implies"]] -%> - <%- implications.each do |i| -%> - * `<%= i[0] %>` version <%= i[1] %> + <%- v.implications.each do |i| -%> + * `<%= i.name %>` version <%= i.version %> <%- end -%> <%- end -%> <%- end -%> diff --git a/backends/ext_pdf_doc/tasks.rake b/backends/ext_pdf_doc/tasks.rake index c4112ff0f..165e70589 100644 --- a/backends/ext_pdf_doc/tasks.rake +++ b/backends/ext_pdf_doc/tasks.rake @@ -149,9 +149,9 @@ rule %r{#{$root}/gen/ext_pdf_doc/.*/adoc/.*_extension\.adoc} => proc { |tname| if ENV.key?("EXT_VERSION") ENV["EXT_VERSION"] else - ext.versions.max { |a, b| Gem::Version.new(a["version"]) <=> Gem::Version.new(b["version"]) }["version"] + ext.versions.max { |a, b| a.version <=> b.version }.version end - ext_version = ext.versions.find { |v| v["version"] == version_num } + ext_version = ext.versions.find { |v| v.version == version_num } FileUtils.mkdir_p File.dirname(t.name) File.write t.name, AsciidocUtils.resolve_links(arch_def.find_replace_links(erb.result(binding))) end diff --git a/backends/ext_pdf_doc/templates/ext_pdf.adoc.erb b/backends/ext_pdf_doc/templates/ext_pdf.adoc.erb index c088ff3e1..1b050f62d 100644 --- a/backends/ext_pdf_doc/templates/ext_pdf.adoc.erb +++ b/backends/ext_pdf_doc/templates/ext_pdf.adoc.erb @@ -1,9 +1,9 @@ [[header]] :description: <%= ext.long_name %> (<%= ext.name %>) -:revdate: <%= ext_version.key?("ratification_date") ? ext_version["ratification_date"] : Date.today %> -:revnumber: <%= ext_version["version"] %> +:revdate: <%= ext_version.ratification_date.nil? ? Date.today : ext_version.ratification_date %> +:revnumber: <%= ext_version.version %> :revmark: <%= - case ext_version["state"] + case ext_version.state when "ratified" <<~STATE This document is in the http://riscv.org/spec-state[Ratified state] + \\ @@ -14,12 +14,12 @@ STATE when "frozen" <<~FROZEN_STATE - This document is in the http://riscv.org/spec-state[Frozen state]. - - Change is extremely unlikely. - A high threshold will be used, and a change will only occur because of some truly - critical issue being identified during the public review cycle. - Any other desired or needed changes can be the subject of a follow-on new extension. + This document is in the http://riscv.org/spec-state[Frozen state]. + \\ + + \\ + Change is extremely unlikely. + \\ + A high threshold will be used, and a change will only occur because of some truly + \\ + critical issue being identified during the public review cycle. + \\ + Any other desired or needed changes can be the subject of a follow-on new extension. + \\ FROZEN_STATE when "development" <<~DEV_STATE @@ -28,7 +28,7 @@ Change should be expected + \\ DEV_STATE else - raise "TODO: #{ext_version["state"]} description" + raise "TODO: #{ext_version.state} description" end %> :company: <%= ext.company.nil? ? "unknown" : ext.company["name"] %> @@ -41,7 +41,7 @@ :title-logo-image: image:risc-v_logo.png["RISC-V International Logo",pdfwidth=3.25in,align=center] :back-cover-image: image:riscv-horizontal-color.svg[opacity=25%] <%- end -%> -<%- unless ext_version["state"] == "ratified" -%> +<%- if ext_version.state == "development" -%> :page-background-image: image:draft.png[opacity=20%] <%- end -%> // Settings @@ -85,7 +85,7 @@ endif::[] // Preamble <%= - case ext_version["state"] + case ext_version.state when "ratified" <<~RATIFIED_STATE [WARNING] @@ -115,7 +115,7 @@ endif::[] ==== DEV_STATE else - raise "TODO: #{ext_version["state"]} description" + raise "TODO: #{ext_version.state} description" end %> @@ -123,17 +123,17 @@ endif::[] == Copyright and license information This document is released under the <%= ext.doc_license.nil? ? "unknown" : ext.doc_license["url"] %>[<%= ext.doc_license.nil? ? "unknown" : ext.doc_license["name"] %>]. -Copyright <%= ext_version["ratification_date"].nil? ? Date.today.year : ext_version["ratification_date"].split("-")[0] %> by <%= ext.company.nil? ? "unknown" : ext.company["name"] %>. +Copyright <%= ext_version.ratification_date.nil? ? Date.today.year : ext_version.ratification_date.split("-")[0] %> by <%= ext.company.nil? ? "unknown" : ext.company["name"] %>. [preface] == Acknowledgements <%- ext.versions.each do |version| -%> -Contributors to version <%= version["version"] %> of the specification (in alphabetical order) include: + +Contributors to version <%= version.version %> of the specification (in alphabetical order) include: + -<%- unless version["contributors"].nil? -%> -<%- version["contributors"].sort { |a, b| a["name"].split(" ").last <=> b["name"].split(" ").last }.each do |c| -%> - * <%= c["name"] %> <<%= c["email"] %>> (<%= c["company"] %>) +<%- unless version.contributors.empty? -%> +<%- version.contributors.sort { |a, b| a.name.split(" ").last <=> b.name.split(" ").last }.each do |c| -%> + * <%= c.name %> <<%= c.email %>> (<%= c.company %>) <%- end -%> <%- end -%> @@ -149,21 +149,29 @@ The following versions have been defined: <%- ext.versions.each do |version| -%> -- -Version:: <%= version["version"] %> -State:: <%= version["state"] %> -<%- if version.key?("ratification_date") && !version["ratification_date"].nil? -%> -Ratification Date:: <%= version["ratification_date"] %> +Version:: <%= version.version %> +State:: <%= version.state %> +<%- unless version.ratification_date.nil? -%> +Ratification Date:: <%= version.ratification_date %> <%- end -%> -<%- if version.key?("url") -%> -Design document:: <%= version["url"] %> +<%- unless version.url.nil? -%> +Design document:: <%= version.url %> <%- end -%> -<%- if version.key?("changes") -%> +<%- unless version.changes.empty? -%> Changes:: -<%= version["changes"] %> + + <% version.changes.each do |c| -%> + * <%= c %> + <% end -%> + <%- end -%> -<%- unless version["implies"].nil? || version["implies"].empty? -%> +<%- unless version.implications.empty? -%> Implies:: -* <%= version["implies"].map { |name, version| "#{name} (#{version})" }.join("\n* ") %> +* <%= version.implications.map { |i| "#{i.name} (#{i.version})" }.join("\n* ") %> +<%- unless version.requirements.empty? -%> +Requires:: +<%= version.requirements.to_asciidoc %> +<%- end -%> <%- end -%> -- <%- end -%> @@ -176,13 +184,22 @@ Implies:: <%- unless ext.implies.nil? -%> === Sub-extensions -The following sub-extensions are defined: +<%- if ext.implies.size > 1 -%> +<%= ext.name %> defines the following #{ext.implies.size} sub-extensions: +<%- else -%> +<%= ext.name %> defines a single sub-extension: +<%- end -%> <%- ext.implies.each do |sub_ext| -%> ==== <%= sub_ext.name %> <%= arch_def.extension(sub_ext.name).description %> +<%- unless sub_ext.requirements.empty? -%> +<%= sub_ext.name %> requires: +<%= sub_ext.requirements.to_asciidoc %> +<%- end -%> + <%- end -%> <%- end -%> @@ -194,14 +211,14 @@ The following <%= ext.instructions.size %> instructions are added by this extens [%autowidth] |=== -| RV32 | RV64 | Mnemonic | Instruction | <%= ext.versions.map { |v| "v#{v["version"]}" }.join(" | ") %> +| RV32 | RV64 | Mnemonic | Instruction | <%= ext.versions.map { |v| "v#{v.version}" }.join(" | ") %> <%- ext.instructions.each do |i| -%> | <%= i.rv32? ? "✓" : "" %> | <%= i.rv64? ? "✓" : "" %> | `<%= i.name %> <%= i.assembly.gsub("x", "r").strip %>` | xref:insns-<%= i.name.gsub('.', '_') %>[<%= i.long_name %>] -| <%= ext.versions.map { |v| i.defined_by?(ext.name, v["version"]) ? "✓" : "" }.join(" | ") %> +| <%= ext.versions.map { |v| i.defined_by?(ext.name, v.version) ? "✓" : "" }.join(" | ") %> <%- end -%> |=== @@ -230,14 +247,14 @@ The following <%= ext.csrs.size %> are added by this extension. [%autowidth] |=== -| RV32 | RV64 | CSR | Name | <%= ext.versions.map { |v| "v#{v["version"]}" }.join(" | ") %> +| RV32 | RV64 | CSR | Name | <%= ext.versions.map { |v| "v#{v.version}" }.join(" | ") %> <%- ext.csrs.each do |csr| -%> | <%= csr.defined_in_base32? ? "✓" : "" %> | <%= csr.defined_in_base64? ? "✓" : "" %> | xref:csrs-<%= csr.name.gsub('.', '_') %>[<%= csr.name %>] | <%= csr.long_name %> -| <%= ext.versions.map { |v| csr.defined_by?(ext.name, v["version"]) ? "✓" : "" }.join(" | ") %> +| <%= ext.versions.map { |v| csr.defined_by?(ext.name, v.version) ? "✓" : "" }.join(" | ") %> <%- end -%> |=== diff --git a/backends/manual/templates/ext.adoc.erb b/backends/manual/templates/ext.adoc.erb index 211fd068b..410576a9c 100644 --- a/backends/manual/templates/ext.adoc.erb +++ b/backends/manual/templates/ext.adoc.erb @@ -5,28 +5,29 @@ == Versions <%- ext.versions.each do |v| -%> -<%= v["version"] %>:: +<%= v.version %>:: State::: - <%= v["state"] %> - <%- if v["state"] == "ratified" -%> + <%= v.state %> + <%- if v.state == "ratified" -%> Ratification date::: - <%= v["ratification_date"] %> + <%= v.ratification_date %> <%- end -%> - <%- if v.key?("changes") -%> + <%- unless v.changes.empty? -%> Changes::: - <%- v["changes"].each do |change| %> + + <%- v.changes.each do |change| %> * <%= change %> <%- end -%> + <%- end -%> - <%- if v.key?("url") -%> + <%- unless v.url.nil? -%> Ratification document::: - <%= v["url"] %> + <%= v.url %> <%- end -%> - <%- if v.key?("implies") -%> + <%- unless v.implications -%> Implies::: - <%- implications = v["implies"][0].is_a?(Array) ? v["implies"] : [v["implies"]] -%> - <%- implications.each do |i| -%> - * `<%= i[0] %>` version <%= i[1] %> + <%- v.implications.each do |i| -%> + * `<%= i.name %>` version <%= i.version %> <%- end -%> <%- end -%> <%- end -%> @@ -35,7 +36,7 @@ <%= ext.description %> -<%- insts = arch_def.instructions.select { |i| ext.versions.any? { |v| i.defined_by?(ext.name, v["version"]) } } -%> +<%- insts = arch_def.instructions.select { |i| ext.versions.any? { |v| i.defined_by?(ext.name, v.version) } } -%> <%- unless insts.empty? -%> == Instructions diff --git a/backends/manual/templates/param_list.adoc.erb b/backends/manual/templates/param_list.adoc.erb index 8e0fb65cf..518e38253 100644 --- a/backends/manual/templates/param_list.adoc.erb +++ b/backends/manual/templates/param_list.adoc.erb @@ -1,7 +1,7 @@ = Architectural Parameters <%- - params = manual_version.extensions.map{ |e| e.params(arch_def) }.flatten.uniq(&:name).sort_by!(&:name) + params = manual_version.extensions.map{ |e| e.params }.flatten.uniq(&:name).sort_by!(&:name) -%> The following <%= params.size %> parameters are defined in this manual: diff --git a/backends/profile_doc/templates/profile.adoc.erb b/backends/profile_doc/templates/profile.adoc.erb index 5a5d48411..f0604bb0c 100644 --- a/backends/profile_doc/templates/profile.adoc.erb +++ b/backends/profile_doc/templates/profile.adoc.erb @@ -422,7 +422,7 @@ associated implementation-defined parameters. .Status |=== -| Profile | v<%= ext.versions.map { |v| v["version"] }.join(" | v") %> +| Profile | v<%= ext.versions.map { |v| v.version }.join(" | v") %> <% profile_release.profiles.each do |profile| -%> | <%= profile.marketing_name %> | <%= profile.version_strongest_presence(ext.name, ext.versions).join(" | ") -%> @@ -431,29 +431,28 @@ associated implementation-defined parameters. |=== <% ext.versions.each do |v| -%> -<%= v["version"] %>:: +<%= v.version %>:: Ratification date::: - <%= v["ratification_date"] %> - <% if v.key?("changes") -%> + <%= v.ratification_date %> + <% unless v.changes.empty? -%> Changes::: - <% v["changes"].each do |c| -%> + <% v.changes.each do |c| -%> * <%= c %> <% end -%> - <% end -%> - <% if v.key?("url") -%> + <%- end -%> + <%- unless v.url.nil? -%> Ratification document::: - <%= v["url"] %> - <% end -%> - <% if v.key?("implies") -%> + <%= v.url %> + <%- end -%> + <%- unless v.implications -%> Implies::: - <% implications = v["implies"][0].is_a?(Array) ? v["implies"] : [v["implies"]] -%> - <% implications.each do |i| -%> - * `<%= i[0] %>` version <%= i[1] %> - <% end -%> - <% end -%> -<% end -%> + <%- v.implications.each do |i| -%> + * `<%= i.name %>` version <%= i.version %> + <%- end -%> + <%- end -%> +<%- end -%> ==== Synopsis diff --git a/lib/arch_def.rb b/lib/arch_def.rb index 5c16477a9..749e2a725 100644 --- a/lib/arch_def.rb +++ b/lib/arch_def.rb @@ -373,7 +373,7 @@ def implemented_extensions @implemented_extensions = [] if @arch_def.key?("implemented_extensions") @arch_def["implemented_extensions"].each do |e| - @implemented_extensions << ExtensionVersion.new(e["name"], e["version"]) + @implemented_extensions << ExtensionVersion.new(e["name"], e["version"], self) end end @implemented_extensions diff --git a/lib/arch_obj_models/csr.rb b/lib/arch_obj_models/csr.rb index 7dc5ee7a9..77613e8ee 100644 --- a/lib/arch_obj_models/csr.rb +++ b/lib/arch_obj_models/csr.rb @@ -536,10 +536,8 @@ def wavedrom_desc(arch_def, effective_xlen, exclude_unimplemented: false, option desc["reg"] << { "bits" => n, type: 1 } end if arch_def.partially_configured? && field.optional_in_cfg?(arch_def) - puts "#{name}.#{field.name} is OPTIONAL #{optional_type}" desc["reg"] << { "bits" => field.location(arch_def, effective_xlen).size, "name" => field.name, type: optional_type } else - puts "#{name}.#{field.name} is NOT OPTIONAL 3" desc["reg"] << { "bits" => field.location(arch_def, effective_xlen).size, "name" => field.name, type: 3 } end last_idx = field.location(arch_def, effective_xlen).max diff --git a/lib/arch_obj_models/extension.rb b/lib/arch_obj_models/extension.rb index 61d40a4ec..85361d8b6 100644 --- a/lib/arch_obj_models/extension.rb +++ b/lib/arch_obj_models/extension.rb @@ -138,7 +138,11 @@ def doc_license # @return [Array] versions hash from config def versions - @data["versions"] + return @versions unless @versions.nil? + + @versions = @data["versions"].map do |v| + ExtensionVersion.new(name, v["version"], arch_def) + end end # @return [Array] Ratified versions hash from config @@ -146,22 +150,22 @@ def ratified_versions @data["versions"].select { |v| v["state"] == "ratified" } end - # @return [String] Mimumum defined version of this extension + # @return [Gem::Version] Mimumum defined version of this extension def min_version - versions.map { |v| Gem::Version.new(v["version"]) }.min + versions.map { |v| v.version }.min end - # @return [String] Maximum defined version of this extension + # @return [Gem::Version] Maximum defined version of this extension def max_version - versions.map { |v| Gem::Version.new(v["version"]) }.max + versions.map { |v| v.version }.max end - # @return [String] Mimumum defined ratified version of this extension + # @return [Gem::Version] Mimumum defined ratified version of this extension # @return [nil] if there is no ratified version def min_ratified_version return nil if ratified_versions.empty? - ratified_versions.map { |v| Gem::Version.new(v["version"]) }.min + ratified_versions.map { |v| v.version }.min end # @return [Array] List of parameters added by this extension @@ -196,9 +200,9 @@ def implies(version_requirement = ">= 0") next when Array if v["implies"][0].is_a?(Array) - implications += v["implies"].map { |e| ExtensionVersion.new(e[0], e[1])} + implications += v["implies"].map { |e| ExtensionVersion.new(e[0], e[1], @arch_def)} else - implications << ExtensionVersion.new(v["implies"][0], v["implies"][1]) + implications << ExtensionVersion.new(v["implies"][0], v["implies"][1], @arch_def) end end end @@ -222,7 +226,7 @@ def instructions def csrs return @csrs unless @csrs.nil? - @csrs = arch_def.csrs.select { |csr| csr.defined_by?(ExtensionVersion.new(name, max_version)) } + @csrs = arch_def.csrs.select { |csr| csr.defined_by?(ExtensionVersion.new(name, max_version, @arch_def)) } end # @return [Array] the list of CSRs implemented by this extension (may be empty) @@ -232,7 +236,7 @@ def implemented_csrs(archdef) return @implemented_csrs unless @implemented_csrs.nil? @implemented_csrs = archdef.implemented_csrs.select do |csr| - versions.any? { |ver| csr.defined_by?(ExtensionVersion.new(name, ver["version"])) } + versions.any? { |ver| csr.defined_by?(ExtensionVersion.new(name, ver["version"], @arch_def)) } end end @@ -243,7 +247,7 @@ def implemented_instructions(archdef) return @implemented_instructions unless @implemented_instructions.nil? @implemented_instructions = archdef.implemented_instructions.select do |inst| - versions.any? { |ver| inst.defined_by?(ExtensionVersion.new(name, ver["version"])) } + versions.any? { |ver| inst.defined_by?(ExtensionVersion.new(name, ver["version"], @arch_def)) } end end @@ -297,22 +301,46 @@ class ExtensionVersion # @return [Gem::Version] Version of the extension attr_reader :version + # @return [Extension] Extension + attr_reader :ext + # @param name [#to_s] The extension name # @param version [Integer,String] The version specifier # @param arch_def [ArchDef] The architecture definition - def initialize(name, version) + def initialize(name, version, arch_def) @name = name.to_s @version = Gem::Version.new(version) + @arch_def = arch_def + unless arch_def.nil? + @ext = arch_def.extension(@name) + raise "Extension #{name} not found in arch def" if @ext.nil? + + @data = @ext.data["versions"].find { |v| v["version"] == version.to_s } + raise "Extension #{name} version #{version} not found in arch def" if @data.nil? + end end - # @return Extension the extension object - def ext(arch_def) - arch_def.extension(name) + # @return [String] The state of the extension version ('ratified', 'developemnt', etc) + def state = @data["state"] + + def ratification_date = @data["ratification_date"] + + def changes = @data["changes"].nil? ? [] : @data["changes"] + + def url = @data["url"] + + def contributors + return @contributors unless @contributors.nil? + + @contributors = [] + @data["contributors"].each do |c| + @contributors << Person.new(c) + end end # @return [Array] The list of parameters for this extension version - def params(arch_def) - ext(arch_def).params.select { |p| p.defined_in_extension_version?(@version) } + def params + @ext.params.select { |p| p.defined_in_extension_version?(@version) } end def to_s @@ -336,6 +364,42 @@ def ==(other) end end + def requirements + r = case @data["requires"] + when nil + AlwaysTrueSchemaCondition.new + when Hash + SchemaCondition.new(@data["requires"]) + else + SchemaCondition.new({"oneOf" => [@data["requires"]]}) + end + if @data.key?("implies") + rs = [r] + implications.map { |e| e.requirements } + rs = rs.reject { |r| r.empty? } + unless rs.empty? + r = SchemaCondition.all_of(*rs.map { |r| r.to_h }) + end + end + r + end + + def implications + return @implications unless @implications.nil? + + @implications = [] + case @data["implies"] + when nil + return @implications + when Array + if @data["implies"][0].is_a?(Array) + @implications += @data["implies"].map { |e| ExtensionVersion.new(e[0], e[1], @arch_def)} + else + @implications << ExtensionVersion.new(@data["implies"][0], @data["implies"][1], @arch_def) + end + end + @implications + end + # @param ext_name [String] Extension name # @param ext_version_requirements [Number,String,Array] Extension version requirements, taking the same inputs as Gem::Requirement # @see https://docs.ruby-lang.org/en/3.0/Gem/Requirement.html#method-c-new Gem::Requirement#new @@ -529,7 +593,7 @@ def satisfying_versions(archdef) return [] if ext.nil? ext.versions.select { |v| @requirement.satisfied_by?(Gem::Version.new(v["version"])) }.map do |v| - ExtensionVersion.new(@name, v["version"]) + ExtensionVersion.new(@name, v["version"], archdef) end end diff --git a/lib/arch_obj_models/manual.rb b/lib/arch_obj_models/manual.rb index 049ed1c47..78df1c388 100644 --- a/lib/arch_obj_models/manual.rb +++ b/lib/arch_obj_models/manual.rb @@ -102,7 +102,7 @@ def extensions next end - @extensions << ExtensionVersion.new(ext[0], ext[1]) + @extensions << ExtensionVersion.new(ext[0], ext[1], arch_def) end @extensions end diff --git a/lib/arch_obj_models/obj.rb b/lib/arch_obj_models/obj.rb index 69cb76387..215c4ec47 100644 --- a/lib/arch_obj_models/obj.rb +++ b/lib/arch_obj_models/obj.rb @@ -274,6 +274,10 @@ def initialize(composition_hash) @hsh = composition_hash end + def to_h = @hsh + + def empty? = false + VERSION_REQ_REGEX = /^((>=)|(>)|(~>)|(<)|(<=)|(=))?\s*[0-9]+(\.[0-9]+(\.[0-9]+(-[a-fA-F0-9]+)?)?)?$/ def is_a_version_requirement(ver) case ver @@ -356,6 +360,45 @@ def first_requirement(req = @hsh) end end + # combine all conds into one using AND + def self.all_of(*conds) + cond = SchemaCondition.new({ + "allOf" => conds + }) + + SchemaCondition.new(cond.minimize) + end + + # @return [Object] Schema for this condition, with basic logic minimization + def minimize(hsh = @hsh) + case hsh + when Hash + if hsh.key?("name") + hsh + else + min_ary = key = nil + if hsh.key?("allOf") + min_ary = hsh["allOf"].map { |element| minimize(element) } + key = "allOf" + elsif hsh.key?("anyOf") + min_ary = hsh["anyOf"].map { |element| minimize(element) } + key = "anyOf" + elsif hsh.key?("oneOf") + min_ary = hsh["oneOf"].map { |element| minimize(element) } + key = "oneOf" + end + min_ary = min_ary.uniq! + if min_ary.size == 1 + min_ary.first + else + { key => min_ary } + end + end + else + hsh + end + end + def to_rb_helper(hsh) if hsh.is_a?(Hash) if hsh.key?("name") @@ -433,4 +476,8 @@ class AlwaysTrueSchemaCondition def to_rb = "true" def satisfied_by? = true + + def empty? = true + + def to_h = {} end diff --git a/lib/arch_obj_models/portfolio.rb b/lib/arch_obj_models/portfolio.rb index d2a461b24..6d0da27e5 100644 --- a/lib/arch_obj_models/portfolio.rb +++ b/lib/arch_obj_models/portfolio.rb @@ -79,8 +79,8 @@ def version_strongest_presence(ext_name, ext_versions) # See if any extension requirement in this profile lists this version as either mandatory or optional. ext_versions.map do |v| - mandatory = mandatory_ext_reqs.any? { |ext_req| ext_req.satisfied_by?(ext_name, v["version"]) } - optional = optional_ext_reqs.any? { |ext_req| ext_req.satisfied_by?(ext_name, v["version"]) } + mandatory = mandatory_ext_reqs.any? { |ext_req| ext_req.satisfied_by?(ext_name, v.version) } + optional = optional_ext_reqs.any? { |ext_req| ext_req.satisfied_by?(ext_name, v.version) } # Just show strongest presence (mandatory stronger than optional). if mandatory diff --git a/lib/resolver.rb b/lib/resolver.rb new file mode 100644 index 000000000..2427ff5b9 --- /dev/null +++ b/lib/resolver.rb @@ -0,0 +1,20 @@ +# given an architecture folder, resolves inheritance and expands some fields + +require "pathname" + +class Resolver + def initialize(arch_folder) + @dir = Pathname.new(arch_folder).realpath + end + + def resolve_all(output_folder) + Dir.glob(@dir / "**" / "*.yaml") do |f| + resolve(f, "#{output_folder}/#{f.gsub("#{@dir.to_s}/", "")}") + end + end + + def resolve(input_file, output_file) + obj = YamlLoader.load(input_file, permitted_classes: [Date]) + File.write(output_file, YAML::dump(obj)) + end +end \ No newline at end of file diff --git a/lib/yaml_resolver.py b/lib/yaml_resolver.py new file mode 100644 index 000000000..47e6c87f3 --- /dev/null +++ b/lib/yaml_resolver.py @@ -0,0 +1,131 @@ +import glob, os + +from copy import deepcopy +from tqdm import tqdm +from ruamel.yaml import YAML +from mergedeep import merge, Strategy + +OUT_DIR="arch_resolved" +UDB_ROOT=os.path.dirname(os.path.dirname(os.path.realpath(__file__))) + +yaml = YAML(typ="rt") +yaml.default_flow_style = False +yaml.preserve_quotes = True + + +def read_yaml(file_path): + with open(file_path, 'r') as file: + data = yaml.load(file) + return data + +def write_yaml(file_path, data): + with open(file_path, 'w') as file: + yaml.dump(data, file) + +def dig(obj, *keys): + if len(keys) == 0: + return obj + + try: + next_obj = obj[keys[0]] + if len(keys) == 1: + return next_obj + else: + return dig(next_obj, *keys[1:]) + except KeyError: + return None + +resolved_objs = {} +def resolve(path, rel_path, arch_root): + if path in resolved_objs: + return resolved_objs[path] + else: + unresolved_data = read_yaml(path) + resolved_objs[path] = _resolve(unresolved_data, [], rel_path, unresolved_data, arch_root) + return resolved_objs[path] + +def _resolve(obj, obj_path, obj_file_path, doc_obj, arch_root): + if not (isinstance(obj, list) or isinstance(obj, dict)): + return obj + + if isinstance(obj, list): + obj = list(map(lambda o: _resolve(o, obj_path, obj_file_path, doc_obj, arch_root), obj)) + return obj + + if "$inherits" in obj: + # handle the inherits key first so that any override will have priority + inherits_targets = [obj["$inherits"]] if isinstance(obj["$inherits"], str) else obj["$inherits"] + obj["$child_of"] = obj["$inherits"] + + new_obj = yaml.load("{}") + + for inherits_target in inherits_targets: + ref_file_path = inherits_target.split("#")[0] + ref_obj_path = inherits_target.split("#")[1].split("/")[1:] + + ref_obj = None + if ref_file_path == "": + ref_file_path = obj_file_path + # this is a reference in the same document + ref_obj = dig(doc_obj, *ref_obj_path) + if ref_obj == None: + raise ValueError(f"{ref_obj_path} cannot be found in #{doc_obj}") + ref_obj = _resolve(ref_obj, ref_obj_path, ref_file_path, doc_obj, arch_root) + else: + # this is a reference to another doc + if not os.path.exists(os.path.join(UDB_ROOT, arch_root, ref_file_path)): + raise ValueError(f"{ref_file_path} does not exist in {arch_root}/") + ref_file_full_path = os.path.join(UDB_ROOT, arch_root, ref_file_path) + + ref_doc_obj = resolve(ref_file_full_path, ref_file_path, arch_root) + ref_obj = dig(ref_doc_obj, *ref_obj_path) + + ref_obj = _resolve(ref_obj, ref_obj_path, ref_file_path, ref_doc_obj, arch_root) + + for key in ref_obj: + if isinstance(new_obj.get(key), dict): + merge(new_obj[key], ref_obj, strategy=Strategy.REPLACE) + else: + new_obj[key] = deepcopy(ref_obj[key]) + + print(f"{obj_file_path} {obj_path} inherits {ref_file_path} {ref_obj_path}") + ref_obj["$parent_of"] = f"{obj_file_path}#/{"/".join(obj_path)}" + + del obj["$inherits"] + + # now new_obj is the child and obj is the parent + # merge them + keys = [] + for key in obj.keys(): + keys.append(key) + for key in new_obj.keys(): + if keys.count(key) == 0: + keys.append(key) + + final_obj = yaml.load('{}') + for key in keys: + if not key in obj: + final_obj[key] = new_obj[key] + elif not key in new_obj: + final_obj[key] = _resolve(obj[key], obj_path + [key], obj_file_path, doc_obj, arch_root) + else: + if isinstance(new_obj[key], dict): + if not isinstance(new_obj[key], dict): + raise ValueError("should be a hash") + final_obj[key] = merge(yaml.load('{}'), new_obj[key], obj[key], strategy=Strategy.REPLACE) + else: + final_obj[key] = _resolve(obj[key], obj_path + [key], obj_file_path, doc_obj, arch_root) + + + return final_obj + else: + for key in obj: + obj[key] = _resolve(obj[key], obj_path + [key], obj_file_path, doc_obj, arch_root) + + return obj + +arch_paths = glob.glob("arch/**/*.yaml", recursive=True, root_dir=UDB_ROOT) +for arch_path in tqdm(arch_paths): + resolved_arch_path = f"{UDB_ROOT}/{OUT_DIR}/{arch_path}" + os.makedirs(os.path.dirname(resolved_arch_path), exist_ok=True) + write_yaml(resolved_arch_path, resolve(arch_path, os.path.join(*arch_path.split("/")[1:]), "arch")) diff --git a/schemas/schema_defs.json b/schemas/schema_defs.json index 92458826d..57c886132 100644 --- a/schemas/schema_defs.json +++ b/schemas/schema_defs.json @@ -91,7 +91,7 @@ }, "requirement_string": { "type": "string", - "pattern": "^((>=)|(>)|(~>)|(<)|(<=)|(=))?\\s*[0-9]+(\\.[0-9]+(\\.[0-9]+(-[a-fA-F0-9]+)?)?)?$" + "pattern": "^((>=)|(>)|(~>)|(<)|(<=)|(=))\\s*[0-9]+(\\.[0-9]+(\\.[0-9]+(-[a-fA-F0-9]+)?)?)?$" }, "version_requirements": { "description": "A (set of) version requirements", From 810e1331766f0e4a785e1c8c4e279dc233b89b87 Mon Sep 17 00:00:00 2001 From: Derek Hower Date: Thu, 21 Nov 2024 09:53:10 -0800 Subject: [PATCH 2/3] Change ext_pdf generator to be optionally version-specific --- .../templates/certificate.adoc.erb | 2 +- backends/ext_pdf_doc/tasks.rake | 52 ++++++++++------ .../ext_pdf_doc/templates/ext_pdf.adoc.erb | 61 ++++++++++++------- .../profile_doc/templates/profile.adoc.erb | 4 +- lib/arch_obj_models/extension.rb | 54 ++++++++-------- 5 files changed, 102 insertions(+), 71 deletions(-) diff --git a/backends/certificate_doc/templates/certificate.adoc.erb b/backends/certificate_doc/templates/certificate.adoc.erb index ef87f9632..291cbbcaf 100644 --- a/backends/certificate_doc/templates/certificate.adoc.erb +++ b/backends/certificate_doc/templates/certificate.adoc.erb @@ -341,7 +341,7 @@ Requirement <%= req.name %> only apply when <%= req.when_pretty %>. <% end -%> // TODO: GitHub issue 92: Use version specified by each profile. -<% insts = arch_def.instructions.select { |i| i.defined_by?(ext.name,ext.min_version) } -%> +<% insts = arch_def.instructions.select { |i| i.defined_by?(ext_db.min_version) } -%> <% unless insts.empty? -%> ==== Instructions diff --git a/backends/ext_pdf_doc/tasks.rake b/backends/ext_pdf_doc/tasks.rake index 165e70589..bbcb92c3a 100644 --- a/backends/ext_pdf_doc/tasks.rake +++ b/backends/ext_pdf_doc/tasks.rake @@ -145,13 +145,18 @@ rule %r{#{$root}/gen/ext_pdf_doc/.*/adoc/.*_extension\.adoc} => proc { |tname| erb.filename = template_path.to_s ext = arch_def.extension(ext_name) - version_num = - if ENV.key?("EXT_VERSION") - ENV["EXT_VERSION"] + version_strs = ENV["VERSION"].split(",") + versions = + if version_strs.include?("all") + ext.versions else - ext.versions.max { |a, b| a.version <=> b.version }.version + vs = ext.versions.select do |ext_ver| + version_strs.include?(ext_ver.version.to_s) + end + vs << ext.max_version if version_strs.include?("latest") + vs.uniq end - ext_version = ext.versions.find { |v| v.version == version_num } + max_version = versions.max { |a, b| a.version <=> b.version } FileUtils.mkdir_p File.dirname(t.name) File.write t.name, AsciidocUtils.resolve_links(arch_def.find_replace_links(erb.result(binding))) end @@ -161,25 +166,36 @@ namespace :gen do Generate PDF documentation for :extension If the extension is custom (from an arch_overlay), also give the config name - DESC - task :ext_pdf, [:extension] do |_t, args| - extension = args[:extension] - Rake::Task[$root / "gen" / "ext_pdf_doc" / "_" / "pdf" / "#{extension}_extension.pdf"].invoke - end + Options: - desc <<~DESC - Generate PDF documentation for :extension that is defined or overlayed in :cfg + * EXT - The extension name + * CFG - The config name, required only when an overlay is required + * VERSION - A list of versions to include. May also be "all" or "latest". + + Examples: + + ./do gen:ext_pdf EXT=Xqci CFG=qc_iu VERSION=latest + ./do gen:ext_pdf EXT=B VERSION=all + ./do gen:ext_pdf EXT=B VERSION=1.0.0 + ./do gen:ext_pdf EXT=B VERSION=1.0.0,1.1.0 - The latest version will be used, but can be overloaded by setting the EXT_VERSION environment variable. DESC - task :cfg_ext_pdf, [:extension, :cfg] do |_t, args| - raise ArgumentError, "Missing required argument :extension" if args[:extension].nil? - raise ArgumentError, "Missing required argument :cfg" if args[:cfg].nil? + task :ext_pdf, [:extension] do |_t, args| + raise ArgumentError, "Missing required argument EXT" if ENV["EXT"].nil? - extension = args[:extension] + extension = ENV["EXT"] + cfg = ENV["CFG"] + version = ENV["VERSION"] + + versions = version.split(",") + raise ArgumentError, "Nothing else should be specified with 'all'" if versions.include?("all") && versions.size > 1 - Rake::Task[$root / "gen" / "ext_pdf_doc" / args[:cfg] / "pdf" / "#{extension}_extension.pdf"].invoke(args) + if cfg.nil? + Rake::Task[$root / "gen" / "ext_pdf_doc" / "_" / "pdf" / "#{extension}_extension.pdf"].invoke + else + Rake::Task[$root / "gen" / "ext_pdf_doc" / cfg / "pdf" / "#{extension}_extension.pdf"].invoke(args) + end end desc <<~DESC diff --git a/backends/ext_pdf_doc/templates/ext_pdf.adoc.erb b/backends/ext_pdf_doc/templates/ext_pdf.adoc.erb index 1b050f62d..46db5142b 100644 --- a/backends/ext_pdf_doc/templates/ext_pdf.adoc.erb +++ b/backends/ext_pdf_doc/templates/ext_pdf.adoc.erb @@ -1,9 +1,9 @@ [[header]] :description: <%= ext.long_name %> (<%= ext.name %>) -:revdate: <%= ext_version.ratification_date.nil? ? Date.today : ext_version.ratification_date %> -:revnumber: <%= ext_version.version %> +:revdate: <%= max_version.ratification_date.nil? ? Date.today : max_version.ratification_date %> +:revnumber: <%= max_version.version %> :revmark: <%= - case ext_version.state + case max_version.state when "ratified" <<~STATE This document is in the http://riscv.org/spec-state[Ratified state] + \\ @@ -28,7 +28,7 @@ Change should be expected + \\ DEV_STATE else - raise "TODO: #{ext_version.state} description" + raise "TODO: #{max_version.state} description" end %> :company: <%= ext.company.nil? ? "unknown" : ext.company["name"] %> @@ -41,7 +41,7 @@ :title-logo-image: image:risc-v_logo.png["RISC-V International Logo",pdfwidth=3.25in,align=center] :back-cover-image: image:riscv-horizontal-color.svg[opacity=25%] <%- end -%> -<%- if ext_version.state == "development" -%> +<%- if max_version.state == "development" -%> :page-background-image: image:draft.png[opacity=20%] <%- end -%> // Settings @@ -85,7 +85,7 @@ endif::[] // Preamble <%= - case ext_version.state + case max_version.state when "ratified" <<~RATIFIED_STATE [WARNING] @@ -115,7 +115,7 @@ endif::[] ==== DEV_STATE else - raise "TODO: #{ext_version.state} description" + raise "TODO: #{max_version.state} description" end %> @@ -123,12 +123,12 @@ endif::[] == Copyright and license information This document is released under the <%= ext.doc_license.nil? ? "unknown" : ext.doc_license["url"] %>[<%= ext.doc_license.nil? ? "unknown" : ext.doc_license["name"] %>]. -Copyright <%= ext_version.ratification_date.nil? ? Date.today.year : ext_version.ratification_date.split("-")[0] %> by <%= ext.company.nil? ? "unknown" : ext.company["name"] %>. +Copyright <%= max_version.ratification_date.nil? ? Date.today.year : max_version.ratification_date.split("-")[0] %> by <%= ext.company.nil? ? "unknown" : ext.company["name"] %>. [preface] == Acknowledgements -<%- ext.versions.each do |version| -%> +<%- versions.each do |version| -%> Contributors to version <%= version.version %> of the specification (in alphabetical order) include: + <%- unless version.contributors.empty? -%> @@ -145,7 +145,13 @@ improved this specification through their comments and questions. [preface] == Versions -The following versions have been defined: +<%- if versions.size > 1 -%> +This specification documents versions <%= versions.map { |v| v.version }.join(', ') %> of <%= ext.name %>: +<%- else -%> +This specification documents version <%= max_version.version %> of <%= ext.name %>. +<%- end -%> + +=== Version History <%- ext.versions.each do |version| -%> -- @@ -182,22 +188,31 @@ Requires:: <%= ext.description %> -<%- unless ext.implies.nil? -%> +<%- implications = versions.map { |v| v.implications }.flatten.uniq -%> +<%- unless implications.nil? -%> === Sub-extensions -<%- if ext.implies.size > 1 -%> -<%= ext.name %> defines the following #{ext.implies.size} sub-extensions: +<%- if implications.size > 1 -%> +<%= ext.name %> defines the following #{implications.size} sub-extensions: <%- else -%> <%= ext.name %> defines a single sub-extension: <%- end -%> -<%- ext.implies.each do |sub_ext| -%> -==== <%= sub_ext.name %> +<%- implications.each do |sub_ext| -%> +==== <%= sub_ext.name %> (<%= sub_ext.version %>) + +<%- if versions.size > 1 -%> +<%= sub_ext.name %> (<%= sub_ext.version %>) is implied by +version <%= versions.select { |v| v.implications.include?(sub_ext)}.map(&:version).join(", ") %> +of <%= ext.name %>. +<%- end -%> <%= arch_def.extension(sub_ext.name).description %> <%- unless sub_ext.requirements.empty? -%> <%= sub_ext.name %> requires: + <%= sub_ext.requirements.to_asciidoc %> + <%- end -%> <%- end -%> @@ -211,27 +226,29 @@ The following <%= ext.instructions.size %> instructions are added by this extens [%autowidth] |=== -| RV32 | RV64 | Mnemonic | Instruction | <%= ext.versions.map { |v| "v#{v.version}" }.join(" | ") %> +| RV32 | RV64 | Mnemonic | Instruction <%- if versions.size > 1 -%>| <%= versions.map { |v| "v#{v.version}" }.join(" | ") %><%- end -%> <%- ext.instructions.each do |i| -%> | <%= i.rv32? ? "✓" : "" %> | <%= i.rv64? ? "✓" : "" %> | `<%= i.name %> <%= i.assembly.gsub("x", "r").strip %>` | xref:insns-<%= i.name.gsub('.', '_') %>[<%= i.long_name %>] +<%- if versions.size > 1 -%> | <%= ext.versions.map { |v| i.defined_by?(ext.name, v.version) ? "✓" : "" }.join(" | ") %> <%- end -%> +<%- end -%> |=== -<%- unless ext.implies.empty? -%> +<%- unless implications.empty? -%> === Instructions by sub-extension +[%autowidth] |=== -| Mnemonic | `<%= ext.name %>` | <%= ext.implies.map { |e| "`#{e.name}`" }.join(" | ") %> +| Mnemonic | <%= implications.map { |e| "`#{e.name}`" }.join(" | ") %> <%- ext.instructions.each do |i| -%> | `<%= i.name %>` -| ✓ -| <%= ext.implies.map { |e| i.defined_by?(e.name, arch_def.extension(e.name).max_version) ? "✓" : "" }.join(" | ") %> +| <%= implications.map { |e| i.defined_by?(e) ? "✓" : "" }.join(" | ") %> <%- end -%> |=== @@ -247,15 +264,17 @@ The following <%= ext.csrs.size %> are added by this extension. [%autowidth] |=== -| RV32 | RV64 | CSR | Name | <%= ext.versions.map { |v| "v#{v.version}" }.join(" | ") %> +| RV32 | RV64 | CSR | Name <%- if versions.size > 1 -%>| <%= versions.map { |v| "v#{v.version}" }.join(" | ") %><%- end -%> <%- ext.csrs.each do |csr| -%> | <%= csr.defined_in_base32? ? "✓" : "" %> | <%= csr.defined_in_base64? ? "✓" : "" %> | xref:csrs-<%= csr.name.gsub('.', '_') %>[<%= csr.name %>] | <%= csr.long_name %> +<%- if versions.size > 1 -%> | <%= ext.versions.map { |v| csr.defined_by?(ext.name, v.version) ? "✓" : "" }.join(" | ") %> <%- end -%> +<%- end -%> |=== diff --git a/backends/profile_doc/templates/profile.adoc.erb b/backends/profile_doc/templates/profile.adoc.erb index f0604bb0c..211975837 100644 --- a/backends/profile_doc/templates/profile.adoc.erb +++ b/backends/profile_doc/templates/profile.adoc.erb @@ -463,8 +463,8 @@ associated implementation-defined parameters. :leveloffset: -3 // TODO: GitHub issue 92: Use version specified by each profile and add version info to inst table below. -<% insts = arch_def.instructions.select { |i| i.defined_by?(ext.name,ext.min_version) } -%> -<% unless insts.empty? -%> +<%- insts = arch_def.instructions.select { |i| i.defined_by?(ext.min_version) } -%> +<%- unless insts.empty? -%> ==== Instructions The following instructions are added by this extension: diff --git a/lib/arch_obj_models/extension.rb b/lib/arch_obj_models/extension.rb index 85361d8b6..60c850586 100644 --- a/lib/arch_obj_models/extension.rb +++ b/lib/arch_obj_models/extension.rb @@ -145,27 +145,27 @@ def versions end end - # @return [Array] Ratified versions hash from config + # @return [Array] Ratified versions hash from config def ratified_versions - @data["versions"].select { |v| v["state"] == "ratified" } + versions.select { |v| v.state == "ratified" } end - # @return [Gem::Version] Mimumum defined version of this extension + # @return [ExtensionVersion] Mimumum defined version of this extension def min_version - versions.map { |v| v.version }.min + versions.min { |a, b| a.version <=> b.version } end - # @return [Gem::Version] Maximum defined version of this extension + # @return [ExtensionVersion] Maximum defined version of this extension def max_version - versions.map { |v| v.version }.max + versions.max { |a, b| a.version <=> b.version } end - # @return [Gem::Version] Mimumum defined ratified version of this extension + # @return [ExtensionVersion] Mimumum defined ratified version of this extension # @return [nil] if there is no ratified version def min_ratified_version return nil if ratified_versions.empty? - ratified_versions.map { |v| v.version }.min + ratified_versions.min { |a, b| a.version <=> b.version } end # @return [Array] List of parameters added by this extension @@ -191,22 +191,9 @@ def initialize(ext_data, arch_def) # @param version_requirement [String] Version requirement # @return [Array] Array of extensions implied by any version of this extension meeting version_requirement def implies(version_requirement = ">= 0") - implications = [] - @data["versions"].each do |v| - next unless Gem::Requirement.new(version_requirement).satisfied_by?(Gem::Version.new(v["version"])) - - case v["implies"] - when nil - next - when Array - if v["implies"][0].is_a?(Array) - implications += v["implies"].map { |e| ExtensionVersion.new(e[0], e[1], @arch_def)} - else - implications << ExtensionVersion.new(v["implies"][0], v["implies"][1], @arch_def) - end - end - end - implications + return [] unless Gem::Requirement.new(version_requirement).satisfied_by?(max_version.version) + + max_version.implications end def conflicts @@ -215,18 +202,18 @@ def conflicts to_extension_requirement_list(@data["conflicts"]) end - # @return [Array] the list of instructions implemented by this extension (may be empty) + # @return [Array] the list of instructions implemented by *any version* of this extension (may be empty) def instructions return @instructions unless @instructions.nil? - @instructions = arch_def.instructions.select { |i| @data["versions"].any? { |version| i.defined_by?(name, version["version"]) }} + @instructions = arch_def.instructions.select { |i| versions.any? { |v| i.defined_by?(v) }} end - # @return [Array] the list of CSRs implemented by this extension (may be empty) + # @return [Array] the list of CSRs implemented by *any version* of this extension (may be empty) def csrs return @csrs unless @csrs.nil? - @csrs = arch_def.csrs.select { |csr| csr.defined_by?(ExtensionVersion.new(name, max_version, @arch_def)) } + @csrs = arch_def.csrs.select { |csr| versions.any? { |v| csr.defined_by?(v) } } end # @return [Array] the list of CSRs implemented by this extension (may be empty) @@ -364,6 +351,14 @@ def ==(other) end end + # @param other [ExtensionVersion] Comparison + # @return [Boolean] Whether or not +other+ is an ExtensionVersion with the same name and version + def eql?(other) + return false unless other.is_a?(ExtensionVersion) + + @name == other.name && @version == other.version + end + def requirements r = case @data["requires"] when nil @@ -392,11 +387,12 @@ def implications return @implications when Array if @data["implies"][0].is_a?(Array) - @implications += @data["implies"].map { |e| ExtensionVersion.new(e[0], e[1], @arch_def)} + @implications += @data["implies"].map { |e| ExtensionVersion.new(e[0], e[1], @arch_def) } else @implications << ExtensionVersion.new(@data["implies"][0], @data["implies"][1], @arch_def) end end + @implications.uniq! @implications end From 466508b6d499e365de7c9defdbe584df9ef007a0 Mon Sep 17 00:00:00 2001 From: Derek Hower Date: Fri, 22 Nov 2024 07:22:37 -0800 Subject: [PATCH 3/3] Complete conversion of ExtensionVersion --- Rakefile | 3 ++ arch/ext/Zca.yaml | 17 --------- .../templates/certificate.adoc.erb | 4 +-- backends/cfg_html_doc/adoc_gen.rake | 2 +- lib/arch_obj_models/extension.rb | 4 +-- lib/arch_obj_models/manual.rb | 2 +- lib/arch_obj_models/portfolio.rb | 35 ++++++++++--------- 7 files changed, 26 insertions(+), 41 deletions(-) delete mode 100644 arch/ext/Zca.yaml diff --git a/Rakefile b/Rakefile index 0b2a9f9a5..b1b3feb28 100644 --- a/Rakefile +++ b/Rakefile @@ -287,6 +287,9 @@ task :regress do ENV["VERSIONS"] = "all" Rake::Task["gen:html_manual"].invoke Rake::Task["gen:html"].invoke("generic_rv64") + ENV["EXT"] = "B" + ENV["VERSION"] = "latest" + Rake::Task["gen:ext_pdf"].invoke Rake::Task["#{$root}/gen/certificate_doc/pdf/MockCertificateModel.pdf"].invoke Rake::Task["#{$root}/gen/certificate_doc/pdf/MC100.pdf"].invoke Rake::Task["#{$root}/gen/profile_doc/pdf/MockProfileRelease.pdf"].invoke diff --git a/arch/ext/Zca.yaml b/arch/ext/Zca.yaml deleted file mode 100644 index f3c3de73f..000000000 --- a/arch/ext/Zca.yaml +++ /dev/null @@ -1,17 +0,0 @@ -# yaml-language-server: $schema=../../schemas/ext_schema.json - -Zca: - long_name: Common compressed instructions - description: | - TODO - type: unprivileged - company: - name: RISC-V International - url: https://riscv.org - versions: - - version: "1.0.0" - state: ratified - ratification_date: 2021-06 - repositories: - - url: https://github.com/riscv/riscv-bitmanip - branch: main diff --git a/backends/certificate_doc/templates/certificate.adoc.erb b/backends/certificate_doc/templates/certificate.adoc.erb index 291cbbcaf..b71e4d1d4 100644 --- a/backends/certificate_doc/templates/certificate.adoc.erb +++ b/backends/certificate_doc/templates/certificate.adoc.erb @@ -297,7 +297,7 @@ Requirement <%= req.name %> only apply when <%= req.when_pretty %>. *Version Requirement*: <%= ext_req.version_requirement %> + -<% ext_db.versions.each do |v| -%> +<% ext.versions.each do |v| -%> <%= v.version %>:: State::: <%= v.state %> @@ -341,7 +341,7 @@ Requirement <%= req.name %> only apply when <%= req.when_pretty %>. <% end -%> // TODO: GitHub issue 92: Use version specified by each profile. -<% insts = arch_def.instructions.select { |i| i.defined_by?(ext_db.min_version) } -%> +<% insts = arch_def.instructions.select { |i| i.defined_by?(ext.min_version) } -%> <% unless insts.empty? -%> ==== Instructions diff --git a/backends/cfg_html_doc/adoc_gen.rake b/backends/cfg_html_doc/adoc_gen.rake index 2e83a6efa..89ccf01ea 100644 --- a/backends/cfg_html_doc/adoc_gen.rake +++ b/backends/cfg_html_doc/adoc_gen.rake @@ -92,7 +92,7 @@ require "ruby-prof" when "ext" puts "Generting full extension list" arch_def.implemented_extensions.each do |ext_version| - lines << " * `#{ext_version.name}` #{ext_version.ext(arch_def).long_name}" + lines << " * `#{ext_version.name}` #{ext_version.ext.long_name}" end when "inst" puts "Generting full instruction list" diff --git a/lib/arch_obj_models/extension.rb b/lib/arch_obj_models/extension.rb index 60c850586..aea15fb7b 100644 --- a/lib/arch_obj_models/extension.rb +++ b/lib/arch_obj_models/extension.rb @@ -588,9 +588,7 @@ def satisfying_versions(archdef) ext = archdef.extension(@name) return [] if ext.nil? - ext.versions.select { |v| @requirement.satisfied_by?(Gem::Version.new(v["version"])) }.map do |v| - ExtensionVersion.new(@name, v["version"], archdef) - end + ext.versions.select { |v| @requirement.satisfied_by?(v.version) } end # @overload diff --git a/lib/arch_obj_models/manual.rb b/lib/arch_obj_models/manual.rb index 78df1c388..86a99576a 100644 --- a/lib/arch_obj_models/manual.rb +++ b/lib/arch_obj_models/manual.rb @@ -97,7 +97,7 @@ def extensions next end - unless ext_obj.versions.any? { |v| v["version"] == ext[1] } + unless ext_obj.versions.any? { |v| v.version == ext[1] } warn "Extension '#{ext[0]}', version '#{ext[1]}' is not defined in the database" next end diff --git a/lib/arch_obj_models/portfolio.rb b/lib/arch_obj_models/portfolio.rb index 6d0da27e5..e378cdc01 100644 --- a/lib/arch_obj_models/portfolio.rb +++ b/lib/arch_obj_models/portfolio.rb @@ -126,11 +126,12 @@ def in_scope_ext_reqs(desired_presence = nil) # Convert String or Hash to object. actual_presence_obj = ExtensionPresence.new(actual_presence) - match = if desired_presence.nil? - true # Always match - else - (actual_presence_obj == desired_presence_converted) - end + match = + if desired_presence.nil? + true # Always match + else + actual_presence_obj == desired_presence_converted + end if match in_scope_ext_reqs << @@ -280,10 +281,10 @@ def all_in_scope_ext_params param = ext.params.find { |p| p.name == param_name } raise "There is no param '#{param_name}' in extension '#{ext_name}" if param.nil? - next unless ext.versions.any? do |ver_hash| - Gem::Requirement.new(ext_data["version"]).satisfied_by?(Gem::Version.new(ver_hash["version"])) && - param.defined_in_extension_version?(ver_hash["version"]) - end + next unless ext.versions.any? do |ext_ver| + Gem::Requirement.new(ext_data["version"]).satisfied_by?(ext_ver.version) && + param.defined_in_extension_version?(ext_ver.version) + end @all_in_scope_ext_params << InScopeExtensionParameter.new(param, param_data["schema"], param_data["note"]) @@ -314,10 +315,10 @@ def in_scope_ext_params(ext_req) ext_param = ext.params.find { |p| p.name == param_name } raise "There is no param '#{param_name}' in extension '#{ext_req.name}" if ext_param.nil? - next unless ext.versions.any? do |ver_hash| - Gem::Requirement.new(ext_data["version"]).satisfied_by?(Gem::Version.new(ver_hash["version"])) && - ext_param.defined_in_extension_version?(ver_hash["version"]) - end + next unless ext.versions.any? do |ext_ver| + Gem::Requirement.new(ext_data["version"]).satisfied_by?(ext_ver.version) && + ext_param.defined_in_extension_version?(ext_ver.version) + end ext_params << InScopeExtensionParameter.new(ext_param, param_data["schema"], param_data["note"]) @@ -336,10 +337,10 @@ def all_out_of_scope_params ext.params.each do |param| next if all_in_scope_ext_params.any? { |c| c.param.name == param.name } - next unless ext.versions.any? do |ver_hash| - Gem::Requirement.new(ext_req.version_requirement).satisfied_by?(Gem::Version.new(ver_hash["version"])) && - param.defined_in_extension_version?(ver_hash["version"]) - end + next unless ext.versions.any? do |ext_ver| + Gem::Requirement.new(ext_req.version_requirement).satisfied_by?(ext_ver.version) && + param.defined_in_extension_version?(ext_ver.version) + end @all_out_of_scope_params << param end