diff --git a/Rakefile b/Rakefile index 4e754773c..a0358f82f 100644 --- a/Rakefile +++ b/Rakefile @@ -338,45 +338,57 @@ namespace :test do end end -namespace :gen do - desc <<~DESC - Generate all certificates and profile PDFs. - DESC - task :cert_profile_pdfs do - puts "===================================" - puts "cert_profile_pdfs: Generating MC100" - puts " 1st target" - puts "===================================" - Rake::Task["#{$root}/gen/certificate_doc/pdf/MC100.pdf"].invoke - - puts "==================================================" - puts "cert_profile_pdfs: Generating MockCertificateModel" - puts " 2nd target" - puts "==================================================" - Rake::Task["#{$root}/gen/certificate_doc/pdf/MockCertificateModel.pdf"].invoke - - puts "===================================" - puts "cert_profile_pdfs: Generating RVA20" - puts " 3rd target" - puts "===================================" - Rake::Task["#{$root}/gen/profile_doc/pdf/RVA20.pdf"].invoke - - puts "===================================" - puts "cert_profile_pdfs: Generating RVA22" - puts " 4th target" - puts "===================================" - Rake::Task["#{$root}/gen/profile_doc/pdf/RVA22.pdf"].invoke - - puts "===================================" - puts "cert_profile_pdfs: Generating RVI20" - puts " 5th target" - puts "===================================" - Rake::Task["#{$root}/gen/profile_doc/pdf/RVI20.pdf"].invoke - - puts "===================================" - puts "cert_profile_pdfs: Generating MockProfileRelease" - puts " 6th target" - puts "===================================" - Rake::Task["#{$root}/gen/profile_doc/pdf/MockProfileRelease.pdf"].invoke - end +desc <<~DESC + Generate all portfolio-based PDFs (certificates and profiles). +DESC +task :portfolio_pdfs do + puts "" + puts "===================================" + puts "Generating MC100" + puts "===================================" + puts "" + Rake::Task["#{$root}/gen/certificate_doc/pdf/MC100.pdf"].invoke + + puts "" + puts "==================================================" + puts "Generating MockCertificateModel" + puts "==================================================" + puts "" + Rake::Task["#{$root}/gen/certificate_doc/pdf/MockCertificateModel.pdf"].invoke + + puts "" + puts "===================================" + puts "Generating RVA20" + puts "===================================" + puts "" + Rake::Task["#{$root}/gen/profile_doc/pdf/RVA20.pdf"].invoke + + puts "" + puts "===================================" + puts "Generating RVA22" + puts "===================================" + puts "" + Rake::Task["#{$root}/gen/profile_doc/pdf/RVA22.pdf"].invoke + + puts "" + puts "===================================" + puts "Generating RVI20" + puts "===================================" + puts "" + Rake::Task["#{$root}/gen/profile_doc/pdf/RVI20.pdf"].invoke + + puts "" + puts "===================================" + puts "Generating MockProfileRelease" + puts "===================================" + puts "" + Rake::Task["#{$root}/gen/profile_doc/pdf/MockProfileRelease.pdf"].invoke end + +# Shortcut targets for building profiles and certificates. +task MC100: "#{$root}/gen/certificate_doc/pdf/MC100.pdf" +task MockCertificateModel: "#{$root}/gen/certificate_doc/pdf/MockCertificateModel.pdf" +task RVI20: "#{$root}/gen/profile_doc/pdf/RVI20.pdf" +task RVA20: "#{$root}/gen/profile_doc/pdf/RVA20.pdf" +task RVA22: "#{$root}/gen/profile_doc/pdf/RVA22.pdf" +task MockProfileRelease: "#{$root}/gen/profile_doc/pdf/MockProfileRelease.pdf" diff --git a/arch/csr/menvcfg.yaml b/arch/csr/menvcfg.yaml index 49d263d8b..8feafff95 100644 --- a/arch/csr/menvcfg.yaml +++ b/arch/csr/menvcfg.yaml @@ -6,7 +6,8 @@ name: menvcfg address: 0x30A long_name: Machine Environment Configuration description: | - Contains bits to enable/disable extensions + Contains fields that control certain characteristics of the execution environment + for modes less privileged than M-mode. The `menvcfg` CSR controls certain characteristics of the execution environment for modes less diff --git a/arch/csr/senvcfg.yaml b/arch/csr/senvcfg.yaml index 1c6aea47d..6feb15134 100644 --- a/arch/csr/senvcfg.yaml +++ b/arch/csr/senvcfg.yaml @@ -5,14 +5,15 @@ kind: csr name: senvcfg address: 0x10A long_name: Supervisor Environment Configuration -description: Contains bits to enable/disable extensions +description: | + Contains fields that control certain characteristics of the U-mode execution environment. priv_mode: S length: 64 definedBy: allOf: - - name: Sm - version: ">=1.12" - name: S + version: ">=1.12" + - name: U fields: CBZE: location: 7 diff --git a/arch/ext/S.yaml b/arch/ext/S.yaml index d7fecf677..6d350dcd8 100644 --- a/arch/ext/S.yaml +++ b/arch/ext/S.yaml @@ -6,12 +6,18 @@ name: S type: privileged long_name: Supervisor mode versions: - - version: "1.12.0" - state: ratified - ratification_date: 2021-12 - requires: - name: U - version: "= 1.12.0" +- version: "1.11.0" + state: ratified + ratification_date: 2019-06 + requires: + name: U + version: "= 1.0.0" +- version: "1.12.0" + state: ratified + ratification_date: 2021-12 + requires: + name: U + version: "= 1.0.0" description: | This chapter describes the RISC-V supervisor-level architecture, which contains a common core that is used with various supervisor-level diff --git a/arch/ext/Sm.yaml b/arch/ext/Sm.yaml index db631b92f..53c1a78a8 100644 --- a/arch/ext/Sm.yaml +++ b/arch/ext/Sm.yaml @@ -460,6 +460,7 @@ params: schema: type: string enum: [little, big, dynamic] + # TODO: Only little available in Sm 1.11 MISA_CSR_IMPLEMENTED: description: | Whether or not the `misa` CSR returns zero or a non-zero value. diff --git a/arch/ext/Sstc.yaml b/arch/ext/Sstc.yaml index 03f2b76e7..a662943a5 100644 --- a/arch/ext/Sstc.yaml +++ b/arch/ext/Sstc.yaml @@ -3,8 +3,8 @@ $schema: "ext_schema.json#" kind: extension name: Sstc -long_name: Superivisor mode timer interrupts -description: Superivisor mode timer interrupts +long_name: Supervisor-mode timer interrupts +description: Supervisor-mode timer interrupts type: privileged versions: - version: "0.9.0" diff --git a/arch/ext/Sv32.yaml b/arch/ext/Sv32.yaml index e638dc938..6ac42cabe 100644 --- a/arch/ext/Sv32.yaml +++ b/arch/ext/Sv32.yaml @@ -9,5 +9,5 @@ type: privileged versions: - version: "1.12.0" state: ratified - ratification_date: unknown + ratification_date: null url: https://github.com/riscv/riscv-isa-manual/releases/download/Priv-v1.12/riscv-privileged-20211203.pdf diff --git a/arch/ext/Sv39.yaml b/arch/ext/Sv39.yaml index bd54871df..646c0380e 100644 --- a/arch/ext/Sv39.yaml +++ b/arch/ext/Sv39.yaml @@ -9,5 +9,5 @@ type: privileged versions: - version: "1.12.0" state: ratified - ratification_date: unknown + ratification_date: null url: https://github.com/riscv/riscv-isa-manual/releases/download/Priv-v1.12/riscv-privileged-20211203.pdf diff --git a/arch/ext/Sv48.yaml b/arch/ext/Sv48.yaml index 40b6416f6..ef7f970e5 100644 --- a/arch/ext/Sv48.yaml +++ b/arch/ext/Sv48.yaml @@ -9,7 +9,7 @@ type: privileged versions: - version: "1.12.0" state: ratified - ratification_date: unknown + ratification_date: null url: https://github.com/riscv/riscv-isa-manual/releases/download/Priv-v1.12/riscv-privileged-20211203.pdf requires: name: Sv39 diff --git a/arch/ext/Sv57.yaml b/arch/ext/Sv57.yaml index 62f1d82a1..18bdd3d15 100644 --- a/arch/ext/Sv57.yaml +++ b/arch/ext/Sv57.yaml @@ -9,7 +9,7 @@ type: privileged versions: - version: "1.12.0" state: ratified - ratification_date: unknown + ratification_date: null url: https://github.com/riscv/riscv-isa-manual/releases/download/Priv-v1.12/riscv-privileged-20211203.pdf requires: name: Sv48 diff --git a/arch/ext/U.yaml b/arch/ext/U.yaml index 40a7a0657..cacb07c3f 100644 --- a/arch/ext/U.yaml +++ b/arch/ext/U.yaml @@ -3,13 +3,15 @@ $schema: "ext_schema.json#" kind: extension name: U -long_name: User-level privilege mode -description: User-level privilege mode +long_name: User-mode privilege level +description: | + User-mode privilege level is supported by an implementation if the U extension is present. + Note that the RISC-V ISA doens't formally define a U extension and it is only discussed in the Privileged ISA manual. type: privileged versions: - - version: "1.12.0" - state: ratified - ratification_date: 2019-12 +- version: "1.0.0" + state: ratified + ratification_date: 2019-12 params: MUTABLE_MISA_U: description: | diff --git a/arch/ext/Zihpm.yaml b/arch/ext/Zihpm.yaml index c5922e781..f5f798b9d 100644 --- a/arch/ext/Zihpm.yaml +++ b/arch/ext/Zihpm.yaml @@ -9,6 +9,6 @@ type: unprivileged versions: - version: "2.0.0" state: ratified - ratification_date: unknown + ratification_date: null requires: name: Smhpm diff --git a/arch/profile/MP-S-64.yaml b/arch/profile/MP-S-64.yaml index cd34a233c..de2718e08 100644 --- a/arch/profile/MP-S-64.yaml +++ b/arch/profile/MP-S-64.yaml @@ -36,6 +36,9 @@ extensions: optional: transitory version: "= 1.11" note: Made this a transitory option + Xmock: + presence: mandatory + version: "~> 0.9.9" extra_notes: - presence: mandatory text: | diff --git a/arch/profile/RVA20S64.yaml b/arch/profile/RVA20S64.yaml index 7af25aa36..939b820f0 100644 --- a/arch/profile/RVA20S64.yaml +++ b/arch/profile/RVA20S64.yaml @@ -11,6 +11,13 @@ introduction: | processors. RVA20S64 is based on privileged architecture version 1.11. extensions: $inherits: "profile/RVI20U64.yaml#/extensions" + U: + presence: mandatory + version: "~> 1.0" + param_constraints: + U_MODE_ENDIANESS: + schema: + const: little`` S: presence: mandatory version: "= 1.11" diff --git a/backends/certificate_doc/tasks.rake b/backends/certificate_doc/tasks.rake index 3972b31f3..df4c6562c 100644 --- a/backends/certificate_doc/tasks.rake +++ b/backends/certificate_doc/tasks.rake @@ -25,15 +25,18 @@ Dir.glob("#{$root}/arch/certificate_model/*.yaml") do |f| __FILE__ ] do |t| # TODO: schema validation - cfg_arch = cfg_arch_for("rv#{base}") - cert_model = cfg_arch.cert_model(cert_model_name) - raise "No certificate model defined for #{cert_model_name}" if cert_model.nil? + base_cert_model = cfg_arch_for("rv#{base}").cert_model(cert_model_name) + raise "No certificate model named '#{cert_model_name}'" if base_cert_model.nil? - # Switch to the generated certificate arch def + # Ask base certification model to create an in-memory config arch for this model. # XXX - Add this to profile releases - cfg_arch = cert_model.to_cfg_arch + cfg_arch = base_cert_model.to_cfg_arch + + # Set globals for ERB template. cert_model = cfg_arch.cert_model(cert_model_name) cert_class = cert_model.cert_class + portfolio = cert_model + portfolio_class = cert_class version = File.basename(t.name, '.adoc').split('-')[1..].join('-') diff --git a/backends/certificate_doc/templates/certificate.adoc.erb b/backends/certificate_doc/templates/certificate.adoc.erb index 41e9fb32f..b37cf7f18 100644 --- a/backends/certificate_doc/templates/certificate.adoc.erb +++ b/backends/certificate_doc/templates/certificate.adoc.erb @@ -201,16 +201,11 @@ None == Instruction Summary -<% - insts = cert_model.in_scope_extensions.map { |ext_cert_model| ext_cert_model.instructions }.flatten.uniq - insts.sort_by!(&:name) --%> - [%autowidth] |=== | Name | Long Name -<% insts.each do |inst| -%> +<% portfolio.in_scope_instructions.each do |inst| -%> | <%= link_to_inst(inst.name) %> | <%= inst.long_name %> <% end # do -%> @@ -387,12 +382,7 @@ The following instructions are added by this extension: [appendix] == Instruction Details -<%= - insts = cert_model.in_scope_extensions.map { |ext_cert_model| ext_cert_model.instructions }.flatten.uniq - insts.sort_by!(&:name) --%> - -<% insts.each do |inst| -%> +<% portfolio.in_scope_instructions.each do |inst| -%> <<< <%= anchor_for_inst(inst.name) %> === <%= inst.name %> diff --git a/backends/profile_doc/tasks.rake b/backends/profile_doc/tasks.rake index 8b68ba048..26f1fb869 100644 --- a/backends/profile_doc/tasks.rake +++ b/backends/profile_doc/tasks.rake @@ -7,11 +7,13 @@ rule %r{#{$root}/gen/profile_doc/adoc/.*\.adoc} => [ Dir.glob("#{$root}/arch/profile_release/**/*.yaml") ].flatten do |t| profile_release_name = Pathname.new(t.name).basename(".adoc").to_s - profile_release = cfg_arch_for("_").profile_release(profile_release_name) raise ArgumentError, "No profile release named '#{profile_release_name}'" if profile_release.nil? + # Set globals for ERB template. profile_class = profile_release.profile_class + portfolio_class = profile_class + portfolio = profile_release template_path = Pathname.new "#{$root}/backends/profile_doc/templates/profile.adoc.erb" erb = ERB.new(template_path.read, trim_mode: "-") diff --git a/backends/profile_doc/templates/profile.adoc.erb b/backends/profile_doc/templates/profile.adoc.erb index 9eeef40c5..1d24b8a1c 100644 --- a/backends/profile_doc/templates/profile.adoc.erb +++ b/backends/profile_doc/templates/profile.adoc.erb @@ -2,7 +2,7 @@ :description: <%= profile_release.marketing_name %> Profile :revdate: <%= profile_release.ratification_date.nil? ? Date.today : profile_release.ratification_date %> -// XXX - Figure out what we really want here - Change percent hash to percent equals. +// TODO - Figure out what we really want here - Change percent hash to percent equals. // :revnumber: <%# profile_release.map(&:version).sort.last %> :revmark: "TODO: revmark" @@ -311,6 +311,7 @@ optional profile extensions for a new profile might be prototyped as non-profile extensions on an earlier profile. // XXX - Need to create render() Ruby function. +// See https://github.com/riscv-software-src/riscv-unified-db/issues/59 // <%# render("#{$root}/backends/portfolio_doc/templates/family_intro.erb", portfolio_class: profile_class) %> == <%= profile_class.name %> Profile Class @@ -344,21 +345,6 @@ Ratification date:: <%= profile_release.ratification_date %> -- <% end # each profile_release -%> -=== Extension Presence - -The <%= profile_class.marketing_name %> Profile Class references -<%= profile_class.referenced_extensions.size %> extensions. - -.Status -|=== -| Extension | <%= profile_class.profiles.map(&:marketing_name).join(" | ") -%> - -<% profile_class.referenced_extensions.sort_by(&:name).each do |ext| -%> -| <%= ext.name %> | <%= profile_class.profiles.map { |profile| profile.extension_presence(ext.name) }.join(" | ") -%> -<% end -%> - -|=== - == <%= profile_release.name %> Profile Release <%= profile_release.introduction %> @@ -370,8 +356,8 @@ associated implementation-defined parameters across all its defined profiles. === <%= profile_release.name %> Description <%= profile_release.description %> - <% end # unless -%> + <% profile_release.profiles.each do |profile| -%> === <%= profile.marketing_name %> Profile @@ -411,6 +397,41 @@ associated implementation-defined parameters. <% end # each profile -%> +<<< +[appendix] +== Profile Comparisons + +=== Release Comparison + +The <%= profile_class.marketing_name %> Profile Class has <%= profile_class.profile_releases.size %> releases that +reference a total of <%= profile_class.referenced_extensions.size %> extensions. + +.Extension Presence +|=== +| Extension | <%= profile_class.profile_releases.map(&:marketing_name).join(" | ") %> + +<% profile_class.referenced_extensions.sort_by(&:name).each do |ext| -%> +| <%= ext.name %> | <%= profile_class.profile_releases.map { |profile_release| profile_release.extension_presence(ext.name) }.join(" | ") %> +<% end -%> +|=== + +=== Profile Comparison + +The <%= profile_release.marketing_name %> Profile Release has <%= profile_release.profiles.size %> profiles that +reference a total of <%= profile_release.referenced_extensions.size %> extensions. + +[NOTE] +Extensions present in a profile are also present in higher-privileged profiles in the same profile release. + +.Extension Presence +|=== +| Extension | <%= profile_release.profiles.map(&:marketing_name).join(" | ") %> + +<% profile_release.referenced_extensions.sort_by(&:name).each do |ext| -%> +| <%= ext.name %> | <%= profile_release.profiles.map { |profile| profile.extension_presence(ext.name) }.join(" | ") %> +<% end -%> +|=== + <<< [appendix] == Extension Details @@ -420,20 +441,24 @@ associated implementation-defined parameters. === <%= ext.name %> Extension <%= ext.long_name %> -.Status +.<%= ext.name %> Extension Presence |=== | Profile | v<%= ext.versions.map { |ext_ver| ext_ver.canonical_version.to_s }.join(" | v") %> <% profile_release.profiles.each do |profile| -%> -| <%= profile.marketing_name %> | <%= profile.version_strongest_presence(ext.name, ext.versions).join(" | ") -%> +| <%= profile.marketing_name %> | <%= profile.version_greatest_presence(ext.name, ext.versions).join(" | ") -%> <% end -%> |=== <% ext.versions.each do |v| -%> <%= v.canonical_version %>:: + State::: + <%= v.state %> + <% if v.state == "ratified" -%> Ratification date::: <%= v.ratification_date %> + <% end # if %> <% unless v.changes.empty? -%> Changes::: @@ -441,18 +466,18 @@ associated implementation-defined parameters. * <%= c %> <% end -%> - <%- end -%> - <%- unless v.url.nil? -%> + <% end -%> + <% unless v.url.nil? -%> Ratification document::: <%= v.url %> - <%- end -%> - <%- unless v.implications -%> + <% end -%> + <% unless v.implications -%> Implies::: - <%- v.implications.each do |i| -%> + <% v.implications.each do |i| -%> * `<%= i.name %>` version <%= i.version %> - <%- end -%> - <%- end -%> -<%- end -%> + <% end -%> + <% end -%> +<% end -%> ==== Synopsis diff --git a/cfgs/generic_rv64/cfg.yaml b/cfgs/generic_rv64/cfg.yaml index c15ff4800..7577c8bd3 100644 --- a/cfgs/generic_rv64/cfg.yaml +++ b/cfgs/generic_rv64/cfg.yaml @@ -16,7 +16,7 @@ implemented_extensions: - [Sm, "1.12.0"] - [Smhpm, "1.12.0"] - [Smpmp, "1.12.0"] - - [U, "1.12.0"] + - [U, "1.0.0"] - [V, "1.0.0"] - [Zicntr, "2.0.0"] - [Zicsr, "2.0.0"] diff --git a/do b/do index 8d18a10eb..92ed0e671 100755 --- a/do +++ b/do @@ -9,6 +9,5 @@ fi source $ROOT/bin/setup - # really long way of invoking rake, but renamed to 'do' $BUNDLE exec --gemfile $ROOT/Gemfile ruby -r rake -e "Rake.application.init('do');Rake.application.load_rakefile;Rake.application.top_level" -- $@ diff --git a/lib/arch_obj_models/certificate.rb b/lib/arch_obj_models/certificate.rb index d422ea6c5..80856b6f3 100644 --- a/lib/arch_obj_models/certificate.rb +++ b/lib/arch_obj_models/certificate.rb @@ -69,7 +69,7 @@ def when_pretty "Parameter #{param_name} == #{param_value}" end else - raise "TODO: when type #{key} not implemented" + raise "Type #{key} not implemented" end end.flatten.join(" and ") end @@ -103,7 +103,7 @@ def when_pretty "Parameter #{param_name} == #{param_value}" end else - raise "TODO: when type #{key} not implemented" + raise "Type #{key} not implemented" end end.flatten.join(" and ") end diff --git a/lib/arch_obj_models/extension.rb b/lib/arch_obj_models/extension.rb index 54d4d23fa..d1f97c6b8 100644 --- a/lib/arch_obj_models/extension.rb +++ b/lib/arch_obj_models/extension.rb @@ -6,7 +6,7 @@ # A parameter (AKA option, AKA implementation-defined value) supported by an extension class ExtensionParameter - # @return [ConfiguredArchitecture] The defining Arch def + # @return [ConfiguredArchitecture] The defining configured architecture attr_reader :cfg_arch # @return [String] Parameter name @@ -293,14 +293,14 @@ def initialize(name, version_str, cfg_arch, fail_if_version_does_not_exist: fals @cfg_arch = cfg_arch @ext = @cfg_arch.extension(@name) - raise "Extension #{name} not found in arch def" if @ext.nil? + raise "Extension #{name} not found in configured architecture #{cfg_arch.name}" if @ext.nil? @data = @ext.data["versions"].find { |v| VersionSpec.new(v["version"]) == @version_spec } if fail_if_version_does_not_exist && @data.nil? - raise ArgumentError, "#{@name}, Version #{version_str} is not defined" + raise ArgumentError, "Version #{version_str} of #{@name} extension in #{cfg_arch.name} is not defined" elsif @data.nil? - warn "#{@name}, Version #{version_str} is not defined" + warn "Version #{version_str} of #{@name} extension in #{cfg_arch.name} is not defined" end end @@ -558,8 +558,8 @@ def initialize(data) end end - def mandatory? = (@presence == mandatory) - def optional? = (@presence == optional) + def mandatory? = (@presence == "mandatory") + def optional? = (@presence == "optional") # Class methods def self.mandatory = "mandatory" @@ -604,6 +604,10 @@ def to_s @optional_type.nil? ? "#{presence}" : "#{presence} (#{optional_type})" end + def to_s_concise + "#{presence}" + end + # @overload ==(other) # @param other [String] A presence string # @return [Boolean] whether or not this ExtensionPresence has the same presence (ignores optional_type) @@ -621,15 +625,43 @@ def ==(other) end end - # Sorts by presence, then by optional_type - def <=>(other) + ###################################################### + # Following comparison operators follow these rules: + # - "mandatory" is greater than "optional" + # - optional_types all have same rank + # - equals compares presence and then optional_type + ###################################################### + + # @overload >(other) + # @param other [ExtensionPresence] An extension presence object + # @return [Boolean] Whether or not this ExtensionPresence is greater-than the other + def >(other) raise ArgumentError, "ExtensionPresence is only comparable to other ExtensionPresence classes" unless other.is_a?(ExtensionPresence) + (self.mandatory? && other.optional?) + end - if @presence != other.presence - @presence <=> other.presence - else - @optional_type <=> other.optional_type - end + # @overload >=(other) + # @param other [ExtensionPresence] An extension presence object + # @return [Boolean] Whether or not this ExtensionPresence is greater-than or equal to the other + def >=(other) + raise ArgumentError, "ExtensionPresence is only comparable to other ExtensionPresence classes" unless other.is_a?(ExtensionPresence) + (self > other) || (self == other) + end + + # @overload <(other) + # @param other [ExtensionPresence] An extension presence object + # @return [Boolean] Whether or not this ExtensionPresence is less-than the other + def <(other) + raise ArgumentError, "ExtensionPresence is only comparable to other ExtensionPresence classes" unless other.is_a?(ExtensionPresence) + (self.optional? && other.mandatory?) + end + + # @overload <=(other) + # @param other [ExtensionPresence] An extension presence object + # @return [Boolean] Whether or not this ExtensionPresence is less-than or equal to the other + def <=(other) + raise ArgumentError, "ExtensionPresence is only comparable to other ExtensionPresence classes" unless other.is_a?(ExtensionPresence) + (self < other) || (self == other) end end @@ -650,8 +682,12 @@ class ExtensionRequirement # @return [Array] Set of requirement specifications def requirement_specs = @requirements + def requirement_specs_to_s + "#{@requirements.map(&:to_s).join(', ')}" + end + def to_s - "#{name} #{@requirements.map(&:to_s).join(', ')}" + "#{name} " + requirement_specs_to_s end # @return [Extension] The extension that this requirement is for @@ -673,7 +709,7 @@ def initialize(name, *requirements, cfg_arch: nil, note: nil, req_id: nil, prese @cfg_arch = cfg_arch @ext = @cfg_arch.extension(@name) - raise ArgumentError, "Could not find extension named '#{@name}'" if @ext.nil? + raise ArgumentError, "Could not find extension named '#{@name}' in #{@cfg_arch.name}" if @ext.nil? requirements = if requirements.empty? @@ -721,7 +757,7 @@ def satisfied_by?(*args) end end else - raise ArgumentError, "Single argument must be an ExtensionVersion or ExtensionRquirement" + raise ArgumentError, "Single argument must be an ExtensionVersion or ExtensionRequirement" end elsif args.size == 2 raise ArgumentError, "First parameter must be an extension name" unless args[0].respond_to?(:to_s) diff --git a/lib/arch_obj_models/portfolio.rb b/lib/arch_obj_models/portfolio.rb index 0afa26720..2ee8f2169 100644 --- a/lib/arch_obj_models/portfolio.rb +++ b/lib/arch_obj_models/portfolio.rb @@ -49,36 +49,49 @@ def description = @data["description"] # @return [Gem::Version] Semantic version of the PortfolioInstance def version = Gem::Version.new(@data["version"]) + # @return [ExtensionPresence] Given an extension +ext_name+, return the presence. + # If the extension name isn't found in the portfolio, return nil. + def extension_presence_obj(ext_name) + # Get extension information from YAML for passed in extension name. + ext_data = @data["extensions"][ext_name] + + ext_data.nil? ? nil : ExtensionPresence.new(ext_data["presence"]) + end + # @return [String] Given an extension +ext_name+, return the presence as a string. # If the extension name isn't found in the portfolio, return "-". def extension_presence(ext_name) - # Get extension information from YAML for passed in extension name. - ext_data = @data["extensions"][ext_name] + ext_presence_obj = extension_presence_obj(ext_name) - ext_data.nil? ? "-" : ExtensionPresence.new(ext_data["presence"]).to_s + ext_presence_obj.nil? ? "-" : ext_presence_obj.to_s end - # Returns the strongest presence string for each of the specified versions. + # Returns the greatest presence string for each of the specified versions. # @param ext_name [String] # @param ext_versions [Array] # @return [Array] - def version_strongest_presence(ext_name, ext_versions) + def version_greatest_presence(ext_name, ext_versions) presences = [] # 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?(v) } - optional = optional_ext_reqs.any? { |ext_req| ext_req.satisfied_by?(v) } - - # Just show strongest presence (mandatory stronger than optional). - presences << - if mandatory - ExtensionPresence.mandatory - elsif optional - ExtensionPresence.optional - else - "-" + greatest_presence = nil + + in_scope_ext_reqs.each do |ext_req| + if ext_req.satisfied_by?(v) + presence = extension_presence_obj(ext_name) + + unless presence.nil? + if greatest_presence.nil? + greatest_presence = presence + elsif presence > greatest_presence + greatest_presence = presence + end + end end + end + + presences << (greatest_presence.nil? ? "-" : greatest_presence.to_s_concise) end presences @@ -108,13 +121,23 @@ def in_scope_ext_reqs(desired_presence = nil) desired_presence.is_a?(ExtensionPresence) ? desired_presence : ExtensionPresence.new(desired_presence) + missing_ext = false + @data["extensions"]&.each do |ext_name, ext_data| next if ext_name[0] == "$" + # Does extension even exist? + # If not, don't raise an error right away so we can find all of the missing extensions and report them all. + ext = cfg_arch.extension(ext_name) + if ext.nil? + puts "Extension #{ext_name} for #{name} not found in database" + missing_ext = true + end + actual_presence = ext_data["presence"] # Could be a String or Hash raise "Missing extension presence for extension #{ext_name}" if actual_presence.nil? - # Convert String or Hash to object. + # Convert presence String or Hash to object. actual_presence_obj = ExtensionPresence.new(actual_presence) match = @@ -137,6 +160,11 @@ def in_scope_ext_reqs(desired_presence = nil) end end end + + # TODO: Change to "raise" when missing extensions added to database so we can make progress until then. + # See https://github.com/riscv-software-src/riscv-unified-db/issues/320 + puts "One or more extensions referenced by #{name} missing in database" if missing_ext + in_scope_ext_reqs end @@ -148,19 +176,20 @@ def optional_type_ext_reqs = in_scope_ext_reqs(ExtensionPresence.optional) def in_scope_extensions return @in_scope_extensions unless @in_scope_extensions.nil? - @in_scope_extensions = in_scope_ext_reqs.map do |er| - obj = cfg_arch.extension(er.name) - - # @todo: change this to raise once all the profile extensions - # are defined - warn "Extension #{er.name} is not defined" if obj.nil? - - obj - end.reject(&:nil?) + @in_scope_extensions = in_scope_ext_reqs.map do |ext_req| + cfg_arch.extension(ext_req.name) + end.reject(&:nil?) # Filter out extensions that don't exist yet. @in_scope_extensions end + # @return [Array] Sorted list of all instructions associated with extensions listed as + # mandatory or optional in portfolio. Uses minimum version of + # extension version that meets extension requirement specified in portfolio. + def in_scope_instructions + in_scope_extensions.map { |ext| ext.instructions }.flatten.uniq.sort + end + # @return [Boolean] Does the profile differentiate between different types of optional. def uses_optional_types? return @uses_optional_types unless @uses_optional_types.nil? @@ -178,6 +207,9 @@ def uses_optional_types? @uses_optional_types end + # Called by rakefile when generating a particular portfolio instance. + # Creates an in-memory data structure used by all portfolio routines that access a cfg_arch. + # # @return [ConfiguredArchitecture] A partially-configured architecture definition corresponding to this portfolio. def to_cfg_arch return @generated_cfg_arch unless @generated_cfg_arch.nil? @@ -198,7 +230,7 @@ def to_cfg_arch "params" => all_in_scope_ext_params.select(&:single_value?).map { |p| [p.name, p.value] }.to_h } - # XXX Add list of prohibited_extensions + # TODO: Add list of prohibited_extensions @generated_cfg_arch = Dir.mktmpdir do |dir| diff --git a/lib/arch_obj_models/profile.rb b/lib/arch_obj_models/profile.rb index 1149689c8..4e7e4910e 100644 --- a/lib/arch_obj_models/profile.rb +++ b/lib/arch_obj_models/profile.rb @@ -111,6 +111,27 @@ def referenced_extensions @referenced_extensions end + + # @return [String] Given an extension +ext_name+, return the presence as a string. + # Returns the greatest presence string across all profiles in the release. + # If the extension name isn't found in the release, return "-". + def extension_presence(ext_name) + greatest_presence = nil + + profiles.each do |profile| + presence = profile.extension_presence_obj(ext_name) + + unless presence.nil? + if greatest_presence.nil? + greatest_presence = presence + elsif presence > greatest_presence + greatest_presence = presence + end + end + end + + greatest_presence.nil? ? "-" : greatest_presence.to_s_concise + end end # Representation of a specific profile in a profile release. @@ -204,7 +225,7 @@ def ext_req_to_adoc(ext_req) ext = cfg_arch.extension(ext_req.name) ret << "* *#{ext_req.name}* " + (ext.nil? ? "" : ext.long_name) ret << "+" - ret << "Version #{ext_req.requirement_specs}" + ret << "Version #{ext_req.requirement_specs_to_s}" ret end diff --git a/lib/arch_obj_models/schema.rb b/lib/arch_obj_models/schema.rb index 041e1da7c..ff11d6b94 100644 --- a/lib/arch_obj_models/schema.rb +++ b/lib/arch_obj_models/schema.rb @@ -107,7 +107,7 @@ def to_pretty_s(schema_hash = @schema_hash) raise "to_pretty_s unknown type #{schema_hash["type"]} in #{schema_hash}" end else - raise "TODO: to_pretty_s schema for #{schema_hash}" + raise "Unsupported schema for #{schema_hash}" end end diff --git a/lib/idl/tests/helpers.rb b/lib/idl/tests/helpers.rb index 3b3132d5d..2d694e5b3 100644 --- a/lib/idl/tests/helpers.rb +++ b/lib/idl/tests/helpers.rb @@ -3,23 +3,23 @@ require "ostruct" # Extension mock that returns an extension name -class MockExtension +class Xmockension attr_reader :name def initialize(name) @name = name end end -MockExtensionParameter = Struct.new(:name, :desc, :schema, :extra_validation, :exts, :type) -MockExtensionParameterWithValue = Struct.new(:name, :desc, :schema, :extra_validation, :exts, :value) +XmockensionParameter = Struct.new(:name, :desc, :schema, :extra_validation, :exts, :type) +XmockensionParameterWithValue = Struct.new(:name, :desc, :schema, :extra_validation, :exts, :value) # ConfiguredArchitecture mock that knows about XLEN and extensions class MockConfiguredArchitecture def param_values = { "XLEN" => 32 } - def params_with_value = [MockExtensionParameterWithValue.new("XLEN", "mxlen", {"type" => "integer", "enum" => [32, 64]}, nil, nil, 32)] + def params_with_value = [XmockensionParameterWithValue.new("XLEN", "mxlen", {"type" => "integer", "enum" => [32, 64]}, nil, nil, 32)] def params_without_value = [] def params = [] - def extensions = [MockExtension.new("I")] + def extensions = [Xmockension.new("I")] def mxlen = 64 def exception_codes = [OpenStruct.new(var: "ACode", num: 0), OpenStruct.new(var: "BCode", num: 1)] def interrupt_codes = [OpenStruct.new(var: "CoolInterrupt", num: 1)]