From d7e446802ba2529394b38902aab62412d584271f Mon Sep 17 00:00:00 2001 From: James Ball Date: Wed, 1 Jan 2025 00:49:05 +0000 Subject: [PATCH 01/21] Finally able to generate MC100-32 PDF and it looks good. --- Rakefile | 41 ++ arch/profile_release/MockProfileRelease.yaml | 3 +- arch/profile_release/RVA20.yaml | 3 +- arch/profile_release/RVA22.yaml | 3 +- arch/profile_release/RVA23.yaml | 7 +- arch/profile_release/RVB23.yaml | 7 +- arch/profile_release/RVI20.yaml | 5 +- backends/certificate_doc/tasks.rake | 103 ++-- .../templates/certificate.adoc.erb | 114 ++-- backends/cfg_html_doc/adoc_gen.rake | 1 + backends/profile_doc/tasks.rake | 263 +++++---- .../profile_doc/templates/profile.adoc.erb | 64 +-- lib/arch_obj_models/certificate.rb | 26 +- lib/arch_obj_models/csr.rb | 216 ++++---- lib/arch_obj_models/csr_field.rb | 168 +++--- .../{obj.rb => database_obj.rb} | 89 ++- lib/arch_obj_models/extension.rb | 90 ++- lib/arch_obj_models/instruction.rb | 46 +- lib/arch_obj_models/manual.rb | 14 +- lib/arch_obj_models/portfolio.rb | 262 ++++++--- lib/arch_obj_models/profile.rb | 88 ++- lib/architecture.rb | 9 +- lib/base_architecture.rb | 29 + lib/cfg_arch.rb | 494 +++-------------- lib/config.rb | 2 +- lib/design.rb | 512 ++++++++++++++++++ lib/idl/ast.rb | 158 +++--- lib/idl/passes/gen_adoc.rb | 4 +- .../passes/reachable_functions_unevaluated.rb | 20 +- lib/idl/symbol_table.rb | 26 +- lib/idl/tests/helpers.rb | 8 +- lib/idl/tests/test_functions.rb | 4 +- lib/idl/type.rb | 8 +- lib/portfolio_design.rb | 206 +++++++ lib/version.rb | 2 +- lib/yaml_resolver.py | 9 +- schemas/cert_model_schema.json | 2 +- schemas/profile_schema.json | 6 +- 38 files changed, 1853 insertions(+), 1259 deletions(-) rename lib/arch_obj_models/{obj.rb => database_obj.rb} (92%) create mode 100644 lib/base_architecture.rb create mode 100644 lib/design.rb create mode 100644 lib/portfolio_design.rb diff --git a/Rakefile b/Rakefile index f9a3d858c..d27f296b1 100644 --- a/Rakefile +++ b/Rakefile @@ -10,6 +10,9 @@ require "yard" require "minitest/test_task" require_relative $root / "lib" / "architecture" +require_relative $root / "lib" / "base_architecture" +require_relative $root / "lib" / "design" +require_relative $root / "lib" / "portfolio_design" directory "#{$root}/.stamps" @@ -19,6 +22,43 @@ end directory "#{$root}/.stamps" +# @param base_isa_name [String] rv32 or rv64 +# @param base [Integer] 32 or 64 +# @return [BaseArchitecture] +def base_arch_for(base_isa_name, base) + Rake::Task["#{$root}/.stamps/resolve-#{base_isa_name}.stamp"].invoke + + @base_archs ||= {} + return @base_archs[base_isa_name] if @base_archs.key?(base_isa_name) + + @base_archs[base_isa_name] = + BaseArchitecture.new( + base_isa_name, + base, + $root / "gen" / "resolved_arch" / base_isa_name, + ) +end + +# @param design_name [String] Profile release name for profiles and certificate model name for certificates +# @param arch [Architecture] The architecture database +# @param base [Integer] 32 or 64 +# @param portfolios [Array] Portfolios in this design +# @return [PortfolioDesign] +def portfolio_design_for(design_name, arch, base, portfolios) + Rake::Task["#{$root}/.stamps/resolve-#{design_name}.stamp"].invoke + + @portfolio_designs ||= {} + return @portfolio_designs[design_name] if @portfolio_designs.key?(design_name) + + @portfolio_designs[design_name] = + PortfolioDesign.new( + design_name, + arch, + base, + portfolios + ) +end + def cfg_arch_for(config_name) Rake::Task["#{$root}/.stamps/resolve-#{config_name}.stamp"].invoke @@ -117,6 +157,7 @@ namespace :test do end task schema: "gen:resolved_arch" do puts "Checking arch files against schema.." + # TODO: Needs to pass in base ISA name (rv32 or rv64). Architecture.new("#{$root}/resolved_arch").validate(show_progress: true) puts "All files validate against their schema" end diff --git a/arch/profile_release/MockProfileRelease.yaml b/arch/profile_release/MockProfileRelease.yaml index 8c4c63543..e3b56fbf6 100644 --- a/arch/profile_release/MockProfileRelease.yaml +++ b/arch/profile_release/MockProfileRelease.yaml @@ -2,7 +2,8 @@ $schema: profile_release_schema.json# kind: profile release name: MockProfileRelease marketing_name: MockProfileRelease Marketing Name -class: MockProfileClass +class: + $ref: profile_class/MockProfileClass.yaml# release: 20 state: ratified # current status ["ratified", "development"] versions: diff --git a/arch/profile_release/RVA20.yaml b/arch/profile_release/RVA20.yaml index c524c091c..59715da25 100644 --- a/arch/profile_release/RVA20.yaml +++ b/arch/profile_release/RVA20.yaml @@ -2,7 +2,8 @@ $schema: profile_release_schema.json# kind: profile release name: RVA20 marketing_name: RVA20 -class: RVA +class: + $ref: profile_class/RVA.yaml# release: 20 state: ratified # current status ["ratified", "development"] ratification_date: "2023-04-03" diff --git a/arch/profile_release/RVA22.yaml b/arch/profile_release/RVA22.yaml index 284ed4e78..7a3350a15 100644 --- a/arch/profile_release/RVA22.yaml +++ b/arch/profile_release/RVA22.yaml @@ -2,7 +2,8 @@ $schema: profile_release_schema.json# kind: profile release name: RVA22 marketing_name: RVA22 -class: RVA +class: + $ref: profile_class/RVA.yaml# release: 22 state: ratified # current status ["ratified", "development"] ratification_date: "2023-04-03" diff --git a/arch/profile_release/RVA23.yaml b/arch/profile_release/RVA23.yaml index 102ad13b3..a883d8a8b 100644 --- a/arch/profile_release/RVA23.yaml +++ b/arch/profile_release/RVA23.yaml @@ -1,8 +1,9 @@ -$schema: profile_schema.json# -kind: profile +$schema: profile_release_schema.json# +kind: profile release name: RVA23 marketing_name: RVA23 -class: RVA +class: + $ref: profile_class/RVA.yaml# release: 23 state: ratified # current status ["ratified", "development"] ratification_date: "2023-04-03" diff --git a/arch/profile_release/RVB23.yaml b/arch/profile_release/RVB23.yaml index 98e453eac..6090ff6bb 100644 --- a/arch/profile_release/RVB23.yaml +++ b/arch/profile_release/RVB23.yaml @@ -1,8 +1,9 @@ -$schema: profile_schema.json# -kind: profile +$schema: profile_release_schema.json# +kind: profile release name: RVB23 marketing_name: RVB23 -class: RVB +class: + $ref: profile_class/RVA.yaml# release: 23 state: ratified # current status ["ratified", "development"] ratification_date: "2023-04-03" diff --git a/arch/profile_release/RVI20.yaml b/arch/profile_release/RVI20.yaml index 55a5a265d..a1a283f03 100644 --- a/arch/profile_release/RVI20.yaml +++ b/arch/profile_release/RVI20.yaml @@ -2,8 +2,9 @@ $schema: profile_release_schema.json# kind: profile release name: RVI20 marketing_name: RVI20 -class: RVI -release: 20 +class: + $ref: profile_class/RVI.yaml# +base: null state: ratified # current status ["ratified", "development"] ratification_date: "2023-04-03" diff --git a/backends/certificate_doc/tasks.rake b/backends/certificate_doc/tasks.rake index 31766c0cd..f8437da8d 100644 --- a/backends/certificate_doc/tasks.rake +++ b/backends/certificate_doc/tasks.rake @@ -18,58 +18,66 @@ Dir.glob("#{$root}/arch/certificate_model/*.yaml") do |f| base = cert_model_obj["base"] raise "Missing certificate model base" if base.nil? + base_isa_name = "rv#{base}" + + puts "UPDATE: Extracted base=#{base} from #{f}" + file "#{$root}/gen/certificate_doc/adoc/#{cert_model_name}.adoc" => [ - "#{$root}/arch/certificate_model/#{cert_model_name}.yaml", + __FILE__, "#{$root}/arch/certificate_class/#{cert_class_name}.yaml", - "#{CERT_DOC_DIR}/templates/certificate.adoc.erb", - __FILE__ + "#{$root}/arch/certificate_model/#{cert_model_name}.yaml", + "#{$root}/lib/arch_obj_models/certificate.rb", + "#{$root}/lib/arch_obj_models/portfolio.rb", + "#{$root}/lib/portfolio_design.rb", + "#{$root}/lib/design.rb", + "#{CERT_DOC_DIR}/templates/certificate.adoc.erb" ] do |t| - puts "UPDATE: Creating bootstrap objects for #{cert_model_name}" - - # Create bootstrap ConfiguredArchitecture object which also creates and contains - # a PartialConfig object for the rv32/rv64 configuration. - bootstrap_cfg_arch = cfg_arch_for("rv#{base}") - - # Creates CertModel object for every certificate model in the database - # using rv32/rv64 PartialConfig object and then returns named CertModel object. - bootstrap_cert_model = bootstrap_cfg_arch.cert_model(cert_model_name) - raise "No certificate model named '#{cert_model_name}'" if bootstrap_cert_model.nil? - - puts "UPDATE: Creating real objects for #{cert_model_name}" - - # Use bootstrap CertModel to create a ConfiguredArchitecture for this CertModel - # to use instead of the the bootstrap one created based on the rv32/rv64 configuration. - cfg_arch = bootstrap_cert_model.to_cfg_arch - - # Use model-specific ConfiguredArchitecture to create CertModel objects again - # for every certificate model in the database and then return named CertModel object. - cert_model = cfg_arch.cert_model(cert_model_name) - - # Set globals for ERB template. - portfolio = cert_model - cert_class = cert_model.cert_class - portfolio = cert_model - portfolio_class = cert_class - - version = File.basename(t.name, '.adoc').split('-')[1..].join('-') - - erb = ERB.new(File.read("#{CERT_DOC_DIR}/templates/certificate.adoc.erb"), trim_mode: "-") - erb.filename = "#{CERT_DOC_DIR}/templates/certificate.adoc.erb" + # Create BaseArchitecture object. Function located in top-level Rakefile. + puts "UPDATE: Creating BaseArchitecture #{base_isa_name} for #{t}" + base_arch = base_arch_for(base_isa_name, base) + + # Create CertModel for specific certificate model as specified in its arch YAML file. + # The Architecture object also creates all other portfolio-related class instances from their arch YAML files. + # None of these objects are provided with a Design object when created. + puts "UPDATE: Creating CertModel for #{cert_model_name} using base #{base_isa_name}" + cert_model = base_arch.cert_model(cert_model_name) + + puts "UPDATE: Creating PortfolioDesign using CertModel #{cert_model_name}" + # Create the one PortfolioDesign object required for the ERB evaluation. + portfolio_design = portfolio_design_for(cert_model_name, base_arch, base, [cert_model]) + + # Create empty binding and then specify explicitly which variables the ERB template can access. + def create_empty_binding + binding + end + erb_binding = create_empty_binding + erb_binding.local_variable_set(:arch, base_arch) + erb_binding.local_variable_set(:design, portfolio_design) + erb_binding.local_variable_set(:cert_class, cert_model.cert_class) + erb_binding.local_variable_set(:portfolio_class, cert_model.cert_class) + erb_binding.local_variable_set(:cert_model, cert_model) + erb_binding.local_variable_set(:portfolio, cert_model) + + template_path = Pathname.new("#{CERT_DOC_DIR}/templates/certificate.adoc.erb") + erb = ERB.new(File.read(template_path), trim_mode: "-") + erb.filename = template_path.to_s FileUtils.mkdir_p File.dirname(t.name) # Convert ERB to final ASCIIDOC. Note that this code is broken up into separate function calls # each with a variable name to aid in running a command-line debugger on this code. - erb_result = erb.result(binding) - erb_result_monospace_converted_to_links = cfg_arch.find_replace_links(erb_result) - erb_result_with_links_added = cfg_arch.find_replace_links(erb_result_monospace_converted_to_links) + puts "UPDATE: Converting ERB template to adoc for #{cert_model_name}" + erb_result = erb.result(erb_binding) + erb_result_monospace_converted_to_links = portfolio_design.find_replace_links(erb_result) + erb_result_with_links_added = portfolio_design.find_replace_links(erb_result_monospace_converted_to_links) erb_result_with_links_resolved = AsciidocUtils.resolve_links(erb_result_with_links_added) - File.write t.name, erb_result_with_links_resolved - puts "Generated adoc source at #{t.name}" + File.write(t.name, erb_result_with_links_resolved) + puts "UPDATE: Generated adoc source at #{t.name}" end file "#{$root}/gen/certificate_doc/pdf/#{cert_model_name}.pdf" => [ + __FILE__, "#{$root}/gen/certificate_doc/adoc/#{cert_model_name}.adoc" ] do |t| adoc_file = "#{$root}/gen/certificate_doc/adoc/#{cert_model_name}.adoc" @@ -88,13 +96,17 @@ Dir.glob("#{$root}/arch/certificate_model/*.yaml") do |f| "-o #{t.name}", adoc_file ].join(" ") + puts "UPDATE: Generated PDF at #{t.name}" end file "#{$root}/gen/certificate_doc/html/#{cert_model_name}.html" => [ + __FILE__, "#{$root}/gen/certificate_doc/adoc/#{cert_model_name}.adoc" ] do |t| adoc_file = "#{$root}/gen/certificate_doc/adoc/#{cert_model_name}.adoc" FileUtils.mkdir_p File.dirname(t.name) + + puts "UPDATE: Generating PDF at #{t.name}" sh [ "asciidoctor", "-w", @@ -107,29 +119,30 @@ Dir.glob("#{$root}/arch/certificate_model/*.yaml") do |f| "-o #{t.name}", adoc_file ].join(" ") + puts "UPDATE: Generated PDF at #{t.name}" end - end namespace :gen do desc <<~DESC - Generate certificate documentation for a specific version as a PDF + Generate certificate documentation for a specific version as a PDF. Required options: cert_model_name - The key of the certification model under arch/certificate_model DESC task :cert_model_pdf, [:cert_model_name] do |_t, args| - if args[:cert_model_name].nil? + cert_model_name = args[:cert_model_name] + if cert_model_name.nil? warn "Missing required option: 'cert_model_name'" exit 1 end - unless File.exist?("#{$root}/arch/certificate_model/#{args[:cert_model_name]}.yaml") - warn "No certification model named '#{args[:cert_model_name]}' found in arch/certificate_model" + unless File.exist?("#{$root}/arch/certificate_model/#{cert_model_name}.yaml") + warn "No certification model named '#{cert_model_name}' found in arch/certificate_model" exit 1 end - Rake::Task["#{$root}/gen/certificate_doc/pdf/#{args[:cert_model_name]}.pdf"].invoke + Rake::Task["#{$root}/gen/certificate_doc/pdf/#{cert_model_name}.pdf"].invoke end task :cert_model_html, [:cert_model_name] do |_t, args| diff --git a/backends/certificate_doc/templates/certificate.adoc.erb b/backends/certificate_doc/templates/certificate.adoc.erb index 4b695f722..b02ce0d50 100644 --- a/backends/certificate_doc/templates/certificate.adoc.erb +++ b/backends/certificate_doc/templates/certificate.adoc.erb @@ -104,7 +104,7 @@ Where: supporting additional optional behaviors for standards already in a certificate. ** The release is updated when certification test changes are made that *can't* cause a previously certified implementation to now fail. - Examples are test changes not designed to increase coverage or fixing a documentation typo. + Examples are test changes not intended to increase coverage or fixing a documentation typo. ** If omitted, defaults to v1.0.0 ** Examples: v1, v1.1, v2.3.1, 0.3.4 (pre-release) @@ -231,7 +231,7 @@ Such WPRI fields are always unimplemented by definition. Certification tests are aware of which fields in the CSRs are WPRI and normally write them with 0 but will also write them with ~0 (all ones) and ensure that reads return 0 in both cases. It is OUT-OF-SCOPE for certification tests to write all possible values of WPRI fields -(especially if they are more than just a few bits) and certification tests aren't designed to be comprehensive +(especially if they are more than just a few bits) and certification tests aren't intended to be comprehensive verification test suites anyways. === Related Specifications @@ -278,8 +278,8 @@ None |=== | Requirement ID | Extension | Version | Long Name | Note -<% ext_reqs.sort.each do |ext_req| -%> -<% ext = cfg_arch.extension(ext_req.name) -%> +<% ext_reqs.each do |ext_req| -%> +<% ext = arch.extension(ext_req.name) -%> | <%= ext_req.req_id %> | <-def,<%= ext_req.name %>>> | <%= ext_req.requirement_specs.map(&:to_s).join(", ") %> @@ -299,7 +299,7 @@ NOTE: <%= extra_note.text %> <% unless cert_model.recommendations.empty? -%> === Recommendations -Recommendations are not strictly mandated but are included to guide implementers making design choices. +Recommendations are not strictly mandated but are included to guide implementers. <% cert_model.recommendations.each do |recommendation| -%> <%= recommendation.text %> @@ -326,13 +326,13 @@ None |=== | Parameter | Type | Allowed Value(s) | Extension(s) | Note -<% cert_model.all_in_scope_ext_params.sort.each do |in_scope_ext_param| -%> +<% cert_model.all_in_scope_ext_params.each do |in_scope_ext_param| -%> <% param = in_scope_ext_param.param -%> <% exts = cert_model.all_in_scope_exts_with_param(param) -%> | <%= param.name_potentially_with_link(exts) %> | <%= param.schema_type %> | <%= in_scope_ext_param.allowed_values %> -| <% exts.sort.each do |ext| -%><-param-<%= param.name %>-def,<%= ext.name %>>> <% end # do ext -%> +| <% exts.each do |ext| -%><-param-<%= param.name %>-def,<%= ext.name %>>> <% end # do ext -%> a| <%= in_scope_ext_param.note %> <% end # do -%> |=== @@ -351,11 +351,11 @@ None |=== | Parameters | Type | Extension(s) -<% cert_model.all_out_of_scope_params.sort.each do |param| -%> +<% cert_model.all_out_of_scope_params.each do |param| -%> <% exts = cert_model.all_in_scope_exts_without_param(param) -%> | <%= param.name_potentially_with_link(exts) %> | <%= param.schema_type %> -| <% exts.sort.each do |ext| -%><-param-<%= param.name %>-def,<%= ext.name %>>> <% end # do ext -%> +| <% exts.each do |ext| -%><-param-<%= param.name %>-def,<%= ext.name %>>> <% end # do ext -%> <% end # do -%> |=== @@ -372,7 +372,7 @@ TODO: Show traps per privilege mode |=== | `xcause.CODE` CSR Field Value | Name -<% cfg_arch.exception_codes.sort_by{ |code| code.num }.each do |code| -%> +<% design.exception_codes.sort_by{ |code| code.num }.each do |code| -%> | <%= code.num %> | <%= code.name %> <% end -%> |=== @@ -381,7 +381,7 @@ TODO: Show traps per privilege mode |=== | `xcause.CODE` CSR Field Value | Name -<% cfg_arch.interrupt_codes.sort_by{ |code| code.num }.each do |code| -%> +<% design.interrupt_codes.sort_by{ |code| code.num }.each do |code| -%> | <%= code.num %> | <%= code.name %> <% end -%> |=== @@ -392,16 +392,11 @@ TODO: List only instructions that exist in this certificate model. Currently lists all possible in present extensions so the I extension is providing both RV32I and RV64I instructions. See https://github.com/riscv-software-src/riscv-unified-db/issues/291 and https://github.com/riscv-software-src/riscv-unified-db/issues/324 -<% - insts = cert_model.in_scope_extensions.map { |ext_cert_model| ext_cert_model.instructions }.flatten.uniq - insts.sort_by!(&:name) --%> - [%autowidth] |=== | Name | Long Name -<% portfolio.in_scope_instructions.each do |inst| -%> +<% design.in_scope_instructions.each do |inst| -%> | <%= link_to_inst(inst.name) %> | <%= inst.long_name %> <% end # do -%> @@ -409,17 +404,13 @@ See https://github.com/riscv-software-src/riscv-unified-db/issues/291 and https: == CSR Summary -<% - csrs = cert_model.in_scope_ext_reqs.map { |ext_req| ext_req.csrs }.flatten.uniq --%> - === By Name [%autowidth] |=== | Name | Long Name | Address | Mode | Primary Extension -<% csrs.sort_by!(&:name).each do |csr| -%> +<% design.in_scope_csrs.sort_by!(&:name).each do |csr| -%> | <-def,<%= csr.name %>>> | <%= csr.long_name %> | <%= "0x#{csr.address.to_s(16)}" %> @@ -434,7 +425,7 @@ See https://github.com/riscv-software-src/riscv-unified-db/issues/291 and https: |=== | Address | Mode | Name | Long Name | Primary Extension -<% csrs.sort_by!(&:address).each do |csr| -%> +<% design.in_scope_csrs.sort_by!(&:address).each do |csr| -%> | <%= "0x#{csr.address.to_s(16)}" %> | <%= csr.priv_mode %> | <-def,<%= csr.name %>>> @@ -479,8 +470,8 @@ Requirement <%= req.name %> only apply when <%= req.when_pretty %>. <<< [appendix] == Extension Details -<% cert_model.in_scope_ext_reqs.sort.each do |ext_req| -%> -<% ext = cfg_arch.extension(ext_req.name) -%> +<% cert_model.in_scope_ext_reqs.each do |ext_req| -%> +<% ext = arch.extension(ext_req.name) -%> [[ext-<%= ext_req.name %>-def]] === Extension <%= ext_req.name %> + @@ -532,7 +523,7 @@ Requirement <%= req.name %> only apply when <%= req.when_pretty %>. <% end -%> // TODO: GitHub issue 92: Use version specified by each profile. -<% insts = cfg_arch.instructions.select { |i| i.defined_by?(ext.min_version) } -%> +<% insts = arch.instructions.select { |i| i.defined_by?(ext.min_version) } -%> <% unless insts.empty? -%> ==== Instructions @@ -540,7 +531,7 @@ The following instructions are added by this extension: [cols="1,3"] |=== -<% insts.sort.each do |inst| -%> +<% insts.each do |inst| -%> | <%= link_to_inst(inst.name) %> | *<%= inst.long_name %>* <% end -%> @@ -550,7 +541,7 @@ The following instructions are added by this extension: <% unless cert_model.in_scope_ext_params(ext_req).empty? -%> ==== IN-SCOPE Parameters -<% cert_model.in_scope_ext_params(ext_req).sort.each do |ext_param| -%> +<% cert_model.in_scope_ext_params(ext_req).each do |ext_param| -%> [[ext-<%= ext_req.name %>-param-<%= ext_param.name %>-def]] <%= ext_param.name %> ⇒ <%= ext_param.param.schema_type %>:: + @@ -563,7 +554,7 @@ The following instructions are added by this extension: <% unless cert_model.out_of_scope_params(ext_req.name).empty? -%> ==== OUT-OF-SCOPE Parameters -<% cert_model.out_of_scope_params(ext_req.name).sort.each do |param| -%> +<% cert_model.out_of_scope_params(ext_req.name).each do |param| -%> [[ext-<%= ext_req.name %>-param-<%= param.name %>-def]] <%= param.name %> ⇒ <%= param.schema_type %>:: + @@ -578,7 +569,7 @@ The following instructions are added by this extension: [appendix] == Instruction Details -<% portfolio.in_scope_instructions.each do |inst| -%> +<% design.in_scope_instructions.each do |inst| -%> <<< <%= anchor_for_inst(inst.name) %> === <%= inst.name %> @@ -622,18 +613,18 @@ RV64:: <%= inst.description %> ==== Access -<% if cert_model.in_scope_extensions.any? { |e| e.name == "H" } -%> +<% if design.in_scope_extensions.any? { |e| e.name == "H" } -%> [cols="^,^,^,^,^"] <% else -%> [cols="^,^,^"] <% end -%> |=== -| M | <% if cert_model.in_scope_extensions.any? { |e| e.name == "H" } -%>HS<% else -%>S<% end -%> | U <% if cert_model.in_scope_extensions.any? { |e| e.name == "H" } -%> | VS | VU <% end -%> +| M | <% if design.in_scope_extensions.any? { |e| e.name == "H" } -%>HS<% else -%>S<% end -%> | U <% if design.in_scope_extensions.any? { |e| e.name == "H" } -%> | VS | VU <% end -%> | [.access-always]#Always# | [.access-<%=inst.access['s']%>]#<%= inst.access['s'].capitalize %># | [.access-<%=inst.access['u']%>]#<%= inst.access['u'].capitalize %># -<% if cert_model.in_scope_extensions.any? { |e| e.name == "H" } %> +<% if design.in_scope_extensions.any? { |e| e.name == "H" } %> | [.access-<%=inst.access['vs']%>]#<%= inst.access['vs'].capitalize %># | [.access-<%=inst.access['vu']%>]#<%= inst.access['vu'].capitalize %># <% end %> @@ -681,13 +672,13 @@ RV64:: <% if inst.key?("operation()") -%> [source,idl,subs="specialchars,macros"] ---- -<%= inst.operation_ast(cfg_arch.symtab).gen_adoc %> +<%= inst.operation_ast(design.symtab).gen_adoc %> ---- <% end -%> ==== Exceptions -<%- exception_list = inst.reachable_exceptions_str(cfg_arch.symtab) -%> +<%- exception_list = inst.reachable_exceptions_str(design.symtab) -%> <%- if exception_list.empty? -%> This instruction does not generate synchronous exceptions. <%- else -%> @@ -706,12 +697,7 @@ This instruction may result in the following synchronous exceptions: [appendix] == CSR Details -<% - csrs = cert_model.in_scope_ext_reqs.map { |ext_req| ext_req.csrs }.flatten.uniq - csrs.sort_by!(&:name) --%> - -<% csrs.each do |csr| -%> +<% design.in_scope_csrs.sort_by(&:name).each do |csr| -%> <<< [[csr-<%= csr.name %>-def]] === <%= csr.name %> @@ -735,22 +721,22 @@ h| CSR Address | <%= "0x#{csr.address.to_s(16)}" %> h| Virtual CSR Address | <%= "0x#{csr.virtual_address.to_s(16)}" %> <% end -%> h| Defining extension a| <%= csr.defined_by_condition.to_asciidoc %> -<% if csr.dynamic_length?(cfg_arch) -%> -h| Length | <%= csr.length_pretty(cfg_arch) %> +<% if csr.dynamic_length?(design) -%> +h| Length | <%= csr.length_pretty(design) %> <% else -%> -h| Length | <%= csr.length_pretty(cfg_arch) %> +h| Length | <%= csr.length_pretty(design) %> <% end -%> h| Privilege Mode | <%= csr.priv_mode %> |=== ==== Format -<% unless csr.dynamic_length?(cfg_arch) || csr.implemented_fields(cfg_arch).any? { |f| f.dynamic_location?(cfg_arch) } -%> +<% unless csr.dynamic_length?(design) || csr.implemented_fields(design).any? { |f| f.dynamic_location?(design) } -%> <%# CSR has a known static length, so there is only one format to display -%> .<%= csr.name %> format [wavedrom, ,svg,subs='attributes',width="100%"] .... -<%= JSON.dump csr.wavedrom_desc(cfg_arch, csr.base.nil? ? 32 : csr.base, optional_type: 2) %> +<%= JSON.dump csr.wavedrom_desc(design, csr.base.nil? ? 32 : csr.base, optional_type: 2) %> .... <% else -%> <%# CSR has a dynamic length, or a field has a dynamic location, @@ -760,13 +746,13 @@ This CSR format changes dynamically with XLEN. .<%= csr.name %> Format when <%= csr.length_cond32 %> [wavedrom, ,svg,subs='attributes',width="100%"] .... -<%= JSON.dump csr.wavedrom_desc(cfg_arch, 32, optional_type: 2) %> +<%= JSON.dump csr.wavedrom_desc(design, 32, optional_type: 2) %> .... .<%= csr.name %> Format when <%= csr.length_cond64 %> [wavedrom, ,svg,subs='attributes',width="100%"] .... -<%= JSON.dump csr.wavedrom_desc(cfg_arch, 64, optional_type: 2) %> +<%= JSON.dump csr.wavedrom_desc(design, 64, optional_type: 2) %> .... @@ -779,34 +765,34 @@ This CSR format changes dynamically with XLEN. |=== @ Name @ Location @ Type @ Reset Value -<%- csr.implemented_fields(cfg_arch).each do |field| -%> +<%- csr.implemented_fields(design).each do |field| -%> @ xref:<%=csr.name%>-<%=field.name%>-def[`<%= field.name %>`] a@ -<%- if field.dynamic_location?(cfg_arch) -%> +<%- if field.dynamic_location?(design) -%> [when,"<%= field.location_cond32 %>"] -- -<%= field.location_pretty(cfg_arch, 32) %> +<%= field.location_pretty(design, 32) %> -- [when,"<%= field.location_cond64 %>"] -- -<%= field.location_pretty(cfg_arch, 64) %> +<%= field.location_pretty(design, 64) %> -- <%- else -%> -<%= field.location_pretty(cfg_arch) %> +<%= field.location_pretty(design) %> <%- end -%> a@ -- -<%= field.type_pretty(cfg_arch.symtab) %> +<%= field.type_pretty(design.symtab) %> -- a@ -- -<%= field.reset_value_pretty(cfg_arch) %> +<%= field.reset_value_pretty(design) %> -- <%- end -%> @@ -814,11 +800,11 @@ a@ ==== Fields -<%- if csr.implemented_fields(cfg_arch).empty? -%> +<%- if csr.implemented_fields(design).empty? -%> This CSR has no fields. However, it must still exist (not cause an `Illegal Instruction` trap) and always return zero on a read. <%- else -%> -<%- csr.implemented_fields(cfg_arch).each do |field| -%> +<%- csr.implemented_fields(design).each do |field| -%> [[<%=csr.name%>-<%=field.name%>-def]] ===== `<%= field.name %>` @@ -828,23 +814,23 @@ IMPORTANT: <%= field.name %> is only defined in <%= field.base32_only? ? "RV32" **** Location:: -<%= field.location_pretty(cfg_arch) %> +<%= field.location_pretty(design) %> Description:: <%= field.description.gsub("\n", " +\n") %> Type:: -<%= field.type_pretty(cfg_arch.symtab) %> +<%= field.type_pretty(design.symtab) %> Reset value:: -<%= field.reset_value_pretty(cfg_arch) %> +<%= field.reset_value_pretty(design) %> **** <%- end -%> <%- end -%> -<%- if csr.implemented_fields(cfg_arch).map(&:has_custom_sw_write?).any? -%> +<%- if csr.implemented_fields(design).map(&:has_custom_sw_write?).any? -%> ==== Software write This CSR may store a value that is different from what software attempts to write. @@ -854,7 +840,7 @@ written value: [idl] ---- -<%- csr.implemented_fields(cfg_arch).each do |field| -%> +<%- csr.implemented_fields(design).each do |field| -%> <%- if field.has_custom_sw_write? -%> <%= field.name %> = <%= field["sw_write(csr_value)"] %> <%- else -%> @@ -871,8 +857,8 @@ This CSR may return a value that is different from what is stored in hardware. [source,idl,subs="specialchars,macros"] ---- -<%= csr.sw_read_ast(cfg_arch.symtab).gen_adoc %> +<%= csr.sw_read_ast(design.symtab).gen_adoc %> ---- <%- end -%> -<% end # do csrs -%> +<% end # do in_scope_csrs -%> diff --git a/backends/cfg_html_doc/adoc_gen.rake b/backends/cfg_html_doc/adoc_gen.rake index 1bf407888..4302a690d 100644 --- a/backends/cfg_html_doc/adoc_gen.rake +++ b/backends/cfg_html_doc/adoc_gen.rake @@ -8,6 +8,7 @@ require "ruby-prof" [ "#{CFG_HTML_DOC_DIR}/templates/#{type}.adoc.erb", "#{$root}/lib/cfg_arch.rb", + "#{$root}/lib/design.rb", "#{$root}/lib/idl/passes/gen_adoc.rb", __FILE__, "#{$root}/.stamps" diff --git a/backends/profile_doc/tasks.rake b/backends/profile_doc/tasks.rake index b18fc3993..52aad0fef 100644 --- a/backends/profile_doc/tasks.rake +++ b/backends/profile_doc/tasks.rake @@ -1,109 +1,184 @@ # frozen_string_literal: true -rule %r{#{$root}/gen/profile_doc/adoc/.*\.adoc} => [ - __FILE__, - "#{$root}/lib/arch_obj_models/profile.rb", - "#{$root}/backends/profile_doc/templates/profile.adoc.erb", - 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? - - template_path = Pathname.new "#{$root}/backends/profile_doc/templates/profile.adoc.erb" - erb = ERB.new(template_path.read, trim_mode: "-") - erb.filename = template_path.to_s - - # Switch to the generated profile certificate cfg arch and set some variables available to ERB template. - cfg_arch = cfg_arch_for("_") - - # Create empty binding and then specify explicitly which variables the ERB template can access. - def create_empty_binding - binding +require "pathname" + +require "asciidoctor-pdf" +require "asciidoctor-diagram" + +require_relative "#{$lib}/idl/passes/gen_adoc" + +PROFILE_DOC_DIR = Pathname.new "#{$root}/backends/profile_doc" + +Dir.glob("#{$root}/arch/profile_release/*.yaml") do |f| + profile_release_name = File.basename(f, ".yaml") + profile_release_obj = YAML.load_file(f, permitted_classes: [Date]) + raise "Can't parse #{f}" if profile_release_obj.nil? + + raise "Ill-formed profile release file #{f}: missing 'class' field" if profile_release_obj['class'].nil? + profile_class_name = File.basename(profile_release_obj['class']['$ref'].split("#")[0], ".yaml") + raise "Ill-formed profile release file #{f}: can't parse class name" if profile_class_name.nil? + + raise "Ill-formed profile release file #{f}: missing 'profiles' field" if profile_release_obj['profiles'].nil? + profile_names = profile_release_obj['profiles'].map {|p| File.basename(p['$ref'].split("#")[0], ".yaml") } + raise "Ill-formed profile release file #{f}: can't parse profile names" if profile_names.nil? + + # Find maximum base across all profiles in the profile release. + max_base = nil + profile_names.each do |profile_name| + profile_pathname = "#{$root}/arch/profile/#{profile_name}.yaml" + profile_obj = YAML.load_file(profile_pathname, permitted_classes: [Date]) + raise "Can't parse #{profile_name}" if profile_obj.nil? + + base = profile_obj["base"] + raise "Missing profile base in #{profile}" if base.nil? + + puts "UPDATE: Extracted base=#{base} from #{f}" + + max_base = base if (max_base.nil? || base > max_base) + end + raise "Couldn't find max_base in the profiles #{profile_names}" if max_base.nil? + puts "UPDATE: Calculated max_base=#{max_base} across profiles in #{profile_release_name}" + + profile_pathnames = profile_names.map {|profile_name| "#{$root}/arch/profile/#{profile_name}.yaml" } + + # Just go with maximum base since it is the most inclusive. + base = max_base + base_isa_name = "rv#{base}" + + file "#{$root}/gen/profile_doc/adoc/#{profile_release_name}.adoc" => [ + __FILE__, + "#{$root}/arch/profile_class/#{profile_class_name}.yaml", + "#{$root}/arch/profile_release/#{profile_release_name}.yaml", + "#{$root}/lib/arch_obj_models/profile.rb", + "#{$root}/lib/arch_obj_models/portfolio.rb", + "#{$root}/lib/portfolio_design.rb", + "#{$root}/lib/design.rb", + "#{$root}/backends/profile_doc/templates/profile.adoc.erb" + ].concat(profile_pathnames) do |t| + # Create BaseArchitecture object. Function located in top-level Rakefile. + puts "UPDATE: Creating BaseArchitecture #{base_isa_name} for #{t}" + base_arch = base_arch_for(base_isa_name, base) + + # Create CertModel for specific certificate model as specified in its arch YAML file. + # The Architecture object also creates all other portfolio-related class instances from their arch YAML files. + # None of these objects are provided with a Design object when created. + puts "UPDATE: Creating Profile Release for #{profile_release_name} using #{base_isa_name}" + profile_release = base_arch.profile_release(profile_release_name) + + puts "UPDATE: Creating PortfolioDesign using ProfileRelease #{profile_release_name}" + # Create the one PortfolioDesign object required for the ERB evaluation. + # Provide it with all the profiles in this ProfileRelease. + portfolio_design = portfolio_design_for(profile_release_name, base_arch, base, + profile_release.profiles) + + # Create empty binding and then specify explicitly which variables the ERB template can access. + def create_empty_binding + binding + end + erb_binding = create_empty_binding + erb_binding.local_variable_set(:arch, base_arch) + erb_binding.local_variable_set(:design, portfolio_design) + erb_binding.local_variable_set(:profile_class, profile_release.profile_class) + erb_binding.local_variable_set(:portfolio_class, profile_release.profile_class) + erb_binding.local_variable_set(:profile_release, profile_release) + + template_path = Pathname.new("#{PROFILE_DOC_DIR}/templates/profile.adoc.erb") + erb = ERB.new(File.read(template_path), trim_mode: "-") + erb.filename = template_path.to_s + + # Convert ERB to final ASCIIDOC. Note that this code is broken up into separate function calls + # each with a variable name to aid in running a command-line debugger on this code. + puts "UPDATE: Converting ERB template to adoc for #{profile_release_name}" + erb_result = erb.result(erb_binding) + erb_result_monospace_converted_to_links = portfolio_design.find_replace_links(erb_result) + erb_result_with_links_added = portfolio_design.find_replace_links(erb_result_monospace_converted_to_links) + erb_result_with_links_resolved = AsciidocUtils.resolve_links(erb_result_with_links_added) + + File.write(t.name, erb_result_with_links_resolved) + puts "UPDATE: Generated adoc source at #{t.name}" end - erb_binding = create_empty_binding - erb_binding.local_variable_set(:cfg_arch, cfg_arch) - erb_binding.local_variable_set(:profile_class, profile_release.profile_class) - erb_binding.local_variable_set(:profile_release, profile_release) - erb_binding.local_variable_set(:portfolio_class, profile_release.profile_class) - - FileUtils.mkdir_p File.dirname(t.name) - File.write t.name, AsciidocUtils.resolve_links(cfg_arch.find_replace_links(erb.result(erb_binding))) - puts "Generated adoc source at #{t.name}" -end -rule %r{#{$root}/gen/profile_doc/pdf/.*\.pdf} => proc { |tname| - profile_release_name = Pathname.new(tname).basename(".pdf") - [__FILE__, "#{$root}/gen/profile_doc/adoc/#{profile_release_name}.adoc"] -} do |t| - profile_release_name = Pathname.new(t.name).basename(".pdf") - - adoc_filename = "#{$root}/gen/profile_doc/adoc/#{profile_release_name}.adoc" - - FileUtils.mkdir_p File.dirname(t.name) - sh [ - "asciidoctor-pdf", - "-w", - "-v", - "-a toc", - "-a compress", - "-a pdf-theme=#{$root}/ext/docs-resources/themes/riscv-pdf.yml", - "-a pdf-fontsdir=#{$root}/ext/docs-resources/fonts", - "-a imagesdir=#{$root}/ext/docs-resources/images", - "-r asciidoctor-diagram", - "-r #{$root}/backends/ext_pdf_doc/idl_lexer", - "-o #{t.name}", - adoc_filename - ].join(" ") - - puts - puts "SUCCESS! File written to #{t.name}" -end + file "#{$root}/gen/profile_doc/pdf/#{profile_release_name}.pdf" => [ + __FILE__, + "#{$root}/gen/profile_doc/adoc/#{profile_release_name}.adoc" + ] do |t| + adoc_file = "#{$root}/gen/profile_doc/adoc/#{profile_release_name}.adoc" + FileUtils.mkdir_p File.dirname(t.name) + + puts "UPDATE: Generating PDF at #{t.name}" + sh [ + "asciidoctor-pdf", + "-w", + "-v", + "-a toc", + "-a compress", + "-a pdf-theme=#{$root}/ext/docs-resources/themes/riscv-pdf.yml", + "-a pdf-fontsdir=#{$root}/ext/docs-resources/fonts", + "-a imagesdir=#{$root}/ext/docs-resources/images", + "-r asciidoctor-diagram", + "-r #{$root}/backends/ext_pdf_doc/idl_lexer", + "-o #{t.name}", + adoc_file + ].join(" ") + puts "UPDATE: Generated PDF at #{t.name}" + end -rule %r{#{$root}/gen/profile_doc/html/.*\.html} => proc { |tname| - profile_release_name = Pathname.new(tname).basename(".html") - [__FILE__, "#{$root}/gen/profile_doc/adoc/#{profile_release_name}.adoc"] -} do |t| - profile_release_name = Pathname.new(t.name).basename(".html") - - adoc_filename = "#{$root}/gen/profile_doc/adoc/#{profile_release_name}.adoc" - - FileUtils.mkdir_p File.dirname(t.name) - sh [ - "asciidoctor", - "-w", - "-v", - "-a toc", - "-a imagesdir=#{$root}/ext/docs-resources/images", - "-r asciidoctor-diagram", - "-r #{$root}/backends/ext_pdf_doc/idl_lexer", - "-o #{t.name}", - adoc_filename - ].join(" ") - - puts - puts "SUCCESS! File written to #{t.name}" + file "#{$root}/gen/profile_doc/html/#{profile_release_name}.html" => [ + __FILE__, + "#{$root}/gen/profile_doc/adoc/#{profile_release_name}.adoc" + ] do |t| + adoc_file = "#{$root}/gen/profile_doc/adoc/#{profile_release_name}.adoc" + FileUtils.mkdir_p File.dirname(t.name) + + puts "UPDATE: Generating PDF at #{t.name}" + sh [ + "asciidoctor", + "-w", + "-v", + "-a toc", + "-a imagesdir=#{$root}/ext/docs-resources/images", + "-r asciidoctor-diagram", + "-r #{$root}/backends/ext_pdf_doc/idl_lexer", + "-o #{t.name}", + adoc_file + ].join(" ") + puts "UPDATE: Generated PDF at #{t.name}" + end end namespace :gen do - desc "Create a specification PDF for +profile_release+" - task :profile, [:profile_release] do |_t, args| - profile_release_name = args[:profile_release] - raise ArgumentError, "Missing required option +profile_release+" if profile_release_name.nil? - - profile_release = cfg_arch_for("_").profile_release(profile_release_name) - raise ArgumentError, "No profile release named '#{profile_release_name}'" if profile_release.nil? + desc <<~DESC + Generate profile documentation for a specific release as a PDF. + + Required options: + profile_release_name - The key of the certification model under arch/certificate_model + DESC + task :profile_release_pdf, [:profile_release_name] do |_t, args| + profile_release_name = args[:profile_release_name] + if profile_release_name.nil? + warn "Missing required option: 'profile_release_name'" + exit 1 + end + + unless File.exist?("#{$root}/arch/profile_release/#{profile_release_name}.yaml") + warn "No profile release named '#{profile_release_name}' found in arch/profile_release" + exit 1 + end Rake::Task["#{$root}/gen/profile_doc/pdf/#{profile_release_name}.pdf"].invoke end - desc "Create a specification HTML for +profile_release+" - task :profile_html, [:profile_release] do |_t, args| - profile_release_name = args[:profile_release] - raise ArgumentError, "Missing required option +profile_release+" if profile_release_name.nil? - - profile_release = cfg_arch_for("_").profile_release(profile_release_name) - raise ArgumentError, "No profile release named '#{profile_release_name}" if profile_release.nil? + task :profile_release_html, [:profile_release_name] do |_t, args| + profile_release_name = args[:profile_release_name] + if profile_release_name.nil? + warn "Missing required option: 'profile_release_name'" + exit 1 + end + + unless File.exist?("#{$root}/arch/profile_release/#{profile_release_name}.yaml") + warn "No certification model named '#{profile_release_name}' found in arch/profile_release" + exit 1 + end Rake::Task["#{$root}/gen/profile_doc/html/#{profile_release_name}.html"].invoke end diff --git a/backends/profile_doc/templates/profile.adoc.erb b/backends/profile_doc/templates/profile.adoc.erb index beed6be90..3f6339ee5 100644 --- a/backends/profile_doc/templates/profile.adoc.erb +++ b/backends/profile_doc/templates/profile.adoc.erb @@ -225,7 +225,7 @@ A profile may specify that certain conditions will cause a requested trap (such as an `ecall` made in the highest-supported privilege mode) or fatal trap to the enclosing execution environment. The profile does not specify the behavior of the enclosing execution environment -in handling requested or fatal traps. +iusually n handling requested or fatal traps. NOTE: In particular, a profile does not specify the set of ECALLs available in the outer execution environment. This should be @@ -351,7 +351,7 @@ Ratification date:: <%= profile_release.ratification_date %> <%= profile_release.introduction %> -<%= profile_release.marketing_name %> has <%= profile_release.referenced_extensions.reduce(0) { |sum, ext| sum + ext.params.size } %> +<%= profile_release.marketing_name %> has <%= profile_release.in_scope_extensions.reduce(0) { |sum, ext| sum + ext.params.size } %> associated implementation-defined parameters across all its defined profiles. <% unless profile_release.description.nil? -%> @@ -384,10 +384,10 @@ Recommendations are not strictly mandated but are included to guide implementers ==== Implementation-dependencies -<%= profile.marketing_name %> has <%= profile.referenced_extensions.reduce(0) { |sum, ext| sum + ext.params.size } %> +<%= profile.marketing_name %> has <%= profile.in_scope_extensions.reduce(0) { |sum, ext| sum + ext.params.size } %> associated implementation-defined parameters. -<% profile.referenced_extensions.each do |ext| -%> +<% profile.in_scope_extensions.each do |ext| -%> <% ext.params.sort_by { |p| p.name }.each do |param| -%> <%= param.name %>:: + @@ -406,13 +406,13 @@ associated implementation-defined parameters. === <%= profile_class.processor_kind %> Profile Releases The <%= profile_class.processor_kind %> processor kind has <%= profile_class.profile_releases_matching_processor_kind.size %> processor -profile releases that reference a total of <%= profile_class.referenced_extensions_matching_processor_kind.size %> extensions. +profile releases that reference a total of <%= profile_class.in_scope_extensions_matching_processor_kind.size %> extensions. .Extension Presence |=== | Extension | <%= profile_class.profile_releases_matching_processor_kind.map(&:marketing_name).join(" | ") %> -<% profile_class.referenced_extensions_matching_processor_kind.sort_by(&:name).each do |ext| -%> +<% profile_class.in_scope_extensions_matching_processor_kind.sort_by(&:name).each do |ext| -%> | <%= ext.name %> | <%= profile_class.profile_releases_matching_processor_kind.map { |profile_release| profile_release.extension_presence(ext.name) }.join(" | ") %> <% end -%> |=== @@ -420,13 +420,13 @@ profile releases that reference a total of <%= profile_class.referenced_extensio === <%= profile_class.marketing_name %> Profile Releases 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. +reference a total of <%= profile_class.in_scope_extensions.size %> extensions. .Extension Presence |=== | Extension | <%= profile_class.profile_releases.map(&:marketing_name).join(" | ") %> -<% profile_class.referenced_extensions.sort_by(&:name).each do |ext| -%> +<% profile_class.in_scope_extensions.each do |ext| -%> | <%= ext.name %> | <%= profile_class.profile_releases.map { |profile_release| profile_release.extension_presence(ext.name) }.join(" | ") %> <% end -%> |=== @@ -434,7 +434,7 @@ reference a total of <%= profile_class.referenced_extensions.size %> extensions. === <%= profile_release.marketing_name %> Profiles The <%= profile_release.marketing_name %> Profile Release has <%= profile_release.profiles.size %> profiles that -reference a total of <%= profile_release.referenced_extensions.size %> extensions. +reference a total of <%= profile_release.in_scope_extensions.size %> extensions. [NOTE] Extensions present in a profile are also present in higher-privileged profiles in the same profile release. @@ -443,7 +443,7 @@ Extensions present in a profile are also present in higher-privileged profiles i |=== | Extension | <%= profile_release.profiles.map(&:marketing_name).join(" | ") %> -<% profile_release.referenced_extensions.sort_by(&:name).each do |ext| -%> +<% profile_release.in_scope_extensions.each do |ext| -%> | <%= ext.name %> | <%= profile_release.profiles.map { |profile| profile.extension_presence(ext.name) }.join(" | ") %> <% end -%> |=== @@ -452,7 +452,7 @@ Extensions present in a profile are also present in higher-privileged profiles i [appendix] == Extension Details -<% profile_release.referenced_extensions.sort.each do |ext| -%> +<% design.in_scope_extensions.each do |ext| -%> <<< === <%= ext.name %> Extension <%= ext.long_name %> @@ -504,7 +504,7 @@ Extensions present in a profile are also present in higher-privileged profiles i :leveloffset: -3 // TODO: GitHub issue 92: Use version specified by each profile and add version info to inst table below. -<%- insts = cfg_arch.instructions.select { |i| i.defined_by?(ext.min_version) } -%> +<%- insts = arch.instructions.select { |i| i.defined_by?(ext.min_version) } -%> <%- unless insts.empty? -%> ==== Instructions @@ -538,14 +538,9 @@ This extension has the following implementation options: [appendix] == Instruction Details -<%= - insts = profile_release.referenced_extensions.map { |ext| ext.instructions }.flatten.uniq - insts.sort_by!(&:name) --%> - -<% insts.each do |inst| -%> +<% design.in_scope_instructions.each do |inst| -%> <<< -[[inst-<%=inst.name.gsub('.', '_')%>-def]] +<%= anchor_for_inst(inst.name) %> === <%= inst.name %> *<%= inst.long_name %>* @@ -588,18 +583,18 @@ RV64:: <%= inst.description %> ==== Access -<% if profile_release.referenced_extensions.any? { |e| e.name == "H" } -%> +<% if design.in_scope_extensions.any? { |e| e.name == "H" } -%> [cols="^,^,^,^,^"] <% else -%> [cols="^,^,^"] <% end -%> |=== -| M | <% if profile_release.referenced_extensions.any? { |e| e.name == "H" } -%>HS<% else -%>S<% end -%> | U <% if profile_release.referenced_extensions.any? { |e| e.name == "H" } -%> | VS | VU <% end -%> +| M | <% if design.in_scope_extensions.any? { |e| e.name == "H" } -%>HS<% else -%>S<% end -%> | U <% if design.in_scope_extensions.any? { |e| e.name == "H" } -%> | VS | VU <% end -%> | [.access-always]#Always# | [.access-<%=inst.access['s']%>]#<%= inst.access['s'].capitalize %># | [.access-<%=inst.access['u']%>]#<%= inst.access['u'].capitalize %># -<% if profile_release.referenced_extensions.any? { |e| e.name == "H" } %> +<% if design.in_scope_extensions.any? { |e| e.name == "H" } %> | [.access-<%=inst.access['vs']%>]#<%= inst.access['vs'].capitalize %># | [.access-<%=inst.access['vu']%>]#<%= inst.access['vu'].capitalize %># <% end %> @@ -649,13 +644,13 @@ RV64:: <% if inst.key?("operation()") -%> [source,idl,subs="specialchars,macros"] ---- -<%= inst.operation_ast(cfg_arch.symtab).gen_adoc %> +<%= inst.operation_ast(design.symtab).gen_adoc %> ---- <% end -%> ==== Exceptions -<% exception_list = inst.reachable_exceptions_str(cfg_arch.symtab) -%> +<% exception_list = inst.reachable_exceptions_str(design.symtab) -%> <% if exception_list.empty? -%> This instruction does not generate synchronous exceptions. <% else -%> @@ -673,12 +668,7 @@ This instruction may result in the following synchronous exceptions: [appendix] == CSR Details -<% - csrs = profile_release.referenced_extensions.map { |ext| ext.csrs }.flatten.uniq - csrs.sort_by!(&:name) --%> - -<% csrs.each do |csr| -%> +<% design.in_scope_csrs.sort_by!(&:name).each do |csr| -%> <<< [[csr-<%= csr.name %>-def]] === <%= csr.name %> @@ -702,22 +692,22 @@ h| CSR Address | <%= "0x#{csr.address.to_s(16)}" %> h| Virtual CSR Address | <%= "0x#{csr.virtual_address.to_s(16)}" %> <% end -%> h| Defining extension a| <%= csr.defined_by_condition.to_asciidoc %> -<% if csr.dynamic_length?(cfg_arch) -%> -h| Length | <%= csr.length_pretty(cfg_arch) %> +<% if csr.dynamic_length?(design) -%> +h| Length | <%= csr.length_pretty(design) %> <% else -%> -h| Length | <%= csr.length_pretty(cfg_arch) %> +h| Length | <%= csr.length_pretty(design) %> <% end -%> h| Privilege Mode | <%= csr.priv_mode %> |=== ==== Format -<% unless csr.dynamic_length?(cfg_arch) || csr.fields.any? { |f| f.dynamic_location?(cfg_arch) } -%> +<% unless csr.dynamic_length?(design) || csr.fields.any? { |f| f.dynamic_location?(design) } -%> <%# CSR has a known static length, so there is only one format to display -%> .<%= csr.name %> format [wavedrom, ,svg,subs='attributes',width="100%"] .... -<%= JSON.dump csr.wavedrom_desc(cfg_arch, csr.base.nil? ? 32 : csr.base) %> +<%= JSON.dump csr.wavedrom_desc(design, csr.base.nil? ? 32 : csr.base) %> .... <% else -%> <%# CSR has a dynamic length, or a field has a dynamic location, @@ -727,13 +717,13 @@ This CSR format changes dynamically with XLEN. .<%= csr.name %> Format when <%= csr.length_cond32 %> [wavedrom, ,svg,subs='attributes',width="100%"] .... -<%= JSON.dump csr.wavedrom_desc(cfg_arch, 32) %> +<%= JSON.dump csr.wavedrom_desc(design, 32) %> .... .<%= csr.name %> Format when <%= csr.length_cond64 %> [wavedrom, ,svg,subs='attributes',width="100%"] .... -<%= JSON.dump csr.wavedrom_desc(cfg_arch, 64) %> +<%= JSON.dump csr.wavedrom_desc(design, 64) %> .... diff --git a/lib/arch_obj_models/certificate.rb b/lib/arch_obj_models/certificate.rb index 2200eb078..e1a523648 100644 --- a/lib/arch_obj_models/certificate.rb +++ b/lib/arch_obj_models/certificate.rb @@ -22,15 +22,11 @@ def mandatory_priv_modes = @data["mandatory_priv_modes"] class CertModel < Portfolio # @param obj_yaml [Hash] Contains contents of Certificate Model yaml file (put in @data) # @param data_path [String] Path to yaml file - # @param cfg_arch [ConfiguredArchitecture] Architecture for a specific configuration - def initialize(obj_yaml, yaml_path, arch: nil) + # @param arch [Architecture] Database of RISC-V standards + def initialize(obj_yaml, yaml_path, arch) super # Calls parent class with the same args I got - unless arch.is_a?(ConfiguredArchitecture) - raise ArgumentError, "For #{name} arch is a #{arch.class} but must be a ConfiguredArchitecture" - end - - puts "UPDATE: Creating CertModel object for #{name} using cfg #{cfg_arch.name}" + puts "UPDATE: Creating CertModel object for #{name} using cfg #{arch.name}" end def unpriv_isa_manual_revision = @data["unpriv_isa_manual_revision"] @@ -40,7 +36,7 @@ def debug_manual_revision = @data["debug_manual_revision"] def tsc_profile return nil if @data["tsc_profile"].nil? - profile = cfg_arch.profile(@data["tsc_profile"]) + profile = arch.profile(@data["tsc_profile"]) raise "No profile '#{@data["tsc_profile"]}'" if profile.nil? @@ -49,7 +45,7 @@ def tsc_profile # @return [CertClass] The certification class that this model belongs to. def cert_class - cert_class = @cfg_arch.ref(@data["class"]['$ref']) + cert_class = @arch.ref(@data["class"]['$ref']) raise "No certificate class named '#{@data["class"]}'" if cert_class.nil? cert_class @@ -61,9 +57,9 @@ def cert_class # Holds extra requirements not associated with extensions or their parameters. class Requirement - def initialize(data, cfg_arch) + def initialize(data, arch) @data = data - @cfg_arch = cfg_arch + @arch = arch end def name = @data["name"] @@ -95,9 +91,9 @@ def when_pretty # Holds a group of Requirement objects to provide a one-level group. # Can't nest RequirementGroup objects to make multi-level group. class RequirementGroup - def initialize(data, cfg_arch) + def initialize(data, arch) @data = data - @cfg_arch = cfg_arch + @arch = arch end def name = @data["name"] @@ -126,7 +122,7 @@ def requirements @requirements = [] @data["requirements"].each do |req| - @requirements << Requirement.new(req, @cfg_arch) + @requirements << Requirement.new(req, @arch) end @requirements end @@ -137,7 +133,7 @@ def requirement_groups @requirement_groups = [] @data["requirement_groups"]&.each do |req_group| - @requirement_groups << RequirementGroup.new(req_group, @cfg_arch) + @requirement_groups << RequirementGroup.new(req_group, @arch) end @requirement_groups end diff --git a/lib/arch_obj_models/csr.rb b/lib/arch_obj_models/csr.rb index 6b4335412..a1cb9044d 100644 --- a/lib/arch_obj_models/csr.rb +++ b/lib/arch_obj_models/csr.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "obj" +require_relative "database_obj" # CSR definition class Csr < DatabaseObject @@ -46,68 +46,68 @@ def defined_in_base64? = @data["base"].nil? || @data["base"] == 64 # @return [Boolean] true if this CSR is defined regardless of the effective XLEN def defined_in_all_bases? = @data["base"].nil? - # @param cfg_arch [ConfiguredArchitecture] A configuration + # @param design [Design] A configuration # @return [Boolean] Whether or not the format of this CSR changes when the effective XLEN changes in some mode - def format_changes_with_xlen?(cfg_arch) - dynamic_length?(cfg_arch) || - implemented_fields(cfg_arch).any? do |f| - f.dynamic_location?(cfg_arch) + def format_changes_with_xlen?(design) + dynamic_length?(design) || + implemented_fields(design).any? do |f| + f.dynamic_location?(design) end end - # @param cfg_arch [ConfiguredArchitecture] A configuration + # @param design [Design] A configuration # @return [Array] List of functions reachable from this CSR's sw_read or a field's sw_write function - def reachable_functions(cfg_arch) + def reachable_functions(design) return @reachable_functions unless @reachable_functions.nil? fns = [] if has_custom_sw_read? - ast = pruned_sw_read_ast(cfg_arch) - symtab = cfg_arch.symtab.deep_clone + ast = pruned_sw_read_ast(design) + symtab = design.symtab.deep_clone symtab.push(ast) fns.concat(ast.reachable_functions(symtab)) end - if cfg_arch.multi_xlen? - implemented_fields_for(cfg_arch, 32).each do |field| - fns.concat(field.reachable_functions(cfg_arch, 32)) + if design.multi_xlen? + implemented_fields_for(design, 32).each do |field| + fns.concat(field.reachable_functions(design, 32)) end - implemented_fields_for(cfg_arch, 64).each do |field| - fns.concat(field.reachable_functions(cfg_arch, 64)) + implemented_fields_for(design, 64).each do |field| + fns.concat(field.reachable_functions(design, 64)) end else - implemented_fields_for(cfg_arch, cfg_arch.mxlen).each do |field| - fns.concat(field.reachable_functions(cfg_arch, cfg_arch.mxlen)) + implemented_fields_for(design, design.mxlen).each do |field| + fns.concat(field.reachable_functions(design, design.mxlen)) end end @reachable_functions = fns.uniq end - # @param cfg_arch [ConfiguredArchitecture] Architecture definition + # @param design [Design] # @return [Array] List of functions reachable from this CSR's sw_read or a field's sw_wirte function, irrespective of context - def reachable_functions_unevaluated(cfg_arch) + def reachable_functions_unevaluated(design) return @reachable_functions_unevaluated unless @reachable_functions_unevaluated.nil? fns = [] if has_custom_sw_read? - ast = sw_read_ast(cfg_arch) - fns.concat(ast.reachable_functions_unevaluated(cfg_arch)) + ast = sw_read_ast(design) + fns.concat(ast.reachable_functions_unevaluated(design)) end fields.each do |field| - fns.concat(field.reachable_functions_unevaluated(cfg_arch)) + fns.concat(field.reachable_functions_unevaluated(design)) end @reachable_functions_unevaluated = fns.uniq end - # @param cfg_arch [ConfiguredArchitecture] A configuration + # @param design [Design] A configuration # @return [Boolean] Whether or not the length of the CSR depends on a runtime value # (e.g., mstatus.SXL) - def dynamic_length?(cfg_arch) + def dynamic_length?(design) return false if @data["length"].is_a?(Integer) # when a CSR is only defined in one base, its length can't change @@ -117,21 +117,21 @@ def dynamic_length?(cfg_arch) when "MXLEN" # mxlen can never change at runtime, so if we have it in the config, the length is not dynamic # if we don't have it in the config, we don't know what the length is - cfg_arch.mxlen.nil? + design.mxlen.nil? when "SXLEN" # dynamic if either we don't know SXLEN or SXLEN is explicitly mutable - [nil, 3264].include?(cfg_arch.param_values["SXLEN"]) + [nil, 3264].include?(design.param_values["SXLEN"]) when "VSXLEN" # dynamic if either we don't know VSXLEN or VSXLEN is explicitly mutable - [nil, 3264].include?(cfg_arch.param_values["VSXLEN"]) + [nil, 3264].include?(design.param_values["VSXLEN"]) else raise "Unexpected length" end end - # @param cfg_arch [ConfiguredArchitecture] Architecture definition + # @param design [Design] # @return [Integer] Smallest length of the CSR in any mode - def min_length(cfg_arch) + def min_length(design) case @data["length"] when "MXLEN", "SXLEN", "VSXLEN" 32 @@ -142,14 +142,14 @@ def min_length(cfg_arch) end end - # @param cfg_arch [ConfiguredArchitecture] A configuration (can be nil if the lenth is not dependent on a config parameter) + # @param design [Design] A configuration (can be nil if the lenth is not dependent on a config parameter) # @param effective_xlen [Integer] The effective xlen, needed since some fields change location with XLEN. If the field location is not determined by XLEN, then this parameter can be nil # @return [Integer] Length, in bits, of the CSR, given effective_xlen - # @return [nil] if the length cannot be determined from the cfg_arch (e.g., because SXLEN is unknown and +effective_xlen+ was not provided) - def length(cfg_arch, effective_xlen = nil) + # @return [nil] if the length cannot be determined from the design (e.g., because SXLEN is unknown and +effective_xlen+ was not provided) + def length(design, effective_xlen = nil) case @data["length"] when "MXLEN" - return cfg_arch.mxlen unless cfg_arch.mxlen.nil? + return design.mxlen unless design.mxlen.nil? if !@data["base"].nil? @data["base"] @@ -158,11 +158,11 @@ def length(cfg_arch, effective_xlen = nil) effective_xlen end when "SXLEN" - if cfg_arch.param_values.key?("SXLEN") - if cfg_arch.param_values["SXLEN"] == 3264 + if design.param_values.key?("SXLEN") + if design.param_values["SXLEN"] == 3264 effective_xlen else - cfg_arch.param_values["SXLEN"] + design.param_values["SXLEN"] end elsif !@data["base"].nil? # if this CSR is only available in one base, then we know its length @@ -172,11 +172,11 @@ def length(cfg_arch, effective_xlen = nil) effective_xlen end when "VSXLEN" - if cfg_arch.param_values.key?("VSXLEN") - if cfg_arch.param_values["VSXLEN"] == 3264 + if design.param_values.key?("VSXLEN") + if design.param_values["VSXLEN"] == 3264 effective_xlen else - cfg_arch.param_values["VSXLEN"] + design.param_values["VSXLEN"] end elsif !@data["base"].nil? # if this CSR is only available in one base, then we know its length @@ -193,28 +193,28 @@ def length(cfg_arch, effective_xlen = nil) end # @return [Integer] The largest length of this CSR in any valid mode/xlen for the config - def max_length(cfg_arch) + def max_length(design) return @data["base"] unless @data["base"].nil? case @data["length"] when "MXLEN" - cfg_arch.mxlen || 64 + design.mxlen || 64 when "SXLEN" - if cfg_arch.param_values.key?("SXLEN") - if cfg_arch.param_values["SXLEN"] == 3264 + if design.param_values.key?("SXLEN") + if design.param_values["SXLEN"] == 3264 64 else - cfg_arch.param_values["SXLEN"] + design.param_values["SXLEN"] end else 64 end when "VSXLEN" - if cfg_arch.param_values.key?("VSXLEN") - if cfg_arch.param_values["VSXLEN"] == 3264 + if design.param_values.key?("VSXLEN") + if design.param_values["VSXLEN"] == 3264 64 else - cfg_arch.param_values["VSXLEN"] + design.param_values["VSXLEN"] end else 64 @@ -254,10 +254,10 @@ def length_cond64 end end - # @param cfg_arch [ConfiguredArchitecture] A configuration + # @param design [Design] A configuration # @return [String] Pretty-printed length string - def length_pretty(cfg_arch, effective_xlen=nil) - if dynamic_length?(cfg_arch) + def length_pretty(design, effective_xlen=nil) + if dynamic_length?(design) cond = case @data["length"] when "MXLEN" @@ -272,14 +272,14 @@ def length_pretty(cfg_arch, effective_xlen=nil) if effective_xlen.nil? <<~LENGTH - #{length(cfg_arch, 32)} when #{cond.sub('%%', '0')} - #{length(cfg_arch, 64)} when #{cond.sub('%%', '1')} + #{length(design, 32)} when #{cond.sub('%%', '0')} + #{length(design, 64)} when #{cond.sub('%%', '1')} LENGTH else - "#{length(cfg_arch, effective_xlen)}-bit" + "#{length(design, effective_xlen)}-bit" end else - "#{length(cfg_arch)}-bit" + "#{length(design)}-bit" end end @@ -306,39 +306,39 @@ def description_html Asciidoctor.convert description end - # @param cfg_arch [ConfiguredArchitecture] A configuration + # @param design [Design] A configuration # @return [Array] All implemented fields for this CSR at the given effective XLEN, sorted by location (smallest location first) # Excluded any fields that are defined by unimplemented extensions or a base that is not effective_xlen - def implemented_fields_for(cfg_arch, effective_xlen) + def implemented_fields_for(design, effective_xlen) @implemented_fields_for ||= {} - key = [cfg_arch.name, effective_xlen].hash + key = [design.name, effective_xlen].hash return @implemented_fields_for[key] if @implemented_fields_for.key?(key) @implemented_fields_for[key] = - implemented_fields(cfg_arch).select do |f| + implemented_fields(design).select do |f| !f.key?("base") || f.base == effective_xlen end end - # @param cfg_arch [ConfiguredArchitecture] A configuration + # @param design [Design] A configuration # @return [Array] All implemented fields for this CSR # Excluded any fields that are defined by unimplemented extensions - def implemented_fields(cfg_arch) + def implemented_fields(design) return @implemented_fields unless @implemented_fields.nil? implemented_bases = - if cfg_arch.param_values["SXLEN"] == 3264 || - cfg_arch.param_values["UXLEN"] == 3264 || - cfg_arch.param_values["VSXLEN"] == 3264 || - cfg_arch.param_values["VUXLEN"] == 3264 + if design.param_values["SXLEN"] == 3264 || + design.param_values["UXLEN"] == 3264 || + design.param_values["VSXLEN"] == 3264 || + design.param_values["VUXLEN"] == 3264 [32, 64] else - [cfg_arch.param_values["XLEN"]] + [design.param_values["XLEN"]] end @implemented_fields = fields.select do |f| - f.exists_in_cfg?(cfg_arch) + f.exists_in_cfg?(design) end end @@ -377,15 +377,15 @@ def field(field_name) field_hash[field_name.to_s] end - # @param cfg_arch [ConfiguredArchitecture] A configuration + # @param design [Design] A configuration # @param effective_xlen [Integer] The effective XLEN to apply, needed when field locations change with XLEN in some mode # @return [Idl::BitfieldType] A bitfield type that can represent all fields of the CSR - def bitfield_type(cfg_arch, effective_xlen = nil) + def bitfield_type(design, effective_xlen = nil) Idl::BitfieldType.new( "Csr#{name.capitalize}Bitfield", - length(cfg_arch, effective_xlen), + length(design, effective_xlen), fields_for(effective_xlen).map(&:name), - fields_for(effective_xlen).map { |f| f.location(cfg_arch, effective_xlen) } + fields_for(effective_xlen).map { |f| f.location(design, effective_xlen) } ) end @@ -414,7 +414,7 @@ def type_checked_sw_read_ast(symtab) ) ast = sw_read_ast(symtab) - symtab.cfg_arch.idl_compiler.type_check( + symtab.design.idl_compiler.type_check( ast, symtab, "CSR[#{name}].sw_read()" @@ -425,7 +425,7 @@ def type_checked_sw_read_ast(symtab) end # @return [FunctionBodyAst] The abstract syntax tree of the sw_read() function - # @param cfg_arch [ConfiguredArchitecture] A configuration + # @param design [Design] A configuration def sw_read_ast(symtab) raise ArgumentError, "Argument should be a symtab" unless symtab.is_a?(Idl::SymbolTable) @@ -433,7 +433,7 @@ def sw_read_ast(symtab) return nil if @data["sw_read()"].nil? # now, parse the function - @sw_read_ast = symtab.cfg_arch.idl_compiler.compile_func_body( + @sw_read_ast = symtab.design.idl_compiler.compile_func_body( @data["sw_read()"], return_type: Idl::Type.new(:bits, width: 128), # big int to hold special return values name: "CSR[#{name}].sw_read()", @@ -450,14 +450,14 @@ def sw_read_ast(symtab) @sw_read_ast end - def pruned_sw_read_ast(cfg_arch) + def pruned_sw_read_ast(design) @pruned_sw_read_asts ||= {} - ast = @pruned_sw_read_asts[cfg_arch.name] + ast = @pruned_sw_read_asts[design.name] return ast unless ast.nil? - ast = type_checked_sw_read_ast(cfg_arch.symtab) + ast = type_checked_sw_read_ast(design.symtab) - symtab = cfg_arch.symtab.global_clone + symtab = design.symtab.global_clone symtab.push(ast) # all CSR instructions are 32-bit symtab.add( @@ -470,9 +470,9 @@ def pruned_sw_read_ast(cfg_arch) ) ast = ast.prune(symtab) - ast.freeze_tree(cfg_arch.symtab) + ast.freeze_tree(design.symtab) - cfg_arch.idl_compiler.type_check( + design.idl_compiler.type_check( ast, symtab, "CSR[#{name}].sw_read()" @@ -481,7 +481,7 @@ def pruned_sw_read_ast(cfg_arch) symtab.pop symtab.release - @pruned_sw_read_asts[cfg_arch.name] = ast + @pruned_sw_read_asts[design.name] = ast end # @example Result for an I-type instruction @@ -493,12 +493,12 @@ def pruned_sw_read_ast(cfg_arch) # {bits: 12, name: 'imm12', attr: [''], type: 6} # ]} # - # @param cfg_arch [ConfiguredArchitecture] A configuration + # @param design [Design] A configuration # @param effective_xlen [Integer,nil] Effective XLEN to use when CSR length is dynamic # @param exclude_unimplemented [Boolean] If true, do not create include unimplemented fields in the figure - # @param optional_type [Integer] Wavedrom type (Fill color) for fields that are optional (not mandatory) in a partially-specified cfg_arch + # @param optional_type [Integer] Wavedrom type (Fill color) for fields that are optional (not mandatory) in a partially-specified design # @return [Hash] A representation of the WaveDrom drawing for the CSR (should be turned into JSON for wavedrom) - def wavedrom_desc(cfg_arch, effective_xlen, exclude_unimplemented: false, optional_type: 2) + def wavedrom_desc(design, effective_xlen, exclude_unimplemented: false, optional_type: 2) desc = { "reg" => [] } @@ -506,57 +506,57 @@ def wavedrom_desc(cfg_arch, effective_xlen, exclude_unimplemented: false, option field_list = if exclude_unimplemented - implemented_fields_for(cfg_arch, effective_xlen) + implemented_fields_for(design, effective_xlen) else fields_for(effective_xlen) end - field_list.sort! { |a, b| a.location(cfg_arch, effective_xlen).min <=> b.location(cfg_arch, effective_xlen).min } + field_list.sort! { |a, b| a.location(design, effective_xlen).min <=> b.location(design, effective_xlen).min } field_list.each do |field| - if field.location(cfg_arch, effective_xlen).min != last_idx + 1 + if field.location(design, effective_xlen).min != last_idx + 1 # have some reserved space - n = field.location(cfg_arch, effective_xlen).min - last_idx - 1 - raise "negative reserved space? #{n} #{name} #{field.location(cfg_arch, effective_xlen).min} #{last_idx + 1}" if n <= 0 + n = field.location(design, effective_xlen).min - last_idx - 1 + raise "negative reserved space? #{n} #{name} #{field.location(design, effective_xlen).min} #{last_idx + 1}" if n <= 0 desc["reg"] << { "bits" => n, type: 1 } end - if cfg_arch.partially_configured? && field.optional_in_cfg?(cfg_arch) - desc["reg"] << { "bits" => field.location(cfg_arch, effective_xlen).size, "name" => field.name, type: optional_type } + if design.partially_configured? && field.optional_in_cfg?(design) + desc["reg"] << { "bits" => field.location(design, effective_xlen).size, "name" => field.name, type: optional_type } else - desc["reg"] << { "bits" => field.location(cfg_arch, effective_xlen).size, "name" => field.name, type: 3 } + desc["reg"] << { "bits" => field.location(design, effective_xlen).size, "name" => field.name, type: 3 } end - last_idx = field.location(cfg_arch, effective_xlen).max + last_idx = field.location(design, effective_xlen).max end - if !field_list.empty? && (field_list.last.location(cfg_arch, effective_xlen).max != (length(cfg_arch, effective_xlen) - 1)) + if !field_list.empty? && (field_list.last.location(design, effective_xlen).max != (length(design, effective_xlen) - 1)) # reserved space at the end - desc["reg"] << { "bits" => (length(cfg_arch, effective_xlen) - 1 - last_idx), type: 1 } + desc["reg"] << { "bits" => (length(design, effective_xlen) - 1 - last_idx), type: 1 } # desc['reg'] << { 'bits' => 1, type: 1 } end - desc["config"] = { "bits" => length(cfg_arch, effective_xlen) } - desc["config"]["lanes"] = length(cfg_arch, effective_xlen) / 16 + desc["config"] = { "bits" => length(design, effective_xlen) } + desc["config"]["lanes"] = length(design, effective_xlen) / 16 desc end - # @param cfg_arch [ConfiguredArchitecture] Architecture def + # @param design [Design] # @return [Boolean] whether or not the CSR is possibly implemented given the supplies config options - def exists_in_cfg?(cfg_arch) - if cfg_arch.fully_configured? - (@data["base"].nil? || (cfg_arch.possible_xlens.include? @data["base"])) && - cfg_arch.transitive_implemented_extensions.any? { |e| defined_by?(e) } + def exists_in_cfg?(design) + if design.fully_configured? + (@data["base"].nil? || (design.possible_xlens.include? @data["base"])) && + design.transitive_implemented_extensions.any? { |e| defined_by?(e) } else - (@data["base"].nil? || (cfg_arch.possible_xlens.include? @data["base"])) && - cfg_arch.prohibited_extensions.none? { |ext_req| ext_req.satisfying_versions.any? { |e| defined_by?(e) } } + (@data["base"].nil? || (design.possible_xlens.include? @data["base"])) && + design.prohibited_extensions.none? { |ext_req| ext_req.satisfying_versions.any? { |e| defined_by?(e) } } end end - # @param cfg_arch [ConfiguredArchitecture] Architecture def + # @param design [Design] # @return [Boolean] whether or not the CSR is optional in the config - def optional_in_cfg?(cfg_arch) - raise "optional_in_cfg? should only be used by a partially-specified arch def" unless cfg_arch.partially_configured? + def optional_in_cfg?(design) + raise "optional_in_cfg? should only be used by a partially-specified arch def" unless design.partially_configured? - exists_in_cfg?(cfg_arch) && - cfg_arch.mandatory_extensions.all? do |ext_req| + exists_in_cfg?(design) && + design.mandatory_extensions.all? do |ext_req| ext_req.satisfying_versions.none? do |ext_ver| defined_by?(ext_ver) end diff --git a/lib/arch_obj_models/csr_field.rb b/lib/arch_obj_models/csr_field.rb index e1095f2cd..4dc0b9d1e 100644 --- a/lib/arch_obj_models/csr_field.rb +++ b/lib/arch_obj_models/csr_field.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "obj" +require_relative "database_obj" require_relative "../idl/passes/gen_option_adoc" @@ -24,7 +24,7 @@ def base # @param parent_csr [Csr] The Csr that defined this field # @param field_data [Hash] Field data from the arch spec def initialize(parent_csr, field_name, field_data) - super(field_data, parent_csr.data_path, arch: parent_csr.arch) + super(field_data, parent_csr.data_path, parent_csr.arch) @name = field_name @parent = parent_csr end @@ -32,30 +32,30 @@ def initialize(parent_csr, field_name, field_data) # @param possible_xlens [Array] List of xlens that be used in any implemented mode # @param extensions [Array] List of extensions implemented # @return [Boolean] whether or not the instruction is implemented given the supplies config options - def exists_in_cfg?(cfg_arch) - if cfg_arch.fully_configured? - parent.exists_in_cfg?(cfg_arch) && - (@data["base"].nil? || cfg_arch.possible_xlens.include?(@data["base"])) && - (@data["definedBy"].nil? || cfg_arch.transitive_implemented_extensions.any? { |ext_ver| defined_by?(ext_ver) }) + def exists_in_cfg?(design) + if design.fully_configured? + parent.exists_in_cfg?(design) && + (@data["base"].nil? || design.possible_xlens.include?(@data["base"])) && + (@data["definedBy"].nil? || design.transitive_implemented_extensions.any? { |ext_ver| defined_by?(ext_ver) }) else - raise "unexpected type" unless cfg_arch.partially_configured? + raise "unexpected type" unless design.partially_configured? - parent.exists_in_cfg?(cfg_arch) && - (@data["base"].nil? || cfg_arch.possible_xlens.include?(@data["base"])) && - (@data["definedBy"].nil? || cfg_arch.prohibited_extensions.none? { |ext_req| ext_req.satisfying_versions.any? { |ext_ver| defined_by?(ext_ver) } }) + parent.exists_in_cfg?(design) && + (@data["base"].nil? || design.possible_xlens.include?(@data["base"])) && + (@data["definedBy"].nil? || design.prohibited_extensions.none? { |ext_req| ext_req.satisfying_versions.any? { |ext_ver| defined_by?(ext_ver) } }) end end - # @return [Boolean] For a partially configured cfg_arch, whether or not the field is optional (not mandatory or prohibited) - def optional_in_cfg?(cfg_arch) - raise "optional_in_cfg? should only be called on a partially configured cfg_arch" unless cfg_arch.partially_configured? + # @return [Boolean] For a partially configured design, whether or not the field is optional (not mandatory or prohibited) + def optional_in_cfg?(design) + raise "optional_in_cfg? should only be called on a partially configured design" unless design.partially_configured? - exists_in_cfg?(cfg_arch) && + exists_in_cfg?(design) && ( if data["definedBy"].nil? - parent.optional_in_cfg?(cfg_arch) + parent.optional_in_cfg?(design) else - cfg_arch.mandatory_extensions.all? do |ext_req| + design.mandatory_extensions.all? do |ext_req| ext_req.satisfying_versions.none? do |ext_ver| defined_by?(ext_ver) end @@ -71,7 +71,7 @@ def type_ast(symtab) return @type_ast unless @type_ast.nil? return nil if @data["type()"].nil? - @type_ast = symtab.cfg_arch.idl_compiler.compile_func_body( + @type_ast = symtab.design.idl_compiler.compile_func_body( @data["type()"], name: "CSR[#{csr.name}].#{name}.type()", input_file: csr.__source, @@ -105,7 +105,7 @@ def type_checked_type_ast(symtab) ) ast = type_ast(symtab) - symtab.cfg_arch.idl_compiler.type_check( + symtab.design.idl_compiler.type_check( ast, symtab, "CSR[#{name}].type()" @@ -137,7 +137,7 @@ def pruned_type_ast(symtab) ast.freeze_tree(symtab) - symtab.cfg_arch.idl_compiler.type_check( + symtab.design.idl_compiler.type_check( ast, symtab, "CSR[#{name}].type()" @@ -162,9 +162,9 @@ def type(symtab) raise ArgumentError, "Argument 1 should be a symtab" unless symtab.is_a?(Idl::SymbolTable) unless @type_cache.nil? - raise "Different cfg_arch for type #{@type_cache.keys}, #{symtab.cfg_arch}" unless @type_cache.key?(symtab.cfg_arch) + raise "Different design for type #{@type_cache.keys}, #{symtab.design}" unless @type_cache.key?(symtab.design) - return @type_cache[symtab.cfg_arch] + return @type_cache[symtab.design] end type = @@ -211,7 +211,7 @@ def type(symtab) end @type_cache ||= {} - @type_cache[symtab.cfg_arch] = type + @type_cache[symtab.design] = type end # @return [String] A pretty-printed type string @@ -242,7 +242,7 @@ def alias range_start = Regexp.last_match(4) range_end = Regexp.last_match(5) - csr_field = cfg_arch.csr(csr_name).field(csr_field) + csr_field = design.csr(csr_name).field(csr_field) range = if range.nil? field.location @@ -257,31 +257,31 @@ def alias end # @return [Array] List of functions called thorugh this field - # @param cfg_arch [ConfiguredArchitecture] a configuration + # @param design [Design] The design # @Param effective_xlen [Integer] 32 or 64; needed because fields can change in different XLENs - def reachable_functions(cfg_arch, effective_xlen) + def reachable_functions(design, effective_xlen) return @reachable_functions unless @reachable_functions.nil? symtab = - if (cfg_arch.configured?) - cfg_arch.symtab + if (design.configured?) + design.symtab else - raise ArgumentError, "Must supply effective_xlen for generic ConfiguredArchitecture" if effective_xlen.nil? + raise ArgumentError, "Must supply effective_xlen for generic Design" if effective_xlen.nil? if effective_xlen == 32 - cfg_arch.symtab_32 + design.symtab_32 else - cfg_arch.symtab_64 + design.symtab_64 end end fns = [] if has_custom_sw_write? - ast = pruned_sw_write_ast(cfg_arch, effective_xlen) + ast = pruned_sw_write_ast(design, effective_xlen) unless ast.nil? sw_write_symtab = symtab.deep_clone sw_write_symtab.push(ast) - sw_write_symtab.add("csr_value", Idl::Var.new("csr_value", csr.bitfield_type(symtab.cfg_arch, effective_xlen))) + sw_write_symtab.add("csr_value", Idl::Var.new("csr_value", csr.bitfield_type(symtab.design, effective_xlen))) fns.concat ast.reachable_functions(sw_write_symtab) end end @@ -334,18 +334,18 @@ def reachable_functions_unevaluated(symtab) # @return [Csr] Parent CSR for this field alias csr parent - # @param cfg_arch [ConfiguredArchitecture] A configuration + # @param design [Design] The design # @return [Boolean] Whether or not the location of the field changes dynamically # (e.g., based on mstatus.SXL) in the configuration - def dynamic_location?(cfg_arch) + def dynamic_location?(design) # if there is no location_rv32, the the field never changes return false unless @data["location"].nil? # the field changes *if* some mode with access can change XLEN - csr.modes_with_access.any? { |mode| cfg_arch.multi_xlen_in_mode?(mode) } + csr.modes_with_access.any? { |mode| design.multi_xlen_in_mode?(mode) } end - # @param cfg_arch [IdL::Compiler] A compiler + # @param design [IdL::Compiler] A compiler # @return [Idl::FunctionBodyAst] Abstract syntax tree of the reset_value function # @return [nil] If the reset_value is not a function def reset_value_ast(symtab) @@ -354,7 +354,7 @@ def reset_value_ast(symtab) return @reset_value_ast unless @reset_value_ast.nil? return nil unless @data.key?("reset_value()") - @reset_value_ast = symtab.cfg_arch.idl_compiler.compile_func_body( + @reset_value_ast = symtab.design.idl_compiler.compile_func_body( @data["reset_value()"], return_type: Idl::Type.new(:bits, width: 64), name: "CSR[#{parent.name}].#{name}.reset_value()", @@ -383,7 +383,7 @@ def type_checked_reset_value_ast(symtab) symtab = symtab.deep_clone symtab.push(ast) symtab.add("__expected_return_type", Idl::Type.new(:bits, width: 64)) - symtab.cfg_arch.idl_compiler.type_check( + symtab.design.idl_compiler.type_check( ast, symtab, "CSR[#{csr.name}].reset_value()" @@ -416,7 +416,7 @@ def pruned_reset_value_ast(symtab) symtab.push(ast) symtab.add("__expected_return_type", Idl::Type.new(:bits, width: 64)) - symtab.cfg_arch.idl_compiler.type_check( + symtab.design.idl_compiler.type_check( ast, symtab, "CSR[#{csr.name}].#{name}.reset_value()" @@ -425,20 +425,20 @@ def pruned_reset_value_ast(symtab) @type_checked_reset_value_asts[symtab_hash] = ast end - # @param cfg_arch [ConfiguredArchitecture] A config + # @param design [Design] The design # @return [Integer] The reset value of this field # @return [String] The string 'UNDEFINED_LEGAL' if, for this config, there is no defined reset value - def reset_value(cfg_arch) - cached_value = @reset_value_cache.nil? ? nil : @reset_value_cache[cfg_arch] + def reset_value(design) + cached_value = @reset_value_cache.nil? ? nil : @reset_value_cache[design] return cached_value if cached_value @reset_value_cache ||= {} - @reset_value_cache[cfg_arch] = + @reset_value_cache[design] = if @data.key?("reset_value") @data["reset_value"] else - symtab = cfg_arch.symtab + symtab = design.symtab ast = pruned_reset_value_ast(symtab.deep_clone) val = ast.return_value(symtab.deep_clone.push(ast)) val = "UNDEFINED_LEGAL" if val == 0x1_0000_0000_0000_0000 @@ -446,22 +446,22 @@ def reset_value(cfg_arch) end end - def dynamic_reset_value?(cfg_arch) + def dynamic_reset_value?(design) return false unless @data["reset_value"].nil? value_result = Idl::AstNode.value_try do - reset_value(cfg_arch) + reset_value(design) false end || true end - def reset_value_pretty(cfg_arch) + def reset_value_pretty(design) str = nil value_result = Idl::AstNode.value_try do - str = reset_value(cfg_arch) + str = reset_value(design) end Idl::AstNode.value_else(value_result) do - ast = reset_value_ast(cfg_arch.symtab) + ast = reset_value_ast(design.symtab) str = ast.gen_option_adoc end str @@ -496,11 +496,11 @@ def type_checked_sw_write_ast(symtab, effective_xlen) ) symtab.add( "csr_value", - Idl::Var.new("csr_value", csr.bitfield_type(symtab.cfg_arch, effective_xlen)) + Idl::Var.new("csr_value", csr.bitfield_type(symtab.design, effective_xlen)) ) ast = sw_write_ast(symtab) - symtab.cfg_arch.idl_compiler.type_check( + symtab.design.idl_compiler.type_check( ast, symtab, "CSR[#{csr.name}].#{name}.sw_write()" @@ -512,7 +512,7 @@ def type_checked_sw_write_ast(symtab, effective_xlen) # @return [Idl::FunctionBodyAst] The abstract syntax tree of the sw_write() function # @return [nil] If there is no sw_write() function - # @param cfg_arch [ConfiguredArchitecture] An architecture definition + # @param symtab [Idl::SymbolTable] The symbol table def sw_write_ast(symtab) raise ArgumentError, "Argument should be a symtab" unless symtab.is_a?(Idl::SymbolTable) @@ -520,7 +520,7 @@ def sw_write_ast(symtab) return nil if @data["sw_write(csr_value)"].nil? # now, parse the function - @sw_write_ast = symtab.cfg_arch.idl_compiler.compile_func_body( + @sw_write_ast = symtab.design.idl_compiler.compile_func_body( @data["sw_write(csr_value)"], return_type: Idl::Type.new(:bits, width: 128), # big int to hold special return values name: "CSR[#{csr.name}].#{name}.sw_write(csr_value)", @@ -538,17 +538,17 @@ def sw_write_ast(symtab) # @return [Idl::FunctionBodyAst] The abstract syntax tree of the sw_write() function, type checked and pruned # @return [nil] if there is no sw_write() function # @param effective_xlen [Integer] effective xlen, needed because fields can change in different bases - # @param cfg_arch [ConfiguredArchitecture] A configuration - def pruned_sw_write_ast(cfg_arch, effective_xlen) + # @param design [Design] The design + def pruned_sw_write_ast(design, effective_xlen) @pruned_sw_write_asts ||= {} - ast = @pruned_sw_write_asts[cfg_arch.name] + ast = @pruned_sw_write_asts[design.name] return ast unless ast.nil? return nil unless @data.key?("sw_write(csr_value)") - raise ArgumentError, "cfg_arch must be configured to prune" if cfg_arch.unconfigured? + raise ArgumentError, "design must be configured to prune" if design.unconfigured? - symtab = cfg_arch.symtab.global_clone + symtab = design.symtab.global_clone symtab.push(ast) # all CSR instructions are 32-bit symtab.add( @@ -561,17 +561,17 @@ def pruned_sw_write_ast(cfg_arch, effective_xlen) ) symtab.add( "csr_value", - Idl::Var.new("csr_value", csr.bitfield_type(cfg_arch, effective_xlen)) + Idl::Var.new("csr_value", csr.bitfield_type(design, effective_xlen)) ) - ast = type_checked_sw_write_ast(cfg_arch.symtab, effective_xlen) + ast = type_checked_sw_write_ast(design.symtab, effective_xlen) ast = ast.prune(symtab) raise "Symbol table didn't come back at global + 1" unless symtab.levels == 2 - ast.freeze_tree(cfg_arch.symtab) + ast.freeze_tree(design.symtab) - cfg_arch.idl_compiler.type_check( + design.idl_compiler.type_check( ast, symtab, "CSR[#{name}].sw_write(csr_value)" @@ -580,13 +580,13 @@ def pruned_sw_write_ast(cfg_arch, effective_xlen) symtab.pop symtab.release - @pruned_sw_write_asts[cfg_arch.name] = ast + @pruned_sw_write_asts[design.name] = ast end - # @param cfg_arch [ConfiguredArchitecture] A config. May be nil if the location is not configturation-dependent + # @param design [Design] The design. May be nil if the location is not design-dependent # @param effective_xlen [Integer] The effective xlen, needed since some fields change location with XLEN. If the field location is not determined by XLEN, then this parameter can be nil # @return [Range] the location within the CSR as a range (single bit fields will be a range of size 1) - def location(cfg_arch, effective_xlen = nil) + def location(design, effective_xlen = nil) key = if @data.key?("location") "location" @@ -599,14 +599,14 @@ def location(cfg_arch, effective_xlen = nil) raise "Missing location for #{csr.name}.#{name} (#{key})?" unless @data.key?(key) if @data[key].is_a?(Integer) - csr_length = csr.length(cfg_arch, effective_xlen || @data["base"]) + csr_length = csr.length(design, effective_xlen || @data["base"]) if csr_length.nil? # we don't know the csr length for sure, so we can only check again max_length - if @data[key] > csr.max_length(cfg_arch) - raise "Location (#{key} = #{@data[key]}) is past the max csr length (#{csr.max_length(cfg_arch)}) in #{csr.name}.#{name}" + if @data[key] > csr.max_length(design) + raise "Location (#{key} = #{@data[key]}) is past the max csr length (#{csr.max_length(design)}) in #{csr.name}.#{name}" end elsif @data[key] > csr_length - raise "Location (#{key} = #{@data[key]}) is past the csr length (#{csr.length(cfg_arch, effective_xlen)}) in #{csr.name}.#{name}" + raise "Location (#{key} = #{@data[key]}) is past the csr length (#{csr.length(design, effective_xlen)}) in #{csr.name}.#{name}" end @data[key]..@data[key] @@ -614,11 +614,11 @@ def location(cfg_arch, effective_xlen = nil) e, s = @data[key].split("-").map(&:to_i) raise "Invalid location" if s > e - csr_length = csr.length(cfg_arch, effective_xlen || @data["base"]) + csr_length = csr.length(design, effective_xlen || @data["base"]) if csr_length.nil? # we don't know the csr length for sure, so we can only check again max_length - if e > csr.max_length(cfg_arch) - raise "Location (#{key} = #{@data[key]}) is past the max csr length (#{csr.max_length(cfg_arch)}) in #{csr.name}.#{name}" + if e > csr.max_length(design) + raise "Location (#{key} = #{@data[key]}) is past the max csr length (#{csr.max_length(design)}) in #{csr.name}.#{name}" end elsif e > csr_length raise "Location (#{key} = #{@data[key]}) is past the csr length (#{csr_length}) in #{csr.name}.#{name}" @@ -641,11 +641,11 @@ def defined_in_base64? = @data["base"].nil? || @data["base"] == 64 # @return [Boolean] Whether or not this field exists for any XLEN def defined_in_all_bases? = @data["base"].nil? - # @param cfg_arch [ConfiguredArchitecture] A config. May be nil if the width of the field is not configuration-dependent + # @param design [Design] The design. May be nil if the width of the field is not design-dependent # @param effective_xlen [Integer] The effective xlen, needed since some fields change location with XLEN. If the field location is not determined by XLEN, then this parameter can be nil # @return [Integer] Number of bits in the field - def width(cfg_arch, effective_xlen) - location(cfg_arch, effective_xlen).size + def width(design, effective_xlen) + location(design, effective_xlen).size end def location_cond32 @@ -675,14 +675,14 @@ def location_cond64 end # @return [String] Pretty-printed location string - def location_pretty(cfg_arch, effective_xlen = nil) + def location_pretty(design, effective_xlen = nil) derangeify = proc { |loc| next loc.min.to_s if loc.size == 1 "#{loc.max}:#{loc.min}" } - if dynamic_location?(cfg_arch) + if dynamic_location?(design) condition = case csr.priv_mode when "M" @@ -697,14 +697,14 @@ def location_pretty(cfg_arch, effective_xlen = nil) if effective_xlen.nil? <<~LOC - * #{derangeify.call(location(cfg_arch, 32))} when #{condition.sub('%%', '0')} - * #{derangeify.call(location(cfg_arch, 64))} when #{condition.sub('%%', '1')} + * #{derangeify.call(location(design, 32))} when #{condition.sub('%%', '0')} + * #{derangeify.call(location(design, 64))} when #{condition.sub('%%', '1')} LOC else - derangeify.call(location(cfg_arch, effective_xlen)) + derangeify.call(location(design, effective_xlen)) end else - derangeify.call(location(cfg_arch, cfg_arch.mxlen)) + derangeify.call(location(design, design.mxlen)) end end @@ -758,7 +758,7 @@ def location_pretty(cfg_arch, effective_xlen = nil) }.freeze # @return [String] Long description of the field type - def type_desc(cfg_arch) - TYPE_DESC_MAP[type(cfg_arch.symtab)] + def type_desc(design) + TYPE_DESC_MAP[type(design.symtab)] end end diff --git a/lib/arch_obj_models/obj.rb b/lib/arch_obj_models/database_obj.rb similarity index 92% rename from lib/arch_obj_models/obj.rb rename to lib/arch_obj_models/database_obj.rb index 1383a256c..eaea68634 100644 --- a/lib/arch_obj_models/obj.rb +++ b/lib/arch_obj_models/database_obj.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -# base for any object representation of the Architecture Definition +# Base class for any object representation of the Architecture. # does two things: # # 1. Makes the raw data for the object accessible via [] @@ -25,7 +25,29 @@ # is warranted, e.g., the CSR Field 'alias' returns a CsrFieldAlias object # instead of a simple string class DatabaseObject - # Exception raised when there is a problem with a schema file + attr_reader :data, :data_path, :name, :long_name, :description + + # @return [Architecture] Architectural standards + attr_reader :arch + + def kind = @data["kind"] + + # @param data [Hash] Hash with fields to be added + # @param data_path [Pathname] Path to the data file + # @param arch [Architecture] Architectural standards + def initialize(data, data_path, arch) + raise ArgumentError, "Bad data" unless data.is_a?(Hash) + raise ArgumentError, "Need Architecture class but it's a #{arch.class}" unless arch.is_a?(Architecture) + + @data = data + @data_path = data_path + @arch = arch + @name = data["name"] + @long_name = data["long_name"] + @description = data["description"] + end + + # Exception raised when there is a problem with a schema file class SchemaError < ::StandardError # result from JsonSchemer.validate attr_reader :result @@ -90,20 +112,7 @@ def initialize(path, result) end end - attr_reader :data, :data_path, :name, :long_name, :description - - # @return [Architecture] If only a specification (no config) is known - # @return [ConfiguredArchitecture] If a specification and config is known - # @return [nil] If neither is known - attr_reader :arch # Use when Architecture class is sufficient - - # @return [ConfiguredArchitecture] If a specification and config is known - # @return [nil] Otherwise - attr_reader :cfg_arch # Use when extra stuff provided by ConfiguredArchitecture is required - - def kind = @data["kind"] - - @@schemas ||= {} + @@schemas ||= {} @@schema_ref_resolver ||= proc do |pattern| if pattern.to_s =~ /^http/ JSON.parse(Net::HTTP.get(pattern)) @@ -180,25 +189,7 @@ def __source # @return [String] An extension name # @return [Array(String, Number)] An extension name and versions # @return [Array<*>] A list of extension names or extension names and versions - def definedBy - @data["definedBy"] - end - - # @param data [Hash] Hash with fields to be added - # @param data_path [Pathname] Path to the data file - def initialize(data, data_path, arch: nil) - raise ArgumentError, "Bad data" unless data.is_a?(Hash) - - @data = data - @data_path = data_path - if arch.is_a?(ConfiguredArchitecture) - @cfg_arch = arch - end - @arch = arch - @name = data["name"] - @long_name = data["long_name"] - @description = data["description"] - end + def definedBy = @data["definedBy"] def inspect self.class.name @@ -218,7 +209,7 @@ def key?(k) = @data.key?(k) # @overload defined_by?(ext_name, ext_version) # @param ext_name [#to_s] An extension name # @param ext_version [#to_s] A specific extension version - # @return [Boolean] Whether or not the instruction is defined by extesion `ext`, version `version` + # @return [Boolean] Whether or not the instruction is defined by extension `ext`, version `version` # @overload defined_by?(ext_version) # @param ext_version [ExtensionVersion] An extension version # @return [Boolean] Whether or not the instruction is defined by ext_version @@ -376,7 +367,7 @@ def <=>(other) end end -# represents a JSON Schema compoisition, e.g.: +# represents a JSON Schema composition, e.g.: # # anyOf: # - oneOf: @@ -390,7 +381,7 @@ def initialize(composition_hash, arch) raise ArgumentError, "composition_hash is nil" if composition_hash.nil? unless is_a_condition?(composition_hash) - raise ArgumentError, "Expecting a JSON schema comdition (got #{composition_hash})" + raise ArgumentError, "Expecting a JSON schema condition (got #{composition_hash})" end @hsh = composition_hash @@ -434,6 +425,12 @@ def to_asciidoc(cond = @hsh, indent = 0) end end + # @overload is_a_condition?(hsh) + # @param hsh [String] Extension name (case sensitive) + # @return [Boolean] True + # @overload is_a_condition?(hsh) + # @param hsh [Hash] Extension name (case sensitive) + # @return [Boolean] True if hash is a JSON schema condition def is_a_condition?(hsh) case hsh when String @@ -469,13 +466,13 @@ def is_a_condition?(hsh) def first_requirement(req = @hsh) case req when String - ExtensionRequirement.new(req, arch: @arch) + ExtensionRequirement.new(req, @arch) when Hash if req.key?("name") if req["version"].nil? - ExtensionRequirement.new(req["name"], arch: @arch) + ExtensionRequirement.new(req["name"], @arch) else - ExtensionRequirement.new(req["name"], req["version"], arch: @arch) + ExtensionRequirement.new(req["name"], req["version"], @arch) end else first_requirement(req[req.keys[0]]) @@ -514,7 +511,7 @@ def minimize(hsh = @hsh) min_ary = hsh["oneOf"].map { |element| minimize(element) } key = "oneOf" end - min_ary = min_ary.uniq! + min_ary = min_ary.uniq if min_ary.size == 1 min_ary.first else @@ -531,14 +528,14 @@ def to_rb_helper(hsh) if hsh.key?("name") if hsh.key?("version") if hsh["version"].is_a?(String) - "(yield ExtensionRequirement.new('#{hsh["name"]}', '#{hsh["version"]}', arch: @arch))" + "(yield ExtensionRequirement.new('#{hsh["name"]}', '#{hsh["version"]}', @arch))" elsif hsh["version"].is_a?(Array) - "(yield ExtensionRequirement.new('#{hsh["name"]}', #{hsh["version"].map { |v| "'#{v}'" }.join(', ')}, arch: @arch))" + "(yield ExtensionRequirement.new('#{hsh["name"]}', #{hsh["version"].map { |v| "'#{v}'" }.join(', ')}, @arch))" else raise "unexpected" end else - "(yield ExtensionRequirement.new('#{hsh["name"]}', arch: @arch))" + "(yield ExtensionRequirement.new('#{hsh["name"]}', @arch))" end else key = hsh.keys[0] @@ -559,7 +556,7 @@ def to_rb_helper(hsh) end end else - "(yield ExtensionRequirement.new('#{hsh}', arch: @arch))" + "(yield ExtensionRequirement.new('#{hsh}', @arch))" end end diff --git a/lib/arch_obj_models/extension.rb b/lib/arch_obj_models/extension.rb index 6d45ff47a..a0dc61c49 100644 --- a/lib/arch_obj_models/extension.rb +++ b/lib/arch_obj_models/extension.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "obj" +require_relative "database_obj" require_relative "schema" require_relative "../version" @@ -77,7 +77,7 @@ def defined_in_extension_version?(version) return true if @data.dig("when", "version").nil? @exts.any? do |ext| - ExtensionRequirement.new(ext.name, @data["when"]["version"], arch: ext.arch).satisfied_by?(version) + ExtensionRequirement.new(ext.name, @data["when"]["version"], ext.arch).satisfied_by?(version) end end @@ -195,9 +195,9 @@ def params # @return [Array] Array of extensions implied by any version of this extension meeting version_requirement def implies(version_requirement = nil) if version_requirement.nil? - return [] unless ExtensionRequirement.new(@new, arch: @arch).satisfied_by?(max_version.version) + return [] unless ExtensionRequirement.new(@new, @arch).satisfied_by?(max_version.version) else - return [] unless ExtensionRequirement.new(@new, version_requirement, arch: @arch).satisfied_by?(max_version.version) + return [] unless ExtensionRequirement.new(@new, version_requirement, @arch).satisfied_by?(max_version.version) end max_version.implications @@ -208,15 +208,15 @@ def conflicts return [] if @data["conflicts"].nil? if @data["conflicts"].is_a?(String) - [ExtensionRequirement.new(@data["conflicts"], arch: @arch)] + [ExtensionRequirement.new(@data["conflicts"], @arch)] elsif @data["conflicts"].is_a?(Hash) - [ExtensionRequirement.new(@data["conflicts"]["name"], @data["conflicts"]["version"], arch: @arch)] + [ExtensionRequirement.new(@data["conflicts"]["name"], @data["conflicts"]["version"], @arch)] elsif @data["conflicts"].is_a?(Array) @data["conflicts"].map do |conflict| if conflict.is_a?(String) - ExtensionRequirement.new(conflict, arch: @arch) + ExtensionRequirement.new(conflict, @arch) elsif conflict.is_a?(Array) - ExtensionRequirement.new(conflict["name"], conflict["version"], arch: @arch) + ExtensionRequirement.new(conflict["name"], conflict["version"], @arch) else raise "Invalid conflicts data: #{conflict.inspect}" end @@ -244,28 +244,29 @@ def csrs # # @param symtab [Idl::SymbolTable] The evaluation context # @return [Array] Array of IDL functions reachable from any instruction or CSR in the extension - def reachable_functions(symtab) - @reachable_functions ||= {} - - return @reachable_functions[symtab] unless @reachable_functions[symtab].nil? - - funcs = [] - - puts "Finding all reachable functions from extension #{name}" - - instructions.each do |inst| - funcs += inst.reachable_functions(symtab, 32) if inst.defined_in_base?(32) - funcs += inst.reachable_functions(symtab, 64) if inst.defined_in_base?(64) - end - - # The one place in this file that needs a ConfiguredArchitecture object instead of just Architecture. - raise "In #{name}, need to provide ConfiguredArchitecture" if cfg_arch.nil? - csrs.each do |csr| - funcs += csr.reachable_functions(cfg_arch) - end - - @reachable_functions[symtab] = funcs.uniq - end + # + # The one place in this file that actually needs a Design object instead of just Architecture. + # Doesn't seem to be called anywhere. If there is somewhere that calls this, pass it a Design object. +# def reachable_functions(symtab) +# @reachable_functions ||= {} +# +# return @reachable_functions[symtab] unless @reachable_functions[symtab].nil? +# +# funcs = [] +# +# puts "Finding all reachable functions from extension #{name}" +# +# instructions.each do |inst| +# funcs += inst.reachable_functions(symtab, 32) if inst.defined_in_base?(32) +# funcs += inst.reachable_functions(symtab, 64) if inst.defined_in_base?(64) +# end +# +# csrs.each do |csr| +# funcs += csr.reachable_functions(design) +# end +# +# @reachable_functions[symtab] = funcs.uniq +# end end # A specific version of an extension @@ -449,7 +450,7 @@ def implications @implications end - # @return [Array] List of extension versions that are implied by with this ExtensionVersion + # @return [Array] List of extension versions that are implied by with this ExtensionVersion. # This list is transitive; if an implication I1 implies another extension I2, # both I1 and I2 are in the returned list def transitive_implications @@ -483,7 +484,7 @@ def transitive_implications # @param ext_version_requirements [String,Array] Extension version requirements # @return [Boolean] whether or not this ExtensionVersion is named `ext_name` and satifies the version requirements def satisfies?(ext_name, *ext_version_requirements) - ExtensionRequirement.new(ext_name, ext_version_requirements).satisfied_by?(self) + ExtensionRequirement.new(ext_name, ext_version_requirements, arch).satisfied_by?(self) end # sorts extension by name, then by version @@ -508,7 +509,7 @@ def implemented_csrs end end - # @return [Array] the list of insts implemented by this extension version (may be empty) + # @return [Array] the list of insts implemented by this extension version (may be empty) def implemented_instructions return @implemented_instructions unless @implemented_instructions.nil? @@ -687,27 +688,23 @@ def to_s "#{name} " + requirement_specs_to_s end - # @return [Extension] The extension that this requirement is for - def extension - return @extension unless @extension.nil? - - raise "Cannot get extension; arch was not initialized" if @arch.nil? - - @extension = @arch.extension(@name) - end + # @return [Extension] The extension corresponding to this requirement + def extension = @ext # @param name [#to_s] Extension name # @param requirements [String] Single requirement # @param requirements [Array] List of requirements, all of which must hold - # @param arch [Architecture] - def initialize(name, *requirements, arch: nil, note: nil, req_id: nil, presence: nil) + # @param arch [Architecture] The architecture database + def initialize(name, *requirements, arch, note: nil, req_id: nil, presence: nil) raise ArgumentError, "For #{name}, arch not allowed to be nil" if arch.nil? raise ArgumentError, "For #{name}, Architecture is required" unless arch.is_a?(Architecture) @name = name.to_s.freeze @arch = arch @ext = @arch.extension(@name) - raise ArgumentError, "Could not find extension named '#{@name}'" if @ext.nil? + if @ext.nil? + raise ArgumentError, "Could not find extension named '#{@name}'" + end requirements = if requirements.empty? @@ -724,10 +721,9 @@ def initialize(name, *requirements, arch: nil, note: nil, req_id: nil, presence: # @return [Array] The list of extension versions that satisfy this extension requirement def satisfying_versions - ext = @arch.extension(@name) - return [] if ext.nil? + return @satisfying_versions unless @satisfying_versions.nil? - ext.versions.select { |v| satisfied_by?(v) } + @satisfying_versions = @ext.versions.select { |v| satisfied_by?(v) } end # @overload diff --git a/lib/arch_obj_models/instruction.rb b/lib/arch_obj_models/instruction.rb index 8528d28e8..ff469ca8a 100644 --- a/lib/arch_obj_models/instruction.rb +++ b/lib/arch_obj_models/instruction.rb @@ -2,7 +2,7 @@ require 'ruby-prof-flamegraph' -require_relative "obj" +require_relative "database_obj" # model of a specific instruction in a specific base (RV32/RV64) @@ -137,25 +137,25 @@ def fill_symtab(global_symtab, effective_xlen, ast) def pruned_operation_ast(global_symtab, effective_xlen) @pruned_asts ||= {} - cfg_arch = global_symtab.cfg_arch + design = global_symtab.design - pruned_ast = @pruned_asts[cfg_arch.name] + pruned_ast = @pruned_asts[design.name] return pruned_ast unless pruned_ast.nil? return nil unless @data.key?("operation()") - type_checked_ast = type_checked_operation_ast(cfg_arch.idl_compiler, global_symtab, effective_xlen) + type_checked_ast = type_checked_operation_ast(design.idl_compiler, global_symtab, effective_xlen) print "Pruning #{name} operation()..." pruned_ast = type_checked_ast.prune(fill_symtab(global_symtab, effective_xlen, type_checked_ast)) puts "done" pruned_ast.freeze_tree(global_symtab) - cfg_arch.idl_compiler.type_check( + design.idl_compiler.type_check( pruned_ast, fill_symtab(global_symtab, effective_xlen, pruned_ast), "#{name}.operation() (pruned)" ) - @pruned_asts[cfg_arch.name] = pruned_ast + @pruned_asts[design.name] = pruned_ast end # @param symtab [Idl::SymbolTable] Symbol table with global scope populated @@ -166,7 +166,7 @@ def reachable_functions(symtab, effective_xlen) [] else # RubyProf.start - ast = type_checked_operation_ast(symtab.cfg_arch.idl_compiler, symtab, effective_xlen) + ast = type_checked_operation_ast(symtab.design.idl_compiler, symtab, effective_xlen) print "Determining reachable funcs from #{name}..." fns = ast.reachable_functions(fill_symtab(symtab, effective_xlen, ast)) puts "done" @@ -186,7 +186,7 @@ def reachable_exceptions(symtab, effective_xlen) else # pruned_ast = pruned_operation_ast(symtab) # type_checked_operation_ast() - type_checked_ast = type_checked_operation_ast(symtab.cfg_arch.idl_compiler, symtab, effective_xlen) + type_checked_ast = type_checked_operation_ast(symtab.design.idl_compiler, symtab, effective_xlen) symtab = fill_symtab(symtab, effective_xlen, pruned_ast) type_checked_ast.reachable_exceptions(symtab) end @@ -214,7 +214,7 @@ def reachable_exceptions_str(symtab, effective_xlen=nil) else etype = symtab.get("ExceptionCode") if effective_xlen.nil? - if symtab.cfg_arch.multi_xlen? + if symtab.design.multi_xlen? if base.nil? ( pruned_ast = pruned_operation_ast(symtab, 32) @@ -241,7 +241,7 @@ def reachable_exceptions_str(symtab, effective_xlen=nil) e end else - effective_xlen = symtab.cfg_arch.mxlen + effective_xlen = symtab.design.mxlen pruned_ast = pruned_operation_ast(symtab, effective_xlen) print "Determining reachable exceptions from #{name}..." e = mask_to_array(pruned_ast.reachable_exceptions(fill_symtab(symtab, effective_xlen, pruned_ast))).map { |code| @@ -644,7 +644,7 @@ def operation_ast(symtab) return nil if @data["operation()"].nil? # now, parse the operation - @operation_ast = symtab.cfg_arch.idl_compiler.compile_inst_operation( + @operation_ast = symtab.design.idl_compiler.compile_inst_operation( self, symtab:, input_file: @data["$source"], @@ -719,7 +719,7 @@ def rv64? def excluded_by?(*args) return false if @data["excludedBy"].nil? - excluded_by = SchemaCondition.new(@data["excludedBy"], @cfg_arch) + excluded_by = SchemaCondition.new(@data["excludedBy"], @arch) ext_ver = if args.size == 1 @@ -730,7 +730,7 @@ def excluded_by?(*args) raise ArgumentError, "First parameter must be an extension name" unless args[0].respond_to?(:to_s) raise ArgumentError, "Second parameter must be an extension version" unless args[1].respond_to?(:to_s) - ExtensionVersion.new(args[0], args[1], @cfg_arch) + ExtensionVersion.new(args[0], args[1], @arch) end excluded_by.satisfied_by? do |r| @@ -738,19 +738,19 @@ def excluded_by?(*args) end end - # @param cfg_arch [ConfiguredArchitecture] The architecture definition + # @param design [Design] The design # @return [Boolean] whether or not the instruction is implemented given the supplies config options - def exists_in_cfg?(cfg_arch) - if cfg_arch.fully_configured? - (@data["base"].nil? || (cfg_arch.possible_xlens.include? @data["base"])) && - cfg_arch.implemented_extensions.any? { |e| defined_by?(e) } && - cfg_arch.implemented_extensions.none? { |e| excluded_by?(e) } + def exists_in_cfg?(design) + if design.fully_configured? + (@data["base"].nil? || (design.possible_xlens.include? @data["base"])) && + design.implemented_extensions.any? { |e| defined_by?(e) } && + design.implemented_extensions.none? { |e| excluded_by?(e) } else - raise "unexpected cfg_arch type" unless cfg_arch.partially_configured? + raise "unexpected design type" unless design.partially_configured? - (@data["base"].nil? || (cfg_arch.possible_xlens.include? @data["base"])) && - cfg_arch.prohibited_extensions.none? { |e| defined_by?(e) } && - cfg_arch.mandatory_extensions.none? { |e| excluded_by?(e) } + (@data["base"].nil? || (design.possible_xlens.include? @data["base"])) && + design.prohibited_extensions.none? { |e| defined_by?(e) } && + design.mandatory_extensions.none? { |e| excluded_by?(e) } end end end diff --git a/lib/arch_obj_models/manual.rb b/lib/arch_obj_models/manual.rb index 396cb041d..473c0024e 100644 --- a/lib/arch_obj_models/manual.rb +++ b/lib/arch_obj_models/manual.rb @@ -2,7 +2,7 @@ require "asciidoctor" -require_relative "obj" +require_relative "database_obj" class Manual < DatabaseObject def versions @@ -61,8 +61,10 @@ class ManualVolume # @return [ManualVersion] The version this volume belongs to attr_reader :version - def cfg_arch = version.cfg_arch + def arch = version.arch + # @param data [Hash] Data from YAML file + # @param version [ManualVersion] def initialize(data, version) @data = data @version = version @@ -93,13 +95,13 @@ def extensions return @extensions if @data["extensions"].nil? @data["extensions"].each do |ext| - ext_obj = cfg_arch.extension(ext[0]) + ext_obj = arch.extension(ext[0]) if ext_obj.nil? warn "Extension '#{ext[0]}' is not in the database" next end - ext_ver = ExtensionVersion.new(ext[0], ext[1], cfg_arch) + ext_ver = ExtensionVersion.new(ext[0], ext[1], arch) unless ext_obj.versions.any? { |known_ver| known_ver == ext_ver } warn "Extension '#{ext[0]}', version '#{ext[1]}' is not defined in the database" next @@ -171,7 +173,7 @@ def instructions @instructions = [] extensions.each do |ext| - ext_obj = @cfg_arch.extension(ext.name) + ext_obj = @arch.extension(ext.name) ext_obj.instructions.each do |inst| @instructions << inst end @@ -185,7 +187,7 @@ def csrs @csrs = [] extensions.each do |ext| - ext_obj = @cfg_arch.extension(ext.name) + ext_obj = @arch.extension(ext.name) ext_obj.csrs.each do |csr| @csrs << csr end diff --git a/lib/arch_obj_models/portfolio.rb b/lib/arch_obj_models/portfolio.rb index 52c95dec2..7b6fdb4e5 100644 --- a/lib/arch_obj_models/portfolio.rb +++ b/lib/arch_obj_models/portfolio.rb @@ -10,8 +10,9 @@ # A variable name with a "_data" suffix indicates it is the raw hash data from the portfolio YAML file. require "tmpdir" +require "forwardable" -require_relative "obj" +require_relative "database_obj" require_relative "schema" ################## @@ -42,6 +43,139 @@ def portfolio_classes_matching_portfolio_kind_and_processor_kind end end +################## +# PortfolioGroup # +################## + +# A portfolio group consists of a one or more profiles. +# Contains common code to aggregrate multiple portfolios for Profile Releases and PortfolioDesign classes. +# This not the base class for ProfileRelease but it does contain one of these. +# This is not a DatabaseObject. +class PortfolioGroup + extend Forwardable + + # Calls to these methods on PortfolioGroup are handled by the Array class. + # Avoids having to call portfolio_group.portfolios. (just call portfolio_group.). + def_delegators :@portfolios, :each, :map, :select + + # @param portfolios [Array] + def initialize(portfolios) + @portfolios = portfolios + end + + # @return [Array] All portfolios in this portfolio group + def portfolios = @portfolios + + # @return [Hash] Fully-constrained parameter values (those with just one possible value for this design). + def param_values + return @param_values unless @param_values.nil? + + @param_values = {} + portfolios.each do |portfolio| + @param_values.merge!(portfolio.all_in_scope_ext_params.select(&:single_value?).map { |p| [p.name, p.value] }.to_h) + end + + @param_values + end + + # @return [Array] Sorted list of all extension requirements listed by the group. + def in_scope_ext_reqs + return @in_scope_ext_reqs unless @in_scope_ext_reqs.nil? + + @in_scope_ext_reqs = [] + portfolios.each do |portfolio| + @in_scope_ext_reqs += portfolio.in_scope_ext_reqs + end + + @in_scope_ext_reqs.uniq(&:name).sort_by(&:name) + end + + # @return [Array] Sorted list of all mandatory extension requirements listed by the group. + def mandatory_ext_reqs + return @mandatory_ext_reqs unless @mandatory_ext_reqs.nil? + + @mandatory_ext_reqs = [] + portfolios.each do |portfolio| + @mandatory_ext_reqs += portfolio.mandatory_ext_reqs + end + + @mandatory_ext_reqs.uniq(&:name).sort_by(&:name) + end + + # @return [Array] Sorted list of all optional extension requirements listed by the group. + def optional_ext_reqs + return @optional_ext_reqs unless @optional_ext_reqs.nil? + + @optional_ext_reqs = [] + portfolios.each do |portfolio| + @optional_ext_reqs += portfolio.optional_ext_reqs + end + + @optional_ext_reqs.uniq(&:name).sort_by(&:name) + end + + # @return [Array] Sorted list of all mandatory or optional extensions referenced by the group. + def in_scope_extensions + return @in_scope_extensions unless @in_scope_extensions.nil? + + @in_scope_extensions = [] + portfolios.each do |portfolio| + @in_scope_extensions += portfolio.in_scope_extensions + end + + @in_scope_extensions.uniq(&:name).sort_by(&:name) + end + + # @return [Array] Sorted list of all instructions associated with extensions listed as + # mandatory or optional in portfolio. Uses instructions provided by the + # minimum version of the extension that meets the extension requirement. + def in_scope_instructions + return @in_scope_instructions unless @in_scope_instructions.nil? + + @in_scope_instructions = [] + portfolios.each do |portfolio| + @in_scope_instructions += portfolio.in_scope_instructions + end + + @in_scope_instructions.uniq(&:name).sort_by(&:name) + end + + # @return [Array] Unsorted list of all CSRs associated with extensions listed as + # mandatory or optional in portfolio. Uses CSRs provided by the + # minimum version of the extension that meets the extension requirement. + def in_scope_csrs + return @in_scope_csrs unless @in_scope_csrs.nil? + + @in_scope_csrs = [] + portfolios.each do |portfolio| + @in_scope_csrs += portfolio.in_scope_csrs + end + + @in_scope_csrs.uniq(&:name) + end + + # @return [String] Given an extension +ext_name+, return the presence as a string. + # Returns the greatest presence string across all profiles in the group. + # If the extension name isn't found in the release, return "-". + def extension_presence(ext_name) + greatest_presence = nil + + portfolios.each do |portfolio| + presence = portfolio.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 + ############# # Portfolio # ############# @@ -52,7 +186,7 @@ class Portfolio < DatabaseObject # @param obj_yaml [Hash] Contains contents of Portfolio yaml file (put in @data) # @param data_path [String] Path to yaml file # @param arch [Architecture] Entire database of RISC-V architecture standards - def initialize(obj_yaml, yaml_path, arch: nil) + def initialize(obj_yaml, yaml_path, arch) super # Calls parent class with same args I got end @@ -62,6 +196,9 @@ def introduction = @data["introduction"] # @return [String] Large enough to need its own heading (generally one level deeper than the "introduction"). def description = @data["description"] + # @return [Integer] 32 or 64 + def base = @data["base"] + # @return [Gem::Version] Semantic version of the Portfolio def version = Gem::Version.new(@data["version"]) @@ -128,7 +265,7 @@ def optional_ext_reqs = in_scope_ext_reqs(ExtensionPresence.optional) def optional_type_ext_reqs = in_scope_ext_reqs(ExtensionPresence.optional) # @param desired_presence [String, Hash, ExtensionPresence] - # @return [Array] - # Extensions with their portfolio information. + # @return [Array] Sorted list of extensions with their portfolio information. # If desired_presence is provided, only returns extensions with that presence. # If desired_presence is a String, only the presence portion of an ExtensionPresence is compared. def in_scope_ext_reqs(desired_presence = nil) @@ -170,11 +307,11 @@ def in_scope_ext_reqs(desired_presence = nil) in_scope_ext_reqs << if ext_data.key?("version") ExtensionRequirement.new( - ext_name, ext_data["version"], arch: @arch, + ext_name, ext_data["version"], @arch, presence: actual_presence_obj, note: ext_data["note"], req_id: "REQ-EXT-#{ext_name}") else ExtensionRequirement.new( - ext_name, arch: @arch, + ext_name, @arch, presence: actual_presence_obj, note: ext_data["note"], req_id: "REQ-EXT-#{ext_name}") end end @@ -183,29 +320,51 @@ def in_scope_ext_reqs(desired_presence = nil) raise "One or more extensions referenced by #{name} missing in database" if missing_ext - in_scope_ext_reqs + in_scope_ext_reqs.sort_by!(&:name) + end + + # @return [Array] Sorted list of all mandatory or optional extensions in portfolio. + # Each extension can have multiple versions (contains ExtensionVersion array). + def in_scope_extensions + return @in_scope_extensions unless @in_scope_extensions.nil? + + @in_scope_extensions = in_scope_ext_reqs.map do |ext_req| + ext_req.extension + end.reject(&:nil?) # Filter out extensions that don't exist yet. + + @in_scope_extensions.sort_by!(&:name) + end + + # @return [ExtensionVersion] List of all mandatory or optional extensions listed in portfolio. + # The minimum version of each extension that satisfies the extension requirements is provided. + def in_scope_min_satisfying_extension_versions + return @in_scope_min_satisfying_extension_versions unless @in_scope_min_satisfying_extension_versions.nil? + + @in_scope_min_satisfying_extension_versions = in_scope_ext_reqs.map do |ext_req| + ext_req.satisfying_versions.min + end.reject(&:nil?) # Filter out extensions that don't exist yet. + + @in_scope_min_satisfying_extension_versions 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. + # mandatory or optional in portfolio. Uses instructions provided by the + # minimum version of the extension that meets the extension requirement. def in_scope_instructions return @in_scope_instructions unless @in_scope_instructions.nil? - # XXX - # @in_scope_instructions = in_scope_ext_reqs.map { |ext_req| ext_req.instructions }.flatten.uniq.sort - @in_scope_instructions = in_scope_extensions.map { |ext| ext.instructions }.flatten.uniq.sort + @in_scope_instructions = + in_scope_min_satisfying_extension_versions.map {|ext_ver| ext_ver.implemented_instructions }.flatten.uniq.sort end - # @return [Array] List of all extensions listed in portfolio. - def in_scope_extensions - return @in_scope_extensions unless @in_scope_extensions.nil? - - @in_scope_extensions = in_scope_ext_reqs.map do |ext_req| - arch.extension(ext_req.name) - end.reject(&:nil?) # Filter out extensions that don't exist yet. + # @return [Array] Unsorted list of all CSRs associated with extensions listed as + # mandatory or optional in portfolio. Uses CSRs provided by the + # minimum version of the extension that meets the extension requirement. + def in_scope_csrs + return @in_scope_csrs unless @in_scope_csrs.nil? - @in_scope_extensions + @in_scope_csrs = + in_scope_min_satisfying_extension_versions.map {|ext_ver| ext_ver.implemented_csrs }.flatten.uniq end # @return [Boolean] Does the profile differentiate between different types of optional. @@ -225,39 +384,6 @@ def uses_optional_types? @uses_optional_types end - # Called by rakefile when generating a portfolio. - # 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? - - # build up a config for the portfolio - config_data = { - "$schema" => "config_schema.json", - "type" => "partially configured", - "kind" => "architecture configuration", - "name" => name, - "description" => "A partially configured architecture definition corresponding to the #{name} portfolio.", - "mandatory_extensions" => mandatory_ext_reqs.map do |ext_req| - { - "name" => ext_req.name, - "version" => ext_req.requirement_specs.map(&:to_s) - } - end, - "params" => all_in_scope_ext_params.select(&:single_value?).map { |p| [p.name, p.value] }.to_h - } - - # TODO: Add list of prohibited_extensions - - @generated_cfg_arch = - Dir.mktmpdir do |dir| - FileUtils.mkdir("#{dir}/#{name}") - File.write("#{dir}/#{name}/cfg.yaml", YAML.safe_dump(config_data, permitted_classes: [Date])) - @generated_cfg_arch = ConfiguredArchitecture.new(name, @arch.path, cfg_path: dir) - end - end - ################################### # InScopeExtensionParameter Class # ################################### @@ -340,7 +466,7 @@ def all_in_scope_ext_params next unless ext.versions.any? do |ext_ver| ver_req = ext_data["version"] || ">= #{ext.min_version.version_spec}" - ExtensionRequirement.new(ext_name, ver_req, arch: @arch).satisfied_by?(ext_ver) && + ExtensionRequirement.new(ext_name, ver_req, @arch).satisfied_by?(ext_ver) && param.defined_in_extension_version?(ext_ver) end @@ -351,7 +477,8 @@ def all_in_scope_ext_params @all_in_scope_ext_params end - # @return [Array] List of extension parameters from portfolio for given extension. + # @param [ExtensionRequirement] + # @return [Array] Sorted list of extension parameters from portfolio for given extension. # These are always IN SCOPE by definition (since they are listed in the portfolio). def in_scope_ext_params(ext_req) raise ArgumentError, "Expecting ExtensionRequirement" unless ext_req.is_a?(ExtensionRequirement) @@ -382,10 +509,11 @@ def in_scope_ext_params(ext_req) InScopeExtensionParameter.new(ext_param, param_data["schema"], param_data["note"]) end - ext_params + ext_params.sort! end - # @return [Array] Parameters out of scope across all in scope extensions (those listed in the portfolio). + # @return [Array] Sorted list of parameters out of scope across all in scope extensions + # (those listed as mandatory or optional in the portfolio). def all_out_of_scope_params return @all_out_of_scope_params unless @all_out_of_scope_params.nil? @@ -403,16 +531,16 @@ def all_out_of_scope_params @all_out_of_scope_params << param end end - @all_out_of_scope_params + @all_out_of_scope_params.sort! end - # @return [Array] Parameters that are out of scope for named extension. + # @return [Array] Sorted list of parameters that are out of scope for named extension. def out_of_scope_params(ext_name) - all_out_of_scope_params.select{ |param| param.exts.any? { |ext| ext.name == ext_name } } + all_out_of_scope_params.select{ |param| param.exts.any? { |ext| ext.name == ext_name } }.sort end # @return [Array] - # All the in-scope extensions (those in the portfolio) that define this parameter in the database + # Sorted list of all in-scope extensions that define this parameter in the database # and the parameter is in-scope (listed in that extension's list of parameters in the portfolio). def all_in_scope_exts_with_param(param) raise ArgumentError, "Expecting ExtensionParameter" unless param.is_a?(ExtensionParameter) @@ -423,8 +551,8 @@ def all_in_scope_exts_with_param(param) param.exts.each do |ext| found = false - in_scope_extensions.each do |in_scope_ext| - if ext.name == in_scope_ext.name + in_scope_extensions.each do |potential_ext| + if ext.name == potential_ext.name found = true next end @@ -437,11 +565,11 @@ def all_in_scope_exts_with_param(param) end # Return intersection of extension names - exts + exts.sort_by!(&:name) end # @return [Array] - # All the in-scope extensions (those in the portfolio) that define this parameter in the database + # List of all in-scope extensions that define this parameter in the database # but the parameter is out-of-scope (not listed in that extension's list of parameters in the portfolio). def all_in_scope_exts_without_param(param) raise ArgumentError, "Expecting ExtensionParameter" unless param.is_a?(ExtensionParameter) @@ -452,8 +580,8 @@ def all_in_scope_exts_without_param(param) param.exts.each do |ext| found = false - in_scope_extensions.each do |in_scope_ext| - if ext.name == in_scope_ext.name + in_scope_extensions.each do |potential_ext| + if ext.name == potential_ext.name found = true next end @@ -466,7 +594,7 @@ def all_in_scope_exts_without_param(param) end # Return intersection of extension names - exts + exts.sort_by!(&:name) end ############################ diff --git a/lib/arch_obj_models/profile.rb b/lib/arch_obj_models/profile.rb index 05acb3433..b569115c2 100644 --- a/lib/arch_obj_models/profile.rb +++ b/lib/arch_obj_models/profile.rb @@ -25,7 +25,7 @@ def doc_license def profile_releases return @profile_releases unless @profile_releases.nil? - @profile_releases = @cfg_arch.profile_releases.select { |pr| pr.profile_class.name == name } + @profile_releases = @arch.profile_releases.select { |pr| pr.profile_class.name == name } @profile_releases end @@ -37,7 +37,7 @@ def profile_releases_matching_processor_kind matching_classes = portfolio_classes_matching_portfolio_kind_and_processor_kind # Look for all profile releases that are from any of the matching classes. - @profile_releases_matching_processor_kind = @cfg_arch.profile_releases.select { |pr| + @profile_releases_matching_processor_kind = @arch.profile_releases.select { |pr| matching_classes.any? { |matching_class| matching_class.name == pr.profile_class.name } } @@ -48,48 +48,45 @@ def profile_releases_matching_processor_kind def profiles return @profiles unless @profiles.nil? - @profiles = @cfg_arch.profiles.select {|profile| profile.profile_class.name == name} + @profiles = @arch.profiles.select {|profile| profile.profile_class.name == name} end # @return [Array] All profiles in database matching my processor kind def profiles_matching_processor_kind return @profiles_matching_processor_kind unless @profiles_matching_processor_kind.nil? - @profiles_matching_processor_kind = @cfg_arch.profiles.select {|profile| profile.profile_class.processor_kind == processor_kind} + @profiles_matching_processor_kind = @arch.profiles.select {|profile| profile.profile_class.processor_kind == processor_kind} end - # @return [Array] List of all extensions referenced by the profile class - def referenced_extensions - return @referenced_extensions unless @referenced_extensions.nil? + # @return [Array] Sorted list of all mandatory or optional extensions across the profile releases belonging + # to the profile class + def in_scope_extensions + return @in_scope_extensions unless @in_scope_extensions.nil? - @referenced_extensions = [] + @in_scope_extensions = [] profiles.each do |profile| - @referenced_extensions += profile.in_scope_extensions + @in_scope_extensions += profile.in_scope_extensions end - @referenced_extensions.uniq!(&:name) - - @referenced_extensions + @in_scope_extensions.uniq(&:name).sort_by!(&:name) end - # @return [Array] List of all extensions referenced by any profile class in the database with my processor kind - def referenced_extensions_matching_processor_kind - return @reference_extensions_matching_processor_kind unless @reference_extensions_matching_processor_kind.nil? + # @return [Array] Sorted list of all potential extensions with my processor kind + def in_scope_extensions_matching_processor_kind + return @in_scope_extensions_matching_processor_kind unless @in_scope_extensions_matching_processor_kind.nil? - @reference_extensions_matching_processor_kind = [] + @in_scope_extensions_matching_processor_kind = [] profiles_matching_processor_kind.each do |profile| - @reference_extensions_matching_processor_kind += profile.in_scope_extensions + @in_scope_extensions_matching_processor_kind += profile.in_scope_extensions end - @reference_extensions_matching_processor_kind.uniq!(&:name) - - @reference_extensions_matching_processor_kind + @in_scope_extensions_matching_processor_kind.uniq(&:name).sort_by!(&:name) end end # A profile release consists of a number of releases each with one or more profiles. # For example, the RVA20 profile release has profiles RVA20U64 and RVA20S64. -# Note there is no Portfolio* base class for a ProfileRelease to inherit from since there is no +# Note there is no Portfolio base class for a ProfileRelease to inherit from since there is no # equivalent to a ProfileRelease in a Certificate so no potential for a shared base class. class ProfileRelease < DatabaseObject def marketing_name = @data["marketing_name"] @@ -116,7 +113,7 @@ def contributors # @return [ProfileClass] Profile Class that this ProfileRelease belongs to def profile_class - profile_class = @cfg_arch.profile_class(@data["class"]) + profile_class = @arch.profile_class(@data["class"]) raise "No profile class named '#{@data["class"]}'" if profile_class.nil? profile_class @@ -128,45 +125,29 @@ def profiles @profiles = [] @data["profiles"].each do |profile_ref| - @profiles << @cfg_arch.ref(profile_ref["$ref"]) + @profiles << @arch.ref(profile_ref["$ref"]) end @profiles end - # @return [Array] List of all extensions referenced by the release - def referenced_extensions - return @referenced_extensions unless @referenced_extensions.nil? + # @return [PortfolioGroup] All portfolios in this profile release + def portfolio_grp + return @portfolio_grp unless @portfolio_grp.nil? - @referenced_extensions = [] - profiles.each do |profile| - @referenced_extensions += profile.in_scope_extensions - end + @portfolio_grp = PortfolioGroup.new(profiles) + end - @referenced_extensions.uniq!(&:name) + ##################################### + # METHODS HANDLED BY PortfolioGroup # + ##################################### - @referenced_extensions - end + # @return [Array] List of all mandatory or optional extensions referenced by this profile release. + def in_scope_extensions = portfolio_grp.in_scope_extensions # @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 + def extension_presence(ext_name) = portfolio_grp.extension_presence(ext_name) end # Representation of a specific profile in a profile release. @@ -176,7 +157,7 @@ def marketing_name = @data["marketing_name"] # @return [ProfileRelease] The profile release this profile belongs to def profile_release - profile_release = @cfg_arch.ref(@data["release"]["$ref"]) + profile_release = @arch.ref(@data["release"]["$ref"]) raise "No profile release named '#{@data["release"]["$ref"]}'" if profile_release.nil? profile_release @@ -195,9 +176,6 @@ def base @data["base"] end - # @return [Array] List of all extensions referenced by the profile - def referenced_extensions = in_scope_extensions - # Too complicated to put in profile ERB template. # @param presence_type [String] # @param heading_level [Integer] @@ -255,7 +233,7 @@ def extensions_to_adoc(presence_type, heading_level) def ext_req_to_adoc(ext_req) ret = [] - ext = cfg_arch.extension(ext_req.name) + ext = arch.extension(ext_req.name) ret << "* *#{ext_req.name}* " + (ext.nil? ? "" : ext.long_name) ret << "+" ret << "Version #{ext_req.requirement_specs_to_s}" diff --git a/lib/architecture.rb b/lib/architecture.rb index e4a624422..9ff8446a8 100644 --- a/lib/architecture.rb +++ b/lib/architecture.rb @@ -39,8 +39,6 @@ require "pathname" require "yaml" -require_relative "idl" - require_relative "arch_obj_models/certificate" require_relative "arch_obj_models/csr" require_relative "arch_obj_models/csr_field" @@ -77,6 +75,11 @@ def validate(show_progress: true) end end + # These instance methods are create when this Architecture class is first loaded. + # This is a Ruby "class" method and so self is the entire Architecture class, not an instance it. + # However, this class method creates normal instance methods and when they are called + # self is an instance of the Architecture class. + # # @!macro [attach] generate_obj_methods # @method $1s # @return [Array<$3>] List of all $1s defined in the standard @@ -98,7 +101,7 @@ def self.generate_obj_methods(fn_name, arch_dir, obj_class) @object_hashes[arch_dir] = {} Dir.glob(@arch_dir / arch_dir / "**" / "*.yaml") do |obj_path| obj_yaml = YAML.load_file(obj_path, permitted_classes: [Date]) - @objects[arch_dir] << obj_class.new(obj_yaml, Pathname.new(obj_path).realpath, arch: self) + @objects[arch_dir] << obj_class.new(obj_yaml, Pathname.new(obj_path).realpath, self) @object_hashes[arch_dir][@objects[arch_dir].last.name] = @objects[arch_dir].last end @objects[arch_dir] diff --git a/lib/base_architecture.rb b/lib/base_architecture.rb new file mode 100644 index 000000000..f63118265 --- /dev/null +++ b/lib/base_architecture.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +# Just adds the concept of base ISA (RV32I or RV64I) to the Architecture class. + +require_relative "architecture" + +class BaseArchitecture < Architecture + # @return [String] Name of base ISA (rv32 or rv64) + attr_reader :name + + # @return [Integer] 32 or 64 + attr_reader :base + + # Initialize a new base architecture definition + # + # @param name [#to_s] The name associated with this base architecture + # @param base [Integer] RISC-V ISA MXLEN parameter value (can be nil if not static) + # @param arch_dir [String,Pathname] Path to a directory with the associated architecture definition + def initialize(name, base, arch_dir) + super(arch_dir) + @name = name.to_s.freeze + @base = base + @base.freeze + end + + # Returns a string representation of the object, suitable for debugging. + # @return [String] A string representation of the object. + def inspect = "BaseArchitecture##{name}" +end diff --git a/lib/cfg_arch.rb b/lib/cfg_arch.rb index 3638c102d..655e4f964 100644 --- a/lib/cfg_arch.rb +++ b/lib/cfg_arch.rb @@ -1,64 +1,47 @@ # frozen_string_literal: true -# Many classes include DatabaseObject have an "cfg_arch" member which is a ConfiguredArchitecture class. -# It combines knowledge of the RISC-V Architecture with a particular configuration. -# A configuration is an instance of the Config object either located in the /cfg directory -# or created at runtime for things like profiles and certificate models. +# Combines knowledge of the RISC-V Architecture with a particular configuration. +# The architecture is an instance of the Architecture object representing yaml files in the /arch directory. +# A configuration is an instance of the Config object representing yaml files in the /cfg directory. require "forwardable" require "ruby-prof" require "tilt" -require_relative "config" require_relative "architecture" +require_relative "design" +require_relative "config" -require_relative "idl" -require_relative "idl/passes/find_return_values" -require_relative "idl/passes/gen_adoc" -require_relative "idl/passes/prune" -require_relative "idl/passes/reachable_exceptions" -require_relative "idl/passes/reachable_functions" - -require_relative "template_helpers" - -include TemplateHelpers - -class ConfiguredArchitecture < Architecture +class ConfiguredArchitecture < Design extend Forwardable - # @return [Idl::Compiler] The IDL compiler - attr_reader :idl_compiler - - # @return [Idl::IsaAst] Abstract syntax tree of global scope - attr_reader :global_ast - - # @return [String] Name of this definition. Special names are: - # * '_' - The generic architecture, with no configuration settings. - # * 'rv32' - A generic RV32 architecture, with only one parameter set (XLEN == 32) - # * 'rv64' - A generic RV64 architecture, with only one parameter set (XLEN == 64) - attr_reader :name - + # Calls to these methods on ConfiguredArchitecture are handled by the @config method of the same name. + # Kind of like inheritence but not quite. def_delegators \ :@config, \ :fully_configured?, :partially_configured?, :unconfigured?, :configured?, \ :mxlen, :param_values - # Returns whether or not it may be possible to switch XLEN given this definition. - # - # There are three cases when this will return true: - # 1. A mode (e.g., U) is known to be implemented, and the CSR bit that controls XLEN in that mode is known to be writeable. - # 2. A mode is known to be implemented, but the writability of the CSR bit that controls XLEN in that mode is not known. - # 3. It is not known if the mode is implemented. - # - # - # @return [Boolean] true if this configuration might execute in multiple xlen environments - # (e.g., that in some mode the effective xlen can be either 32 or 64, depending on CSR values) - def multi_xlen? - return true if @mxlen.nil? - - ["S", "U", "VS", "VU"].any? { |mode| multi_xlen_in_mode?(mode) } + # @param config_name [#to_s] The configuration name which corresponds to a folder name under cfg_path + # @param arch_dir [String,Pathname] Path to a directory with a fully merged/resolved architecture definition + # @param overlay_path [String] Optional path to a directory that overlays the architecture + # @param cfg_path [String] Optional path to where to find configuration file + def initialize(config_name, arch_dir, overlay_path: nil, cfg_path: "#{$root}/cfgs") + @config = Config.create("#{cfg_path}/#{config_name}/cfg.yaml") + arch = Architecture.new(arch_dir) + super(config_name, arch, overlay_path: overlay_path) end + # Returns a string representation of the object, suitable for debugging. + # @return [String] A string representation of the object. + def inspect = "ConfiguredArchitecture##{name}" + + ########################################### + # OVERRIDEN ABSTRACT METHODS # + # # + # These raise an error in the base class. # + ########################################### + # Returns whether or not it may be possible to switch XLEN in +mode+ given this definition. # # There are three cases when this will return true: @@ -146,122 +129,6 @@ def multi_xlen_in_mode?(mode) end end - # @return [Array] List of possible XLENs in any mode for this config - def possible_xlens = multi_xlen? ? [32, 64] : [mxlen] - - # hash for Hash lookup - def hash = @name_sym.hash - - # @return [Idl::SymbolTable] Symbol table with global scope - # @return [nil] if the architecture is not configured (use symtab_32 or symtab_64) - def symtab - raise NotImplementedError, "Un-configured ConfiguredArchitectures have no symbol table" if @symtab.nil? - - @symtab - end - - def config_type = @config.type - - # Initialize a new configured architecture definition - # - # @param config_name [#to_s] The name of a configuration, which must correspond - # to a folder name under cfg_path - def initialize(config_name, arch_path, overlay_path: nil, cfg_path: "#{$root}/cfgs") - super(arch_path) - - @name = config_name.to_s.freeze - @name_sym = @name.to_sym.freeze - - @obj_cache = {} - - @config = Config.create("#{cfg_path}/#{config_name}/cfg.yaml") - @mxlen = @config.mxlen - @mxlen.freeze - - @idl_compiler = Idl::Compiler.new - - @symtab = Idl::SymbolTable.new(self) - custom_globals_path = overlay_path.nil? ? Pathname.new("/does/not/exist") : overlay_path / "isa" / "globals.isa" - idl_path = File.exist?(custom_globals_path) ? custom_globals_path : $root / "arch" / "isa" / "globals.isa" - @global_ast = @idl_compiler.compile_file( - idl_path - ) - @global_ast.add_global_symbols(@symtab) - @symtab.deep_freeze - @global_ast.freeze_tree(@symtab) - end - - # Returns a string representation of the object, suitable for debugging. - # @return [String] A string representation of the object. - def inspect = "ConfiguredArchitecture##{name}" - - # type check all IDL, including globals, instruction ops, and CSR functions - # - # @param config [Config] Configuration - # @param show_progress [Boolean] whether to show progress bars - # @param io [IO] where to write progress bars - # @return [void] - def type_check(show_progress: true, io: $stdout) - io.puts "Type checking IDL code for #{@config.name}..." - progressbar = - if show_progress - ProgressBar.create(title: "Instructions", total: instructions.size) - end - - instructions.each do |inst| - progressbar.increment if show_progress - if @mxlen == 32 - inst.type_checked_operation_ast(@idl_compiler, @symtab, 32) if inst.rv32? - elsif @mxlen == 64 - inst.type_checked_operation_ast(@idl_compiler, @symtab, 64) if inst.rv64? - inst.type_checked_operation_ast(@idl_compiler, @symtab, 32) if possible_xlens.include?(32) && inst.rv32? - end - end - - progressbar = - if show_progress - ProgressBar.create(title: "CSRs", total: csrs.size) - end - - csrs.each do |csr| - progressbar.increment if show_progress - if csr.has_custom_sw_read? - if (possible_xlens.include?(32) && csr.defined_in_base32?) || (possible_xlens.include?(64) && csr.defined_in_base64?) - csr.type_checked_sw_read_ast(@symtab) - end - end - csr.fields.each do |field| - unless field.type_ast(@symtab).nil? - if ((possible_xlens.include?(32) && csr.defined_in_base32? && field.defined_in_base32?) || - (possible_xlens.include?(64) && csr.defined_in_base64? && field.defined_in_base64?)) - field.type_checked_type_ast(@symtab) - end - end - unless field.reset_value_ast(@symtab).nil? - if ((possible_xlens.include?(32) && csr.defined_in_base32? && field.defined_in_base32?) || - (possible_xlens.include?(64) && csr.defined_in_base64? && field.defined_in_base64?)) - field.type_checked_reset_value_ast(@symtab) if csr.defined_in_base32? && field.defined_in_base32? - end - end - unless field.sw_write_ast(@symtab).nil? - field.type_checked_sw_write_ast(@symtab, 32) if possible_xlens.include?(32) && csr.defined_in_base32? && field.defined_in_base32? - field.type_checked_sw_write_ast(@symtab, 64) if possible_xlens.include?(64) && csr.defined_in_base64? && field.defined_in_base64? - end - end - end - - progressbar = - if show_progress - ProgressBar.create(title: "Functions", total: functions.size) - end - functions.each do |func| - progressbar.increment if show_progress - func.type_check(@symtab) - end - - puts "done" if show_progress - end - # @return [Array] List of all parameters with one known value in the config def params_with_value return @params_with_value unless @params_with_value.nil? @@ -282,9 +149,8 @@ def params_with_value end end elsif @config.partially_configured? - mandatory_extensions.each do |ext_requirement| - ext = extension(ext_requirement.name) - ext.params.each do |ext_param| + mandatory_extensions.each do |ext_req| + ext_req.extension.params.each do |ext_param| # Params listed in the config always only have one value. next unless @config.param_values.key?(ext_param.name) @@ -305,7 +171,7 @@ def params_without_value return @params_without_value unless @params_without_value.nil? @params_without_value = [] - extensions.each do |ext| + arch.extensions.each do |ext| ext.params.each do |ext_param| # Params listed in the config always only have one value. next if @config.param_values.key?(ext_param.name) @@ -316,36 +182,25 @@ def params_without_value @params_without_value end + # @return [Array] List of all implemented extension versions. def implemented_extensions - @implemented_extensions ||= - @config.implemented_extensions.map do |e| - ExtensionVersion.new(e["name"], e["version"], self, fail_if_version_does_not_exist: true) - end - end + return @implemented_extensions unless @implemented_extensions.nil? - # @return [Array] List of all extensions known to be implemented in this config, including transitive implications - def transitive_implemented_extensions - return @transitive_implemented_extensions unless @transitive_implemented_extensions.nil? - - raise "implemented_extensions is only valid for a fully configured defintion" unless @config.fully_configured? - - list = implemented_extensions - list.each do |e| - implications = e.transitive_implications - list.concat(implications) unless implications.empty? + @implemented_extensions = @config.implemented_extensions.map do |e| + ExtensionVersion.new(e["name"], e["version"], arch, fail_if_version_does_not_exist: true) end - @transitive_implemented_extensions = list.uniq.sort end # @return [Array] List of all mandatory extension requirements def mandatory_extensions - @mandatory_extensions ||= - @config.mandatory_extensions.map do |e| - ext = extension(e["name"]) - raise "Cannot find extension #{e['name']} in the architecture definition" if ext.nil? + return @mandatory_extensions unless @mandatory_extensions.nil? - ExtensionRequirement.new(e["name"], *e["version"], presence: "mandatory", arch: self) - end + @mandatory_extensions = @config.mandatory_extensions.map do |e| + ext = extension(e["name"]) + raise "Cannot find extension #{e['name']} in the architecture definition" if ext.nil? + + ExtensionRequirement.new(e["name"], *e["version"], arch, presence: "mandatory") + end end # @return [Array] List of all extensions that are prohibited. @@ -360,7 +215,7 @@ def prohibited_extensions ext = extension(e["name"]) raise "Cannot find extension #{e['name']} in the architecture definition" if ext.nil? - ExtensionRequirement.new(e["name"], *e["version"], presence: "mandatory", arch: self) + ExtensionRequirement.new(e["name"], *e["version"], arch, presence: "mandatory") end # now add any extensions that are prohibited by a mandatory extension @@ -394,7 +249,7 @@ def prohibited_extensions @prohibited_extensions << ExtensionRequirement.new( ext_ver_list[0].ext.name, ">= #{ext_ver_list.min.version_spec.canonical}", - presence: "prohibited", arch: self + arch, presence: "prohibited" ) elsif ext_ver_list.size == (ext_ver_list[0].ext.versions.size - 1) # excludes all but one version @@ -404,8 +259,8 @@ def prohibited_extensions allowed_version = allowed_version_list[0] @prohibited_extensions << ExtensionRequirement.new( - ext_ver_list[0].ext.name, "!= #{allowed_version.version_spec.canonical}", - presence: "prohibited", arch: self + ext_ver_list[0].ext.name, "!= #{allowed_version.version_spec.canonical}", arch, + presence: "prohibited" ) else # need to group @@ -418,38 +273,14 @@ def prohibited_extensions @prohibited_extensions end - # @overload prohibited_ext?(ext) - # Returns true if the ExtensionVersion +ext+ is prohibited - # @param ext [ExtensionVersion] An extension version - # @return [Boolean] - # - # @overload prohibited_ext?(ext) - # Returns true if any version of the extension named +ext+ is prohibited - # @param ext [String] An extension name - # @return [Boolean] - def prohibited_ext?(ext) - if ext.is_a?(ExtensionVersion) - prohibited_extensions.any? { |ext_req| ext_req.satisfied_by?(ext) } - elsif ext.is_a?(String) || ext.is_a?(Symbol) - prohibited_extensions.any? { |ext_req| ext_req.name == ext.to_s } - else - raise ArgumentError, "Argument to prohibited_ext? should be an ExtensionVersion or a String" - end - end - # @overload ext?(ext_name) # @param ext_name [#to_s] Extension name (case sensitive) - # @return [Boolean] True if the extension `name` is implemented + # @return [Boolean] True if the extension `name` must be implemented + # # @overload ext?(ext_name, ext_version_requirements) # @param ext_name [#to_s] Extension name (case sensitive) # @param ext_version_requirements [Number,String,Array] Extension version requirements - # @return [Boolean] True if the extension `name` meeting `ext_version_requirements` is implemented - # @example Checking extension presence with a version requirement - # ConfigurationArchitecture.ext?(:S, ">= 1.12") - # @example Checking extension presence with multiple version requirements - # ConfigurationArchitecture.ext?(:S, ">= 1.12", "< 1.15") - # @example Checking extension precsence with a precise version requirement - # ConfigurationArchitecture.ext?(:S, 1.12) + # @return [Boolean] True if the extension `name` meeting `ext_version_requirements` must be implemented def ext?(ext_name, *ext_version_requirements) @ext_cache ||= {} cached_result = @ext_cache[[ext_name, ext_version_requirements]] @@ -461,7 +292,7 @@ def ext?(ext_name, *ext_version_requirements) if ext_version_requirements.empty? e.name == ext_name.to_s else - requirement = ExtensionRequirement.new(ext_name, *ext_version_requirements, arch: self) + requirement = ExtensionRequirement.new(ext_name, *ext_version_requirements, arch) requirement.satisfied_by?(e) end end @@ -470,7 +301,7 @@ def ext?(ext_name, *ext_version_requirements) if ext_version_requirements.empty? e.name == ext_name.to_s else - requirement = ExtensionRequirement.new(ext_name, *ext_version_requirements, arch: self) + requirement = ExtensionRequirement.new(ext_name, *ext_version_requirements, arch) e.satisfying_versions.all? do |ext_ver| requirement.satisfied_by?(ext_ver) end @@ -484,223 +315,20 @@ def ext?(ext_name, *ext_version_requirements) @ext_cache[[ext_name, ext_version_requirements]] = result end - # @return [Array] All exception codes known to be implemented - def implemented_exception_codes - return @implemented_exception_codes unless @implemented_exception_codes.nil? - - @implemented_exception_codes = - implemented_extensions.reduce([]) do |list, ext_version| - ecodes = extension(ext_version.name)["exception_codes"] - next list if ecodes.nil? - - ecodes.each do |ecode| - # double check that all the codes are unique - raise "Duplicate exception code" if list.any? { |e| e.num == ecode["num"] || e.name == ecode["name"] || e.var == ecode["var"] } - - unless ecode.dig("when", "version").nil? - # check version - next unless ext?(ext_version.name.to_sym, ecode["when"]["version"]) - end - list << ExceptionCode.new(ecode["name"], ecode["var"], ecode["num"], self) - end - list - end - end - - # @return [Array] All interrupt codes known to be implemented - def implemented_interrupt_codes - return @implemented_interrupt_codes unless @implemented_interrupt_codes.nil? - - @implemented_interupt_codes = - implemented_extensions.reduce([]) do |list, ext_version| - icodes = extension(ext_version.name)["interrupt_codes"] - next list if icodes.nil? - - icodes.each do |icode| - # double check that all the codes are unique - raise "Duplicate interrupt code" if list.any? { |i| i.num == icode["num"] || i.name == icode["name"] || i.var == icode["var"] } - - unless ecode.dig("when", "version").nil? - # check version - next unless ext?(ext_version.name.to_sym, ecode["when"]["version"]) - end - list << InterruptCode.new(icode["name"], icode["var"], icode["num"], self) - end - list - end - end - - # @return [Array] List of all functions defined by the architecture - def functions - return @functions unless @functions.nil? - - @functions = @global_ast.functions - end - - # @return [Array] List of all implemented CSRs - def transitive_implemented_csrs - @transitive_implemented_csrs ||= - transitive_implemented_extensions.map(&:implemented_csrs).flatten.uniq.sort - end - - # @return [Array] List of all implemented instructions - def transitive_implemented_instructions - @transitive_implemented_instructions ||= - transitive_implemented_extensions.map(&:implemented_instructions).flatten.uniq.sort - end - - # @return [Array] List of all reachable IDL functions for the config - def implemented_functions - return @implemented_functions unless @implemented_functions.nil? - - @implemented_functions = [] - - puts " Finding all reachable functions from instruction operations" - - transitive_implemented_instructions.each do |inst| - @implemented_functions << - if inst.base.nil? - if multi_xlen? - (inst.reachable_functions(symtab, 32) + - inst.reachable_functions(symtab, 64)) - else - inst.reachable_functions(symtab, mxlen) - end - else - inst.reachable_functions(symtab, inst.base) - end - end - raise "?" unless @implemented_functions.is_a?(Array) - @implemented_functions = @implemented_functions.flatten - @implemented_functions.uniq!(&:name) - - puts " Finding all reachable functions from CSR operations" - - transitive_implemented_csrs.each do |csr| - csr_funcs = csr.reachable_functions(self) - csr_funcs.each do |f| - @implemented_functions << f unless @implemented_functions.any? { |i| i.name == f.name } - end - end - - @implemented_functions - end - - # given an adoc string, find names of CSR/Instruction/Extension enclosed in `monospace` - # and replace them with links to the relevant object page - # - # @param adoc [String] Asciidoc source - # @return [String] Asciidoc source, with link placeholders - def find_replace_links(adoc) - adoc.gsub(/`([\w.]+)`/) do |match| - name = Regexp.last_match(1) - csr_name, field_name = name.split(".") - csr = csr(csr_name) - if !field_name.nil? && !csr.nil? && csr.field?(field_name) - "%%LINK%csr_field;#{csr_name}.#{field_name};#{csr_name}.#{field_name}%%" - elsif !csr.nil? - "%%LINK%csr;#{csr_name};#{csr_name}%%" - elsif instruction(name) - "%%LINK%inst;#{name};#{name}%%" - elsif extension(name) - "%%LINK%ext;#{name};#{name}%%" - else - match - end - end - end - - # Returns an environment hash suitable for use with ERb templates. - # - # This method returns a hash containing the architecture definition and other - # relevant data that can be used to generate ERb templates. - # - # @return [Hash] An environment hash suitable for use with ERb templates. - def erb_env - return @env unless @env.nil? - - @env = Class.new - @env.instance_variable_set(:@cfg, @cfg) - @env.instance_variable_set(:@params, @params) - @env.instance_variable_set(:@arch_gen, self) - - # add each parameter, either as a method (lowercase) or constant (uppercase) - params_with_value.each do |param| - @env.const_set(param.name, param.value) unless @env.const_defined?(param.name) - end - - params_without_value.each do |param| - @env.const_set(param.name, :unknown) unless @env.const_defined?(param.name) - end - - @env.instance_exec do - # method to check if a given extension (with an optional version number) is present - # - # @param ext_name [String,#to_s] Name of the extension - # @param ext_requirement [String, #to_s] Version string, as a Gem Requirement (https://guides.rubygems.org/patterns/#pessimistic-version-constraint) - # @return [Boolean] whether or not extension +ext_name+ meeting +ext_requirement+ is implemented in the config - def ext?(ext_name, ext_requirement = ">= 0") - @arch_gen.ext?(ext_name.to_s, ext_requirement) - end - - # @return [Array] List of possible XLENs for any implemented mode - def possible_xlens - @arch_gen.possible_xlens - end - - # insert a hyperlink to an object - # At this point, we insert a placeholder since it will be up - # to the backend to create a specific link - # - # @params type [Symbol] Type (:section, :csr, :inst, :ext) - # @params name [#to_s] Name of the object - def link_to(type, name) - "%%LINK%#{type};#{name}%%" - end - - # info on interrupt and exception codes - - # @returns [Hash] architecturally-defined exception codes and their names - def exception_codes - @arch_gen.exception_codes - end - - # returns [Hash] architecturally-defined interrupt codes and their names - def interrupt_codes - @arch_gen.interrupt_codes - end + ##################################### + # METHODS RESTRICTING PARENT METHOD # + ##################################### - # @returns [Hash] architecturally-defined exception codes and their names - def implemented_exception_codes - @arch_gen.implemented_exception_codes - end - - # returns [Hash] architecturally-defined interrupt codes and their names - def implemented_interrupt_codes - @arch_gen.implemented_interrupt_codes - end - end - - @env + # @return [Array] List of all extensions known to be implemented in this config, including transitive implications + def transitive_implemented_extensions + raise "implemented_extensions is only valid for a fully configured definition" unless @config.fully_configured? + super end - private :erb_env - # passes _erb_template_ through ERB within the content of this config - # - # @param erb_template [String] ERB source - # @return [String] The rendered text - def render_erb(erb_template, what = "") - t = Tempfile.new("template") - t.write erb_template - t.flush - begin - Tilt["erb"].new(t.path, trim: "-").render(erb_env) - rescue - warn "While rendering ERB template: #{what}" - raise - ensure - t.close - t.unlink - end + # Override base class to reject a nil value. + # @return [Idl::SymbolTable] Symbol table with global scope + def symtab + raise NotImplementedError, "Un-configured ConfiguredArchitectures have no symbol table" if @symtab.nil? + super end end diff --git a/lib/config.rb b/lib/config.rb index 5efe7b684..7d86d628e 100644 --- a/lib/config.rb +++ b/lib/config.rb @@ -3,7 +3,7 @@ require "pathname" # This class represents a configuration file (e.g., cfgs/*/cfg.yaml), independent of the Architecture. -# Can either be in the /cfg directory or created at runtime in memory by the certificate tasks.rake file. +# Located in the /cfg directory. class Config # @return [Hash] A hash mapping parameter name to value for any parameter that has # been configured with a value. May be empty. diff --git a/lib/design.rb b/lib/design.rb new file mode 100644 index 000000000..a714d21b9 --- /dev/null +++ b/lib/design.rb @@ -0,0 +1,512 @@ +# frozen_string_literal: true + +# The Design class is used when exporting information from the Architecture class. +# The Architecture represents the "front-end" with objects providing access to the architecture database +# in the /arch directory YAML files. It is the Ruby API to access the architecture database. +# The Design contains common code such as IDL and ERB support used by multiple "back-ends" to +# export the "front-end" architecture database to various types of documents. +# The Design adds the concept of an mxlen which isn't present in the Architecture. +# +# A Design provides support when exporting any of the following to ASCIIDOC/HTML/PDF: +# - Entire RISC-V ISA manual +# - Config (under /cfg directory) with a possible overlay +# - Profile release +# - Certificate +# - Extension +# +# The Design class contains an Architecture object but isn't inherited from it. +# This was done so code that only needs an Architecture object can make this clear +# by using the Architecture object instead of the Design object (i.e., to support encapsulation). +# +# This Design class is an abstract base class for designs using either a config (under /cfg) or a +# portfolio (profile release or certificate). +# Abstract methods all call raise() if not overriden by a child class. These methods are all grouped +# together in this Ruby file to make it easier to find them. + +require "ruby-prof" +require "tilt" + +require_relative "architecture" + +require_relative "idl" +require_relative "idl/passes/find_return_values" +require_relative "idl/passes/gen_adoc" +require_relative "idl/passes/prune" +require_relative "idl/passes/reachable_exceptions" +require_relative "idl/passes/reachable_functions" + +require_relative "template_helpers" + +include TemplateHelpers + +class Design + # @return [String] Name of design + attr_reader :name + + # @return [Architecture] The RISC-V architecture + attr_reader :arch + + # @return [Idl::Compiler] The IDL compiler + attr_reader :idl_compiler + + # @return [Idl::IsaAst] Abstract syntax tree of global scope + attr_reader :global_ast + + # @return [Idl::SymbolTable] Symbol table with global scope + # Don't use attr_reader so this can be clearly overridden by child class. + def symtab = @symtab + + # hash for Hash lookup + def hash = @name_sym.hash + + # @param name [#to_s] The design name + # @param arch [Architecture] The entire architecture + # @param overlay_path [String] Optional path to a directory that overlays the architecture + def initialize(name, arch, overlay_path: nil) + raise ArgumentError, "arch must be an Architecture but is a #{arch.class}" unless arch.is_a?(Architecture) + + @name = name.to_s.freeze + @name_sym = @name.to_sym.freeze + @arch = arch + + @idl_compiler = Idl::Compiler.new + @symtab = Idl::SymbolTable.new(self) + custom_globals_path = overlay_path.nil? ? Pathname.new("/does/not/exist") : overlay_path / "isa" / "globals.isa" + + idl_path = File.exist?(custom_globals_path) ? custom_globals_path : $root / "arch" / "isa" / "globals.isa" + @global_ast = @idl_compiler.compile_file(idl_path) + @global_ast.add_global_symbols(@symtab) + @symtab.deep_freeze + @global_ast.freeze_tree(@symtab) + end + + # Returns a string representation of the object, suitable for debugging. + # @return [String] A string representation of the object. + def inspect = "Design##{name}" + + #################### + # ABSTRACT METHODS # + #################### + + # @return [Integer] 32, 64, or nil (if dynamic or unconfigured) + def mxlen + raise "Abstract Method: Must be provided in child class" + end + + # Returns whether or not it may be possible to switch XLEN in +mode+ given this definition. + # @param mode [String] mode to check. One of "M", "S", "U", "VS", "VU" + # @return [Boolean] true if might execute in multiple xlen environments in +mode+ + # (e.g., that in some mode the effective xlen can be either 32 or 64, depending on CSR values) + def multi_xlen_in_mode?(mode) + raise "Abstract Method: Must be provided in child class" + end + + # @return [Hash] Fully-constrained parameter values (those with just one possible value for this design) + def param_values + raise "Abstract Method: Must be provided in child class" + end + + # @return [Array] List of all parameters fully-constrained to one specific value + def params_with_value + raise "Abstract Method: Must be provided in child class" + end + + # @return [Array] List of all available parameters not yet full-constrained to one specific value + def params_without_value + raise "Abstract Method: Must be provided in child class" + end + + # @return [Array] List of all implemented extension versions. + def implemented_extensions + raise "Abstract Method: Must be provided in child class" + end + + # @return [Array] List of all mandatory extension requirements + def mandatory_extensions + raise "Abstract Method: Must be provided in child class" + end + + # @return [Array] List of all extensions that are prohibited. + # This includes extensions explicitly prohibited by the design + # and extensions that conflict with a mandatory extension. + def prohibited_extensions + raise "Abstract Method: Must be provided in child class" + end + + # @return [Boolean] True if all parameters are fully-constrained in the design + def fully_configured? + raise "Abstract Method: Must be provided in child class" + end + + # @return [Boolean] True if some parameters aren't fully-constrained yet in the design + def partially_configured? + raise "Abstract Method: Must be provided in child class" + end + + # @return [Boolean] True if all parameters aren't constrained at all in the design + def unconfigured? + raise "Abstract Method: Must be provided in child class" + end + + # @return [Boolean] True if not unconfigured (so either fully_configured or partially_configured). + # This isn't an abstract method but is located here for clarity. + def configured? = !unconfigured? + + # @overload ext?(ext_name) + # @param ext_name [#to_s] Extension name (case sensitive) + # @return [Boolean] True if the extension `name` must be implemented + # + # @overload ext?(ext_name, ext_version_requirements) + # @param ext_name [#to_s] Extension name (case sensitive) + # @param ext_version_requirements [Number,String,Array] Extension version requirements + # @return [Boolean] True if the extension `name` meeting `ext_version_requirements` must be implemented + # + # @example Checking extension presence with a version requirement + # Design.ext?(:S, ">= 1.12") + # @example Checking extension presence with multiple version requirements + # Design.ext?(:S, ">= 1.12", "< 1.15") + # @example Checking extension presence with a precise version requirement + # Design.ext?(:S, 1.12) + def ext?(ext_name, *ext_version_requirements) + raise "Abstract Method: Must be provided in child class" + end + + ################### + # REGULAR METHODS # + ################### + + # Returns whether or not it may be possible to switch XLEN given this definition. + # + # There are three cases when this will return true: + # 1. A mode (e.g., U) is known to be implemented, and the CSR bit that controls XLEN in that mode is known to be writeable. + # 2. A mode is known to be implemented, but the writability of the CSR bit that controls XLEN in that mode is not known. + # 3. It is not known if the mode is implemented. + # + # + # @return [Boolean] true if might execute in multiple xlen environments + # (e.g., that in some mode the effective xlen can be either 32 or 64, depending on CSR values) + def multi_xlen? + return true if @mxlen.nil? + + ["S", "U", "VS", "VU"].any? { |mode| multi_xlen_in_mode?(mode) } + end + + # @return [Array] List of possible XLENs in any mode for this design + def possible_xlens = multi_xlen? ? [32, 64] : [mxlen] + + # type check all IDL, including globals, instruction ops, and CSR functions + # + # @param show_progress [Boolean] whether to show progress bars + # @param io [IO] where to write progress bars + # @return [void] + def type_check(show_progress: true, io: $stdout) + io.puts "Type checking IDL code for #{@name}..." + progressbar = + if show_progress + ProgressBar.create(title: "Instructions", total: instructions.size) + end + + instructions.each do |inst| + progressbar.increment if show_progress + if @mxlen == 32 + inst.type_checked_operation_ast(@idl_compiler, @symtab, 32) if inst.rv32? + elsif @mxlen == 64 + inst.type_checked_operation_ast(@idl_compiler, @symtab, 64) if inst.rv64? + inst.type_checked_operation_ast(@idl_compiler, @symtab, 32) if possible_xlens.include?(32) && inst.rv32? + end + end + + progressbar = + if show_progress + ProgressBar.create(title: "CSRs", total: csrs.size) + end + + csrs.each do |csr| + progressbar.increment if show_progress + if csr.has_custom_sw_read? + if (possible_xlens.include?(32) && csr.defined_in_base32?) || (possible_xlens.include?(64) && csr.defined_in_base64?) + csr.type_checked_sw_read_ast(@symtab) + end + end + csr.fields.each do |field| + unless field.type_ast(@symtab).nil? + if ((possible_xlens.include?(32) && csr.defined_in_base32? && field.defined_in_base32?) || + (possible_xlens.include?(64) && csr.defined_in_base64? && field.defined_in_base64?)) + field.type_checked_type_ast(@symtab) + end + end + unless field.reset_value_ast(@symtab).nil? + if ((possible_xlens.include?(32) && csr.defined_in_base32? && field.defined_in_base32?) || + (possible_xlens.include?(64) && csr.defined_in_base64? && field.defined_in_base64?)) + field.type_checked_reset_value_ast(@symtab) if csr.defined_in_base32? && field.defined_in_base32? + end + end + unless field.sw_write_ast(@symtab).nil? + field.type_checked_sw_write_ast(@symtab, 32) if possible_xlens.include?(32) && csr.defined_in_base32? && field.defined_in_base32? + field.type_checked_sw_write_ast(@symtab, 64) if possible_xlens.include?(64) && csr.defined_in_base64? && field.defined_in_base64? + end + end + end + + progressbar = + if show_progress + ProgressBar.create(title: "Functions", total: functions.size) + end + functions.each do |func| + progressbar.increment if show_progress + func.type_check(@symtab) + end + + puts "done" if show_progress + end + + # @return [Array] List of all extensions known to be implemented in this design, including transitive implications + def transitive_implemented_extensions + return @transitive_implemented_extensions unless @transitive_implemented_extensions.nil? + + list = implemented_extensions + list.each do |e| + implications = e.transitive_implications + list.concat(implications) unless implications.empty? + end + @transitive_implemented_extensions = list.uniq.sort + end + + # @overload prohibited_ext?(ext) + # Returns true if the ExtensionVersion +ext+ is prohibited + # @param ext [ExtensionVersion] An extension version + # @return [Boolean] + # + # @overload prohibited_ext?(ext) + # Returns true if any version of the extension named +ext+ is prohibited + # @param ext [String] An extension name + # @return [Boolean] + def prohibited_ext?(ext) + if ext.is_a?(ExtensionVersion) + prohibited_extensions.any? { |ext_req| ext_req.satisfied_by?(ext) } + elsif ext.is_a?(String) || ext.is_a?(Symbol) + prohibited_extensions.any? { |ext_req| ext_req.name == ext.to_s } + else + raise ArgumentError, "Argument to prohibited_ext? should be an ExtensionVersion or a String" + end + end + + # @return [Array] All exception codes known to be implemented + def implemented_exception_codes + return @implemented_exception_codes unless @implemented_exception_codes.nil? + + @implemented_exception_codes = + implemented_extensions.reduce([]) do |list, ext_version| + ecodes = extension(ext_version.name)["exception_codes"] + next list if ecodes.nil? + + ecodes.each do |ecode| + # double check that all the codes are unique + raise "Duplicate exception code" if list.any? { |e| e.num == ecode["num"] || e.name == ecode["name"] || e.var == ecode["var"] } + + unless ecode.dig("when", "version").nil? + # check version + next unless ext?(ext_version.name.to_sym, ecode["when"]["version"]) + end + list << ExceptionCode.new(ecode["name"], ecode["var"], ecode["num"], arch) + end + list + end + end + + # @return [Array] All interrupt codes known to be implemented + def implemented_interrupt_codes + return @implemented_interrupt_codes unless @implemented_interrupt_codes.nil? + + @implemented_interupt_codes = + implemented_extensions.reduce([]) do |list, ext_version| + icodes = extension(ext_version.name)["interrupt_codes"] + next list if icodes.nil? + + icodes.each do |icode| + # double check that all the codes are unique + raise "Duplicate interrupt code" if list.any? { |i| i.num == icode["num"] || i.name == icode["name"] || i.var == icode["var"] } + + unless ecode.dig("when", "version").nil? + # check version + next unless ext?(ext_version.name.to_sym, ecode["when"]["version"]) + end + list << InterruptCode.new(icode["name"], icode["var"], icode["num"], arch) + end + list + end + end + + # @return [Array] List of all functions defined by the architecture + def functions + return @functions unless @functions.nil? + + @functions = @global_ast.functions + end + + # @return [Array] List of all implemented CSRs + def transitive_implemented_csrs + @transitive_implemented_csrs ||= + transitive_implemented_extensions.map(&:implemented_csrs).flatten.uniq.sort + end + + # @return [Array] List of all implemented instructions + def transitive_implemented_instructions + @transitive_implemented_instructions ||= + transitive_implemented_extensions.map(&:implemented_instructions).flatten.uniq.sort + end + + # @return [Array] List of all reachable IDL functions for the design + def implemented_functions + return @implemented_functions unless @implemented_functions.nil? + + @implemented_functions = [] + + puts " Finding all reachable functions from instruction operations" + + transitive_implemented_instructions.each do |inst| + @implemented_functions << + if inst.base.nil? + if multi_xlen? + (inst.reachable_functions(symtab, 32) + + inst.reachable_functions(symtab, 64)) + else + inst.reachable_functions(symtab, mxlen) + end + else + inst.reachable_functions(symtab, inst.base) + end + end + raise "?" unless @implemented_functions.is_a?(Array) + @implemented_functions = @implemented_functions.flatten.uniq(&:name) + + puts " Finding all reachable functions from CSR operations" + + transitive_implemented_csrs.each do |csr| + csr_funcs = csr.reachable_functions(self) + csr_funcs.each do |f| + @implemented_functions << f unless @implemented_functions.any? { |i| i.name == f.name } + end + end + + @implemented_functions + end + + # given an adoc string, find names of CSR/Instruction/Extension enclosed in `monospace` + # and replace them with links to the relevant object page + # + # @param adoc [String] Asciidoc source + # @return [String] Asciidoc source, with link placeholders + def find_replace_links(adoc) + adoc.gsub(/`([\w.]+)`/) do |match| + name = Regexp.last_match(1) + csr_name, field_name = name.split(".") + csr = arch.csr(csr_name) + if !field_name.nil? && !csr.nil? && csr.field?(field_name) + "%%LINK%csr_field;#{csr_name}.#{field_name};#{csr_name}.#{field_name}%%" + elsif !csr.nil? + "%%LINK%csr;#{csr_name};#{csr_name}%%" + elsif arch.instruction(name) + "%%LINK%inst;#{name};#{name}%%" + elsif arch.extension(name) + "%%LINK%ext;#{name};#{name}%%" + else + match + end + end + end + + # Returns an environment hash suitable for the render() function in ERB templates. + # + # This method returns a hash containing the architecture definition and other + # relevant data that can be used to generate ERb templates. + # + # @return [Hash] An environment hash suitable for use with ERb templates. + def render_erb_env + return @env unless @env.nil? + + @env = Class.new + @env.instance_variable_set(:@design, design) + @env.instance_variable_set(:@params, @param_values) + @env.instance_variable_set(:@arch, arch) + + # add each parameter, either as a method (lowercase) or constant (uppercase) + params_with_value.each do |param| + @env.const_set(param.name, param.value) unless @env.const_defined?(param.name) + end + + params_without_value.each do |param| + @env.const_set(param.name, :unknown) unless @env.const_defined?(param.name) + end + + @env.instance_exec do + # method to check if a given extension (with an optional version number) is present + # + # @param ext_name [String,#to_s] Name of the extension + # @param ext_requirement [String, #to_s] Version string, as a Gem Requirement (https://guides.rubygems.org/patterns/#pessimistic-version-constraint) + # @return [Boolean] whether or not extension +ext_name+ meeting +ext_requirement+ is implemented in the design + def ext?(ext_name, ext_requirement = ">= 0") + @design.ext?(ext_name.to_s, ext_requirement) + end + + # @return [Array] List of possible XLENs for any implemented mode + def possible_xlens + @design.possible_xlens + end + + # insert a hyperlink to an object + # At this point, we insert a placeholder since it will be up + # to the backend to create a specific link + # + # @param type [Symbol] Type (:section, :csr, :inst, :ext) + # @param name [#to_s] Name of the object + def link_to(type, name) + "%%LINK%#{type};#{name}%%" + end + + # info on interrupt and exception codes + + # @returns [Hash] architecturally-defined exception codes and their names + def exception_codes + @arch.exception_codes + end + + # returns [Hash] architecturally-defined interrupt codes and their names + def interrupt_codes + @arch.interrupt_codes + end + + # @returns [Hash] architecturally-defined exception codes and their names + def implemented_exception_codes + @design.implemented_exception_codes + end + + # returns [Hash] architecturally-defined interrupt codes and their names + def implemented_interrupt_codes + @design.implemented_interrupt_codes + end + end + + @env + end + private :render_erb_env + + # Passes _erb_template_ through ERB within the content of this render_erb_env + # + # @param erb_template [String] ERB source + # @return [String] The rendered text + def render_erb(erb_template, what = "") + t = Tempfile.new("template") + t.write erb_template + t.flush + begin + Tilt["erb"].new(t.path, trim: "-").render(render_erb_env) + rescue + warn "While rendering ERB template: #{what}" + raise + ensure + t.close + t.unlink + end + end +end diff --git a/lib/idl/ast.rb b/lib/idl/ast.rb index 001233991..2993161ed 100644 --- a/lib/idl/ast.rb +++ b/lib/idl/ast.rb @@ -564,7 +564,7 @@ def type_check(symtab) type_error "no symbol named '#{name}' on line #{lineno}" if symtab.get(name).nil? end - # @!macro type_no_cfg_arch + # @!macro type_no_design def type(symtab) return @type unless @type.nil? @@ -588,11 +588,11 @@ def const? = @const def value(symtab) # can't do this.... a const might be in a template function, with different values at call time # if @const - # # consts never change, so we can look them up by cfg_arch - # var = @vars[symtab.cfg_arch] + # # consts never change, so we can look them up by design + # var = @vars[symtab.design] # if var.nil? # var = symtab.get(name) - # @vars[symtab.cfg_arch] = var + # @vars[symtab.design] = var # end # type_error "Variable '#{name}' was not found" if var.nil? # value_error "Value of '#{name}' not known" if var.value.nil? @@ -793,7 +793,7 @@ def type_check(symtab) type_error "#{expression.text_value} is not an array" unless expression_type.kind == :array type_error "#{expression.text_value} must be a constant" unless expression_type.const? - if symtab.cfg_arch.fully_configured? && (expression_type.width == :unknown) + if symtab.design.fully_configured? && (expression_type.width == :unknown) type_error "#{expression.text_value} must have a known value at compile time" end end @@ -1063,7 +1063,7 @@ def type(symtab) end # @!macro value_no_args - def value(_symtab, _cfg_arch) = raise InternalError, "Enum defintions have no value" + def value(_symtab, _design) = raise InternalError, "Enum defintions have no value" # @return [String] enum name def name = @user_type.text_value @@ -1119,11 +1119,11 @@ def type_check(_symtab) def element_names(symtab) case name when "ExtensionName" - symtab.cfg_arch.extensions.map(&:name) + symtab.design.arch.extensions.map(&:name) when "ExceptionCode" - symtab.cfg_arch.exception_codes.map(&:var) + symtab.design.exception_codes.map(&:var) when "InterruptCode" - symtab.cfg_arch.interrupt_codes.map(&:var) + symtab.design.interrupt_codes.map(&:var) else type_error "Unknown builtin enum type '#{name}'" end @@ -1132,17 +1132,17 @@ def element_names(symtab) def element_values(symtab) case name when "ExtensionName" - (0...symtab.cfg_arch.extensions.size).to_a + (0...symtab.design.arch.extensions.size).to_a when "ExceptionCode" - symtab.cfg_arch.exception_codes.map(&:num) + symtab.design.exception_codes.map(&:num) when "InterruptCode" - symtab.cfg_arch.interrupt_codes.map(&:num) + symtab.design.interrupt_codes.map(&:num) else type_error "Unknown builtin enum type '#{name}'" end end - # @!macro type_no_cfg_arch + # @!macro type_no_design def type(symtab) return @type unless @type.nil? @@ -1328,7 +1328,7 @@ def type(symtab) def name = @name.text_value # @!macro value_no_args - def value(_symtab, _cfg_arch) = raise AstNode::InternalError, "Bitfield defintions have no value" + def value(_symtab, _design) = raise AstNode::InternalError, "Bitfield defintions have no value" # @!macro to_idl def to_idl @@ -1689,10 +1689,10 @@ def type_check(symtab) end def var(symtab) - variable = @vars[symtab.cfg_arch] + variable = @vars[symtab.design] if variable.nil? variable = symtab.get(lhs.text_value) - @vars[symtab.cfg_arch] = variable + @vars[symtab.design] = variable end variable end @@ -2023,15 +2023,15 @@ def initialize(input, interval, csr_field, write_value) def type(symtab) if field(symtab).defined_in_all_bases? - if symtab.cfg_arch.mxlen == 64 && symtab.cfg_arch.multi_xlen? - Type.new(:bits, width: [field(symtab).location(symtab.cfg_arch, 32).size, field(symtab).location(symtab.cfg_arch, 64).size].max) + if symtab.design.mxlen == 64 && symtab.design.multi_xlen? + Type.new(:bits, width: [field(symtab).location(symtab.design, 32).size, field(symtab).location(symtab.design, 64).size].max) else - Type.new(:bits, width: field(symtab).location(symtab.cfg_arch, symtab.cfg_arch.mxlen).size) + Type.new(:bits, width: field(symtab).location(symtab.design, symtab.design.mxlen).size) end elsif field(symtab).base64_only? - Type.new(:bits, width: field(symtab).location(symtab.cfg_arch, 64).size) + Type.new(:bits, width: field(symtab).location(symtab.design, 64).size) elsif field(symtab).base32_only? - Type.new(:bits, width: field(symtab).location(symtab.cfg_arch, 32).size) + Type.new(:bits, width: field(symtab).location(symtab.design, 32).size) else internal_error "Unexpected base for field" end @@ -2048,7 +2048,7 @@ def type_check(symtab) type_error "Cannot write to read-only CSR field" end end - # ok, we don't know the type because the cfg_arch isn't configured + # ok, we don't know the type because the design isn't configured write_value.type_check(symtab) type_error "Incompatible type in assignment" unless write_value.type(symtab).convertable_to?(type(symtab)) @@ -2275,7 +2275,7 @@ def decl_type(symtab) dtype = Type.new(:array, width: ary_size.value(symtab), sub_type: dtype, qualifiers:) end value_else(value_result) do - type_error "Array size must be known at compile time" if symtab.cfg_arch.fully_configured? + type_error "Array size must be known at compile time" if symtab.design.fully_configured? dtype = Type.new(:array, width: :unknown, sub_type: dtype, qualifiers:) end end @@ -2300,8 +2300,8 @@ def type_check(symtab, add_sym = true) ary_size.value(symtab) end value_else(value_result) do - # if this is a fully configured ConfiguredArchitecture, this is an error because all constants are supposed to be known - if symtab.cfg_arch.fully_configured? + # if this is a fully configured Design, this is an error because all constants are supposed to be known + if symtab.design.fully_configured? type_error "Array size (#{ary_size.text_value}) must be known at compile time" else # otherwise, it's ok that we don't know the value yet, as long as the value is a const @@ -2591,10 +2591,10 @@ def type(symtab) when :enum_ref Type.new(:bits, width: etype.enum_class.width) when :csr - if etype.csr.dynamic_length?(symtab.cfg_arch) + if etype.csr.dynamic_length?(symtab.design) Type.new(:bits, width: :unknown) else - Type.new(:bits, width: etype.csr.length(symtab.cfg_arch)) + Type.new(:bits, width: etype.csr.length(symtab.design)) end end end @@ -3449,7 +3449,7 @@ def initialize(input, interval, class_name, member_name) def freeze_tree(global_symtab) return if frozen? - enum_def_ast = global_symtab.cfg_arch.global_ast.enums.find { |e| e.name == @enum_class_name } + enum_def_ast = global_symtab.design.global_ast.enums.find { |e| e.name == @enum_class_name } @enum_def_type = if enum_def_ast.is_a?(BuiltinEnumDefinitionAst) @@ -3471,7 +3471,7 @@ def type_check(symtab) type_error "#{@enum_class_name} has no member '#{@member_name}'" if enum_def_type.value(@member_name).nil? end - # @!macro type_no_cfg_arch + # @!macro type_no_design def type(symtab) internal_error "Not frozen?" unless frozen? type_error "No enum named #{@enum_class_name}" if @enum_def_type.nil? @@ -3479,7 +3479,7 @@ def type(symtab) @enum_def_type.ref_type end - # @!macro value_no_cfg_arch + # @!macro value_no_design def value(symtab) internal_error "Must call type_check first" if @enum_def_type.nil? @@ -3902,7 +3902,7 @@ def type(_symtab) end # @!macro value_no_args - def value(_symtab, _cfg_arch) = internal_error "Why are you calling value for an lval?" + def value(_symtab, _design) = internal_error "Why are you calling value for an lval?" def to_idl = "-" end @@ -4015,7 +4015,7 @@ def expected_return_type(symtab) symtab.get("__expected_return_type") else # need to find the type to get the right symbol table - func_type = @func_type_cache[symtab.cfg_arch] + func_type = @func_type_cache[symtab.design] return func_type.return_type(EMPTY_ARRAY, self) unless func_type.nil? func_type = symtab.get_global(func_def.name) @@ -4034,7 +4034,7 @@ def expected_return_type(symtab) end func_type.return_type(template_values.sort { |a, b| a.template_index <=> b.template_index }.map(&:value), self) else - @func_type_cache[symtab.cfg_arch]= func_type + @func_type_cache[symtab.design]= func_type func_type.return_type(EMPTY_ARRAY, self) end end @@ -4208,7 +4208,7 @@ def type_check(symtab) end end value_else(value_result) do - type_error "Bit width must be known at compile time" if symtab.cfg_arch.fully_configured? + type_error "Bit width must be known at compile time" if symtab.design.fully_configured? end end unless ["Bits", "String", "XReg", "Boolean", "U32", "U64"].include?(@type_name) @@ -4583,7 +4583,7 @@ def arg_nodes end def func_type(symtab) - func_def_type = @func_def_type_cache[symtab.cfg_arch] + func_def_type = @func_def_type_cache[symtab.design] return func_def_type unless func_def_type.nil? func_def_type = symtab.get(@name) @@ -4593,14 +4593,14 @@ def func_type(symtab) type_error "#{@name} is not a function (it's a #{func_def_type.class.name})" end - @func_def_type_cache[symtab.cfg_arch] = func_def_type + @func_def_type_cache[symtab.design] = func_def_type end # @!macro type_check def type_check(symtab) level = symtab.levels - unknown_ok = symtab.cfg_arch.partially_configured? + unknown_ok = symtab.design.partially_configured? tvals = template_values(symtab, unknown_ok:) func_def_type = func_type(symtab) @@ -4652,7 +4652,7 @@ def type_check(symtab) def type(symtab) return ConstBoolType if name == "implemented?" - func_type(symtab).return_type(template_values(symtab, unknown_ok: symtab.cfg_arch.partially_configured?), self) + func_type(symtab).return_type(template_values(symtab, unknown_ok: symtab.design.partially_configured?), self) end # @!macro value @@ -4669,9 +4669,9 @@ def value(symtab) extname_ref = arg_nodes[0] type_error "First argument should be a ExtensionName" unless extname_ref.type(symtab).kind == :enum_ref && extname_ref.class_name == "ExtensionName" - return symtab.cfg_arch.ext?(arg_nodes[0].member_name) if symtab.cfg_arch.fully_configured? + return symtab.design.ext?(arg_nodes[0].member_name) if symtab.design.fully_configured? - if symtab.cfg_arch.ext?(arg_nodes[0].member_name) + if symtab.design.ext?(arg_nodes[0].member_name) # we can know if it is implemented, but not if it's not implemented for a partially configured return true end @@ -4732,12 +4732,12 @@ def type_check(symtab) type_error "#{text_value} is not a type" unless type.is_a?(Type) end - # @!macro type_no_cfg_arch + # @!macro type_no_design def type(symtab) - typ = @type_cache[symtab.cfg_arch] + typ = @type_cache[symtab.design] return typ unless typ.nil? - @type_cache[symtab.cfg_arch] = symtab.get(text_value) + @type_cache[symtab.design] = symtab.get(text_value) end # @!macro to_idl @@ -4964,7 +4964,7 @@ def arguments_list_str # return the return type, which may be a tuple of multiple types def return_type(symtab) - cached = @cached_return_type[symtab.cfg_arch] + cached = @cached_return_type[symtab.design] return cached unless cached.nil? unless symtab.levels == 2 @@ -4972,12 +4972,12 @@ def return_type(symtab) end if @return_type_nodes.empty? - @cached_return_type[symtab.cfg_arch] = VoidType + @cached_return_type[symtab.design] = VoidType return VoidType end unless templated? - # with no templates, the return type does not change for a given cfg_arch + # with no templates, the return type does not change for a given design rtype = if @return_type_nodes.size == 1 rtype = @return_type_nodes[0].type(symtab) @@ -4995,7 +4995,7 @@ def return_type(symtab) raise "??????" if rtype.nil? - return @cached_return_type[symtab.cfg_arch] = rtype + return @cached_return_type[symtab.design] = rtype end if templated? @@ -5742,7 +5742,7 @@ def freeze_tree(symtab) @value = nil end @type = calc_type(symtab) - @cfg_arch = symtab.cfg_arch # remember cfg_arch, used in gen_adoc pass + @design = symtab.design # remember design, used in gen_adoc pass freeze end @@ -5756,17 +5756,17 @@ def type_check(symtab) type_error "No CSR named #{csr_name}" if csr_def(symtab).nil? end type_error "CSR[#{csr_name(symtab)}] has no field named #{@field_name}" if field_def(symtab).nil? - type_error "CSR[#{csr_name(symtab)}].#{@field_name} is not defined in RV32" if symtab.cfg_arch.mxlen == 32 && !field_def(symtab).defined_in_base32? - type_error "CSR[#{csr_name(symtab)}].#{@field_name} is not defined in RV64" if symtab.cfg_arch.mxlen == 64 && !field_def(symtab).defined_in_base64? + type_error "CSR[#{csr_name(symtab)}].#{@field_name} is not defined in RV32" if symtab.design.mxlen == 32 && !field_def(symtab).defined_in_base32? + type_error "CSR[#{csr_name(symtab)}].#{@field_name} is not defined in RV64" if symtab.design.mxlen == 64 && !field_def(symtab).defined_in_base64? end def csr_def(symtab) - cfg_arch = symtab.cfg_arch + design = symtab.design if @idx.is_a?(IntLiteralAst) - cfg_arch.csrs.find { |c| c.address == @idx.value(symtab) } + design.arch.csrs.find { |c| c.address == @idx.value(symtab) } else - cfg_arch.csr(@idx) + design.arch.csr(@idx) end end @@ -5806,14 +5806,14 @@ def calc_type(symtab) end end if fd.defined_in_all_bases? - Type.new(:bits, width: symtab.cfg_arch.possible_xlens.map{ |xlen| fd.width(symtab.cfg_arch, xlen) }.max) + Type.new(:bits, width: symtab.design.possible_xlens.map{ |xlen| fd.width(symtab.design, xlen) }.max) elsif fd.base64_only? - if symtab.cfg_arch.possible_xlens.include?(64) - Type.new(:bits, width: fd.width(symtab.cfg_arch, 64)) + if symtab.design.possible_xlens.include?(64) + Type.new(:bits, width: fd.width(symtab.design, 64)) end elsif fd.base32_only? - if symtab.cfg_arch.possible_xlens.include?(32) - Type.new(:bits, width: fd.width(symtab.cfg_arch, 32)) + if symtab.design.possible_xlens.include?(32) + Type.new(:bits, width: fd.width(symtab.design, 32)) end else internal_error "unexpected field base" @@ -5837,7 +5837,7 @@ def calc_value(symtab) value_error "'#{csr_name(symtab)}.#{field_name(symtab)}' is not RO" end - field_def(symtab).reset_value(symtab.cfg_arch) + field_def(symtab).reset_value(symtab.design) end end @@ -5877,14 +5877,14 @@ def initialize(input, interval, idx) def freeze_tree(symtab) return if frozen? - @cfg_arch = symtab.cfg_arch # remember cfg_arch, used by gen_adoc pass + @design = symtab.design # remember design, used by gen_adoc pass @idx.freeze_tree(symtab) freeze end # @!macro type def type(symtab) - cfg_arch = symtab.cfg_arch + design = symtab.design cd = csr_def(symtab) if cd.nil? @@ -5896,16 +5896,16 @@ def type(symtab) Bits64Type end else - CsrType.new(cd, cfg_arch) + CsrType.new(cd, design) end end # @!macro type_check def type_check(symtab) - cfg_arch = symtab.cfg_arch + design = symtab.design idx_text = @idx.is_a?(String) ? @idx : @idx.text_value - if !cfg_arch.csr(idx_text).nil? + if !design.arch.csr(idx_text).nil? # this is a known csr name # nothing else to check @@ -5916,7 +5916,7 @@ def type_check(symtab) value_result = value_try do idx_value = @idx.value(symtab) - csr_index = cfg_arch.csrs.index { |csr| csr.address == idx_value } + csr_index = design.arch.csrs.index { |csr| csr.address == idx_value } type_error "No csr number '#{idx_value}' was found" if csr_index.nil? :ok end @@ -5925,9 +5925,9 @@ def type_check(symtab) end def csr_def(symtab) - cfg_arch = symtab.cfg_arch + design = symtab.design idx_text = @idx.is_a?(String) ? @idx : @idx.text_value - csr = cfg_arch.csr(idx_text) + csr = design.arch.csr(idx_text) if !csr.nil? # this is a known csr name csr @@ -5935,7 +5935,7 @@ def csr_def(symtab) # this is an expression value_result = value_try do idx_value = @idx.value(symtab) - return cfg_arch.csrs.find { |csr| csr.address == idx_value } + return design.arch.csrs.find { |csr| csr.address == idx_value } end # || we don't know at compile time which CSR this is... nil @@ -5956,10 +5956,10 @@ def csr_name(symtab) def value(symtab) cd = csr_def(symtab) value_error "CSR number not knowable" if cd.nil? - if symtab.cfg_arch.fully_configured? - value_error "CSR is not implemented" unless symtab.cfg_arch.transitive_implemented_csrs.any? { |icsr| icsr.name == cd.name } + if symtab.design.fully_configured? + value_error "CSR is not implemented" unless symtab.design.transitive_implemented_csrs.any? { |icsr| icsr.name == cd.name } else - value_error "CSR is not defined" unless symtab.cfg_arch.csrs.any? { |icsr| icsr.name == cd.name } + value_error "CSR is not defined" unless symtab.design.arch.csrs.any? { |icsr| icsr.name == cd.name } end cd.fields.each { |f| value_error "#{csr_name(symtab)}.#{f.name} not RO" unless f.type(symtab) == "RO" } @@ -5987,7 +5987,7 @@ def initialize(input, interval, csr, expression) end def type_check(symtab) - cfg_arch = symtab.cfg_arch + design = symtab.design csr.type_check(symtab) expression.type_check(symtab) @@ -6055,12 +6055,12 @@ def type_check(symtab) end def type(symtab) - cfg_arch = symtab.cfg_arch + design = symtab.design case function_name when "sw_read" if csr_known?(symtab) - Type.new(:bits, width: cfg_arch.csr(csr.csr_name(symtab)).length(cfg_arch)) + Type.new(:bits, width: design.arch.csr(csr.csr_name(symtab)).length(design)) else Type.new(:bits, width: symtab.mxlen.nil? ? :unknown : symtab.mxlen) end @@ -6122,10 +6122,10 @@ def initialize(input, interval, idx) def type_check(symtab) if idx.is_a?(IntLiteralAst) # make sure this value is a defined CSR - index = symtab.cfg_arch.csrs.index { |csr| csr.address == idx.value(symtab) } + index = symtab.design.arch.csrs.index { |csr| csr.address == idx.value(symtab) } type_error "No csr number '#{idx.value(symtab)}' was found" if index.nil? else - csr = symtab.cfg_arch.csr(idx.text_value) + csr = symtab.design.arch.csr(idx.text_value) type_error "No csr named '#{idx.text_value}' was found" if csr.nil? end end @@ -6133,15 +6133,15 @@ def type_check(symtab) def csr_def(symtab) if idx.is_a?(IntLiteralAst) # make sure this value is a defined CSR - symtab.cfg_arch.csrs.find { |csr| csr.address == idx.text_value.to_i } + symtab.design.arch.csrs.find { |csr| csr.address == idx.text_value.to_i } else - symtab.cfg_arch.csr(idx.text_value) + symtab.design.arch.csr(idx.text_value) end end # @!macro type def type(symtab) - CsrType.new(csr_def(symtab), symtab.cfg_arch) + CsrType.new(csr_def(symtab), symtab.design) end def name(symtab) diff --git a/lib/idl/passes/gen_adoc.rb b/lib/idl/passes/gen_adoc.rb index 6213411fd..63a80a6cb 100644 --- a/lib/idl/passes/gen_adoc.rb +++ b/lib/idl/passes/gen_adoc.rb @@ -301,7 +301,7 @@ def gen_adoc(indent = 0, indent_spaces: 2) if idx_text =~ /[0-9]+/ "#{' '*indent}#{csr_text}" else - if @cfg_arch.csr(csr_text).nil? + if @design.arch.csr(csr_text).nil? "#{' '*indent}#{csr_text}" else "#{' '*indent}%%LINK%csr_field;#{idx_text}.#{@field_name};#{csr_text}%%" @@ -324,7 +324,7 @@ def gen_adoc(indent = 0, indent_spaces: 2) # we don't have the symtab to map this to a csr name "#{' '*indent}#{csr_text}" else - if @cfg_arch.csr(csr_text).nil? + if @design.arch.csr(csr_text).nil? "#{' '*indent}#{csr_text}" else "#{' '*indent}%%LINK%csr;#{idx_text};#{csr_text}%%" diff --git a/lib/idl/passes/reachable_functions_unevaluated.rb b/lib/idl/passes/reachable_functions_unevaluated.rb index 22364b168..ed5ef129f 100644 --- a/lib/idl/passes/reachable_functions_unevaluated.rb +++ b/lib/idl/passes/reachable_functions_unevaluated.rb @@ -4,43 +4,43 @@ module Idl class AstNode - # @param cfg_arch [ConfiguredArchitecture] Architecture definition + # @param design [Design] The design # @return [Array] List of all functions that can be reached (via function calls) from this node, without considering value evaluation - def reachable_functions_unevaluated(cfg_arch) + def reachable_functions_unevaluated(design) children.reduce([]) do |list, e| - list.concat e.reachable_functions_unevaluated(cfg_arch) + list.concat e.reachable_functions_unevaluated(design) end.uniq(&:name) end end class FunctionCallExpressionAst - def reachable_functions_unevaluated(cfg_arch) + def reachable_functions_unevaluated(design) fns = [] if template? template_arg_nodes.each do |t| - fns.concat(t.reachable_functions_unevaluated(cfg_arch)) + fns.concat(t.reachable_functions_unevaluated(design)) end end arg_nodes.each do |a| - fns.concat(a.reachable_functions_unevaluated(cfg_arch)) + fns.concat(a.reachable_functions_unevaluated(design)) end - func_def_ast = cfg_arch.function(name) + func_def_ast = design.function(name) raise "No function '#{name}' found in Architecture def" if func_def_ast.nil? - fns += func_def_ast.reachable_functions_unevaluated(cfg_arch) + fns += func_def_ast.reachable_functions_unevaluated(design) fns.uniq(&:name) end end class FunctionDefAst - def reachable_functions_unevaluated(cfg_arch) + def reachable_functions_unevaluated(design) fns = [self] unless builtin? body.stmts.each do |stmt| - fns += stmt.reachable_functions_unevaluated(cfg_arch) + fns += stmt.reachable_functions_unevaluated(design) end end diff --git a/lib/idl/symbol_table.rb b/lib/idl/symbol_table.rb index 94c580fc4..55a546189 100644 --- a/lib/idl/symbol_table.rb +++ b/lib/idl/symbol_table.rb @@ -75,7 +75,7 @@ def value=(new_value) # scoped symbol table holding known symbols at a current point in parsing class SymbolTable - def cfg_arch = @cfg_arch + def design = @design # @return [Integer] 32 or 64, the XLEN in M-mode attr_reader :mxlen @@ -86,15 +86,15 @@ class DuplicateSymError < StandardError def hash return @frozen_hash unless @frozen_hash.nil? - [@scopes.hash, @cfg_arch.hash].hash + [@scopes.hash, @design.hash].hash end - def initialize(cfg_arch) - raise "Must provide cfg_arch" if cfg_arch.nil? - raise "The cfg_arch must be a ConfiguredArchitecture but is a #{cfg_arch.class}" unless cfg_arch.is_a?(ConfiguredArchitecture) + def initialize(design) + raise "Must provide design" if design.nil? + raise "The design must be a Design but is a #{design.class}" unless design.is_a?(Design) - @cfg_arch = cfg_arch - @mxlen = cfg_arch.unconfigured? ? nil : cfg_arch.mxlen + @design = design + @mxlen = design.unconfigured? ? nil : design.mxlen @callstack = [nil] @scopes = [{ "X" => Var.new( @@ -115,7 +115,7 @@ def initialize(cfg_arch) ) }] - cfg_arch.params_with_value.each do |param_with_value| + design.params_with_value.each do |param_with_value| type = Type.from_json_schema(param_with_value.schema).make_const if type.kind == :array && type.width == :unknown type = Type.new(:array, width: param_with_value.value.length, sub_type: type.sub_type, qualifiers: [:const]) @@ -133,7 +133,7 @@ def initialize(cfg_arch) end # now add all parameters, even those not implemented - cfg_arch.params_without_value.each do |param| + design.params_without_value.each do |param| if param.exts.size == 1 add!(param.name, Var.new(param.name, param.idl_type.clone.make_const)) else @@ -159,7 +159,7 @@ def deep_freeze @scopes.freeze # set frozen_hash so that we can quickly compare symtabs - @frozen_hash = [@scopes.hash, @cfg_arch.hash].hash + @frozen_hash = [@scopes.hash, @design.hash].hash # set up the global clone that be used as a mutable table @global_clone_pool = [] @@ -168,7 +168,7 @@ def deep_freeze copy = SymbolTable.allocate copy.instance_variable_set(:@scopes, [@scopes[0]]) copy.instance_variable_set(:@callstack, [@callstack[0]]) - copy.instance_variable_set(:@cfg_arch, @cfg_arch) + copy.instance_variable_set(:@design, @design) copy.instance_variable_set(:@mxlen, @mxlen) copy.instance_variable_set(:@global_clone_pool, @global_clone_pool) copy.instance_variable_set(:@in_use, false) @@ -181,7 +181,7 @@ def deep_freeze # @return [String] inspection string def inspect - "SymbolTable[#{@cfg_arch.name}]#{frozen? ? ' (frozen)' : ''}" + "SymbolTable[#{@design.name}]#{frozen? ? ' (frozen)' : ''}" end # pushes a new scope @@ -344,7 +344,7 @@ def global_clone copy = SymbolTable.allocate copy.instance_variable_set(:@scopes, [@scopes[0]]) copy.instance_variable_set(:@callstack, [@callstack[0]]) - copy.instance_variable_set(:@cfg_arch, @cfg_arch) + copy.instance_variable_set(:@design, @design) copy.instance_variable_set(:@mxlen, @mxlen) copy.instance_variable_set(:@global_clone_pool, @global_clone_pool) copy.instance_variable_set(:@in_use, false) diff --git a/lib/idl/tests/helpers.rb b/lib/idl/tests/helpers.rb index 2d694e5b3..9f94bad59 100644 --- a/lib/idl/tests/helpers.rb +++ b/lib/idl/tests/helpers.rb @@ -13,8 +13,8 @@ def initialize(name) 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 +# Design class mock that knows about XLEN and extensions +class MockDesign def param_values = { "XLEN" => 32 } def params_with_value = [XmockensionParameterWithValue.new("XLEN", "mxlen", {"type" => "integer", "enum" => [32, 64]}, nil, nil, 32)] def params_without_value = [] @@ -35,8 +35,8 @@ def name = "mock" module TestMixin def setup - @cfg_arch = MockConfiguredArchitecture.new - @symtab = Idl::SymbolTable.new(@cfg_arch) + @design = MockDesign.new + @symtab = Idl::SymbolTable.new(@design) @compiler = Idl::Compiler.new end end diff --git a/lib/idl/tests/test_functions.rb b/lib/idl/tests/test_functions.rb index d079348ca..52eaab2d1 100644 --- a/lib/idl/tests/test_functions.rb +++ b/lib/idl/tests/test_functions.rb @@ -72,7 +72,7 @@ def test_that_reachable_raise_analysis_respects_transitive_known_values ast = @compiler.compile_file(path) ast.add_global_symbols(@symtab) @symtab.deep_freeze - @cfg_arch.global_ast = ast + @design.global_ast = ast ast.freeze_tree(@symtab) test_ast = ast.functions.select { |f| f.name == "test" }[0] @@ -137,7 +137,7 @@ def test_that_reachable_raise_analysis_respects_known_paths_down_an_unknown_path ast = @compiler.compile_file(path) ast.add_global_symbols(@symtab) @symtab.deep_freeze - @cfg_arch.global_ast = ast + @design.global_ast = ast ast.freeze_tree(@symtab) test_ast = ast.functions.select { |f| f.name == "test" }[0] diff --git a/lib/idl/type.rb b/lib/idl/type.rb index 7ad04a4ee..f73cfe8fa 100644 --- a/lib/idl/type.rb +++ b/lib/idl/type.rb @@ -61,10 +61,10 @@ def qualify(qualifier) self end - def self.from_typename(type_name, cfg_arch) + def self.from_typename(type_name, design) case type_name when 'XReg' - return Type.new(:bits, width: cfg_arch.param_values['XLEN']) + return Type.new(:bits, width: design.param_values['XLEN']) when 'FReg' return Type.new(:freg, width: 32) when 'DReg' @@ -609,8 +609,8 @@ def clone class CsrType < Type attr_reader :csr - def initialize(csr, cfg_arch, qualifiers: []) - super(:csr, name: csr.name, csr: csr, width: csr.max_length(cfg_arch), qualifiers: qualifiers) + def initialize(csr, design, qualifiers: []) + super(:csr, name: csr.name, csr: csr, width: csr.max_length(design), qualifiers: qualifiers) end def fields diff --git a/lib/portfolio_design.rb b/lib/portfolio_design.rb new file mode 100644 index 000000000..3fb867e29 --- /dev/null +++ b/lib/portfolio_design.rb @@ -0,0 +1,206 @@ +# frozen_string_literal: true + +# Combines knowledge of the architecture database with one or more portfolios (profile or certificate). +# +# Used in portfolio-based ERB templates to gather information about the "design". +# The "design" corresponds to the file being created by the ERB template and facilitates +# sharing ERB template fragments between different kinds of portfolios (mostly in the appendices). +# For example, a certificate model has one portfolio but a profile release has multiple portfolios +# but they both have just one PortfolioDesign object. + +require "ruby-prof" +require "tilt" + +require_relative "architecture" +require_relative "design" +require_relative "arch_obj_models/portfolio" + +class PortfolioDesign < Design + # @param base_isa_name [#to_s] The name of the base ISA configuration (rv32 or rv64) + # @param arch [Architecture] The database of RISC-V standards + # @param mxlen [Integer] Comes from portfolio YAML "base" (either 32 or 64) + # @param portfolios [Array] Portfolios being converted to adoc + # @param overlay_path [String] Optional path to a directory that overlays the architecture + def initialize(base_isa_name, arch, base, portfolios, overlay_path: nil) + raise ArgumentError, "base must be 32 or 64 but is #{base}" unless (base == 32 || base == 64) + raise ArgumentError, "arch must be an Architecture but is a #{arch.class}" unless arch.is_a?(Architecture) + raise ArgumentError, "portfolios must be an Array but is a #{portfolios.class}" unless portfolios.is_a?(Array) + + @mxlen = base + @mxlen.freeze + + # The PortfolioGroup has an Array inside it and forwards common Array methods to its internal Array. + # Can call @portfolio_grp.each or @portfolio_grp.map and they are handled by the normal Array methods. + @portfolio_grp = PortfolioGroup.new(portfolios) + + # Sanity check base passed in to me. + max_base = portfolios.map(&:base).max + raise ArgumentError, "Provided base of #{base} but maximum base across all provided portfolios is #{max_base}" unless base == max_base + + super(base_isa_name, arch, overlay_path: overlay_path) + end + + # Returns a string representation of the object, suitable for debugging. + # @return [String] A string representation of the object. + def inspect = "PortfolioDesign##{name}" + + ################################## + # METHODS REQUIRED BY BASE CLASS # + ################################## + + # @return [Integer] + def mxlen = @mxlen + + # Returns whether or not it may be possible to switch XLEN in +mode+ given this definition. + # + # There are three cases when this will return true: + # 1. +mode+ (e.g., U) is known to be implemented, and the CSR bit that controls XLEN in +mode+ is known to be writeable. + # 2. +mode+ is known to be implemented, but the writability of the CSR bit that controls XLEN in +mode+ is not known. + # 3. It is not known if +mode+ is implemented. + # + # Will return false if +mode+ is not possible (e.g., because U is a prohibited extension) + # + # @param mode [String] mode to check. One of "M", "S", "U", "VS", "VU" + # @return [Boolean] true if might execute in multiple xlen environments in +mode+ + # (e.g., that in some mode the effective xlen can be either 32 or 64, depending on CSR values) + # + # Assume portfolios (profiles and certificates) don't need this ISA feature. + def multi_xlen_in_mode?(mode) = false + + # @return [Array] List of all parameters fully-constrained to one specific value + def params_with_value + return @params_with_value unless @params_with_value.nil? + + @params_with_value = [] + + in_scope_ext_reqs.each do |ext_req| + ext_req.extension.params.each do |ext_param| + next unless param_values.key?(ext_param.name) + + @params_with_value << ExtensionParameterWithValue.new(ext_param, param_values[ext_param.name]) + end + end + + @params_with_value + end + + # @return [Array] List of all available parameters not yet full-constrained to one specific value + def params_without_value + return @params_without_value unless @params_without_value.nil? + + @params_without_value = [] + arch.extensions.each do |ext| + ext.params.each do |ext_param| + next if param_values.key?(ext_param.name) + + @params_without_value << ext_param + end + end + @params_without_value + end + + def implemented_extensions + # Only supported by fully-configured configurations and a portfolio corresponds to a + # partially-configured configuration. See the Config class for details. + raise "Not supported for portfolio #{name}" + end + + # @return [Array] List of all extensions that are prohibited. + # This includes extensions explicitly prohibited by the design + # and extensions that conflict with a mandatory extension. + # + # Assume there are none of these in a portfolio for now. + def prohibited_extensions = [] + + # @overload ext?(ext_name) + # @param ext_name [#to_s] Extension name (case sensitive) + # @return [Boolean] True if the extension `name` must be implemented + # + # @overload ext?(ext_name, ext_version_requirements) + # @param ext_name [#to_s] Extension name (case sensitive) + # @param ext_version_requirements [Number,String,Array] Extension version requirements + # @return [Boolean] True if the extension `name` meeting `ext_version_requirements` must be implemented + # + # @example Checking extension presence with a version requirement + # PortfolioDesign.ext?(:S, ">= 1.12") + # @example Checking extension presence with multiple version requirements + # PortfolioDesign.ext?(:S, ">= 1.12", "< 1.15") + # @example Checking extension precsence with a precise version requirement + # PortfolioDesign.ext?(:S, 1.12) + def ext?(ext_name, *ext_version_requirements) + @ext_cache ||= {} + cached_result = @ext_cache[[ext_name, ext_version_requirements]] + return cached_result unless cached_result.nil? + + result = + mandatory_extensions.any? do |ext| + if ext_version_requirements.empty? + ext.name == ext_name.to_s + else + requirement = ExtensionRequirement.new(ext_name, *ext_version_requirements, arch) + ext.satisfying_versions.all? do |ext_ver| + requirement.satisfied_by?(ext_ver) + end + end + end + + @ext_cache[[ext_name, ext_version_requirements]] = result + end + + # + # A Portfolio corresponds to a partially-configured design. + # See the Config class for details. + # + # @return [Boolean] True if all parameters are fully-constrained in the design + def fully_configured? = false + + # @return [Boolean] True if some parameters aren't fully-constrained yet in the design + def partially_configured? = true + + # @return [Boolean] True if all parameters aren't constrained at all in the design + def unconfigured? = false + + ##################################### + # METHODS HANDLED BY PortfolioGroup # + ##################################### + + # @return [Array] List of all mandatory extension requirements + def mandatory_extensions = @portfolio_grp.mandatory_ext_reqs + + # @return [Hash] Fully-constrained parameter values (those with just one possible value for this design). + def param_values = @portfolio_grp.param_values + + # @return [Array] List of all mandatory or optional extensions referenced by this design. + def in_scope_extensions = @portfolio_grp.in_scope_extensions + + # @return [Array] List of all mandatory or optional extension requirements referenced by this design. + def in_scope_ext_reqs = @portfolio_grp.in_scope_ext_reqs + + # @return [Array] Sorted list of all instructions associated with extensions listed as + # mandatory or optional in portfolio. Uses instructions provided by the + # minimum version of the extension that meets the extension requirement. + def in_scope_instructions = @portfolio_grp.in_scope_instructions + + # @return [Array] Unsorted list of all CSRs associated with extensions listed as + # mandatory or optional in portfolio. Uses CSRs provided by the + # minimum version of the extension that meets the extension requirement. + def in_scope_csrs = @portfolio_grp.in_scope_csrs + + # @return [String] Given an extension +ext_name+, return the presence as a string. + # Returns the greatest presence string across all portfolios in this design. + # If the extension name isn't found in this design, return "-". + def extension_presence(ext_name) = @portfolio_grp.extension_presence(ext_name) + + ################################################ + # ADDITIONAL METHODS UNIQUE TO PortfolioDesign # + ################################################ + + # @return [Array] All exception codes used in this design. + # TODO: XXX: Make this actually filter out unused exception codes in the design. + def exception_codes = arch.exception_codes + + # @return [Array] All interrupt codes used in this design. + # TODO: XXX: Make this actually filter out unused interrupt codes in the design. + def interrupt_codes = arch.interrupt_codes + +end diff --git a/lib/version.rb b/lib/version.rb index 5ea01b048..5bf8c6483 100644 --- a/lib/version.rb +++ b/lib/version.rb @@ -188,7 +188,7 @@ def satisfied_by?(version, ext) matching_ver = ext.versions.find { |v| v.version_spec == v_spec } raise "Can't find version?" if matching_ver.nil? - matching_ver.compatible?(ExtensionVersion.new(ext.name, v_spec.to_s, ext.cfg_arch)) + matching_ver.compatible?(ExtensionVersion.new(ext.name, v_spec.to_s, ext.arch)) end end end diff --git a/lib/yaml_resolver.py b/lib/yaml_resolver.py index 0e1d3053e..8aca7464b 100644 --- a/lib/yaml_resolver.py +++ b/lib/yaml_resolver.py @@ -421,12 +421,15 @@ def resolve_file(rel_path : str | Path, arch_dir: str | Path, resolved_dir: str write_yaml(resolved_path, resolved_obj) if do_checks and ("$schema" in resolved_obj): - schema = _get_schema(resolved_obj["$schema"]) + schema_uri = resolved_obj["$schema"] + schema_relpath = schema_uri.split("#")[0] + schema = _get_schema(schema_uri) try: schema.validate(instance=resolved_obj) except ValidationError as e: - print(f"JSON Schema Validation Error for {rel_path}:") - print(best_match(schema.iter_errors(resolved_obj)).message) + print(f"JSON Schema Validation Error for {rel_path}: ") + print(" ", best_match(schema.iter_errors(resolved_obj)).message) + print(f" Used schema {schema_relpath}") exit(1) os.chmod(resolved_path, 0o444) diff --git a/schemas/cert_model_schema.json b/schemas/cert_model_schema.json index 65383a4e2..59369db12 100644 --- a/schemas/cert_model_schema.json +++ b/schemas/cert_model_schema.json @@ -2,7 +2,7 @@ "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", - "required": ["$schema", "kind", "name", "long_name"], + "required": ["$schema", "kind", "name", "long_name", "base"], "additionalProperties": false, "properties": { "$inherits": { diff --git a/schemas/profile_schema.json b/schemas/profile_schema.json index 40e6f2e88..009a84953 100644 --- a/schemas/profile_schema.json +++ b/schemas/profile_schema.json @@ -2,7 +2,7 @@ "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", - "required": ["$schema", "kind", "name"], + "required": ["$schema", "kind", "name", "base"], "properties": { "$schema": { "type": "string", @@ -15,6 +15,10 @@ "name": { "type": "string", "description": "Name (database key) of this Profile" + }, + "base": { + "type": "integer", + "description": "32 for RV32I or 64 for RV64I" } } } From 99a683c99aaace98752e59cab0ee6f4255cef96b Mon Sep 17 00:00:00 2001 From: james-ball-qualcomm Date: Wed, 1 Jan 2025 19:55:43 +0000 Subject: [PATCH 02/21] Forgot to switch profile_class method of a profile_release to use the updated $ref syntax (matching certificates). --- lib/arch_obj_models/profile.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/arch_obj_models/profile.rb b/lib/arch_obj_models/profile.rb index b569115c2..f33f3c560 100644 --- a/lib/arch_obj_models/profile.rb +++ b/lib/arch_obj_models/profile.rb @@ -113,7 +113,7 @@ def contributors # @return [ProfileClass] Profile Class that this ProfileRelease belongs to def profile_class - profile_class = @arch.profile_class(@data["class"]) + profile_class = @arch.ref(@data["class"]['$ref']) raise "No profile class named '#{@data["class"]}'" if profile_class.nil? profile_class From f9b9c1f9195cbf7fb05f0014dd6906a56a88e421 Mon Sep 17 00:00:00 2001 From: james-ball-qualcomm Date: Wed, 1 Jan 2025 22:34:06 +0000 Subject: [PATCH 03/21] Forgot to make dest directory in profiles tasks.rake --- backends/profile_doc/tasks.rake | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/backends/profile_doc/tasks.rake b/backends/profile_doc/tasks.rake index 52aad0fef..1f6e581a7 100644 --- a/backends/profile_doc/tasks.rake +++ b/backends/profile_doc/tasks.rake @@ -53,7 +53,7 @@ Dir.glob("#{$root}/arch/profile_release/*.yaml") do |f| "#{$root}/lib/arch_obj_models/portfolio.rb", "#{$root}/lib/portfolio_design.rb", "#{$root}/lib/design.rb", - "#{$root}/backends/profile_doc/templates/profile.adoc.erb" + "#{PROFILE_DOC_DIR}/templates/profile.adoc.erb" ].concat(profile_pathnames) do |t| # Create BaseArchitecture object. Function located in top-level Rakefile. puts "UPDATE: Creating BaseArchitecture #{base_isa_name} for #{t}" @@ -86,6 +86,8 @@ Dir.glob("#{$root}/arch/profile_release/*.yaml") do |f| erb = ERB.new(File.read(template_path), trim_mode: "-") erb.filename = template_path.to_s + FileUtils.mkdir_p File.dirname(t.name) + # Convert ERB to final ASCIIDOC. Note that this code is broken up into separate function calls # each with a variable name to aid in running a command-line debugger on this code. puts "UPDATE: Converting ERB template to adoc for #{profile_release_name}" From 9cb57ea335d0c89b701ee35c20ed84ea0ba2e810 Mon Sep 17 00:00:00 2001 From: James Ball Date: Wed, 1 Jan 2025 14:35:09 -0800 Subject: [PATCH 04/21] Fixing comments --- backends/profile_doc/tasks.rake | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/backends/profile_doc/tasks.rake b/backends/profile_doc/tasks.rake index 52aad0fef..5949269f9 100644 --- a/backends/profile_doc/tasks.rake +++ b/backends/profile_doc/tasks.rake @@ -59,7 +59,7 @@ Dir.glob("#{$root}/arch/profile_release/*.yaml") do |f| puts "UPDATE: Creating BaseArchitecture #{base_isa_name} for #{t}" base_arch = base_arch_for(base_isa_name, base) - # Create CertModel for specific certificate model as specified in its arch YAML file. + # Create PortfolioRelease for specific portfolio release as specified in its arch YAML file. # The Architecture object also creates all other portfolio-related class instances from their arch YAML files. # None of these objects are provided with a Design object when created. puts "UPDATE: Creating Profile Release for #{profile_release_name} using #{base_isa_name}" @@ -151,7 +151,7 @@ namespace :gen do Generate profile documentation for a specific release as a PDF. Required options: - profile_release_name - The key of the certification model under arch/certificate_model + profile_release_name - The key of the profile release under arch/portfolio_release DESC task :profile_release_pdf, [:profile_release_name] do |_t, args| profile_release_name = args[:profile_release_name] @@ -176,7 +176,7 @@ namespace :gen do end unless File.exist?("#{$root}/arch/profile_release/#{profile_release_name}.yaml") - warn "No certification model named '#{profile_release_name}' found in arch/profile_release" + warn "No profile release named '#{profile_release_name}' found in arch/profile_release" exit 1 end From 4cfcda5eb2af1d30ea14440f9a335918e6b15083 Mon Sep 17 00:00:00 2001 From: james-ball-qualcomm Date: Thu, 2 Jan 2025 00:42:24 +0000 Subject: [PATCH 05/21] Fixes to uniq! (returns nil sometimes, not good). --- lib/arch_obj_models/portfolio.rb | 12 +++++++----- lib/arch_obj_models/profile.rb | 5 +++-- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/lib/arch_obj_models/portfolio.rb b/lib/arch_obj_models/portfolio.rb index 7b6fdb4e5..5c17218df 100644 --- a/lib/arch_obj_models/portfolio.rb +++ b/lib/arch_obj_models/portfolio.rb @@ -87,7 +87,7 @@ def in_scope_ext_reqs @in_scope_ext_reqs += portfolio.in_scope_ext_reqs end - @in_scope_ext_reqs.uniq(&:name).sort_by(&:name) + @in_scope_ext_reqs = @in_scope_ext_reqs.uniq(&:name).sort_by(&:name) end # @return [Array] Sorted list of all mandatory extension requirements listed by the group. @@ -99,7 +99,7 @@ def mandatory_ext_reqs @mandatory_ext_reqs += portfolio.mandatory_ext_reqs end - @mandatory_ext_reqs.uniq(&:name).sort_by(&:name) + @mandatory_ext_reqs = @mandatory_ext_reqs.uniq(&:name).sort_by(&:name) end # @return [Array] Sorted list of all optional extension requirements listed by the group. @@ -111,7 +111,7 @@ def optional_ext_reqs @optional_ext_reqs += portfolio.optional_ext_reqs end - @optional_ext_reqs.uniq(&:name).sort_by(&:name) + @optional_ext_reqs = @optional_ext_reqs.uniq(&:name).sort_by(&:name) end # @return [Array] Sorted list of all mandatory or optional extensions referenced by the group. @@ -123,7 +123,8 @@ def in_scope_extensions @in_scope_extensions += portfolio.in_scope_extensions end - @in_scope_extensions.uniq(&:name).sort_by(&:name) + @in_scope_extensions = @in_scope_extensions.uniq(&:name).sort_by(&:name) + end # @return [Array] Sorted list of all instructions associated with extensions listed as @@ -137,7 +138,8 @@ def in_scope_instructions @in_scope_instructions += portfolio.in_scope_instructions end - @in_scope_instructions.uniq(&:name).sort_by(&:name) + @in_scope_instructions = + @in_scope_instructions.uniq(&:name).sort_by(&:name) end # @return [Array] Unsorted list of all CSRs associated with extensions listed as diff --git a/lib/arch_obj_models/profile.rb b/lib/arch_obj_models/profile.rb index f33f3c560..d74c22dad 100644 --- a/lib/arch_obj_models/profile.rb +++ b/lib/arch_obj_models/profile.rb @@ -68,7 +68,7 @@ def in_scope_extensions @in_scope_extensions += profile.in_scope_extensions end - @in_scope_extensions.uniq(&:name).sort_by!(&:name) + @in_scope_extensions = @in_scope_extensions.uniq(&:name).sort_by(&:name) end # @return [Array] Sorted list of all potential extensions with my processor kind @@ -80,7 +80,8 @@ def in_scope_extensions_matching_processor_kind @in_scope_extensions_matching_processor_kind += profile.in_scope_extensions end - @in_scope_extensions_matching_processor_kind.uniq(&:name).sort_by!(&:name) + @in_scope_extensions_matching_processor_kind = + @in_scope_extensions_matching_processor_kind.uniq(&:name).sort_by(&:name) end end From e8465dd8c2925d8e525e5d7ddcf68381e7c33595 Mon Sep 17 00:00:00 2001 From: James Ball Date: Wed, 1 Jan 2025 16:55:04 -0800 Subject: [PATCH 06/21] Improve tasks.rake message --- backends/certificate_doc/tasks.rake | 2 +- backends/profile_doc/tasks.rake | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/backends/certificate_doc/tasks.rake b/backends/certificate_doc/tasks.rake index f8437da8d..941ba21df 100644 --- a/backends/certificate_doc/tasks.rake +++ b/backends/certificate_doc/tasks.rake @@ -42,7 +42,7 @@ Dir.glob("#{$root}/arch/certificate_model/*.yaml") do |f| puts "UPDATE: Creating CertModel for #{cert_model_name} using base #{base_isa_name}" cert_model = base_arch.cert_model(cert_model_name) - puts "UPDATE: Creating PortfolioDesign using CertModel #{cert_model_name}" + puts "UPDATE: Creating PortfolioDesign using certificate model #{cert_model_name}" # Create the one PortfolioDesign object required for the ERB evaluation. portfolio_design = portfolio_design_for(cert_model_name, base_arch, base, [cert_model]) diff --git a/backends/profile_doc/tasks.rake b/backends/profile_doc/tasks.rake index 3efc60dfc..fd5739c83 100644 --- a/backends/profile_doc/tasks.rake +++ b/backends/profile_doc/tasks.rake @@ -65,7 +65,7 @@ Dir.glob("#{$root}/arch/profile_release/*.yaml") do |f| puts "UPDATE: Creating Profile Release for #{profile_release_name} using #{base_isa_name}" profile_release = base_arch.profile_release(profile_release_name) - puts "UPDATE: Creating PortfolioDesign using ProfileRelease #{profile_release_name}" + puts "UPDATE: Creating PortfolioDesign using profile release #{profile_release_name}" # Create the one PortfolioDesign object required for the ERB evaluation. # Provide it with all the profiles in this ProfileRelease. portfolio_design = portfolio_design_for(profile_release_name, base_arch, base, From 0674ef77b814491785c5a9c86277731705353305 Mon Sep 17 00:00:00 2001 From: james-ball-qualcomm Date: Thu, 2 Jan 2025 18:57:09 +0000 Subject: [PATCH 07/21] Found some extra places to rename cfg_arch to design. --- backends/arch_gen/lib/arch_gen.rb | 4 ++-- docs/ruby/Instruction.html | 8 ++++---- docs/ruby/method_list.html | 2 +- lib/arch_obj_models/csr.rb | 12 ++++++------ lib/arch_obj_models/csr_field.rb | 24 ++++++++++++------------ lib/arch_obj_models/instruction.rb | 2 +- 6 files changed, 26 insertions(+), 26 deletions(-) diff --git a/backends/arch_gen/lib/arch_gen.rb b/backends/arch_gen/lib/arch_gen.rb index cea28daf9..d4dca1c11 100644 --- a/backends/arch_gen/lib/arch_gen.rb +++ b/backends/arch_gen/lib/arch_gen.rb @@ -644,7 +644,7 @@ def maybe_add_csr(csr_name, extra_env = {}) impl_ext end belongs = - csr_obj.exists_in_cfg?(cfg_arch_mock) + csr_obj.exists_in_design?(cfg_arch_mock) @implemented_csrs ||= [] @@ -898,7 +898,7 @@ def maybe_add_inst(inst_name, extra_env = {}) impl_ext end belongs = - inst_obj.exists_in_cfg?(cfg_arch_mock) + inst_obj.exists_in_design?(cfg_arch_mock) @implemented_instructions ||= [] @implemented_instructions << inst_name if belongs diff --git a/docs/ruby/Instruction.html b/docs/ruby/Instruction.html index c343c424e..ab0ac71c7 100644 --- a/docs/ruby/Instruction.html +++ b/docs/ruby/Instruction.html @@ -314,7 +314,7 @@

  • - #exists_in_cfg?(possible_xlens, extensions) ⇒ Boolean + #exists_in_design?(possible_xlens, extensions) ⇒ Boolean @@ -1310,9 +1310,9 @@

    -

    +

    - #exists_in_cfg?(possible_xlens, extensions) ⇒ Boolean + #exists_in_design?(possible_xlens, extensions) ⇒ Boolean @@ -1398,7 +1398,7 @@

    # File 'lib/arch_def.rb', line 1178
     
    -def exists_in_cfg?(possible_xlens, extensions)
    +def exists_in_design?(possible_xlens, extensions)
       (@data["base"].nil? || (possible_xlens.include?(@data["base"]))) &&
         extensions.any? { |e| defined_by?(e) } &&
         extensions.none? { |e| excluded_by?(e) }
    diff --git a/docs/ruby/method_list.html b/docs/ruby/method_list.html
    index 25dc25cdb..e63b227ee 100644
    --- a/docs/ruby/method_list.html
    +++ b/docs/ruby/method_list.html
    @@ -1142,7 +1142,7 @@ 

    Method List

  • diff --git a/lib/arch_obj_models/csr.rb b/lib/arch_obj_models/csr.rb index a1cb9044d..5b441bb51 100644 --- a/lib/arch_obj_models/csr.rb +++ b/lib/arch_obj_models/csr.rb @@ -338,7 +338,7 @@ def implemented_fields(design) end @implemented_fields = fields.select do |f| - f.exists_in_cfg?(design) + f.exists_in_design?(design) end end @@ -521,7 +521,7 @@ def wavedrom_desc(design, effective_xlen, exclude_unimplemented: false, optional desc["reg"] << { "bits" => n, type: 1 } end - if design.partially_configured? && field.optional_in_cfg?(design) + if design.partially_configured? && field.optional_in_design?(design) desc["reg"] << { "bits" => field.location(design, effective_xlen).size, "name" => field.name, type: optional_type } else desc["reg"] << { "bits" => field.location(design, effective_xlen).size, "name" => field.name, type: 3 } @@ -540,7 +540,7 @@ def wavedrom_desc(design, effective_xlen, exclude_unimplemented: false, optional # @param design [Design] # @return [Boolean] whether or not the CSR is possibly implemented given the supplies config options - def exists_in_cfg?(design) + def exists_in_design?(design) if design.fully_configured? (@data["base"].nil? || (design.possible_xlens.include? @data["base"])) && design.transitive_implemented_extensions.any? { |e| defined_by?(e) } @@ -552,10 +552,10 @@ def exists_in_cfg?(design) # @param design [Design] # @return [Boolean] whether or not the CSR is optional in the config - def optional_in_cfg?(design) - raise "optional_in_cfg? should only be used by a partially-specified arch def" unless design.partially_configured? + def optional_in_design?(design) + raise "optional_in_design? should only be used by a partially-specified arch def" unless design.partially_configured? - exists_in_cfg?(design) && + exists_in_design?(design) && design.mandatory_extensions.all? do |ext_req| ext_req.satisfying_versions.none? do |ext_ver| defined_by?(ext_ver) diff --git a/lib/arch_obj_models/csr_field.rb b/lib/arch_obj_models/csr_field.rb index 4dc0b9d1e..143b4f612 100644 --- a/lib/arch_obj_models/csr_field.rb +++ b/lib/arch_obj_models/csr_field.rb @@ -17,9 +17,7 @@ class CsrField < DatabaseObject # @return [Integer] The base XLEN required for this CsrField to exist. One of [32, 64] # @return [nil] if the CsrField exists in any XLEN - def base - @data["base"] - end + def base = @data["base"] # @param parent_csr [Csr] The Csr that defined this field # @param field_data [Hash] Field data from the arch spec @@ -32,28 +30,28 @@ def initialize(parent_csr, field_name, field_data) # @param possible_xlens [Array] List of xlens that be used in any implemented mode # @param extensions [Array] List of extensions implemented # @return [Boolean] whether or not the instruction is implemented given the supplies config options - def exists_in_cfg?(design) + def exists_in_design?(design) if design.fully_configured? - parent.exists_in_cfg?(design) && + parent.exists_in_design?(design) && (@data["base"].nil? || design.possible_xlens.include?(@data["base"])) && (@data["definedBy"].nil? || design.transitive_implemented_extensions.any? { |ext_ver| defined_by?(ext_ver) }) else raise "unexpected type" unless design.partially_configured? - parent.exists_in_cfg?(design) && + parent.exists_in_design?(design) && (@data["base"].nil? || design.possible_xlens.include?(@data["base"])) && (@data["definedBy"].nil? || design.prohibited_extensions.none? { |ext_req| ext_req.satisfying_versions.any? { |ext_ver| defined_by?(ext_ver) } }) end end # @return [Boolean] For a partially configured design, whether or not the field is optional (not mandatory or prohibited) - def optional_in_cfg?(design) - raise "optional_in_cfg? should only be called on a partially configured design" unless design.partially_configured? + def optional_in_design?(design) + raise "optional_in_design? should only be called on a partially configured design" unless design.partially_configured? - exists_in_cfg?(design) && + exists_in_design?(design) && ( if data["definedBy"].nil? - parent.optional_in_cfg?(design) + parent.optional_in_design?(design) else design.mandatory_extensions.all? do |ext_req| ext_req.satisfying_versions.none? do |ext_ver| @@ -147,7 +145,7 @@ def pruned_type_ast(symtab) @pruned_type_asts[symtab_hash] = ast end - # returns the definitive type for a configuration + # returns the definitive type for a design # # @param symtab [SymbolTable] Symbol table # @return [String] @@ -162,7 +160,9 @@ def type(symtab) raise ArgumentError, "Argument 1 should be a symtab" unless symtab.is_a?(Idl::SymbolTable) unless @type_cache.nil? - raise "Different design for type #{@type_cache.keys}, #{symtab.design}" unless @type_cache.key?(symtab.design) + unless @type_cache.key?(symtab.design) + raise "Different design for type #{@type_cache.keys}, #{symtab.design}" + end return @type_cache[symtab.design] end diff --git a/lib/arch_obj_models/instruction.rb b/lib/arch_obj_models/instruction.rb index ff469ca8a..5899df9af 100644 --- a/lib/arch_obj_models/instruction.rb +++ b/lib/arch_obj_models/instruction.rb @@ -740,7 +740,7 @@ def excluded_by?(*args) # @param design [Design] The design # @return [Boolean] whether or not the instruction is implemented given the supplies config options - def exists_in_cfg?(design) + def exists_in_design?(design) if design.fully_configured? (@data["base"].nil? || (design.possible_xlens.include? @data["base"])) && design.implemented_extensions.any? { |e| defined_by?(e) } && From 70c1d7933f3113a9a3791a9b8fc9b5e65c0808c6 Mon Sep 17 00:00:00 2001 From: James Ball Date: Thu, 2 Jan 2025 20:12:44 +0000 Subject: [PATCH 08/21] Looks like I found a bug in the csr_field type_cache. It was barfing if a different design was passed to it which seems bizzare since it goes out of its way to support having a different type for different designs. So, I modified it to work like the type cache in ast and do what I think it wanted to do in the first place. --- .vscode/launch.json | 9 +++++++++ lib/arch_obj_models/csr_field.rb | 8 ++------ 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 09d8dae10..19c008950 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -19,6 +19,15 @@ "args": [], "askParameters": false }, + { + "type": "rdbg", + "name": "portfolios", + "request": "launch", + "command": "bundle exec rake", + "script": "portfolios", + "args": [], + "askParameters": false + }, { "type": "rdbg", "name": "RVA20", diff --git a/lib/arch_obj_models/csr_field.rb b/lib/arch_obj_models/csr_field.rb index 4dc0b9d1e..d40c61e19 100644 --- a/lib/arch_obj_models/csr_field.rb +++ b/lib/arch_obj_models/csr_field.rb @@ -27,6 +27,7 @@ def initialize(parent_csr, field_name, field_data) super(field_data, parent_csr.data_path, parent_csr.arch) @name = field_name @parent = parent_csr + @type_cache = {} end # @param possible_xlens [Array] List of xlens that be used in any implemented mode @@ -161,11 +162,7 @@ def pruned_type_ast(symtab) def type(symtab) raise ArgumentError, "Argument 1 should be a symtab" unless symtab.is_a?(Idl::SymbolTable) - unless @type_cache.nil? - raise "Different design for type #{@type_cache.keys}, #{symtab.design}" unless @type_cache.key?(symtab.design) - - return @type_cache[symtab.design] - end + return @type_cache[symtab.design] if @type_cache.key?(symtab.design) type = if @data.key?("type") @@ -210,7 +207,6 @@ def type(symtab) # end end - @type_cache ||= {} @type_cache[symtab.design] = type end From e06c4f571f3cc271ba23cbcb91029cb2524998cd Mon Sep 17 00:00:00 2001 From: James Ball Date: Thu, 2 Jan 2025 22:57:56 +0000 Subject: [PATCH 09/21] Rename routines ending in _extensions to _ext_reqs or _ext_vers to make it clearer the return type since I found bugs in the code related to this. --- backends/cfg_html_doc/adoc_gen.rake | 4 +- .../cfg_html_doc/templates/config.adoc.erb | 2 +- backends/cfg_html_doc/templates/ext.adoc.erb | 2 +- backends/cfg_html_doc/templates/toc.adoc.erb | 2 +- lib/arch_obj_models/csr.rb | 6 +- lib/arch_obj_models/csr_field.rb | 6 +- lib/arch_obj_models/extension.rb | 4 +- lib/arch_obj_models/instruction.rb | 19 ++++-- lib/cfg_arch.rb | 65 +++++++++---------- lib/design.rb | 26 ++++---- lib/portfolio_design.rb | 14 ++-- 11 files changed, 77 insertions(+), 73 deletions(-) diff --git a/backends/cfg_html_doc/adoc_gen.rake b/backends/cfg_html_doc/adoc_gen.rake index 4302a690d..eab5651f2 100644 --- a/backends/cfg_html_doc/adoc_gen.rake +++ b/backends/cfg_html_doc/adoc_gen.rake @@ -42,7 +42,7 @@ require "ruby-prof" # RubyProf::FlatPrinter.new(result).print(STDOUT) end when "ext" - cfg_arch.transitive_implemented_extensions.each do |ext_version| + cfg_arch.transitive_implemented_ext_vers.each do |ext_version| ext = cfg_arch.extension(ext_version.name) path = dir_path / "#{ext.name}.adoc" puts " Generating #{path}" @@ -90,7 +90,7 @@ require "ruby-prof" end when "ext" puts "Generting full extension list" - cfg_arch.transitive_implemented_extensions.each do |ext_version| + cfg_arch.transitive_implemented_ext_vers.each do |ext_version| lines << " * `#{ext_version.name}` #{ext_version.ext.long_name}" end when "inst" diff --git a/backends/cfg_html_doc/templates/config.adoc.erb b/backends/cfg_html_doc/templates/config.adoc.erb index 73898d049..2a777cf39 100644 --- a/backends/cfg_html_doc/templates/config.adoc.erb +++ b/backends/cfg_html_doc/templates/config.adoc.erb @@ -4,7 +4,7 @@ |=== | Name | Version -<%- cfg_arch.transitive_implemented_extensions.sort{ |a,b| a.name <=> b.name }.each do |e| -%> +<%- cfg_arch.transitive_implemented_ext_vers.sort{ |a,b| a.name <=> b.name }.each do |e| -%> | `<%= e.name %>` | <%= e.version_spec %> <%- end -%> |=== diff --git a/backends/cfg_html_doc/templates/ext.adoc.erb b/backends/cfg_html_doc/templates/ext.adoc.erb index 8111a2ae5..089409ee0 100644 --- a/backends/cfg_html_doc/templates/ext.adoc.erb +++ b/backends/cfg_html_doc/templates/ext.adoc.erb @@ -7,7 +7,7 @@ Implemented Version:: <%= ext_version.version_str %> == Versions <%- ext.versions.each do |v| -%> -<%- implemented = cfg_arch.transitive_implemented_extensions.include?(v) -%> +<%- implemented = cfg_arch.transitive_implemented_ext_vers.include?(v) -%> <%= v.version_str %>:: Ratification date::: <%= v.ratification_date %> diff --git a/backends/cfg_html_doc/templates/toc.adoc.erb b/backends/cfg_html_doc/templates/toc.adoc.erb index 4f6b5d269..fb4d2142e 100644 --- a/backends/cfg_html_doc/templates/toc.adoc.erb +++ b/backends/cfg_html_doc/templates/toc.adoc.erb @@ -1,7 +1,7 @@ * xref:ROOT:config.adoc[Configuration] .Extensions -<%- cfg_arch.transitive_implemented_extensions.sort { |a, b| a.name <=> b.name }.each do |ext| -%> +<%- cfg_arch.transitive_implemented_ext_vers.sort { |a, b| a.name <=> b.name }.each do |ext| -%> * %%LINK%ext;<%= ext.name %>;<%= ext.name %>%% <%- end -%> diff --git a/lib/arch_obj_models/csr.rb b/lib/arch_obj_models/csr.rb index 5b441bb51..9bd386be1 100644 --- a/lib/arch_obj_models/csr.rb +++ b/lib/arch_obj_models/csr.rb @@ -543,10 +543,10 @@ def wavedrom_desc(design, effective_xlen, exclude_unimplemented: false, optional def exists_in_design?(design) if design.fully_configured? (@data["base"].nil? || (design.possible_xlens.include? @data["base"])) && - design.transitive_implemented_extensions.any? { |e| defined_by?(e) } + design.transitive_implemented_ext_vers.any? { |e| defined_by?(e) } else (@data["base"].nil? || (design.possible_xlens.include? @data["base"])) && - design.prohibited_extensions.none? { |ext_req| ext_req.satisfying_versions.any? { |e| defined_by?(e) } } + design.prohibited_ext_reqs.none? { |ext_req| ext_req.satisfying_versions.any? { |e| defined_by?(e) } } end end @@ -556,7 +556,7 @@ def optional_in_design?(design) raise "optional_in_design? should only be used by a partially-specified arch def" unless design.partially_configured? exists_in_design?(design) && - design.mandatory_extensions.all? do |ext_req| + design.mandatory_ext_reqs.all? do |ext_req| ext_req.satisfying_versions.none? do |ext_ver| defined_by?(ext_ver) end diff --git a/lib/arch_obj_models/csr_field.rb b/lib/arch_obj_models/csr_field.rb index 44f9b0940..3fc1c83ad 100644 --- a/lib/arch_obj_models/csr_field.rb +++ b/lib/arch_obj_models/csr_field.rb @@ -35,13 +35,13 @@ def exists_in_design?(design) if design.fully_configured? parent.exists_in_design?(design) && (@data["base"].nil? || design.possible_xlens.include?(@data["base"])) && - (@data["definedBy"].nil? || design.transitive_implemented_extensions.any? { |ext_ver| defined_by?(ext_ver) }) + (@data["definedBy"].nil? || design.transitive_implemented_ext_vers.any? { |ext_ver| defined_by?(ext_ver) }) else raise "unexpected type" unless design.partially_configured? parent.exists_in_design?(design) && (@data["base"].nil? || design.possible_xlens.include?(@data["base"])) && - (@data["definedBy"].nil? || design.prohibited_extensions.none? { |ext_req| ext_req.satisfying_versions.any? { |ext_ver| defined_by?(ext_ver) } }) + (@data["definedBy"].nil? || design.prohibited_ext_reqs.none? { |ext_req| ext_req.satisfying_versions.any? { |ext_ver| defined_by?(ext_ver) } }) end end @@ -54,7 +54,7 @@ def optional_in_design?(design) if data["definedBy"].nil? parent.optional_in_design?(design) else - design.mandatory_extensions.all? do |ext_req| + design.mandatory_ext_reqs.all? do |ext_req| ext_req.satisfying_versions.none? do |ext_ver| defined_by?(ext_ver) end diff --git a/lib/arch_obj_models/extension.rb b/lib/arch_obj_models/extension.rb index a0dc61c49..1c24adf6c 100644 --- a/lib/arch_obj_models/extension.rb +++ b/lib/arch_obj_models/extension.rb @@ -500,7 +500,7 @@ def <=>(other) end end - # @return [Array] the list of CSRs implemented by this extension version (may be empty) + # @return [Array] List of CSRs implemented by this extension version (may be empty) def implemented_csrs return @implemented_csrs unless @implemented_csrs.nil? @@ -509,7 +509,7 @@ def implemented_csrs end end - # @return [Array] the list of insts implemented by this extension version (may be empty) + # @return [Array] List of insts implemented by this extension version (may be empty) def implemented_instructions return @implemented_instructions unless @implemented_instructions.nil? diff --git a/lib/arch_obj_models/instruction.rb b/lib/arch_obj_models/instruction.rb index 5899df9af..029858b28 100644 --- a/lib/arch_obj_models/instruction.rb +++ b/lib/arch_obj_models/instruction.rb @@ -739,18 +739,23 @@ def excluded_by?(*args) end # @param design [Design] The design - # @return [Boolean] whether or not the instruction is implemented given the supplies config options + # @return [Boolean] whether or not the instruction is implemented given the supplied design + # + # TODO: Does this function actually work for a partially configured design? + # It is calling DatabaseObject.defined_by? with ExtensionRequirement objects + # returned from Design.prohibited_ext_reqs() and Design.mandatory_ext_reqs() + # but only accepts an ExtensionVersion object. def exists_in_design?(design) if design.fully_configured? - (@data["base"].nil? || (design.possible_xlens.include? @data["base"])) && - design.implemented_extensions.any? { |e| defined_by?(e) } && - design.implemented_extensions.none? { |e| excluded_by?(e) } + (@data["base"].nil? || (design.possible_xlens.include?(@data["base"]))) && + design.implemented_ext_vers.any? { |ext_ver| defined_by?(ext_ver) } && + design.implemented_ext_vers.none? { |ext_ver| excluded_by?(ext_ver) } else raise "unexpected design type" unless design.partially_configured? - (@data["base"].nil? || (design.possible_xlens.include? @data["base"])) && - design.prohibited_extensions.none? { |e| defined_by?(e) } && - design.mandatory_extensions.none? { |e| excluded_by?(e) } + (@data["base"].nil? || (design.possible_xlens.include?(@data["base"]))) && + design.prohibited_ext_reqs.none? { |ext_req| defined_by?(ext_req) } && + design.mandatory_ext_reqs.none? { |ext_req| excluded_by?(ext_req) } end end end diff --git a/lib/cfg_arch.rb b/lib/cfg_arch.rb index 655e4f964..9c8c362f5 100644 --- a/lib/cfg_arch.rb +++ b/lib/cfg_arch.rb @@ -137,9 +137,8 @@ def params_with_value return @params_with_value if @config.unconfigured? if @config.fully_configured? - transitive_implemented_extensions.each do |ext_version| - ext = extension(ext_version.name) - ext.params.each do |ext_param| + transitive_implemented_ext_vers.each do |ext_version| + ext_version.extension.params.each do |ext_param| next unless @config.param_values.key?(ext_param.name) @params_with_value << ExtensionParameterWithValue.new( @@ -149,7 +148,7 @@ def params_with_value end end elsif @config.partially_configured? - mandatory_extensions.each do |ext_req| + mandatory_ext_reqs.each do |ext_req| ext_req.extension.params.each do |ext_param| # Params listed in the config always only have one value. next unless @config.param_values.key?(ext_param.name) @@ -183,19 +182,19 @@ def params_without_value end # @return [Array] List of all implemented extension versions. - def implemented_extensions - return @implemented_extensions unless @implemented_extensions.nil? + def implemented_ext_vers + return @implemented_ext_vers unless @implemented_ext_vers.nil? - @implemented_extensions = @config.implemented_extensions.map do |e| + @implemented_ext_vers = @config.implemented_ext_vers.map do |e| ExtensionVersion.new(e["name"], e["version"], arch, fail_if_version_does_not_exist: true) end end # @return [Array] List of all mandatory extension requirements - def mandatory_extensions - return @mandatory_extensions unless @mandatory_extensions.nil? + def mandatory_ext_reqs + return @mandatory_ext_reqs unless @mandatory_ext_reqs.nil? - @mandatory_extensions = @config.mandatory_extensions.map do |e| + @mandatory_ext_reqs = @config.mandatory_ext_reqs.map do |e| ext = extension(e["name"]) raise "Cannot find extension #{e['name']} in the architecture definition" if ext.nil? @@ -206,12 +205,12 @@ def mandatory_extensions # @return [Array] List of all extensions that are prohibited. # This includes extensions explicitly prohibited by the config file # and extensions that conflict with a mandatory extension. - def prohibited_extensions - return @prohibited_extensions unless @prohibited_extensions.nil? + def prohibited_ext_reqs + return @prohibited_ext_reqs unless @prohibited_ext_reqs.nil? if @config.partially_configured? - @prohibited_extensions = - @config.prohibited_extensions.map do |e| + @prohibited_ext_reqs = + @config.prohibited_ext_reqs.map do |e| ext = extension(e["name"]) raise "Cannot find extension #{e['name']} in the architecture definition" if ext.nil? @@ -219,34 +218,34 @@ def prohibited_extensions end # now add any extensions that are prohibited by a mandatory extension - mandatory_extensions.each do |ext_req| + mandatory_ext_reqs.each do |ext_req| ext_req.extension.conflicts.each do |conflict| - if @prohibited_extensions.none? { |prohibited_ext| prohibited_ext.name == conflict.name } - @prohibited_extensions << conflict + if @prohibited_ext_reqs.none? { |prohibited_ext_req| prohibited_ext_req.name == conflict.name } + @prohibited_ext_reqs << conflict else # pick whichever requirement is more expansive - p = @prohibited_extensions.find { |prohibited_ext| prohibited_ext.name == confict.name } + p = @prohibited_ext_reqs.find { |prohibited_ext_req| prohibited_ext_req.name == confict.name } if p.version_requirement.subsumes?(conflict.version_requirement) - @prohibited_extensions.delete(p) - @prohibited_extensions << conflict + @prohibited_ext_reqs.delete(p) + @prohibited_ext_reqs << conflict end end end end - @prohibited_extensions + @prohibited_ext_reqs elsif @config.fully_configured? - prohibited_ext_versions = [] + prohibited_ext_vers = [] extensions.each do |ext| ext.versions.each do |ext_ver| - prohibited_ext_versions << ext_ver unless transitive_implemented_extensions.include?(ext_ver) + prohibited_ext_vers << ext_ver unless transitive_implemented_ext_vers.include?(ext_ver) end end - @prohibited_extensions = [] - prohibited_ext_versions.group_by(&:name).each_value do |ext_ver_list| + @prohibited_ext_reqs = [] + prohibited_ext_vers.group_by(&:name).each_value do |ext_ver_list| if ext_ver_list.sort == ext_ver_list[0].ext.versions.sort # excludes every version - @prohibited_extensions << + @prohibited_ext_reqs << ExtensionRequirement.new( ext_ver_list[0].ext.name, ">= #{ext_ver_list.min.version_spec.canonical}", arch, presence: "prohibited" @@ -257,7 +256,7 @@ def prohibited_extensions raise "Expected only a single element" unless allowed_version_list.size == 1 allowed_version = allowed_version_list[0] - @prohibited_extensions << + @prohibited_ext_reqs << ExtensionRequirement.new( ext_ver_list[0].ext.name, "!= #{allowed_version.version_spec.canonical}", arch, presence: "prohibited" @@ -268,9 +267,9 @@ def prohibited_extensions end end else - @prohibited_extensions = [] + @prohibited_ext_reqs = [] end - @prohibited_extensions + @prohibited_ext_reqs end # @overload ext?(ext_name) @@ -288,7 +287,7 @@ def ext?(ext_name, *ext_version_requirements) result = if @config.fully_configured? - transitive_implemented_extensions.any? do |e| + transitive_implemented_ext_vers.any? do |e| if ext_version_requirements.empty? e.name == ext_name.to_s else @@ -297,7 +296,7 @@ def ext?(ext_name, *ext_version_requirements) end end elsif @config.partially_configured? - mandatory_extensions.any? do |e| + mandatory_ext_reqs.any? do |e| if ext_version_requirements.empty? e.name == ext_name.to_s else @@ -320,8 +319,8 @@ def ext?(ext_name, *ext_version_requirements) ##################################### # @return [Array] List of all extensions known to be implemented in this config, including transitive implications - def transitive_implemented_extensions - raise "implemented_extensions is only valid for a fully configured definition" unless @config.fully_configured? + def transitive_implemented_ext_vers + raise "transitive_implemented_ext_vers is only valid for a fully configured definition" unless @config.fully_configured? super end diff --git a/lib/design.rb b/lib/design.rb index a714d21b9..a262eea96 100644 --- a/lib/design.rb +++ b/lib/design.rb @@ -117,19 +117,19 @@ def params_without_value end # @return [Array] List of all implemented extension versions. - def implemented_extensions + def implemented_ext_vers raise "Abstract Method: Must be provided in child class" end # @return [Array] List of all mandatory extension requirements - def mandatory_extensions + def mandatory_ext_reqs raise "Abstract Method: Must be provided in child class" end # @return [Array] List of all extensions that are prohibited. # This includes extensions explicitly prohibited by the design # and extensions that conflict with a mandatory extension. - def prohibited_extensions + def prohibited_ext_reqs raise "Abstract Method: Must be provided in child class" end @@ -261,15 +261,15 @@ def type_check(show_progress: true, io: $stdout) end # @return [Array] List of all extensions known to be implemented in this design, including transitive implications - def transitive_implemented_extensions - return @transitive_implemented_extensions unless @transitive_implemented_extensions.nil? + def transitive_implemented_ext_vers + return @transitive_implemented_ext_vers unless @transitive_implemented_ext_vers.nil? - list = implemented_extensions + list = implemented_ext_vers list.each do |e| implications = e.transitive_implications list.concat(implications) unless implications.empty? end - @transitive_implemented_extensions = list.uniq.sort + @transitive_implemented_ext_vers = list.uniq.sort end # @overload prohibited_ext?(ext) @@ -283,9 +283,9 @@ def transitive_implemented_extensions # @return [Boolean] def prohibited_ext?(ext) if ext.is_a?(ExtensionVersion) - prohibited_extensions.any? { |ext_req| ext_req.satisfied_by?(ext) } + prohibited_ext_reqs.any? { |ext_req| ext_req.satisfied_by?(ext) } elsif ext.is_a?(String) || ext.is_a?(Symbol) - prohibited_extensions.any? { |ext_req| ext_req.name == ext.to_s } + prohibited_ext_reqs.any? { |ext_req| ext_req.name == ext.to_s } else raise ArgumentError, "Argument to prohibited_ext? should be an ExtensionVersion or a String" end @@ -296,7 +296,7 @@ def implemented_exception_codes return @implemented_exception_codes unless @implemented_exception_codes.nil? @implemented_exception_codes = - implemented_extensions.reduce([]) do |list, ext_version| + implemented_ext_vers.reduce([]) do |list, ext_version| ecodes = extension(ext_version.name)["exception_codes"] next list if ecodes.nil? @@ -319,7 +319,7 @@ def implemented_interrupt_codes return @implemented_interrupt_codes unless @implemented_interrupt_codes.nil? @implemented_interupt_codes = - implemented_extensions.reduce([]) do |list, ext_version| + implemented_ext_vers.reduce([]) do |list, ext_version| icodes = extension(ext_version.name)["interrupt_codes"] next list if icodes.nil? @@ -347,13 +347,13 @@ def functions # @return [Array] List of all implemented CSRs def transitive_implemented_csrs @transitive_implemented_csrs ||= - transitive_implemented_extensions.map(&:implemented_csrs).flatten.uniq.sort + transitive_implemented_ext_vers.map(&:implemented_csrs).flatten.uniq.sort end # @return [Array] List of all implemented instructions def transitive_implemented_instructions @transitive_implemented_instructions ||= - transitive_implemented_extensions.map(&:implemented_instructions).flatten.uniq.sort + transitive_implemented_ext_vers.map(&:implemented_instructions).flatten.uniq.sort end # @return [Array] List of all reachable IDL functions for the design diff --git a/lib/portfolio_design.rb b/lib/portfolio_design.rb index 3fb867e29..ad3a24ec6 100644 --- a/lib/portfolio_design.rb +++ b/lib/portfolio_design.rb @@ -99,7 +99,7 @@ def params_without_value @params_without_value end - def implemented_extensions + def implemented_ext_vers # Only supported by fully-configured configurations and a portfolio corresponds to a # partially-configured configuration. See the Config class for details. raise "Not supported for portfolio #{name}" @@ -109,8 +109,8 @@ def implemented_extensions # This includes extensions explicitly prohibited by the design # and extensions that conflict with a mandatory extension. # - # Assume there are none of these in a portfolio for now. - def prohibited_extensions = [] + # TODO: Assume there are none of these in a portfolio for now. + def prohibited_ext_reqs = [] # @overload ext?(ext_name) # @param ext_name [#to_s] Extension name (case sensitive) @@ -133,12 +133,12 @@ def ext?(ext_name, *ext_version_requirements) return cached_result unless cached_result.nil? result = - mandatory_extensions.any? do |ext| + mandatory_ext_reqs.any? do |ext_req| if ext_version_requirements.empty? - ext.name == ext_name.to_s + ext_req.name == ext_name.to_s else requirement = ExtensionRequirement.new(ext_name, *ext_version_requirements, arch) - ext.satisfying_versions.all? do |ext_ver| + ext_req.satisfying_versions.all? do |ext_ver| requirement.satisfied_by?(ext_ver) end end @@ -165,7 +165,7 @@ def unconfigured? = false ##################################### # @return [Array] List of all mandatory extension requirements - def mandatory_extensions = @portfolio_grp.mandatory_ext_reqs + def mandatory_ext_reqs = @portfolio_grp.mandatory_ext_reqs # @return [Hash] Fully-constrained parameter values (those with just one possible value for this design). def param_values = @portfolio_grp.param_values From df2a905af493ec4e9665d615606c935e7e0fd477 Mon Sep 17 00:00:00 2001 From: james-ball-qualcomm Date: Fri, 3 Jan 2025 01:49:59 +0000 Subject: [PATCH 10/21] RVB23 listed RVA as its class erroneously --- arch/profile_release/RVB23.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/profile_release/RVB23.yaml b/arch/profile_release/RVB23.yaml index 6090ff6bb..53eae651a 100644 --- a/arch/profile_release/RVB23.yaml +++ b/arch/profile_release/RVB23.yaml @@ -3,7 +3,7 @@ kind: profile release name: RVB23 marketing_name: RVB23 class: - $ref: profile_class/RVA.yaml# + $ref: profile_class/RVB.yaml# release: 23 state: ratified # current status ["ratified", "development"] ratification_date: "2023-04-03" From d990076987feec060c603784f222e3a311b84217 Mon Sep 17 00:00:00 2001 From: James Ball Date: Fri, 3 Jan 2025 02:09:46 +0000 Subject: [PATCH 11/21] Renamed mandatory_extensions to mandatory_ext_reqs in places I shouldn't have (i.e. in @config). Also true for implemented and prohibited. --- lib/cfg_arch.rb | 6 +++--- lib/config.rb | 4 ---- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/lib/cfg_arch.rb b/lib/cfg_arch.rb index 9c8c362f5..39c8e3376 100644 --- a/lib/cfg_arch.rb +++ b/lib/cfg_arch.rb @@ -185,7 +185,7 @@ def params_without_value def implemented_ext_vers return @implemented_ext_vers unless @implemented_ext_vers.nil? - @implemented_ext_vers = @config.implemented_ext_vers.map do |e| + @implemented_ext_vers = @config.implemented_extensions.map do |e| ExtensionVersion.new(e["name"], e["version"], arch, fail_if_version_does_not_exist: true) end end @@ -194,7 +194,7 @@ def implemented_ext_vers def mandatory_ext_reqs return @mandatory_ext_reqs unless @mandatory_ext_reqs.nil? - @mandatory_ext_reqs = @config.mandatory_ext_reqs.map do |e| + @mandatory_ext_reqs = @config.mandatory_extensions.map do |e| ext = extension(e["name"]) raise "Cannot find extension #{e['name']} in the architecture definition" if ext.nil? @@ -210,7 +210,7 @@ def prohibited_ext_reqs if @config.partially_configured? @prohibited_ext_reqs = - @config.prohibited_ext_reqs.map do |e| + @config.prohibited_extensions.map do |e| ext = extension(e["name"]) raise "Cannot find extension #{e['name']} in the architecture definition" if ext.nil? diff --git a/lib/config.rb b/lib/config.rb index 7d86d628e..344686551 100644 --- a/lib/config.rb +++ b/lib/config.rb @@ -142,10 +142,6 @@ def prohibited_extensions end end end - - # def prohibited_ext?(ext_name, cfg_arch) = prohibited_extensions(cfg_arch).any? { |e| e.name == ext_name.to_s } - - # def ext?(ext_name, cfg_arch) = mandatory_extensions(cfg_arch).any? { |e| e.name == ext_name.to_s } end ################################################################################################################ From 924ffdee5ccbb6de18748a6396dbf5d9ec688a1f Mon Sep 17 00:00:00 2001 From: James Ball Date: Fri, 3 Jan 2025 19:41:09 +0000 Subject: [PATCH 12/21] Fixed instructions and CSRs in #291 (CRDs list things like instructions, CSRs, and traps that aren't in the configuration). Still need to fix traps and interrupts. --- lib/arch_obj_models/csr.rb | 30 +++++++++++++---------------- lib/arch_obj_models/csr_field.rb | 15 +++++---------- lib/arch_obj_models/database_obj.rb | 22 ++++++++++++++++----- lib/arch_obj_models/extension.rb | 28 +++++++++++++++++++++++++++ lib/arch_obj_models/instruction.rb | 5 ++--- lib/arch_obj_models/portfolio.rb | 28 +++++++++++++++++++-------- lib/config.rb | 3 --- lib/portfolio_design.rb | 6 ++++-- 8 files changed, 89 insertions(+), 48 deletions(-) diff --git a/lib/arch_obj_models/csr.rb b/lib/arch_obj_models/csr.rb index 9bd386be1..0cc4b88fc 100644 --- a/lib/arch_obj_models/csr.rb +++ b/lib/arch_obj_models/csr.rb @@ -46,7 +46,7 @@ def defined_in_base64? = @data["base"].nil? || @data["base"] == 64 # @return [Boolean] true if this CSR is defined regardless of the effective XLEN def defined_in_all_bases? = @data["base"].nil? - # @param design [Design] A configuration + # @param design [Design] The design # @return [Boolean] Whether or not the format of this CSR changes when the effective XLEN changes in some mode def format_changes_with_xlen?(design) dynamic_length?(design) || @@ -55,7 +55,7 @@ def format_changes_with_xlen?(design) end end - # @param design [Design] A configuration + # @param design [Design] The design # @return [Array] List of functions reachable from this CSR's sw_read or a field's sw_write function def reachable_functions(design) return @reachable_functions unless @reachable_functions.nil? @@ -104,7 +104,7 @@ def reachable_functions_unevaluated(design) @reachable_functions_unevaluated = fns.uniq end - # @param design [Design] A configuration + # @param design [Design] The design # @return [Boolean] Whether or not the length of the CSR depends on a runtime value # (e.g., mstatus.SXL) def dynamic_length?(design) @@ -142,7 +142,7 @@ def min_length(design) end end - # @param design [Design] A configuration (can be nil if the lenth is not dependent on a config parameter) + # @param design [Design] The design (can be nil if the length is not dependent on a config parameter) # @param effective_xlen [Integer] The effective xlen, needed since some fields change location with XLEN. If the field location is not determined by XLEN, then this parameter can be nil # @return [Integer] Length, in bits, of the CSR, given effective_xlen # @return [nil] if the length cannot be determined from the design (e.g., because SXLEN is unknown and +effective_xlen+ was not provided) @@ -254,7 +254,7 @@ def length_cond64 end end - # @param design [Design] A configuration + # @param design [Design] The design # @return [String] Pretty-printed length string def length_pretty(design, effective_xlen=nil) if dynamic_length?(design) @@ -306,7 +306,7 @@ def description_html Asciidoctor.convert description end - # @param design [Design] A configuration + # @param design [Design] The design # @return [Array] All implemented fields for this CSR at the given effective XLEN, sorted by location (smallest location first) # Excluded any fields that are defined by unimplemented extensions or a base that is not effective_xlen def implemented_fields_for(design, effective_xlen) @@ -321,7 +321,7 @@ def implemented_fields_for(design, effective_xlen) end end - # @param design [Design] A configuration + # @param design [Design] The design # @return [Array] All implemented fields for this CSR # Excluded any fields that are defined by unimplemented extensions def implemented_fields(design) @@ -377,7 +377,7 @@ def field(field_name) field_hash[field_name.to_s] end - # @param design [Design] A configuration + # @param design [Design] The design # @param effective_xlen [Integer] The effective XLEN to apply, needed when field locations change with XLEN in some mode # @return [Idl::BitfieldType] A bitfield type that can represent all fields of the CSR def bitfield_type(design, effective_xlen = nil) @@ -425,7 +425,7 @@ def type_checked_sw_read_ast(symtab) end # @return [FunctionBodyAst] The abstract syntax tree of the sw_read() function - # @param design [Design] A configuration + # @param design [Design] The design def sw_read_ast(symtab) raise ArgumentError, "Argument should be a symtab" unless symtab.is_a?(Idl::SymbolTable) @@ -493,7 +493,7 @@ def pruned_sw_read_ast(design) # {bits: 12, name: 'imm12', attr: [''], type: 6} # ]} # - # @param design [Design] A configuration + # @param design [Design] The design # @param effective_xlen [Integer,nil] Effective XLEN to use when CSR length is dynamic # @param exclude_unimplemented [Boolean] If true, do not create include unimplemented fields in the figure # @param optional_type [Integer] Wavedrom type (Fill color) for fields that are optional (not mandatory) in a partially-specified design @@ -543,10 +543,10 @@ def wavedrom_desc(design, effective_xlen, exclude_unimplemented: false, optional def exists_in_design?(design) if design.fully_configured? (@data["base"].nil? || (design.possible_xlens.include? @data["base"])) && - design.transitive_implemented_ext_vers.any? { |e| defined_by?(e) } + design.transitive_implemented_ext_vers.any? { |ext_ver| defined_by?(ext_ver) } else (@data["base"].nil? || (design.possible_xlens.include? @data["base"])) && - design.prohibited_ext_reqs.none? { |ext_req| ext_req.satisfying_versions.any? { |e| defined_by?(e) } } + design.prohibited_ext_reqs.none? { |ext_req| ext_req.satisfying_versions.any? { |ext_ver| defined_by?(ext_ver) } } end end @@ -556,10 +556,6 @@ def optional_in_design?(design) raise "optional_in_design? should only be used by a partially-specified arch def" unless design.partially_configured? exists_in_design?(design) && - design.mandatory_ext_reqs.all? do |ext_req| - ext_req.satisfying_versions.none? do |ext_ver| - defined_by?(ext_ver) - end - end + design.mandatory_ext_reqs.all? { |ext_req| ext_req.satisfying_versions.none? { |ext_ver| defined_by?(ext_ver) } } end end diff --git a/lib/arch_obj_models/csr_field.rb b/lib/arch_obj_models/csr_field.rb index 3fc1c83ad..36f13b1b8 100644 --- a/lib/arch_obj_models/csr_field.rb +++ b/lib/arch_obj_models/csr_field.rb @@ -28,9 +28,8 @@ def initialize(parent_csr, field_name, field_data) @type_cache = {} end - # @param possible_xlens [Array] List of xlens that be used in any implemented mode - # @param extensions [Array] List of extensions implemented - # @return [Boolean] whether or not the instruction is implemented given the supplies config options + # @param design [Design] The design + # @return [Boolean] whether or not the instruction is implemented given the supplied design def exists_in_design?(design) if design.fully_configured? parent.exists_in_design?(design) && @@ -54,11 +53,7 @@ def optional_in_design?(design) if data["definedBy"].nil? parent.optional_in_design?(design) else - design.mandatory_ext_reqs.all? do |ext_req| - ext_req.satisfying_versions.none? do |ext_ver| - defined_by?(ext_ver) - end - end + design.mandatory_ext_reqs.all? { |ext_req| ext_req.satisfying_versions.none? { |ext_ver| defined_by?(ext_ver) } } end ) end @@ -146,7 +141,7 @@ def pruned_type_ast(symtab) @pruned_type_asts[symtab_hash] = ast end - # returns the definitive type for a design + # Returns the definitive type for the design part of the symbol table. # # @param symtab [SymbolTable] Symbol table # @return [String] @@ -250,9 +245,9 @@ def alias @alias end - # @return [Array] List of functions called thorugh this field # @param design [Design] The design # @Param effective_xlen [Integer] 32 or 64; needed because fields can change in different XLENs + # @return [Array] List of functions called thorough this field def reachable_functions(design, effective_xlen) return @reachable_functions unless @reachable_functions.nil? diff --git a/lib/arch_obj_models/database_obj.rb b/lib/arch_obj_models/database_obj.rb index eaea68634..f82f7d734 100644 --- a/lib/arch_obj_models/database_obj.rb +++ b/lib/arch_obj_models/database_obj.rb @@ -206,13 +206,23 @@ def keys = @data.keys # @return (see Hash#key?) def key?(k) = @data.key?(k) + # @param ext_ver [ExtensionVersion] Version of the extension + # @param design [Design] The backend design + # @return [Boolean] Whether or not the object is defined-by the given ExtensionVersion in the given Design. + def in_scope?(ext_ver, design) + raise ArgumentError, "Require an ExtensionVersion object but got a #{ext_ver.class} object" unless ext_ver.is_a?(ExtensionVersion) + raise ArgumentError, "Require a Design object but got a #{design.class} object" unless design.is_a?(Design) + + defined_by?(ext_ver) + end + # @overload defined_by?(ext_name, ext_version) # @param ext_name [#to_s] An extension name # @param ext_version [#to_s] A specific extension version - # @return [Boolean] Whether or not the instruction is defined by extension `ext`, version `version` + # @return [Boolean] Whether or not the object is defined by extension `ext`, version `version` # @overload defined_by?(ext_version) # @param ext_version [ExtensionVersion] An extension version - # @return [Boolean] Whether or not the instruction is defined by ext_version + # @return [Boolean] Whether or not the object is defined by ext_version def defined_by?(*args) ext_ver = if args.size == 1 @@ -228,7 +238,7 @@ def defined_by?(*args) raise ArgumentError, "Unsupported number of arguments (#{args.size})" end - defined_by_condition.satisfied_by? { |req| req.satisfied_by?(ext_ver) } + defined_by_condition.satisfied_by? { |ext_req| ext_req.satisfied_by?(ext_ver) } end # because of multiple ("allOf") conditions, we generally can't return a list of extension versions here.... @@ -564,7 +574,6 @@ def to_rb_helper(hsh) # return a string that can be eval'd to determine if the objects in +ary_name+ # meet the Condition # - # @param ary_name [String] Name of a ruby string in the eval binding # @return [Boolean] If the condition is met def to_rb to_rb_helper(@hsh) @@ -592,7 +601,10 @@ def satisfied_by?(&block) raise ArgumentError, "Expecting one argument to block" unless block.arity == 1 - eval to_rb + # Written to allow debug breakpoints on individual lines. + to_rb_expr = to_rb + ret = eval to_rb_expr + ret end def satisfying_ext_versions diff --git a/lib/arch_obj_models/extension.rb b/lib/arch_obj_models/extension.rb index 1c24adf6c..ae17dd2b7 100644 --- a/lib/arch_obj_models/extension.rb +++ b/lib/arch_obj_models/extension.rb @@ -517,6 +517,34 @@ def implemented_instructions inst.defined_by?(self) end end + + # @param design [Design] The design + # @return [Array] List of CSRs in-scope for this design for this extension version (may be empty) + # Factors in effect of design's xlen in the appropriate mode for the CSR. + def in_scope_csrs(design) + raise ArgumentError, "Require a Design object but got a #{design.class} object" unless design.is_a?(Design) + + return @in_scope_csrs unless @in_scope_csrs.nil? + + @in_scope_csrs = @arch.csrs.select do |csr| + csr.defined_by?(self) && + (csr.base.nil? || (design.possible_xlens.include?(csr.base))) + end + end + + # @param design [Design] The design + # @return [Array] List of instructions in-scope for this design for this extension version (may be empty) + # Factors in effect of design's xlen in the appropriate mode for the instruction. + def in_scope_instructions(design) + raise ArgumentError, "Require a Design object but got a #{design.class} object" unless design.is_a?(Design) + + return @in_scope_instructions unless @in_scope_instructions.nil? + + @in_scope_instructions = @arch.instructions.select do |inst| + inst.defined_by?(self) && + (inst.base.nil? || (design.possible_xlens.include?(inst.base))) + end + end end # Is the extension mandatory, optional, various kinds of optional, etc. diff --git a/lib/arch_obj_models/instruction.rb b/lib/arch_obj_models/instruction.rb index 029858b28..73ecb293b 100644 --- a/lib/arch_obj_models/instruction.rb +++ b/lib/arch_obj_models/instruction.rb @@ -743,8 +743,7 @@ def excluded_by?(*args) # # TODO: Does this function actually work for a partially configured design? # It is calling DatabaseObject.defined_by? with ExtensionRequirement objects - # returned from Design.prohibited_ext_reqs() and Design.mandatory_ext_reqs() - # but only accepts an ExtensionVersion object. + # returned from Design.mandatory_ext_reqs() but only accepts an ExtensionVersion object. def exists_in_design?(design) if design.fully_configured? (@data["base"].nil? || (design.possible_xlens.include?(@data["base"]))) && @@ -754,7 +753,7 @@ def exists_in_design?(design) raise "unexpected design type" unless design.partially_configured? (@data["base"].nil? || (design.possible_xlens.include?(@data["base"]))) && - design.prohibited_ext_reqs.none? { |ext_req| defined_by?(ext_req) } && + design.prohibited_ext_reqs.none? { |ext_req| ext_req.satisfying_versions.any? { |ext_ver| defined_by?(ext_ver) } } design.mandatory_ext_reqs.none? { |ext_req| excluded_by?(ext_req) } end end diff --git a/lib/arch_obj_models/portfolio.rb b/lib/arch_obj_models/portfolio.rb index 5c17218df..312ec89f9 100644 --- a/lib/arch_obj_models/portfolio.rb +++ b/lib/arch_obj_models/portfolio.rb @@ -127,30 +127,36 @@ def in_scope_extensions end + # @param design [Design] The design # @return [Array] Sorted list of all instructions associated with extensions listed as # mandatory or optional in portfolio. Uses instructions provided by the # minimum version of the extension that meets the extension requirement. - def in_scope_instructions + def in_scope_instructions(design) + raise ArgumentError, "Require a Design object but got a #{design.class} object" unless design.is_a?(Design) + return @in_scope_instructions unless @in_scope_instructions.nil? @in_scope_instructions = [] portfolios.each do |portfolio| - @in_scope_instructions += portfolio.in_scope_instructions + @in_scope_instructions += portfolio.in_scope_instructions(design) end @in_scope_instructions = @in_scope_instructions.uniq(&:name).sort_by(&:name) end + # @param design [Design] The design # @return [Array] Unsorted list of all CSRs associated with extensions listed as # mandatory or optional in portfolio. Uses CSRs provided by the # minimum version of the extension that meets the extension requirement. - def in_scope_csrs + def in_scope_csrs(design) + raise ArgumentError, "Require a Design object but got a #{design.class} object" unless design.is_a?(Design) + return @in_scope_csrs unless @in_scope_csrs.nil? @in_scope_csrs = [] portfolios.each do |portfolio| - @in_scope_csrs += portfolio.in_scope_csrs + @in_scope_csrs += portfolio.in_scope_csrs(design) end @in_scope_csrs.uniq(&:name) @@ -349,24 +355,30 @@ def in_scope_min_satisfying_extension_versions @in_scope_min_satisfying_extension_versions end + # @param design [Design] The design # @return [Array] Sorted list of all instructions associated with extensions listed as # mandatory or optional in portfolio. Uses instructions provided by the # minimum version of the extension that meets the extension requirement. - def in_scope_instructions + def in_scope_instructions(design) + raise ArgumentError, "Require a Design object but got a #{design.class} object" unless design.is_a?(Design) + return @in_scope_instructions unless @in_scope_instructions.nil? @in_scope_instructions = - in_scope_min_satisfying_extension_versions.map {|ext_ver| ext_ver.implemented_instructions }.flatten.uniq.sort + in_scope_min_satisfying_extension_versions.map {|ext_ver| ext_ver.in_scope_instructions(design) }.flatten.uniq.sort end + # @param design [Design] The design # @return [Array] Unsorted list of all CSRs associated with extensions listed as # mandatory or optional in portfolio. Uses CSRs provided by the # minimum version of the extension that meets the extension requirement. - def in_scope_csrs + def in_scope_csrs(design) + raise ArgumentError, "Require a Design object but got a #{design.class} object" unless design.is_a?(Design) + return @in_scope_csrs unless @in_scope_csrs.nil? @in_scope_csrs = - in_scope_min_satisfying_extension_versions.map {|ext_ver| ext_ver.implemented_csrs }.flatten.uniq + in_scope_min_satisfying_extension_versions.map {|ext_ver| ext_ver.in_scope_csrs(design) }.flatten.uniq end # @return [Boolean] Does the profile differentiate between different types of optional. diff --git a/lib/config.rb b/lib/config.rb index 344686551..e52b84e54 100644 --- a/lib/config.rb +++ b/lib/config.rb @@ -178,7 +178,4 @@ def implemented_extensions def mandatory_extensions = raise "mandatory_extensions is only available for a PartialConfig" def prohibited_extensions = raise "prohibited_extensions is only available for a PartialConfig" - - # def prohibited_ext?(ext_name, cfg_arch) = !ext?(ext_name, cfg_arch) - # def ext?(ext_name, cfg_arch) = implemented_extensions(cfg_arch).any? { |e| e.name == ext_name.to_s } end diff --git a/lib/portfolio_design.rb b/lib/portfolio_design.rb index ad3a24ec6..cc67502ff 100644 --- a/lib/portfolio_design.rb +++ b/lib/portfolio_design.rb @@ -179,12 +179,14 @@ def in_scope_ext_reqs = @portfolio_grp.in_scope_ext_reqs # @return [Array] Sorted list of all instructions associated with extensions listed as # mandatory or optional in portfolio. Uses instructions provided by the # minimum version of the extension that meets the extension requirement. - def in_scope_instructions = @portfolio_grp.in_scope_instructions + # Factors in things like XLEN in design. + def in_scope_instructions = @portfolio_grp.in_scope_instructions(self) # @return [Array] Unsorted list of all CSRs associated with extensions listed as # mandatory or optional in portfolio. Uses CSRs provided by the # minimum version of the extension that meets the extension requirement. - def in_scope_csrs = @portfolio_grp.in_scope_csrs + # Factors in things like XLEN in design. + def in_scope_csrs = @portfolio_grp.in_scope_csrs(self) # @return [String] Given an extension +ext_name+, return the presence as a string. # Returns the greatest presence string across all portfolios in this design. From 05d7ed9f595a92ca6c7152f299445d1b2485b991 Mon Sep 17 00:00:00 2001 From: James Ball Date: Sat, 4 Jan 2025 00:15:18 +0000 Subject: [PATCH 13/21] Working on getting list of exceptions and interrupts correct for the design. --- backends/certificate_doc/tasks.rake | 5 +- .../templates/certificate.adoc.erb | 4 +- backends/profile_doc/tasks.rake | 5 +- lib/arch_obj_models/exception_code.rb | 4 +- lib/arch_obj_models/portfolio.rb | 67 +++++++++++++++++++ lib/design.rb | 2 +- lib/idl/ast.rb | 8 +-- lib/portfolio_design.rb | 21 ++---- 8 files changed, 89 insertions(+), 27 deletions(-) diff --git a/backends/certificate_doc/tasks.rake b/backends/certificate_doc/tasks.rake index 941ba21df..c9735feea 100644 --- a/backends/certificate_doc/tasks.rake +++ b/backends/certificate_doc/tasks.rake @@ -47,10 +47,11 @@ Dir.glob("#{$root}/arch/certificate_model/*.yaml") do |f| portfolio_design = portfolio_design_for(cert_model_name, base_arch, base, [cert_model]) # Create empty binding and then specify explicitly which variables the ERB template can access. - def create_empty_binding + # Seems to use this method name in stack backtraces (hence its name). + def evaluate_erb binding end - erb_binding = create_empty_binding + erb_binding = evaluate_erb erb_binding.local_variable_set(:arch, base_arch) erb_binding.local_variable_set(:design, portfolio_design) erb_binding.local_variable_set(:cert_class, cert_model.cert_class) diff --git a/backends/certificate_doc/templates/certificate.adoc.erb b/backends/certificate_doc/templates/certificate.adoc.erb index b02ce0d50..e4ff70de3 100644 --- a/backends/certificate_doc/templates/certificate.adoc.erb +++ b/backends/certificate_doc/templates/certificate.adoc.erb @@ -372,7 +372,7 @@ TODO: Show traps per privilege mode |=== | `xcause.CODE` CSR Field Value | Name -<% design.exception_codes.sort_by{ |code| code.num }.each do |code| -%> +<% design.in_scope_exception_codes.sort_by{ |code| code.num }.each do |code| -%> | <%= code.num %> | <%= code.name %> <% end -%> |=== @@ -381,7 +381,7 @@ TODO: Show traps per privilege mode |=== | `xcause.CODE` CSR Field Value | Name -<% design.interrupt_codes.sort_by{ |code| code.num }.each do |code| -%> +<% design.in_scope_interrupt_codes.sort_by{ |code| code.num }.each do |code| -%> | <%= code.num %> | <%= code.name %> <% end -%> |=== diff --git a/backends/profile_doc/tasks.rake b/backends/profile_doc/tasks.rake index fd5739c83..b9b0b92cd 100644 --- a/backends/profile_doc/tasks.rake +++ b/backends/profile_doc/tasks.rake @@ -72,10 +72,11 @@ Dir.glob("#{$root}/arch/profile_release/*.yaml") do |f| profile_release.profiles) # Create empty binding and then specify explicitly which variables the ERB template can access. - def create_empty_binding + # Seems to use this method name in stack backtraces (hence its name). + def evaluate_erb binding end - erb_binding = create_empty_binding + erb_binding = evaluate_erb erb_binding.local_variable_set(:arch, base_arch) erb_binding.local_variable_set(:design, portfolio_design) erb_binding.local_variable_set(:profile_class, profile_release.profile_class) diff --git a/lib/arch_obj_models/exception_code.rb b/lib/arch_obj_models/exception_code.rb index 633230315..082dad310 100644 --- a/lib/arch_obj_models/exception_code.rb +++ b/lib/arch_obj_models/exception_code.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -# a synchroncous exception code +# A synchroncous exception code class ExceptionCode # @return [String] Long-form display name (can include special characters) attr_reader :name @@ -23,5 +23,5 @@ def initialize(name, var, number, ext) end end -# all the same informatin as ExceptinCode, but for interrupts +# Define InterruptCode class (all the same members as ExceptionCode) InterruptCode = Class.new(ExceptionCode) diff --git a/lib/arch_obj_models/portfolio.rb b/lib/arch_obj_models/portfolio.rb index 312ec89f9..77d2bafdc 100644 --- a/lib/arch_obj_models/portfolio.rb +++ b/lib/arch_obj_models/portfolio.rb @@ -162,6 +162,36 @@ def in_scope_csrs(design) @in_scope_csrs.uniq(&:name) end + # @param design [Design] The design + # @return [Array] Unsorted list of all in-scope exception codes. + def in_scope_exception_codes(design) + raise ArgumentError, "Require a Design object but got a #{design.class} object" unless design.is_a?(Design) + + return @in_scope_exception_codes unless @in_scope_exception_codes.nil? + + @in_scope_exception_codes = [] + portfolios.each do |portfolio| + @in_scope_exception_codes += portfolio.in_scope_exception_codes(design) + end + + @in_scope_exception_codes.uniq(&:name) + end + + # @param design [Design] The design + # @return [Array] Unsorted list of all in-scope interrupt codes. + def in_scope_interrupt_codes(design) + raise ArgumentError, "Require a Design object but got a #{design.class} object" unless design.is_a?(Design) + + return @in_scope_interrupt_codes unless @in_scope_interrupt_codes.nil? + + @in_scope_interrupt_codes = [] + portfolios.each do |portfolio| + @in_scope_interrupt_codes += portfolio.in_scope_interrupt_codes(design) + end + + @in_scope_interrupt_codes.uniq(&:name) + end + # @return [String] Given an extension +ext_name+, return the presence as a string. # Returns the greatest presence string across all profiles in the group. # If the extension name isn't found in the release, return "-". @@ -381,6 +411,43 @@ def in_scope_csrs(design) in_scope_min_satisfying_extension_versions.map {|ext_ver| ext_ver.in_scope_csrs(design) }.flatten.uniq end + # @param design [Design] The design + # @return [Array] Unsorted list of all in-scope exception codes. + # TODO: See https://github.com/riscv-software-src/riscv-unified-db/issues/291 + # TODO: Still needs work and haven't created in_scope_interrupt_codes yet. + # TODO: Extensions should provide conditional information ("when" statements?) + # that we evaluate here to determine if a particular exception code can + # actually be generated in a design. + # Also, probably shouldn't be calling "ext?" since that doesn't the in_scope lists of extensions. + def in_scope_exception_codes(design) + raise ArgumentError, "Require a Design object but got a #{design.class} object" unless design.is_a?(Design) + + return @in_scope_exception_codes unless @in_scope_exception_codes.nil? + + @in_scope_exception_codes = + in_scope_min_satisfying_extension_versions.reduce([]) do |list, ext_version| + ecodes = ext_version.ext["exception_codes"] + next list if ecodes.nil? + + ecodes.each do |ecode| + # Require all exception codes be unique in a given portfolio. + raise "Duplicate exception code" if list.any? { |e| e.num == ecode["num"] || e.name == ecode["name"] || e.var == ecode["var"] } + + unless ecode.dig("when", "version").nil? + # check version + next unless design.ext?(ext_version.name.to_sym, ecode["when"]["version"]) + end + list << ExceptionCode.new(ecode["name"], ecode["var"], ecode["num"], arch) + end + list + end + end + + # @param design [Design] The design + # @return [Array] Unsorted list of all in-scope interrupt codes. + # TODO: Actually implement this to use Design. See in_scope_exception_codes() above. + def in_scope_interrupt_codes(design) = arch.interrupt_codes + # @return [Boolean] Does the profile differentiate between different types of optional. def uses_optional_types? return @uses_optional_types unless @uses_optional_types.nil? diff --git a/lib/design.rb b/lib/design.rb index a262eea96..2c74cf8c0 100644 --- a/lib/design.rb +++ b/lib/design.rb @@ -297,7 +297,7 @@ def implemented_exception_codes @implemented_exception_codes = implemented_ext_vers.reduce([]) do |list, ext_version| - ecodes = extension(ext_version.name)["exception_codes"] + ecodes = ext_version.ext["exception_codes"] next list if ecodes.nil? ecodes.each do |ecode| diff --git a/lib/idl/ast.rb b/lib/idl/ast.rb index 2993161ed..211f55c29 100644 --- a/lib/idl/ast.rb +++ b/lib/idl/ast.rb @@ -1121,9 +1121,9 @@ def element_names(symtab) when "ExtensionName" symtab.design.arch.extensions.map(&:name) when "ExceptionCode" - symtab.design.exception_codes.map(&:var) + symtab.design.arch.exception_codes.map(&:var) when "InterruptCode" - symtab.design.interrupt_codes.map(&:var) + symtab.design.arch.interrupt_codes.map(&:var) else type_error "Unknown builtin enum type '#{name}'" end @@ -1134,9 +1134,9 @@ def element_values(symtab) when "ExtensionName" (0...symtab.design.arch.extensions.size).to_a when "ExceptionCode" - symtab.design.exception_codes.map(&:num) + symtab.design.arch.exception_codes.map(&:num) when "InterruptCode" - symtab.design.interrupt_codes.map(&:num) + symtab.design.arch.interrupt_codes.map(&:num) else type_error "Unknown builtin enum type '#{name}'" end diff --git a/lib/portfolio_design.rb b/lib/portfolio_design.rb index cc67502ff..1c51f3f0a 100644 --- a/lib/portfolio_design.rb +++ b/lib/portfolio_design.rb @@ -125,7 +125,7 @@ def prohibited_ext_reqs = [] # PortfolioDesign.ext?(:S, ">= 1.12") # @example Checking extension presence with multiple version requirements # PortfolioDesign.ext?(:S, ">= 1.12", "< 1.15") - # @example Checking extension precsence with a precise version requirement + # @example Checking extension presence with a precise version requirement # PortfolioDesign.ext?(:S, 1.12) def ext?(ext_name, *ext_version_requirements) @ext_cache ||= {} @@ -188,21 +188,14 @@ def in_scope_instructions = @portfolio_grp.in_scope_instructions(self) # Factors in things like XLEN in design. def in_scope_csrs = @portfolio_grp.in_scope_csrs(self) + # @return [Array] Unsorted list of all in-scope exception codes. + def in_scope_exception_codes = @portfolio_grp.in_scope_exception_codes(self) + + # @return [Array] Unsorted list of all in-scope interrupt codes. + def in_scope_interrupt_codes = @portfolio_grp.in_scope_interrupt_codes(self) + # @return [String] Given an extension +ext_name+, return the presence as a string. # Returns the greatest presence string across all portfolios in this design. # If the extension name isn't found in this design, return "-". def extension_presence(ext_name) = @portfolio_grp.extension_presence(ext_name) - - ################################################ - # ADDITIONAL METHODS UNIQUE TO PortfolioDesign # - ################################################ - - # @return [Array] All exception codes used in this design. - # TODO: XXX: Make this actually filter out unused exception codes in the design. - def exception_codes = arch.exception_codes - - # @return [Array] All interrupt codes used in this design. - # TODO: XXX: Make this actually filter out unused interrupt codes in the design. - def interrupt_codes = arch.interrupt_codes - end From 2a113a0a2742372ff6cfb07b8c58a5b90e9f3426 Mon Sep 17 00:00:00 2001 From: James Ball Date: Wed, 8 Jan 2025 23:27:51 +0000 Subject: [PATCH 14/21] Created IDesign that unit-level and Design can both use. --- .vscode/launch.json | 9 +++ backends/certificate_doc/tasks.rake | 12 +-- backends/profile_doc/tasks.rake | 13 ++-- lib/arch_obj_models/database_obj.rb | 2 +- lib/arch_obj_models/extension.rb | 4 +- lib/arch_obj_models/portfolio.rb | 14 ++-- lib/architecture.rb | 26 ++++++- lib/base_architecture.rb | 29 ------- lib/cfg_arch.rb | 23 ++++-- lib/design.rb | 117 +++------------------------- lib/idesign.rb | 103 ++++++++++++++++++++++++ lib/idl/symbol_table.rb | 2 +- lib/idl/tests/helpers.rb | 18 +++-- lib/portfolio_design.rb | 2 +- 14 files changed, 193 insertions(+), 181 deletions(-) delete mode 100644 lib/base_architecture.rb create mode 100644 lib/idesign.rb diff --git a/.vscode/launch.json b/.vscode/launch.json index 19c008950..929e09803 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -36,6 +36,15 @@ "script": "gen:profile[RVA20]", "args": [], "askParameters": false + }, + { + "type": "rdbg", + "name": "Smoke test", + "request": "launch", + "command": "bundle exec rake", + "script": "test:smoke", + "args": [], + "askParameters": false } ] } diff --git a/backends/certificate_doc/tasks.rake b/backends/certificate_doc/tasks.rake index c9735feea..b726aa88b 100644 --- a/backends/certificate_doc/tasks.rake +++ b/backends/certificate_doc/tasks.rake @@ -32,19 +32,19 @@ Dir.glob("#{$root}/arch/certificate_model/*.yaml") do |f| "#{$root}/lib/design.rb", "#{CERT_DOC_DIR}/templates/certificate.adoc.erb" ] do |t| - # Create BaseArchitecture object. Function located in top-level Rakefile. - puts "UPDATE: Creating BaseArchitecture #{base_isa_name} for #{t}" - base_arch = base_arch_for(base_isa_name, base) + # Create Architecture object. Function located in top-level Rakefile. + puts "UPDATE: Creating Architecture #{base_isa_name} for #{t}" + arch = arch_for(base_isa_name, base) # Create CertModel for specific certificate model as specified in its arch YAML file. # The Architecture object also creates all other portfolio-related class instances from their arch YAML files. # None of these objects are provided with a Design object when created. puts "UPDATE: Creating CertModel for #{cert_model_name} using base #{base_isa_name}" - cert_model = base_arch.cert_model(cert_model_name) + cert_model = arch.cert_model(cert_model_name) puts "UPDATE: Creating PortfolioDesign using certificate model #{cert_model_name}" # Create the one PortfolioDesign object required for the ERB evaluation. - portfolio_design = portfolio_design_for(cert_model_name, base_arch, base, [cert_model]) + portfolio_design = portfolio_design_for(cert_model_name, arch, base, [cert_model]) # Create empty binding and then specify explicitly which variables the ERB template can access. # Seems to use this method name in stack backtraces (hence its name). @@ -52,7 +52,7 @@ Dir.glob("#{$root}/arch/certificate_model/*.yaml") do |f| binding end erb_binding = evaluate_erb - erb_binding.local_variable_set(:arch, base_arch) + erb_binding.local_variable_set(:arch, arch) erb_binding.local_variable_set(:design, portfolio_design) erb_binding.local_variable_set(:cert_class, cert_model.cert_class) erb_binding.local_variable_set(:portfolio_class, cert_model.cert_class) diff --git a/backends/profile_doc/tasks.rake b/backends/profile_doc/tasks.rake index b9b0b92cd..18ff79fa9 100644 --- a/backends/profile_doc/tasks.rake +++ b/backends/profile_doc/tasks.rake @@ -55,21 +55,20 @@ Dir.glob("#{$root}/arch/profile_release/*.yaml") do |f| "#{$root}/lib/design.rb", "#{PROFILE_DOC_DIR}/templates/profile.adoc.erb" ].concat(profile_pathnames) do |t| - # Create BaseArchitecture object. Function located in top-level Rakefile. - puts "UPDATE: Creating BaseArchitecture #{base_isa_name} for #{t}" - base_arch = base_arch_for(base_isa_name, base) + # Create Architecture object. Function located in top-level Rakefile. + puts "UPDATE: Creating Architecture #{base_isa_name} for #{t}" + arch = arch_for(base_isa_name, base) # Create PortfolioRelease for specific portfolio release as specified in its arch YAML file. # The Architecture object also creates all other portfolio-related class instances from their arch YAML files. # None of these objects are provided with a Design object when created. puts "UPDATE: Creating Profile Release for #{profile_release_name} using #{base_isa_name}" - profile_release = base_arch.profile_release(profile_release_name) + profile_release = arch.profile_release(profile_release_name) puts "UPDATE: Creating PortfolioDesign using profile release #{profile_release_name}" # Create the one PortfolioDesign object required for the ERB evaluation. # Provide it with all the profiles in this ProfileRelease. - portfolio_design = portfolio_design_for(profile_release_name, base_arch, base, - profile_release.profiles) + portfolio_design = portfolio_design_for(profile_release_name, arch, base, profile_release.profiles) # Create empty binding and then specify explicitly which variables the ERB template can access. # Seems to use this method name in stack backtraces (hence its name). @@ -77,7 +76,7 @@ Dir.glob("#{$root}/arch/profile_release/*.yaml") do |f| binding end erb_binding = evaluate_erb - erb_binding.local_variable_set(:arch, base_arch) + erb_binding.local_variable_set(:arch, arch) erb_binding.local_variable_set(:design, portfolio_design) erb_binding.local_variable_set(:profile_class, profile_release.profile_class) erb_binding.local_variable_set(:portfolio_class, profile_release.profile_class) diff --git a/lib/arch_obj_models/database_obj.rb b/lib/arch_obj_models/database_obj.rb index f82f7d734..fe066d349 100644 --- a/lib/arch_obj_models/database_obj.rb +++ b/lib/arch_obj_models/database_obj.rb @@ -211,7 +211,7 @@ def key?(k) = @data.key?(k) # @return [Boolean] Whether or not the object is defined-by the given ExtensionVersion in the given Design. def in_scope?(ext_ver, design) raise ArgumentError, "Require an ExtensionVersion object but got a #{ext_ver.class} object" unless ext_ver.is_a?(ExtensionVersion) - raise ArgumentError, "Require a Design object but got a #{design.class} object" unless design.is_a?(Design) + raise ArgumentError, "Require an IDesign object but got a #{design.class} object" unless design.is_a?(IDesign) defined_by?(ext_ver) end diff --git a/lib/arch_obj_models/extension.rb b/lib/arch_obj_models/extension.rb index ae17dd2b7..6ec80b731 100644 --- a/lib/arch_obj_models/extension.rb +++ b/lib/arch_obj_models/extension.rb @@ -522,7 +522,7 @@ def implemented_instructions # @return [Array] List of CSRs in-scope for this design for this extension version (may be empty) # Factors in effect of design's xlen in the appropriate mode for the CSR. def in_scope_csrs(design) - raise ArgumentError, "Require a Design object but got a #{design.class} object" unless design.is_a?(Design) + raise ArgumentError, "Require an IDesign object but got a #{design.class} object" unless design.is_a?(IDesign) return @in_scope_csrs unless @in_scope_csrs.nil? @@ -536,7 +536,7 @@ def in_scope_csrs(design) # @return [Array] List of instructions in-scope for this design for this extension version (may be empty) # Factors in effect of design's xlen in the appropriate mode for the instruction. def in_scope_instructions(design) - raise ArgumentError, "Require a Design object but got a #{design.class} object" unless design.is_a?(Design) + raise ArgumentError, "Require an IDesign object but got a #{design.class} object" unless design.is_a?(IDesign) return @in_scope_instructions unless @in_scope_instructions.nil? diff --git a/lib/arch_obj_models/portfolio.rb b/lib/arch_obj_models/portfolio.rb index 77d2bafdc..e6e4cd4f9 100644 --- a/lib/arch_obj_models/portfolio.rb +++ b/lib/arch_obj_models/portfolio.rb @@ -132,7 +132,7 @@ def in_scope_extensions # mandatory or optional in portfolio. Uses instructions provided by the # minimum version of the extension that meets the extension requirement. def in_scope_instructions(design) - raise ArgumentError, "Require a Design object but got a #{design.class} object" unless design.is_a?(Design) + raise ArgumentError, "Require an IDesign object but got a #{design.class} object" unless design.is_a?(IDesign) return @in_scope_instructions unless @in_scope_instructions.nil? @@ -150,7 +150,7 @@ def in_scope_instructions(design) # mandatory or optional in portfolio. Uses CSRs provided by the # minimum version of the extension that meets the extension requirement. def in_scope_csrs(design) - raise ArgumentError, "Require a Design object but got a #{design.class} object" unless design.is_a?(Design) + raise ArgumentError, "Require an IDesign object but got a #{design.class} object" unless design.is_a?(IDesign) return @in_scope_csrs unless @in_scope_csrs.nil? @@ -165,7 +165,7 @@ def in_scope_csrs(design) # @param design [Design] The design # @return [Array] Unsorted list of all in-scope exception codes. def in_scope_exception_codes(design) - raise ArgumentError, "Require a Design object but got a #{design.class} object" unless design.is_a?(Design) + raise ArgumentError, "Require an IDesign object but got a #{design.class} object" unless design.is_a?(IDesign) return @in_scope_exception_codes unless @in_scope_exception_codes.nil? @@ -180,7 +180,7 @@ def in_scope_exception_codes(design) # @param design [Design] The design # @return [Array] Unsorted list of all in-scope interrupt codes. def in_scope_interrupt_codes(design) - raise ArgumentError, "Require a Design object but got a #{design.class} object" unless design.is_a?(Design) + raise ArgumentError, "Require an IDesign object but got a #{design.class} object" unless design.is_a?(IDesign) return @in_scope_interrupt_codes unless @in_scope_interrupt_codes.nil? @@ -390,7 +390,7 @@ def in_scope_min_satisfying_extension_versions # mandatory or optional in portfolio. Uses instructions provided by the # minimum version of the extension that meets the extension requirement. def in_scope_instructions(design) - raise ArgumentError, "Require a Design object but got a #{design.class} object" unless design.is_a?(Design) + raise ArgumentError, "Require an IDesign object but got a #{design.class} object" unless design.is_a?(IDesign) return @in_scope_instructions unless @in_scope_instructions.nil? @@ -403,7 +403,7 @@ def in_scope_instructions(design) # mandatory or optional in portfolio. Uses CSRs provided by the # minimum version of the extension that meets the extension requirement. def in_scope_csrs(design) - raise ArgumentError, "Require a Design object but got a #{design.class} object" unless design.is_a?(Design) + raise ArgumentError, "Require an IDesign object but got a #{design.class} object" unless design.is_a?(IDesign) return @in_scope_csrs unless @in_scope_csrs.nil? @@ -420,7 +420,7 @@ def in_scope_csrs(design) # actually be generated in a design. # Also, probably shouldn't be calling "ext?" since that doesn't the in_scope lists of extensions. def in_scope_exception_codes(design) - raise ArgumentError, "Require a Design object but got a #{design.class} object" unless design.is_a?(Design) + raise ArgumentError, "Require an IDesign object but got a #{design.class} object" unless design.is_a?(IDesign) return @in_scope_exception_codes unless @in_scope_exception_codes.nil? diff --git a/lib/architecture.rb b/lib/architecture.rb index 9ff8446a8..8220af195 100644 --- a/lib/architecture.rb +++ b/lib/architecture.rb @@ -50,13 +50,31 @@ require_relative "arch_obj_models/profile" class Architecture - # @return [Pathname] Path to the directory with the standard YAML files + # @return [String] Best name to identify architecture + attr_reader :name + + # @return [Integer] 32 for RV32I or 64 for RV64I + attr_reader :base + + # @return [Pathname] Path to the directory containing YAML files defining the RISC-V standards attr_reader :path - # @param arch_dir [Sting,Pathname] Path to a directory with a fully merged/resolved architecture defintion - def initialize(arch_dir) + # Initialize a new architecture definition + # + # @param name [#to_s] The name associated with this architecture + # @param base [Integer] RISC-V base ISA width (32 for RV32I/RV32E, 64 for RV64I, nil if unknown) + # @param arch_dir [String, Pathname] Path to a directory with a fully merged/resolved architecture definition + def initialize(name, base, arch_dir) + @name = name.to_s.freeze + + unless base.nil? + raise "Unsupported base ISA value of #{base}. Supported values are 32 or 64." unless base == 32 || base == 64 + end + @base = base + @base.freeze + @arch_dir = Pathname.new(arch_dir) - raise "Arch directory not found: #{arch_dir}" unless @arch_dir.exist? + raise "Architecture directory #{arch_dir} not found" unless @arch_dir.exist? @arch_dir = @arch_dir.realpath @path = @arch_dir # alias diff --git a/lib/base_architecture.rb b/lib/base_architecture.rb deleted file mode 100644 index f63118265..000000000 --- a/lib/base_architecture.rb +++ /dev/null @@ -1,29 +0,0 @@ -# frozen_string_literal: true - -# Just adds the concept of base ISA (RV32I or RV64I) to the Architecture class. - -require_relative "architecture" - -class BaseArchitecture < Architecture - # @return [String] Name of base ISA (rv32 or rv64) - attr_reader :name - - # @return [Integer] 32 or 64 - attr_reader :base - - # Initialize a new base architecture definition - # - # @param name [#to_s] The name associated with this base architecture - # @param base [Integer] RISC-V ISA MXLEN parameter value (can be nil if not static) - # @param arch_dir [String,Pathname] Path to a directory with the associated architecture definition - def initialize(name, base, arch_dir) - super(arch_dir) - @name = name.to_s.freeze - @base = base - @base.freeze - end - - # Returns a string representation of the object, suitable for debugging. - # @return [String] A string representation of the object. - def inspect = "BaseArchitecture##{name}" -end diff --git a/lib/cfg_arch.rb b/lib/cfg_arch.rb index 39c8e3376..db9aeda60 100644 --- a/lib/cfg_arch.rb +++ b/lib/cfg_arch.rb @@ -19,16 +19,20 @@ class ConfiguredArchitecture < Design # Kind of like inheritence but not quite. def_delegators \ :@config, \ - :fully_configured?, :partially_configured?, :unconfigured?, :configured?, \ - :mxlen, :param_values + :fully_configured?, :partially_configured?, :unconfigured?, :configured?, :param_values # @param config_name [#to_s] The configuration name which corresponds to a folder name under cfg_path + # @param base [Integer] RISC-V base ISA width (32 for RV32I/RV32E, 64 for RV64I, nil if unknown) # @param arch_dir [String,Pathname] Path to a directory with a fully merged/resolved architecture definition # @param overlay_path [String] Optional path to a directory that overlays the architecture # @param cfg_path [String] Optional path to where to find configuration file - def initialize(config_name, arch_dir, overlay_path: nil, cfg_path: "#{$root}/cfgs") + def initialize(config_name, base, arch_dir, overlay_path: nil, cfg_path: "#{$root}/cfgs") @config = Config.create("#{cfg_path}/#{config_name}/cfg.yaml") - arch = Architecture.new(arch_dir) + @mxlen = @config.mxlen + @mxlen.freeze + + arch = Architecture.new(config_name, base, arch_dir) + super(config_name, arch, overlay_path: overlay_path) end @@ -42,6 +46,9 @@ def inspect = "ConfiguredArchitecture##{name}" # These raise an error in the base class. # ########################################### + # @return [Integer] 32, 64, or nil if dynamic or undefined. + def mxlen = @mxlen + # Returns whether or not it may be possible to switch XLEN in +mode+ given this definition. # # There are three cases when this will return true: @@ -55,11 +62,11 @@ def inspect = "ConfiguredArchitecture##{name}" # @return [Boolean] true if this configuration might execute in multiple xlen environments in +mode+ # (e.g., that in some mode the effective xlen can be either 32 or 64, depending on CSR values) def multi_xlen_in_mode?(mode) - return false if mxlen == 32 + return false if @mxlen == 32 case mode when "M" - mxlen.nil? + @mxlen.nil? when "S" return true if unconfigured? @@ -195,7 +202,7 @@ def mandatory_ext_reqs return @mandatory_ext_reqs unless @mandatory_ext_reqs.nil? @mandatory_ext_reqs = @config.mandatory_extensions.map do |e| - ext = extension(e["name"]) + ext = arch.extension(e["name"]) raise "Cannot find extension #{e['name']} in the architecture definition" if ext.nil? ExtensionRequirement.new(e["name"], *e["version"], arch, presence: "mandatory") @@ -211,7 +218,7 @@ def prohibited_ext_reqs if @config.partially_configured? @prohibited_ext_reqs = @config.prohibited_extensions.map do |e| - ext = extension(e["name"]) + ext = arch.extension(e["name"]) raise "Cannot find extension #{e['name']} in the architecture definition" if ext.nil? ExtensionRequirement.new(e["name"], *e["version"], arch, presence: "mandatory") diff --git a/lib/design.rb b/lib/design.rb index 2c74cf8c0..90968ae94 100644 --- a/lib/design.rb +++ b/lib/design.rb @@ -19,13 +19,13 @@ # by using the Architecture object instead of the Design object (i.e., to support encapsulation). # # This Design class is an abstract base class for designs using either a config (under /cfg) or a -# portfolio (profile release or certificate). -# Abstract methods all call raise() if not overriden by a child class. These methods are all grouped -# together in this Ruby file to make it easier to find them. +# portfolio (profile release or certificate). The abstract methods exist in the IDesign base class +# and call raise() if not overriden by a child class. require "ruby-prof" require "tilt" +require_relative "idesign" require_relative "architecture" require_relative "idl" @@ -39,10 +39,7 @@ include TemplateHelpers -class Design - # @return [String] Name of design - attr_reader :name - +class Design < IDesign # @return [Architecture] The RISC-V architecture attr_reader :arch @@ -63,16 +60,13 @@ def hash = @name_sym.hash # @param arch [Architecture] The entire architecture # @param overlay_path [String] Optional path to a directory that overlays the architecture def initialize(name, arch, overlay_path: nil) - raise ArgumentError, "arch must be an Architecture but is a #{arch.class}" unless arch.is_a?(Architecture) + super(name) - @name = name.to_s.freeze - @name_sym = @name.to_sym.freeze + raise ArgumentError, "arch must be an Architecture but is a #{arch.class}" unless arch.is_a?(Architecture) @arch = arch - @idl_compiler = Idl::Compiler.new @symtab = Idl::SymbolTable.new(self) custom_globals_path = overlay_path.nil? ? Pathname.new("/does/not/exist") : overlay_path / "isa" / "globals.isa" - idl_path = File.exist?(custom_globals_path) ? custom_globals_path : $root / "arch" / "isa" / "globals.isa" @global_ast = @idl_compiler.compile_file(idl_path) @global_ast.add_global_symbols(@symtab) @@ -84,97 +78,6 @@ def initialize(name, arch, overlay_path: nil) # @return [String] A string representation of the object. def inspect = "Design##{name}" - #################### - # ABSTRACT METHODS # - #################### - - # @return [Integer] 32, 64, or nil (if dynamic or unconfigured) - def mxlen - raise "Abstract Method: Must be provided in child class" - end - - # Returns whether or not it may be possible to switch XLEN in +mode+ given this definition. - # @param mode [String] mode to check. One of "M", "S", "U", "VS", "VU" - # @return [Boolean] true if might execute in multiple xlen environments in +mode+ - # (e.g., that in some mode the effective xlen can be either 32 or 64, depending on CSR values) - def multi_xlen_in_mode?(mode) - raise "Abstract Method: Must be provided in child class" - end - - # @return [Hash] Fully-constrained parameter values (those with just one possible value for this design) - def param_values - raise "Abstract Method: Must be provided in child class" - end - - # @return [Array] List of all parameters fully-constrained to one specific value - def params_with_value - raise "Abstract Method: Must be provided in child class" - end - - # @return [Array] List of all available parameters not yet full-constrained to one specific value - def params_without_value - raise "Abstract Method: Must be provided in child class" - end - - # @return [Array] List of all implemented extension versions. - def implemented_ext_vers - raise "Abstract Method: Must be provided in child class" - end - - # @return [Array] List of all mandatory extension requirements - def mandatory_ext_reqs - raise "Abstract Method: Must be provided in child class" - end - - # @return [Array] List of all extensions that are prohibited. - # This includes extensions explicitly prohibited by the design - # and extensions that conflict with a mandatory extension. - def prohibited_ext_reqs - raise "Abstract Method: Must be provided in child class" - end - - # @return [Boolean] True if all parameters are fully-constrained in the design - def fully_configured? - raise "Abstract Method: Must be provided in child class" - end - - # @return [Boolean] True if some parameters aren't fully-constrained yet in the design - def partially_configured? - raise "Abstract Method: Must be provided in child class" - end - - # @return [Boolean] True if all parameters aren't constrained at all in the design - def unconfigured? - raise "Abstract Method: Must be provided in child class" - end - - # @return [Boolean] True if not unconfigured (so either fully_configured or partially_configured). - # This isn't an abstract method but is located here for clarity. - def configured? = !unconfigured? - - # @overload ext?(ext_name) - # @param ext_name [#to_s] Extension name (case sensitive) - # @return [Boolean] True if the extension `name` must be implemented - # - # @overload ext?(ext_name, ext_version_requirements) - # @param ext_name [#to_s] Extension name (case sensitive) - # @param ext_version_requirements [Number,String,Array] Extension version requirements - # @return [Boolean] True if the extension `name` meeting `ext_version_requirements` must be implemented - # - # @example Checking extension presence with a version requirement - # Design.ext?(:S, ">= 1.12") - # @example Checking extension presence with multiple version requirements - # Design.ext?(:S, ">= 1.12", "< 1.15") - # @example Checking extension presence with a precise version requirement - # Design.ext?(:S, 1.12) - def ext?(ext_name, *ext_version_requirements) - raise "Abstract Method: Must be provided in child class" - end - - ################### - # REGULAR METHODS # - ################### - # Returns whether or not it may be possible to switch XLEN given this definition. # # There are three cases when this will return true: @@ -203,10 +106,10 @@ def type_check(show_progress: true, io: $stdout) io.puts "Type checking IDL code for #{@name}..." progressbar = if show_progress - ProgressBar.create(title: "Instructions", total: instructions.size) + ProgressBar.create(title: "Instructions", total: arch.instructions.size) end - instructions.each do |inst| + arch.instructions.each do |inst| progressbar.increment if show_progress if @mxlen == 32 inst.type_checked_operation_ast(@idl_compiler, @symtab, 32) if inst.rv32? @@ -218,10 +121,10 @@ def type_check(show_progress: true, io: $stdout) progressbar = if show_progress - ProgressBar.create(title: "CSRs", total: csrs.size) + ProgressBar.create(title: "CSRs", total: arch.csrs.size) end - csrs.each do |csr| + arch.csrs.each do |csr| progressbar.increment if show_progress if csr.has_custom_sw_read? if (possible_xlens.include?(32) && csr.defined_in_base32?) || (possible_xlens.include?(64) && csr.defined_in_base64?) diff --git a/lib/idesign.rb b/lib/idesign.rb new file mode 100644 index 000000000..949b90a9f --- /dev/null +++ b/lib/idesign.rb @@ -0,0 +1,103 @@ +# frozen_string_literal: true + +# The IDesign class (Interface Design) contains the abstract methods for the Design class. +# Putting these methods into IDesign rather than Design allows unit-level tests to create +# a MockDesign class that only has to implement the require abstract interfaces without all +# the other baggage that a Design class adds (e.g., having an Architecture object). + +class IDesign + # @return [String] Name of design + attr_reader :name + + # @param name [#to_s] The design name + def initialize(name) + @name = name.to_s.freeze + @name_sym = @name.to_sym.freeze + end + + # @return [Boolean] True if not unconfigured (so either fully_configured or partially_configured). + def configured? = !unconfigured? + + #################### + # ABSTRACT METHODS # + #################### + + # @return [Integer] 32, 64, or nil (if dynamic or unconfigured) + def mxlen + raise "Abstract Method: Must be provided in child class" + end + + # Returns whether or not it may be possible to switch XLEN in +mode+ given this definition. + # @param mode [String] mode to check. One of "M", "S", "U", "VS", "VU" + # @return [Boolean] true if might execute in multiple xlen environments in +mode+ + # (e.g., that in some mode the effective xlen can be either 32 or 64, depending on CSR values) + def multi_xlen_in_mode?(mode) + raise "Abstract Method: Must be provided in child class" + end + + # @return [Hash] Fully-constrained parameter values (those with just one possible value for this design) + def param_values + raise "Abstract Method: Must be provided in child class" + end + + # @return [Array] List of all parameters fully-constrained to one specific value + def params_with_value + raise "Abstract Method: Must be provided in child class" + end + + # @return [Array] List of all available parameters not yet full-constrained to one specific value + def params_without_value + raise "Abstract Method: Must be provided in child class" + end + + # @return [Array] List of all implemented extension versions. + def implemented_ext_vers + raise "Abstract Method: Must be provided in child class" + end + + # @return [Array] List of all mandatory extension requirements + def mandatory_ext_reqs + raise "Abstract Method: Must be provided in child class" + end + + # @return [Array] List of all extensions that are prohibited. + # This includes extensions explicitly prohibited by the design + # and extensions that conflict with a mandatory extension. + def prohibited_ext_reqs + raise "Abstract Method: Must be provided in child class" + end + + # @return [Boolean] True if all parameters are fully-constrained in the design + def fully_configured? + raise "Abstract Method: Must be provided in child class" + end + + # @return [Boolean] True if some parameters aren't fully-constrained yet in the design + def partially_configured? + raise "Abstract Method: Must be provided in child class" + end + + # @return [Boolean] True if all parameters aren't constrained at all in the design + def unconfigured? + raise "Abstract Method: Must be provided in child class" + end + + # @overload ext?(ext_name) + # @param ext_name [#to_s] Extension name (case sensitive) + # @return [Boolean] True if the extension `name` must be implemented + # + # @overload ext?(ext_name, ext_version_requirements) + # @param ext_name [#to_s] Extension name (case sensitive) + # @param ext_version_requirements [Number,String,Array] Extension version requirements + # @return [Boolean] True if the extension `name` meeting `ext_version_requirements` must be implemented + # + # @example Checking extension presence with a version requirement + # Design.ext?(:S, ">= 1.12") + # @example Checking extension presence with multiple version requirements + # Design.ext?(:S, ">= 1.12", "< 1.15") + # @example Checking extension presence with a precise version requirement + # Design.ext?(:S, 1.12) + def ext?(ext_name, *ext_version_requirements) + raise "Abstract Method: Must be provided in child class" + end +end diff --git a/lib/idl/symbol_table.rb b/lib/idl/symbol_table.rb index 55a546189..5372a8610 100644 --- a/lib/idl/symbol_table.rb +++ b/lib/idl/symbol_table.rb @@ -91,7 +91,7 @@ def hash def initialize(design) raise "Must provide design" if design.nil? - raise "The design must be a Design but is a #{design.class}" unless design.is_a?(Design) + raise "The design must be an IDesign but is a #{design.class}" unless design.is_a?(IDesign) @design = design @mxlen = design.unconfigured? ? nil : design.mxlen diff --git a/lib/idl/tests/helpers.rb b/lib/idl/tests/helpers.rb index 9f94bad59..a7f0be497 100644 --- a/lib/idl/tests/helpers.rb +++ b/lib/idl/tests/helpers.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require "ostruct" +require_relative "../../idesign" # Extension mock that returns an extension name class Xmockension @@ -14,22 +15,23 @@ def initialize(name) XmockensionParameterWithValue = Struct.new(:name, :desc, :schema, :extra_validation, :exts, :value) # Design class mock that knows about XLEN and extensions -class MockDesign +class MockDesign < IDesign + def initialize + super("mock") + end + + def mxlen = 64 def param_values = { "XLEN" => 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 = [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)] + def implemented_ext_vers = [Xmockension.new("I")] + def implemented_exception_codes = [OpenStruct.new(var: "ACode", num: 0), OpenStruct.new(var: "BCode", num: 1)] + def implemented_interrupt_codes = [OpenStruct.new(var: "CoolInterrupt", num: 1)] def fully_configured? = false def partially_configured? = true def unconfigured? = false - def name = "mock" - attr_accessor :global_ast end diff --git a/lib/portfolio_design.rb b/lib/portfolio_design.rb index 1c51f3f0a..7ed475c58 100644 --- a/lib/portfolio_design.rb +++ b/lib/portfolio_design.rb @@ -48,7 +48,7 @@ def inspect = "PortfolioDesign##{name}" # METHODS REQUIRED BY BASE CLASS # ################################## - # @return [Integer] + # @return [Integer] 32 or 64. Might be nil if dynamic (not sure if dynamic required for portfolios). def mxlen = @mxlen # Returns whether or not it may be possible to switch XLEN in +mode+ given this definition. From 989db17786edd8953ad9e6d319a4be0702b555b6 Mon Sep 17 00:00:00 2001 From: James Ball Date: Thu, 9 Jan 2025 14:48:27 +0000 Subject: [PATCH 15/21] Fixes due to introduction of Design class and removal of ConfiguredArchitecture inheritence from Architecture. --- .gitignore | 1 + backends/cfg_html_doc/adoc_gen.rake | 2 +- backends/common_templates/adoc/csr.adoc.erb | 24 +++---- backends/common_templates/adoc/inst.adoc.erb | 2 +- backends/ext_pdf_doc/tasks.rake | 6 +- .../ext_pdf_doc/templates/ext_pdf.adoc.erb | 8 +-- backends/manual/tasks.rake | 62 +++++++++---------- backends/manual/templates/csr.adoc.erb | 38 ++++++------ backends/manual/templates/ext.adoc.erb | 2 +- backends/manual/templates/func.adoc.erb | 2 +- .../manual/templates/instruction.adoc.erb | 6 +- lib/arch_obj_models/extension.rb | 43 ++++++------- 12 files changed, 95 insertions(+), 101 deletions(-) diff --git a/.gitignore b/.gitignore index a9bdc6be4..47d2d0cf0 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,7 @@ diag-ditaa-* arch/manual/isa/**/riscv-isa-manual gen +resolved_arch node_modules _site images diff --git a/backends/cfg_html_doc/adoc_gen.rake b/backends/cfg_html_doc/adoc_gen.rake index eab5651f2..0bc3e42f6 100644 --- a/backends/cfg_html_doc/adoc_gen.rake +++ b/backends/cfg_html_doc/adoc_gen.rake @@ -43,7 +43,7 @@ require "ruby-prof" end when "ext" cfg_arch.transitive_implemented_ext_vers.each do |ext_version| - ext = cfg_arch.extension(ext_version.name) + ext = cfg_arch.arch.extension(ext_version.name) path = dir_path / "#{ext.name}.adoc" puts " Generating #{path}" File.write(path, cfg_arch.find_replace_links(erb.result(binding))) diff --git a/backends/common_templates/adoc/csr.adoc.erb b/backends/common_templates/adoc/csr.adoc.erb index 0f9187ded..04d9fd1e6 100644 --- a/backends/common_templates/adoc/csr.adoc.erb +++ b/backends/common_templates/adoc/csr.adoc.erb @@ -13,17 +13,17 @@ h| CSR Address | <%= "0x#{csr.address.to_s(16)}" %> <%- if csr.priv_mode == 'VS' -%> h| Virtual CSR Address | <%= "0x#{csr.virtual_address.to_s(16)}" %> <%- end -%> -h| Length | <%= csr.length_pretty(cfg_arch) %> +h| Length | <%= csr.length_pretty(design) %> h| Privilege Mode | <%= csr.priv_mode %> |=== == Format -<%- unless csr.dynamic_length?(cfg_arch) || csr.fields.any? { |f| f.dynamic_location?(cfg_arch) } -%> +<%- unless csr.dynamic_length?(design) || csr.fields.any? { |f| f.dynamic_location?(design) } -%> <%# CSR has a known static length, so there is only one format to display -%> .<%= csr.name %> format [wavedrom, ,svg,subs='attributes',width="100%"] .... -<%= JSON.dump csr.wavedrom_desc(cfg_arch, 64) %> +<%= JSON.dump csr.wavedrom_desc(design, 64) %> .... <%- else -%> <%# CSR has a dynamic length, or a field has a dynamic location, @@ -33,13 +33,13 @@ This CSR format changes dynamically. .<%= csr.name %> Format when <%= csr.length_cond32 %> [wavedrom, ,svg,subs='attributes',width="100%"] .... -<%= JSON.dump csr.wavedrom_desc(cfg_arch, 32) %> +<%= JSON.dump csr.wavedrom_desc(design, 32) %> .... .<%= csr.name %> Format when <%= csr.length_cond64 %> [wavedrom, ,svg,subs='attributes',width="100%"] .... -<%= JSON.dump csr.wavedrom_desc(cfg_arch, 64) %> +<%= JSON.dump csr.wavedrom_desc(design, 64) %> .... <%- end -%> @@ -51,9 +51,9 @@ This CSR format changes dynamically. <%- csr.fields.each do |field| -%> @ <%= link_to_csr_field(csr.name, field.name) %> -@ <%= field.location_pretty(cfg_arch) %> -@ <%= field.type_pretty(cfg_arch.symtab) %> -@ <%= field.reset_value_pretty(cfg_arch) %> +@ <%= field.location_pretty(design) %> +@ <%= field.type_pretty(design.symtab) %> +@ <%= field.reset_value_pretty(design) %> <%- end -%> |=== @@ -72,16 +72,16 @@ This CSR has no fields. However, it must still exist (not cause an `Illegal Inst [example] **** Location:: -<%= field.location_pretty(cfg_arch) %> +<%= field.location_pretty(design) %> Description:: <%= field.description %> Type:: -<%= field.type_pretty(cfg_arch.symtab) %> +<%= field.type_pretty(design.symtab) %> Reset value:: -<%= field.reset_value_pretty(cfg_arch) %> +<%= field.reset_value_pretty(design) %> **** @@ -115,6 +115,6 @@ This CSR may return a value that is different from what is stored in hardware. [source,idl,subs="specialchars,macros"] ---- -<%= csr.sw_read_ast(cfg_arch.symtab).gen_adoc %> +<%= csr.sw_read_ast(design.symtab).gen_adoc %> ---- <%- end -%> diff --git a/backends/common_templates/adoc/inst.adoc.erb b/backends/common_templates/adoc/inst.adoc.erb index 85a704477..8c4218762 100644 --- a/backends/common_templates/adoc/inst.adoc.erb +++ b/backends/common_templates/adoc/inst.adoc.erb @@ -75,7 +75,7 @@ Operation:: <%- unless inst.data["operation()"].nil? -%> [source,idl,subs="specialchars,macros"] ---- -<%= inst.operation_ast(cfg_arch.symtab).gen_adoc %> +<%= inst.operation_ast(design.symtab).gen_adoc %> ---- <%- end -%> diff --git a/backends/ext_pdf_doc/tasks.rake b/backends/ext_pdf_doc/tasks.rake index 04a4b0856..8c7e7d396 100644 --- a/backends/ext_pdf_doc/tasks.rake +++ b/backends/ext_pdf_doc/tasks.rake @@ -129,7 +129,7 @@ rule %r{#{$root}/gen/ext_pdf_doc/.*/adoc/.*_extension\.adoc} => proc { |tname| } do |t| config_name = Pathname.new(t.name).relative_path_from("#{$root}/gen/ext_pdf_doc").to_s.split("/")[0] - cfg_arch = cfg_arch_for(config_name) + design = cfg_arch_for(config_name) ext_name = Pathname.new(t.name).basename(".adoc").to_s.split("_")[0..-2].join("_") @@ -137,7 +137,7 @@ rule %r{#{$root}/gen/ext_pdf_doc/.*/adoc/.*_extension\.adoc} => proc { |tname| erb = ERB.new(template_path.read, trim_mode: "-") erb.filename = template_path.to_s - ext = cfg_arch.extension(ext_name) + ext = design.arch.extension(ext_name) version_strs = ENV["VERSION"].split(",") versions = if version_strs.include?("all") @@ -153,7 +153,7 @@ rule %r{#{$root}/gen/ext_pdf_doc/.*/adoc/.*_extension\.adoc} => proc { |tname| max_version = versions.max { |a, b| a.version <=> b.version } FileUtils.mkdir_p File.dirname(t.name) - File.write t.name, AsciidocUtils.resolve_links(cfg_arch.find_replace_links(erb.result(binding))) + File.write t.name, AsciidocUtils.resolve_links(design.find_replace_links(erb.result(binding))) end namespace :gen do diff --git a/backends/ext_pdf_doc/templates/ext_pdf.adoc.erb b/backends/ext_pdf_doc/templates/ext_pdf.adoc.erb index 2d214931f..59f1f5690 100644 --- a/backends/ext_pdf_doc/templates/ext_pdf.adoc.erb +++ b/backends/ext_pdf_doc/templates/ext_pdf.adoc.erb @@ -208,7 +208,7 @@ version <%= versions.select { |v| v.implications.include?(sub_ext)}.map(&:versio of <%= ext.name %>. <%- end -%> -<%= cfg_arch.extension(sub_ext.name).description %> +<%= design.arch.extension(sub_ext.name).description %> <%- unless sub_ext.requirement_condition.empty? -%> <%= sub_ext.name %> requires: @@ -287,7 +287,7 @@ The following <%= ext.csrs.size %> are added by this extension. <%- ext.csrs.each do |csr| -%> <<< :leveloffset: +2 -<%= partial "adoc/csr.adoc.erb", { csr: csr, cfg_arch: cfg_arch } %> +<%= partial "adoc/csr.adoc.erb", { csr: csr, design: design } %> :leveloffset: -2 <%- end -%> @@ -300,7 +300,7 @@ The following <%= ext.csrs.size %> are added by this extension. <%- ext.instructions.each do |i| -%> :leveloffset: +2 -<%= partial "adoc/inst.adoc.erb", { inst: i, cfg_arch: cfg_arch } %> +<%= partial "adoc/inst.adoc.erb", { inst: i, design: design } %> :leveloffset: -2 <<< @@ -310,7 +310,7 @@ The following <%= ext.csrs.size %> are added by this extension. <<< == IDL Functions -<%- ext.reachable_functions(cfg_arch.symtab).sort { |a,b| a.name <=> b.name }.each do |f| -%> +<%- ext.reachable_functions(design.symtab).sort { |a,b| a.name <=> b.name }.each do |f| -%> [#<%= f.name %>-func-def] === <%= f.name %><%- if f.builtin? -%> (builtin)<%- end -%> diff --git a/backends/manual/tasks.rake b/backends/manual/tasks.rake index d7573071e..27b3e285e 100644 --- a/backends/manual/tasks.rake +++ b/backends/manual/tasks.rake @@ -40,7 +40,7 @@ directory MANUAL_GEN_DIR / "html" file MANUAL_GEN_DIR / "antora" / "antora.yml" => (MANUAL_GEN_DIR / "antora").to_s do |t| File.write t.name, <<~ANTORA name: riscv_manual - version: #{cfg_arch.manual_version?} + version: #{cfg_arch.arch.manual_version?} nav: - modules/nav.adoc title: RISC-V ISA Manual @@ -52,7 +52,7 @@ rule %r{#{MANUAL_GEN_DIR}/.*/.*/antora/modules/chapters/pages/.*\.adoc} do |t| parts = t.name.sub("#{MANUAL_GEN_DIR}/", "").split("/") manual_name = parts[0] version_name = parts[1] - manual = cfg_arch_for("_").manual(parts[0]) + manual = cfg_arch_for("_").arch.manual(parts[0]) manual_version = manual.version(parts[1]) chapter_name = File.basename(t.name, ".adoc") @@ -86,7 +86,7 @@ rule %r{#{MANUAL_GEN_DIR}/.*/.*/antora/antora.yml} => proc { |tname| ] } do |t| parts = t.name.sub("#{MANUAL_GEN_DIR}/", "").split("/") - manual_version = cfg_arch_for("_").manual(parts[0])&.version(parts[1]) + manual_version = cfg_arch_for("_").arch.manual(parts[0])&.version(parts[1]) raise "Can't find any manual version for '#{parts[0]}' '#{parts[1]}'" if manual_version.nil? @@ -123,7 +123,7 @@ rule %r{#{MANUAL_GEN_DIR}/.*/.*/antora/nav.adoc} => proc { |tname| ] } do |t| parts = t.name.sub("#{MANUAL_GEN_DIR}/", "").split("/") - manual_version = cfg_arch_for("_").manual(parts[0])&.version(parts[1]) + manual_version = cfg_arch_for("_").arch.manual(parts[0])&.version(parts[1]) raise "Can't find any manual version for '#{parts[0]}' '#{parts[1]}'" if manual_version.nil? @@ -132,7 +132,7 @@ rule %r{#{MANUAL_GEN_DIR}/.*/.*/antora/nav.adoc} => proc { |tname| raise "There is no navigation file for manual '#{parts[0]}' at '#{nav_template_path}'" end - raise "no cfg_arch" if manual_version.cfg_arch.nil? + raise "no arch" if manual_version.arch.nil? erb = ERB.new(nav_template_path.read, trim_mode: "-") erb.filename = nav_template_path.to_s @@ -166,7 +166,7 @@ rule %r{#{MANUAL_GEN_DIR}/.*/.*/antora/modules/ROOT/pages/index.adoc} => proc { ] } do |t| parts = t.name.sub("#{MANUAL_GEN_DIR}/", "").split("/") - manual_version = cfg_arch_for("_").manual(parts[0])&.version(parts[1]) + manual_version = cfg_arch_for("_").arch.manual(parts[0])&.version(parts[1]) raise "Can't find any manual version for '#{parts[0]}' '#{parts[1]}'" if manual_version.nil? @@ -190,8 +190,8 @@ rule %r{#{MANUAL_GEN_DIR}/.*/.*/antora/modules/insts/pages/.*.adoc} => [ ] do |t| inst_name = File.basename(t.name, ".adoc") - cfg_arch = cfg_arch_for("_") - inst = cfg_arch.instruction(inst_name) + design = cfg_arch_for("_") + inst = design.arch.instruction(inst_name) raise "Can't find instruction '#{inst_name}'" if inst.nil? inst_template_path = $root / "backends" / "manual" / "templates" / "instruction.adoc.erb" @@ -199,7 +199,7 @@ rule %r{#{MANUAL_GEN_DIR}/.*/.*/antora/modules/insts/pages/.*.adoc} => [ erb.filename = inst_template_path.to_s FileUtils.mkdir_p File.dirname(t.name) - File.write t.name, AntoraUtils.resolve_links(cfg_arch.find_replace_links(erb.result(binding))) + File.write t.name, AntoraUtils.resolve_links(design.find_replace_links(erb.result(binding))) end # rule to create csr appendix page @@ -210,20 +210,16 @@ rule %r{#{MANUAL_GEN_DIR}/.*/.*/antora/modules/csrs/pages/.*\.adoc} => [ ] do |t| csr_name = File.basename(t.name, ".adoc") - cfg_arch = cfg_arch_for("_") - # cfg_arch_32 = cfg_arch_for("_32") - - csr = cfg_arch.csr(csr_name) + design = cfg_arch_for("_") + csr = design.arch.csr(csr_name) raise "Can't find csr '#{csr_name}'" if csr.nil? - # csr_32 = cfg_arch_32.csr(csr_name) - csr_template_path = $root / "backends" / "common_templates" / "adoc" / "csr.adoc.erb" erb = ERB.new(csr_template_path.read, trim_mode: "-") erb.filename = csr_template_path.to_s FileUtils.mkdir_p File.dirname(t.name) - File.write t.name, AntoraUtils.resolve_links(cfg_arch.find_replace_links(erb.result(binding))) + File.write t.name, AntoraUtils.resolve_links(design.find_replace_links(erb.result(binding))) end # rule to create ext appendix page @@ -233,8 +229,8 @@ rule %r{#{MANUAL_GEN_DIR}/.*/.*/antora/modules/exts/pages/.*.adoc} => [ ] do |t| ext_name = File.basename(t.name, ".adoc") - cfg_arch = cfg_arch_for("_") - ext = cfg_arch.extension(ext_name) + design = cfg_arch_for("_") + ext = design.arch.extension(ext_name) raise "Can't find extension '#{ext_name}'" if ext.nil? ext_template_path = $root / "backends" / "manual" / "templates" / "ext.adoc.erb" @@ -242,7 +238,7 @@ rule %r{#{MANUAL_GEN_DIR}/.*/.*/antora/modules/exts/pages/.*.adoc} => [ erb.filename = ext_template_path.to_s FileUtils.mkdir_p File.dirname(t.name) - File.write t.name, AntoraUtils.resolve_links(cfg_arch.find_replace_links(erb.result(binding))) + File.write t.name, AntoraUtils.resolve_links(design.find_replace_links(erb.result(binding))) end # rule to create IDL function appendix page @@ -250,14 +246,14 @@ rule %r{#{MANUAL_GEN_DIR}/.*/.*/antora/modules/funcs/pages/funcs.adoc} => [ __FILE__, ($root / "backends" / "manual" / "templates" / "func.adoc.erb").to_s ] do |t| - cfg_arch = cfg_arch_for("_") + design = cfg_arch_for("_") funcs_template_path = $root / "backends" / "manual" / "templates" / "func.adoc.erb" erb = ERB.new(funcs_template_path.read, trim_mode: "-") erb.filename = funcs_template_path.to_s FileUtils.mkdir_p File.dirname(t.name) - File.write t.name, AntoraUtils.resolve_links(cfg_arch.find_replace_links(erb.result(binding))) + File.write t.name, AntoraUtils.resolve_links(design.find_replace_links(erb.result(binding))) end # rule to create IDL function appendix page @@ -265,16 +261,16 @@ rule %r{#{MANUAL_GEN_DIR}/.*/.*/antora/modules/params/pages/param_list.adoc} => __FILE__, ($root / "backends" / "manual" / "templates" / "param_list.adoc.erb").to_s ] do |t| - cfg_arch = cfg_arch_for("_") + design = cfg_arch_for("_") parts = t.name.sub("#{MANUAL_GEN_DIR}/", "").split("/") - manual_version = cfg_arch.manual(parts[0])&.version(parts[1]) + manual_version = design.arch.manual(parts[0])&.version(parts[1]) param_list_template_path = $root / "backends" / "manual" / "templates" / "param_list.adoc.erb" erb = ERB.new(param_list_template_path.read, trim_mode: "-") erb.filename = param_list_template_path.to_s FileUtils.mkdir_p File.dirname(t.name) - File.write t.name, AntoraUtils.resolve_links(cfg_arch.find_replace_links(erb.result(binding))) + File.write t.name, AntoraUtils.resolve_links(design.find_replace_links(erb.result(binding))) end rule %r{#{MANUAL_GEN_DIR}/.*/top/.*/antora/landing/antora.yml} => [ @@ -283,8 +279,8 @@ rule %r{#{MANUAL_GEN_DIR}/.*/top/.*/antora/landing/antora.yml} => [ parts = t.name.sub("#{MANUAL_GEN_DIR}/", "").split("/") manual_name = parts[0] - cfg_arch = cfg_arch_for("_") - manual = cfg_arch.manual(manual_name) + design = cfg_arch_for("_") + manual = design.arch.manual(manual_name) raise "Can't find any manual version for '#{manual_name}'" if manual.nil? FileUtils.mkdir_p File.basename(t.name) @@ -316,7 +312,7 @@ rule %r{#{MANUAL_GEN_DIR}/.*/top/.*/antora/landing/modules/ROOT/pages/index.adoc erb = ERB.new(landing_template_path.read, trim_mode: "-") erb.filename = landing_template_path.to_s - manual = cfg_arch_for("_").manual(manual_name) + manual = cfg_arch_for("_").arch.manual(manual_name) FileUtils.mkdir_p File.dirname(t.name) File.write t.name, erb.result(binding) @@ -344,7 +340,7 @@ rule %r{#{MANUAL_GEN_DIR}/.*/top/.*/antora/playbook/playbook.yml} => proc { |tna erb = ERB.new(playbook_template_path.read, trim_mode: "-") erb.filename = playbook_template_path.to_s - manual = cfg_arch_for("_").manual(manual_name) + manual = cfg_arch_for("_").arch.manual(manual_name) FileUtils.mkdir_p File.dirname(t.name) File.write t.name, erb.result(binding) @@ -400,15 +396,15 @@ namespace :gen do raise ArgumentError, "Missing required environment variable VERSIONS\n\n#{html_manual_desc}" if ENV["VERSIONS"].nil? versions, output_hash = versions_from_env(ENV["MANUAL_NAME"]) - cfg_arch = cfg_arch_for("_") + design = cfg_arch_for("_") - manual = cfg_arch.manual(ENV["MANUAL_NAME"]) + manual = design.arch.manual(ENV["MANUAL_NAME"]) raise "No manual named '#{ENV['MANUAL_NAME']}" if manual.nil? # check out the correct version of riscv-isa-manual, if needed versions.each do |version| - version_obj = cfg_arch.manual_version(version) + version_obj = design.arch.manual_version(version) manual.repo_path = MANUAL_GEN_DIR / ENV["MANUAL_NAME"] / version / "riscv-isa-manual" @@ -473,8 +469,8 @@ namespace :serve do port = ENV.key?("PORT") ? ENV["PORT"] : 8000 - cfg_arch = cfg_arch_for("_") - manual = cfg_arch.manuals.find { |m| m.name == ENV["MANUAL_NAME"] } + design = cfg_arch_for("_") + manual = design.arch.manuals.find { |m| m.name == ENV["MANUAL_NAME"] } raise "No manual '#{ENV['MANUAL_NAME']}'" if manual.nil? _, output_hash = versions_from_env(manual) diff --git a/backends/manual/templates/csr.adoc.erb b/backends/manual/templates/csr.adoc.erb index e4e241cf5..3966a3668 100644 --- a/backends/manual/templates/csr.adoc.erb +++ b/backends/manual/templates/csr.adoc.erb @@ -5,7 +5,7 @@ *<%= csr.long_name %>* -<%= cfg_arch.render_erb(csr.description, "#{csr.name}.description") %> +<%= design.render_erb(csr.description, "#{csr.name}.description") %> == Attributes [%autowidth] @@ -15,7 +15,7 @@ h| CSR Address | <%= "0x#{csr.address.to_s(16)}" %> <%- if csr.priv_mode == 'VS' -%> h| Virtual CSR Address | <%= "0x#{csr.virtual_address.to_s(16)}" %> <%- end -%> -<%- if csr.dynamic_length?(cfg_arch) || csr.data["length"] == "MXLEN" -%> +<%- if csr.dynamic_length?(design) || csr.data["length"] == "MXLEN" -%> h| Length a| @@ -26,22 +26,22 @@ a| [when,"<%= csr.length_cond64 %>"] -- -<%= csr.length_pretty(cfg_arch, 64) %> +<%= csr.length_pretty(design, 64) %> -- <%- else -%> -h| Length | <%= csr.length_pretty(cfg_arch) %> +h| Length | <%= csr.length_pretty(design) %> <%- end -%> h| Privilege Mode | <%= csr.priv_mode %> |=== == Format -<%- unless csr.dynamic_length?(cfg_arch) || csr.fields.any? { |f| f.dynamic_location?(cfg_arch) } || csr.data["length"] == "MXLEN" -%> +<%- unless csr.dynamic_length?(design) || csr.fields.any? { |f| f.dynamic_location?(design) } || csr.data["length"] == "MXLEN" -%> <%# CSR has a known static length, so there is only one format to display -%> .<%= csr.name %> format [wavedrom, ,svg,subs='attributes',width="100%"] .... -<%= JSON.dump csr.wavedrom_desc(cfg_arch, 64) %> +<%= JSON.dump csr.wavedrom_desc(design, 64) %> .... <%- else -%> <%# CSR has a dynamic length, or a field has a dynamic location, @@ -52,7 +52,7 @@ This CSR format changes dynamically. -- [wavedrom, ,svg,subs='attributes',width="100%"] .... -<%= JSON.dump csr_32.wavedrom_desc(cfg_arch_32, 32) %> +<%= JSON.dump csr_32.wavedrom_desc(design, 32) %> .... -- @@ -60,7 +60,7 @@ This CSR format changes dynamically. -- [wavedrom, ,svg,subs='attributes',width="100%"] .... -<%= JSON.dump csr.wavedrom_desc(cfg_arch, 64) %> +<%= JSON.dump csr.wavedrom_desc(design, 64) %> .... -- <%- end -%> @@ -75,31 +75,31 @@ This CSR format changes dynamically. <%- csr.fields.each do |field| -%> @ xref:<%=csr.name%>-<%=field.name%>-def[`<%= field.name %>`] a@ -<%- if field.dynamic_location?(cfg_arch) -%> +<%- if field.dynamic_location?(design) -%> [when,"<%= field.location_cond32 %>"] -- -<%= field.location_pretty(cfg_arch, 32) %> +<%= field.location_pretty(design, 32) %> -- [when,"<%= field.location_cond64 %>"] -- -<%= field.location_pretty(cfg_arch, 64) %> +<%= field.location_pretty(design, 64) %> -- <%- else -%> -<%= field.location_pretty(cfg_arch) %> +<%= field.location_pretty(design) %> <%- end -%> a@ -- -<%= field.type_pretty(cfg_arch.symtab) %> +<%= field.type_pretty(design.symtab) %> -- a@ -- -<%= field.reset_value_pretty(cfg_arch) %> +<%= field.reset_value_pretty(design) %> -- <%- end -%> @@ -121,16 +121,16 @@ IMPORTANT: <%= field.name %> is only defined in <%= field.base32_only? ? "RV32" **** Location:: -<%= field.location_pretty(cfg_arch) %> +<%= field.location_pretty(design) %> Description:: -<%= cfg_arch.render_erb(field.description, "#{csr.name}.#{field.name}.description").gsub("\n", " +\n") %> +<%= design.render_erb(field.description, "#{csr.name}.#{field.name}.description").gsub("\n", " +\n") %> Type:: -<%= field.type_pretty(cfg_arch.symtab) %> +<%= field.type_pretty(design.symtab) %> Reset value:: -<%= field.reset_value_pretty(cfg_arch) %> +<%= field.reset_value_pretty(design) %> **** @@ -164,6 +164,6 @@ This CSR may return a value that is different from what is stored in hardware. [source,idl,subs="specialchars,macros"] ---- -<%= csr.sw_read_ast(cfg_arch.symtab).gen_adoc %> +<%= csr.sw_read_ast(design.symtab).gen_adoc %> ---- <%- end -%> diff --git a/backends/manual/templates/ext.adoc.erb b/backends/manual/templates/ext.adoc.erb index 71a46f857..553d5ef22 100644 --- a/backends/manual/templates/ext.adoc.erb +++ b/backends/manual/templates/ext.adoc.erb @@ -36,7 +36,7 @@ <%= ext.description %> -<%- insts = cfg_arch.instructions.select { |i| ext.versions.any? { |v| i.defined_by?(v) } } -%> +<%- insts = design.arch.instructions.select { |i| ext.versions.any? { |v| i.defined_by?(v) } } -%> <%- unless insts.empty? -%> == Instructions diff --git a/backends/manual/templates/func.adoc.erb b/backends/manual/templates/func.adoc.erb index 01d693ae3..ccb25381c 100644 --- a/backends/manual/templates/func.adoc.erb +++ b/backends/manual/templates/func.adoc.erb @@ -4,7 +4,7 @@ = Functions -<%- cfg_arch.functions.each do |f| -%> +<%- design.functions.each do |f| -%> [#<%= f.name %>-func-def] == <%= f.name %><%- if f.builtin? -%> (builtin)<%- end -%> diff --git a/backends/manual/templates/instruction.adoc.erb b/backends/manual/templates/instruction.adoc.erb index 1c67a226c..8a52089f5 100644 --- a/backends/manual/templates/instruction.adoc.erb +++ b/backends/manual/templates/instruction.adoc.erb @@ -11,7 +11,7 @@ This instruction is defined by: This instruction is included in the following profiles: -<%- cfg_arch.profiles.each do |profile| -%> +<%- design.arch.profiles.each do |profile| -%> <%- in_profile_mandatory = profile.mandatory_ext_reqs.any? do |ext_req| ext_versions = ext_req.satisfying_versions @@ -128,7 +128,7 @@ IDL:: + [source,idl,subs="specialchars,macros"] ---- -<%= inst.operation_ast(inst.cfg_arch.symtab).gen_adoc %> +<%= inst.operation_ast(design.symtab).gen_adoc %> ---- <%- end -%> @@ -142,7 +142,7 @@ Sail:: <%- end -%> ==== -<% exception_list = inst.reachable_exceptions_str(inst.cfg_arch.symtab, 64) -%> +<% exception_list = inst.reachable_exceptions_str(design.symtab, 64) -%> <%- unless exception_list.empty? -%> == Exceptions diff --git a/lib/arch_obj_models/extension.rb b/lib/arch_obj_models/extension.rb index 6ec80b731..468ab5474 100644 --- a/lib/arch_obj_models/extension.rb +++ b/lib/arch_obj_models/extension.rb @@ -244,29 +244,26 @@ def csrs # # @param symtab [Idl::SymbolTable] The evaluation context # @return [Array] Array of IDL functions reachable from any instruction or CSR in the extension - # - # The one place in this file that actually needs a Design object instead of just Architecture. - # Doesn't seem to be called anywhere. If there is somewhere that calls this, pass it a Design object. -# def reachable_functions(symtab) -# @reachable_functions ||= {} -# -# return @reachable_functions[symtab] unless @reachable_functions[symtab].nil? -# -# funcs = [] -# -# puts "Finding all reachable functions from extension #{name}" -# -# instructions.each do |inst| -# funcs += inst.reachable_functions(symtab, 32) if inst.defined_in_base?(32) -# funcs += inst.reachable_functions(symtab, 64) if inst.defined_in_base?(64) -# end -# -# csrs.each do |csr| -# funcs += csr.reachable_functions(design) -# end -# -# @reachable_functions[symtab] = funcs.uniq -# end + def reachable_functions(symtab) + @reachable_functions ||= {} + + return @reachable_functions[symtab] unless @reachable_functions[symtab].nil? + + funcs = [] + + puts "Finding all reachable functions from extension #{name}" + + instructions.each do |inst| + funcs += inst.reachable_functions(symtab, 32) if inst.defined_in_base?(32) + funcs += inst.reachable_functions(symtab, 64) if inst.defined_in_base?(64) + end + + csrs.each do |csr| + funcs += csr.reachable_functions(design) + end + + @reachable_functions[symtab] = funcs.uniq + end end # A specific version of an extension From 953785afd62e7f73d83958d78826215e88987dc7 Mon Sep 17 00:00:00 2001 From: James Ball Date: Thu, 9 Jan 2025 15:26:26 +0000 Subject: [PATCH 16/21] One more fix --- lib/cfg_arch.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/cfg_arch.rb b/lib/cfg_arch.rb index db9aeda60..0b7600d31 100644 --- a/lib/cfg_arch.rb +++ b/lib/cfg_arch.rb @@ -145,7 +145,7 @@ def params_with_value if @config.fully_configured? transitive_implemented_ext_vers.each do |ext_version| - ext_version.extension.params.each do |ext_param| + ext_version.ext.params.each do |ext_param| next unless @config.param_values.key?(ext_param.name) @params_with_value << ExtensionParameterWithValue.new( @@ -243,7 +243,7 @@ def prohibited_ext_reqs @prohibited_ext_reqs elsif @config.fully_configured? prohibited_ext_vers = [] - extensions.each do |ext| + arch.extensions.each do |ext| ext.versions.each do |ext_ver| prohibited_ext_vers << ext_ver unless transitive_implemented_ext_vers.include?(ext_ver) end From 4db2d6875fc3d349c98a23809eaa0550c1ecb24d Mon Sep 17 00:00:00 2001 From: James Ball Date: Thu, 9 Jan 2025 16:22:02 +0000 Subject: [PATCH 17/21] Fix to bug found after merging in main. --- lib/design.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/design.rb b/lib/design.rb index 90968ae94..150cbf5c4 100644 --- a/lib/design.rb +++ b/lib/design.rb @@ -329,9 +329,9 @@ def render_erb_env return @env unless @env.nil? @env = Class.new - @env.instance_variable_set(:@design, design) + @env.instance_variable_set(:@design, self) @env.instance_variable_set(:@params, @param_values) - @env.instance_variable_set(:@arch, arch) + @env.instance_variable_set(:@arch, @arch) # add each parameter, either as a method (lowercase) or constant (uppercase) params_with_value.each do |param| From c9b2d6b36cb0b2363c0c784ea943942e8f6f8ff6 Mon Sep 17 00:00:00 2001 From: James Ball Date: Thu, 9 Jan 2025 18:42:27 +0000 Subject: [PATCH 18/21] Change comment to kick off another regression test on GitHub server (seems to have failed incorrectly). --- lib/design.rb | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/design.rb b/lib/design.rb index 150cbf5c4..9e59c0ea6 100644 --- a/lib/design.rb +++ b/lib/design.rb @@ -321,9 +321,6 @@ def find_replace_links(adoc) # Returns an environment hash suitable for the render() function in ERB templates. # - # This method returns a hash containing the architecture definition and other - # relevant data that can be used to generate ERb templates. - # # @return [Hash] An environment hash suitable for use with ERb templates. def render_erb_env return @env unless @env.nil? From 57d5f086159aa5639e969497456ddbf4fae1d5dc Mon Sep 17 00:00:00 2001 From: James Ball Date: Thu, 9 Jan 2025 19:06:44 +0000 Subject: [PATCH 19/21] task for profile is now profile_release_pdf. --- .github/workflows/pages.yml | 12 ++++++++---- .github/workflows/regress.yml | 2 +- .vscode/launch.json | 2 +- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml index 572cdb945..2e24a24b8 100644 --- a/.github/workflows/pages.yml +++ b/.github/workflows/pages.yml @@ -60,15 +60,19 @@ jobs: - name: Copy cfg html run: cp -R gen/cfg_html_doc/generic_rv64/html _site/example_cfg - name: Create RVA20 Profile Release PDF Spec - run: ./do gen:profile[RVA20] + run: ./do gen:profile_release_pdf[RVA20] - name: Copy RVA20 Profile Release PDF run: cp gen/profile_doc/pdf/RVA20.pdf _site/pdfs/RVA20.pdf - name: Create RVA22 Profile Release PDF Spec - run: ./do gen:profile[RVA22] + run: ./do gen:profile_release_pdf[RVA22] - name: Copy RVA22 Profile Release PDF run: cp gen/profile_doc/pdf/RVA22.pdf _site/pdfs/RVA22.pdf + - name: Copy RVA23 Profile Release PDF + run: cp gen/profile_doc/pdf/RVA23.pdf _site/pdfs/RVA23.pdf + - name: Copy RVB23 Profile Release PDF + run: cp gen/profile_doc/pdf/RVB23.pdf _site/pdfs/RVB23.pdf - name: Create RVI20 Profile Release PDF Spec - run: ./do gen:profile[RVI20] + run: ./do gen:profile_release_pdf[RVI20] - name: Copy RVI20 Profile Release PDF run: cp gen/profile_doc/pdf/RVA20.pdf _site/pdfs/RVI20.pdf - name: Create MC100-32 PDF Spec @@ -86,7 +90,7 @@ jobs: - name: Create MC100-64 HTML Spec run: ./do gen:cert_model_html[MC100-64] - name: Copy MC100-64 HTML - run: cp gen/certificate_doc/html/MC100-64.html _site/htmls/MC100-64.html + un: cp gen/certificate_doc/html/MC100-64.html _site/htmls/MC100-64.html - name: Copy manual html run: cp -R gen/manual/isa/top/all/html _site/manual - name: Setup Pages diff --git a/.github/workflows/regress.yml b/.github/workflows/regress.yml index 5d7b86a93..1a35ed9d3 100644 --- a/.github/workflows/regress.yml +++ b/.github/workflows/regress.yml @@ -160,4 +160,4 @@ jobs: name: Build container run: ./bin/build_container - name: Generate extension PDF - run: ./do gen:profile[MockProfileRelease] + run: ./do gen:profile_release_pdf[MockProfileRelease] diff --git a/.vscode/launch.json b/.vscode/launch.json index 929e09803..ee859125c 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -33,7 +33,7 @@ "name": "RVA20", "request": "launch", "command": "bundle exec rake", - "script": "gen:profile[RVA20]", + "script": "gen:profile_release_pdf[RVA20]", "args": [], "askParameters": false }, From 3f3bcbd0bf387b84a63f88b62788590f96a239dc Mon Sep 17 00:00:00 2001 From: James Ball Date: Fri, 10 Jan 2025 03:25:32 +0000 Subject: [PATCH 20/21] Renaming certificate to CRD in the backend. Also added proc_ prefix to frontend /arch files. And, also igore *.bak files in Git. --- .github/workflows/pages.yml | 16 ++-- .github/workflows/regress.yml | 2 +- .gitignore | 1 + .pre-commit-config.yaml | 8 +- .vscode/launch.json | 4 +- Rakefile | 58 ++++++------- arch/certificate_class/MC.yaml | 13 --- .../MockCertificateClass.yaml | 13 --- arch/certificate_model/MC100-64.yaml | 20 ----- arch/certificate_model/MC200-64.yaml | 20 ----- arch/certificate_model/MC300-64.yaml | 20 ----- arch/proc_cert_class/MC.yaml | 13 +++ arch/proc_cert_class/MockProcCertClass.yaml | 13 +++ .../MC100-32.yaml | 14 ++- arch/proc_cert_model/MC100-64.yaml | 20 +++++ .../MC200-32.yaml | 14 +-- arch/proc_cert_model/MC200-64.yaml | 20 +++++ .../MC300-32.yaml | 14 +-- arch/proc_cert_model/MC300-64.yaml | 20 +++++ .../MockProcCertModel.yaml} | 12 +-- backends/cfg_html_doc/templates/ext.adoc.erb | 2 +- backends/{certificate_doc => crd}/tasks.rake | 86 +++++++++---------- .../templates/crd.adoc.erb} | 72 ++++++++-------- cert_flow.txt | 22 ++--- lib/arch_obj_models/certificate.rb | 26 +++--- lib/arch_obj_models/portfolio.rb | 2 +- lib/architecture.rb | 30 +++---- lib/portfolio_design.rb | 2 +- ...chema.json => proc_cert_class_schema.json} | 4 +- ...chema.json => proc_cert_model_schema.json} | 28 +++--- 30 files changed, 296 insertions(+), 293 deletions(-) delete mode 100644 arch/certificate_class/MC.yaml delete mode 100644 arch/certificate_class/MockCertificateClass.yaml delete mode 100644 arch/certificate_model/MC100-64.yaml delete mode 100644 arch/certificate_model/MC200-64.yaml delete mode 100644 arch/certificate_model/MC300-64.yaml create mode 100644 arch/proc_cert_class/MC.yaml create mode 100644 arch/proc_cert_class/MockProcCertClass.yaml rename arch/{certificate_model => proc_cert_model}/MC100-32.yaml (92%) create mode 100644 arch/proc_cert_model/MC100-64.yaml rename arch/{certificate_model => proc_cert_model}/MC200-32.yaml (67%) create mode 100644 arch/proc_cert_model/MC200-64.yaml rename arch/{certificate_model => proc_cert_model}/MC300-32.yaml (51%) create mode 100644 arch/proc_cert_model/MC300-64.yaml rename arch/{certificate_model/MockCertificateModel.yaml => proc_cert_model/MockProcCertModel.yaml} (95%) rename backends/{certificate_doc => crd}/tasks.rake (51%) rename backends/{certificate_doc/templates/certificate.adoc.erb => crd/templates/crd.adoc.erb} (90%) rename schemas/{cert_class_schema.json => proc_cert_class_schema.json} (93%) rename schemas/{cert_model_schema.json => proc_cert_model_schema.json} (86%) diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml index 2e24a24b8..51469dc16 100644 --- a/.github/workflows/pages.yml +++ b/.github/workflows/pages.yml @@ -76,21 +76,21 @@ jobs: - name: Copy RVI20 Profile Release PDF run: cp gen/profile_doc/pdf/RVA20.pdf _site/pdfs/RVI20.pdf - name: Create MC100-32 PDF Spec - run: ./do gen:cert_model_pdf[MC100-32] + run: ./do gen:proc_cert_model_pdf[MC100-32] - name: Copy MC100-32 PDF - run: cp gen/certificate_doc/pdf/MC100-32.pdf _site/pdfs/MC100-32.pdf + run: cp gen/crd/pdf/MC100-32.pdf _site/pdfs/MC100-32.pdf - name: Create MC100-32 HTML Spec - run: ./do gen:cert_model_html[MC100-32] + run: ./do gen:proc_cert_model_html[MC100-32] - name: Copy MC100-32 HTML - run: cp gen/certificate_doc/html/MC100-32.html _site/htmls/MC100-32.html + run: cp gen/crd/html/MC100-32.html _site/htmls/MC100-32.html - name: Create MC100-64 PDF Spec - run: ./do gen:cert_model_pdf[MC100-64] + run: ./do gen:proc_cert_model_pdf[MC100-64] - name: Copy MC100-64 PDF - run: cp gen/certificate_doc/pdf/MC100-64.pdf _site/pdfs/MC100-64.pdf + run: cp gen/crd/pdf/MC100-64.pdf _site/pdfs/MC100-64.pdf - name: Create MC100-64 HTML Spec - run: ./do gen:cert_model_html[MC100-64] + run: ./do gen:proc_cert_model_html[MC100-64] - name: Copy MC100-64 HTML - un: cp gen/certificate_doc/html/MC100-64.html _site/htmls/MC100-64.html + un: cp gen/crd/html/MC100-64.html _site/htmls/MC100-64.html - name: Copy manual html run: cp -R gen/manual/isa/top/all/html _site/manual - name: Setup Pages diff --git a/.github/workflows/regress.yml b/.github/workflows/regress.yml index 1a35ed9d3..dc9ab17b7 100644 --- a/.github/workflows/regress.yml +++ b/.github/workflows/regress.yml @@ -131,7 +131,7 @@ jobs: name: Build container run: ./bin/build_container - name: Generate extension PDF - run: ./do gen:cert_model_pdf[MockCertificateModel] + run: ./do gen:proc_cert_model_pdf[MockProcCertModel] regress-gen-profile: runs-on: ubuntu-latest needs: regress-smoke diff --git a/.gitignore b/.gitignore index 47d2d0cf0..ce0c1b31c 100644 --- a/.gitignore +++ b/.gitignore @@ -15,4 +15,5 @@ resolved_arch node_modules _site images +*.bak *.log diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7cbbcf5a9..c5c579177 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -46,12 +46,12 @@ repos: args: ["--schemafile", "schemas/ext_schema.json"] - id: check-jsonschema alias: check-jsonschema-cert-model - files: ^arch/certificate_model/.*\.(yaml|yml)$ - args: ["--schemafile", "schemas/cert_model_schema.json"] + files: ^arch/proc_cert_model/.*\.(yaml|yml)$ + args: ["--schemafile", "schemas/proc_cert_model_schema.json"] - id: check-jsonschema alias: check-jsonschema-cert-class - files: ^arch/certificate_class/.*\.(yaml|yml)$ - args: ["--schemafile", "schemas/cert_class_schema.json"] + files: ^arch/proc_cert_class/.*\.(yaml|yml)$ + args: ["--schemafile", "schemas/proc_cert_class_schema.json"] # Commenting because throwing errors and not sure this is complete yet # - id: check-jsonschema # alias: check-jsonschema-manual-version diff --git a/.vscode/launch.json b/.vscode/launch.json index ee859125c..325f1cb86 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -6,7 +6,7 @@ "name": "MC100-32", "request": "launch", "command": "bundle exec rake", - "script": "gen:cert_model_pdf[MC100-32]", + "script": "gen:proc_cert_model_pdf[MC100-32]", "args": [], "askParameters": false }, @@ -15,7 +15,7 @@ "name": "MC200-32", "request": "launch", "command": "bundle exec rake", - "script": "gen:cert_model_pdf[MC200-32]", + "script": "gen:proc_cert_model_pdf[MC200-32]", "args": [], "askParameters": false }, diff --git a/Rakefile b/Rakefile index dd064ead8..f2caaf94d 100644 --- a/Rakefile +++ b/Rakefile @@ -38,7 +38,7 @@ def arch_for(base_isa_name, base) ) end -# @param design_name [String] Profile release name for profiles and certificate model name for certificates +# @param design_name [String] Profile release name for profiles and processor certificate model name for certificates # @param arch [Architecture] The architecture database # @param base [Integer] 32 or 64 # @param portfolios [Array] Portfolios in this design @@ -357,7 +357,7 @@ namespace :test do Rake::Task["gen:html"].invoke("generic_rv64") - Rake::Task["#{$root}/gen/certificate_doc/pdf/MockCertificateModel.pdf"].invoke + Rake::Task["#{$root}/gen/crd/pdf/MockProcCertModel.pdf"].invoke Rake::Task["#{$root}/gen/profile_doc/pdf/MockProfileRelease.pdf"].invoke puts @@ -381,31 +381,31 @@ desc <<~DESC Generate all portfolio-based PDF artifacts (certificates and profiles) DESC task :portfolios do - portfolio_start_msg("MockCertificateModel") - Rake::Task["#{$root}/gen/certificate_doc/pdf/MockCertificateModel.pdf"].invoke - portfolio_start_msg("MockProfileRelease") + portfolio_start_msg("MockProcCertModel CRD") + Rake::Task["#{$root}/gen/crd/pdf/MockProcCertModel.pdf"].invoke + portfolio_start_msg("MockProfileRelease CRD") Rake::Task["#{$root}/gen/profile_doc/pdf/MockProfileRelease.pdf"].invoke - portfolio_start_msg("MC100-32") - Rake::Task["#{$root}/gen/certificate_doc/pdf/MC100-32.pdf"].invoke - portfolio_start_msg("MC100-64") - Rake::Task["#{$root}/gen/certificate_doc/pdf/MC100-64.pdf"].invoke - portfolio_start_msg("MC200-32") - Rake::Task["#{$root}/gen/certificate_doc/pdf/MC200-32.pdf"].invoke - portfolio_start_msg("MC200-64") - Rake::Task["#{$root}/gen/certificate_doc/pdf/MC200-64.pdf"].invoke - portfolio_start_msg("MC300-32") - Rake::Task["#{$root}/gen/certificate_doc/pdf/MC300-32.pdf"].invoke - portfolio_start_msg("MC300-64") - Rake::Task["#{$root}/gen/certificate_doc/pdf/MC300-64.pdf"].invoke - portfolio_start_msg("RVI20") + portfolio_start_msg("MC100-32 CRD") + Rake::Task["#{$root}/gen/crd/pdf/MC100-32.pdf"].invoke + portfolio_start_msg("MC100-64 CRD") + Rake::Task["#{$root}/gen/crd/pdf/MC100-64.pdf"].invoke + portfolio_start_msg("MC200-32 CRD") + Rake::Task["#{$root}/gen/crd/pdf/MC200-32.pdf"].invoke + portfolio_start_msg("MC200-64 CRD") + Rake::Task["#{$root}/gen/crd/pdf/MC200-64.pdf"].invoke + portfolio_start_msg("MC300-32 CRD") + Rake::Task["#{$root}/gen/crd/pdf/MC300-32.pdf"].invoke + portfolio_start_msg("MC300-64 CRD") + Rake::Task["#{$root}/gen/crd/pdf/MC300-64.pdf"].invoke + portfolio_start_msg("RVI20 Profile Release") Rake::Task["#{$root}/gen/profile_doc/pdf/RVI20.pdf"].invoke - portfolio_start_msg("RVA20") + portfolio_start_msg("RVA20 Profile Release") Rake::Task["#{$root}/gen/profile_doc/pdf/RVA20.pdf"].invoke - portfolio_start_msg("RVA22") + portfolio_start_msg("RVA22 Profile Release") Rake::Task["#{$root}/gen/profile_doc/pdf/RVA22.pdf"].invoke - portfolio_start_msg("RVA23") + portfolio_start_msg("RVA23 Profile Release") Rake::Task["#{$root}/gen/profile_doc/pdf/RVA23.pdf"].invoke - portfolio_start_msg("RVB23") + portfolio_start_msg("RVB23 Profile Release") Rake::Task["#{$root}/gen/profile_doc/pdf/RVB23.pdf"].invoke end @@ -418,13 +418,13 @@ def portfolio_start_msg(name) end # Shortcut targets for building profiles and certificates. -task "MockCertificateModel": "#{$root}/gen/certificate_doc/pdf/MockCertificateModel.pdf" -task "MC100-32": "#{$root}/gen/certificate_doc/pdf/MC100-32.pdf" -task "MC100-64": "#{$root}/gen/certificate_doc/pdf/MC100-64.pdf" -task "MC200-32": "#{$root}/gen/certificate_doc/pdf/MC200-32.pdf" -task "MC200-64": "#{$root}/gen/certificate_doc/pdf/MC200-64.pdf" -task "MC300-32": "#{$root}/gen/certificate_doc/pdf/MC300-32.pdf" -task "MC300-64": "#{$root}/gen/certificate_doc/pdf/MC300-64.pdf" +task "MockProcCertModel": "#{$root}/gen/crd/pdf/MockProcCertModel.pdf" +task "MC100-32": "#{$root}/gen/crd/pdf/MC100-32.pdf" +task "MC100-64": "#{$root}/gen/crd/pdf/MC100-64.pdf" +task "MC200-32": "#{$root}/gen/crd/pdf/MC200-32.pdf" +task "MC200-64": "#{$root}/gen/crd/pdf/MC200-64.pdf" +task "MC300-32": "#{$root}/gen/crd/pdf/MC300-32.pdf" +task "MC300-64": "#{$root}/gen/crd/pdf/MC300-64.pdf" task "MockProfileRelease": "#{$root}/gen/profile_doc/pdf/MockProfileRelease.pdf" task "RVI20": "#{$root}/gen/profile_doc/pdf/RVI20.pdf" task "RVA20": "#{$root}/gen/profile_doc/pdf/RVA20.pdf" diff --git a/arch/certificate_class/MC.yaml b/arch/certificate_class/MC.yaml deleted file mode 100644 index ae5eb1977..000000000 --- a/arch/certificate_class/MC.yaml +++ /dev/null @@ -1,13 +0,0 @@ -# yaml-language-server: $schema=../../schemas/cert_class_schema.json - -$schema: cert_class_schema.json# -kind: Processor CRD -processor_kind: Microcontroller -name: MC -long_name: Microcontroller Class CRD - -introduction: | - The MC (Microcontroller Class) targets processors running low-level software on an RTOS or bare-metal. - -mandatory_priv_modes: - - M diff --git a/arch/certificate_class/MockCertificateClass.yaml b/arch/certificate_class/MockCertificateClass.yaml deleted file mode 100644 index e358b8684..000000000 --- a/arch/certificate_class/MockCertificateClass.yaml +++ /dev/null @@ -1,13 +0,0 @@ -# yaml-language-server: $schema=../../schemas/cert_class_schema.json - -$schema: cert_class_schema.json# -kind: Processor CRD -processor_kind: Apps Processor -name: MockCertificateClass -long_name: Mock Certificate Class Long Name - -introduction: | - Here's the Mock Certificate Class introduction. - -mandatory_priv_modes: - - M diff --git a/arch/certificate_model/MC100-64.yaml b/arch/certificate_model/MC100-64.yaml deleted file mode 100644 index bede0a7a0..000000000 --- a/arch/certificate_model/MC100-64.yaml +++ /dev/null @@ -1,20 +0,0 @@ -# yaml-language-server: $schema=../../schemas/cert_model_schema.json - -$schema: cert_model_schema.json# -kind: certificate model -name: MC100-64 -long_name: Basic 64-bit Microcontroller Certificate -class: - $ref: certificate_class/MC.yaml# - -$inherits: "certificate_model/MC100-32.yaml#" - -# XLEN used by rakefile -base: 64 - -extensions: - Sm: - parameters: - XLEN: - schema: - const: 64 diff --git a/arch/certificate_model/MC200-64.yaml b/arch/certificate_model/MC200-64.yaml deleted file mode 100644 index a4925bdb8..000000000 --- a/arch/certificate_model/MC200-64.yaml +++ /dev/null @@ -1,20 +0,0 @@ -# yaml-language-server: $schema=../../schemas/cert_model_schema.json - -$schema: cert_model_schema.json# -kind: certificate model -name: MC200-64 -long_name: Intermediate 64-bit Microcontroller Certificate -class: - $ref: certificate_class/MC.yaml# - -$inherits: "certificate_model/MC200-32.yaml#" - -# XLEN used by rakefile -base: 64 - -extensions: - Sm: - parameters: - XLEN: - schema: - const: 64 diff --git a/arch/certificate_model/MC300-64.yaml b/arch/certificate_model/MC300-64.yaml deleted file mode 100644 index 251b49782..000000000 --- a/arch/certificate_model/MC300-64.yaml +++ /dev/null @@ -1,20 +0,0 @@ -# yaml-language-server: $schema=../../schemas/cert_model_schema.json - -$schema: cert_model_schema.json# -kind: certificate model -name: MC300-64 -long_name: Advanced 64-bit Microcontroller Certificate -class: - $ref: certificate_class/MC.yaml# - -$inherits: "certificate_model/MC300-32.yaml#" - -# XLEN used by rakefile -base: 64 - -extensions: - Sm: - parameters: - XLEN: - schema: - const: 64 diff --git a/arch/proc_cert_class/MC.yaml b/arch/proc_cert_class/MC.yaml new file mode 100644 index 000000000..2008a3a3c --- /dev/null +++ b/arch/proc_cert_class/MC.yaml @@ -0,0 +1,13 @@ +# yaml-language-server: $schema=../../schemas/proc_cert_class_schema.json + +$schema: proc_cert_class_schema.json# +kind: Processor Certificate Class +processor_kind: Microcontroller +name: MC +long_name: Microcontroller Processor Certificate Class + +introduction: | + The MC (Microcontroller Class) targets processors running low-level software on an RTOS or bare-metal. + +mandatory_priv_modes: + - M diff --git a/arch/proc_cert_class/MockProcCertClass.yaml b/arch/proc_cert_class/MockProcCertClass.yaml new file mode 100644 index 000000000..8e39ad897 --- /dev/null +++ b/arch/proc_cert_class/MockProcCertClass.yaml @@ -0,0 +1,13 @@ +# yaml-language-server: $schema=../../schemas/proc_cert_class_schema.json + +$schema: proc_cert_class_schema.json# +kind: Processor Certificate Class +processor_kind: Apps Processor +name: MockProcCertClass +long_name: Mock Processor Certificate Class Long Name + +introduction: | + Here's the Mock Certificate Class introduction. + +mandatory_priv_modes: + - M diff --git a/arch/certificate_model/MC100-32.yaml b/arch/proc_cert_model/MC100-32.yaml similarity index 92% rename from arch/certificate_model/MC100-32.yaml rename to arch/proc_cert_model/MC100-32.yaml index a2a0d00db..4ebff9a41 100644 --- a/arch/certificate_model/MC100-32.yaml +++ b/arch/proc_cert_model/MC100-32.yaml @@ -1,11 +1,11 @@ -# yaml-language-server: $schema=../../schemas/cert_model_schema.json +# yaml-language-server: $schema=../../schemas/proc_cert_model_schema.json -$schema: cert_model_schema.json# -kind: certificate model +$schema: proc_cert_model_schema.json# +kind: Processor Certificate Model name: MC100-32 long_name: Basic 32-bit Microcontroller Certificate class: - $ref: certificate_class/MC.yaml# + $ref: proc_cert_class/MC.yaml# # Semantic versions within the model versions: @@ -68,10 +68,8 @@ revision_history: - Initial version introduction: | - The MC100 Processor CRD (Certification Requirements Document) defines the requirements - a processor implementation must meet in order to be eligible for the associated MC100 certificate. - MC100 is a basic RISC-V processor with minimal M-mode support and has 32-bit and 64-bit variants. - + The MC100 Processor Certificate targets basic RISC-V microcontrollers. + It supports either a 32-bit (MC100-32) or 64-bit (MC100-64) base ISA. MC100 is not intended for the smallest possible microcontrollers but rather for applications benefiting from a minimal but standardized microcontroller. It consists of: diff --git a/arch/proc_cert_model/MC100-64.yaml b/arch/proc_cert_model/MC100-64.yaml new file mode 100644 index 000000000..13c595d76 --- /dev/null +++ b/arch/proc_cert_model/MC100-64.yaml @@ -0,0 +1,20 @@ +# yaml-language-server: $schema=../../schemas/proc_cert_model_schema.json + +$schema: proc_cert_model_schema.json# +kind: Processor Certificate Model +name: MC100-64 +long_name: Basic 64-bit Microcontroller Certificate +class: + $ref: proc_cert_class/MC.yaml# + +$inherits: "proc_cert_model/MC100-32.yaml#" + +# XLEN used by rakefile +base: 64 + +extensions: + Sm: + parameters: + XLEN: + schema: + const: 64 diff --git a/arch/certificate_model/MC200-32.yaml b/arch/proc_cert_model/MC200-32.yaml similarity index 67% rename from arch/certificate_model/MC200-32.yaml rename to arch/proc_cert_model/MC200-32.yaml index ee2d1049a..f2b52c847 100644 --- a/arch/certificate_model/MC200-32.yaml +++ b/arch/proc_cert_model/MC200-32.yaml @@ -1,11 +1,11 @@ -# yaml-language-server: $schema=../../schemas/cert_model_schema.json +# yaml-language-server: $schema=../../schemas/proc_cert_model_schema.json -$schema: cert_model_schema.json# -kind: certificate model +$schema: proc_cert_model_schema.json# +kind: Processor Certificate Model name: MC200-32 long_name: Intermediate 32-bit Microcontroller Certificate class: - $ref: certificate_class/MC.yaml# + $ref: proc_cert_class/MC.yaml# # Semantic versions within the model versions: @@ -14,7 +14,7 @@ versions: # XLEN used by rakefile base: 32 -$inherits: "certificate_model/MC100-32.yaml#" +$inherits: "proc_cert_model/MC100-32.yaml#" revision_history: - revision: "0.1.0" @@ -23,7 +23,9 @@ revision_history: - First created introduction: | - MC200 is an intermedicate RISC-V microcontroller that adds the following mandatory extensions to the MC100-series: + The MC200 Processor Certificate targets intermediate RISC-V microcontrollers. + It supports either a 32-bit (MC200-32) or 64-bit (MC200-64) base ISA. + The MC200 adds the following mandatory extensions to the MC100: * U extension (User-mode privilege level) * Smpmp extension (M-mode PMP) diff --git a/arch/proc_cert_model/MC200-64.yaml b/arch/proc_cert_model/MC200-64.yaml new file mode 100644 index 000000000..c3e551c08 --- /dev/null +++ b/arch/proc_cert_model/MC200-64.yaml @@ -0,0 +1,20 @@ +# yaml-language-server: $schema=../../schemas/proc_cert_model_schema.json + +$schema: proc_cert_model_schema.json# +kind: Processor Certificate Model +name: MC200-64 +long_name: Intermediate 64-bit Microcontroller Certificate +class: + $ref: proc_cert_class/MC.yaml# + +$inherits: "proc_cert_model/MC200-32.yaml#" + +# XLEN used by rakefile +base: 64 + +extensions: + Sm: + parameters: + XLEN: + schema: + const: 64 diff --git a/arch/certificate_model/MC300-32.yaml b/arch/proc_cert_model/MC300-32.yaml similarity index 51% rename from arch/certificate_model/MC300-32.yaml rename to arch/proc_cert_model/MC300-32.yaml index 512dd0b61..ca52f44ec 100644 --- a/arch/certificate_model/MC300-32.yaml +++ b/arch/proc_cert_model/MC300-32.yaml @@ -1,11 +1,11 @@ -# yaml-language-server: $schema=../../schemas/cert_model_schema.json +# yaml-language-server: $schema=../../schemas/proc_cert_model_schema.json -$schema: cert_model_schema.json# -kind: certificate model +$schema: proc_cert_model_schema.json# +kind: Processor Certificate Model name: MC300-32 long_name: Advanced 32-bit Microcontroller Certificate class: - $ref: certificate_class/MC.yaml# + $ref: proc_cert_class/MC.yaml# # Semantic versions within the model versions: @@ -14,7 +14,7 @@ versions: # XLEN used by rakefile base: 32 -$inherits: "certificate_model/MC200-32.yaml#" +$inherits: "proc_cert_model/MC200-32.yaml#" revision_history: - revision: "0.1.0" @@ -23,7 +23,9 @@ revision_history: - First created introduction: | - MC300 is an advanced RISC-V microcontroller that adds the following mandatory extensions to the MC200-series: + The MC300 Processor Certificate targets advanced RISC-V microcontrollers. + It supports either a 32-bit (MC300-32) or 64-bit (MC300-64) base ISA. + The MC300 adds the following mandatory extensions to the MC200: * S extension (Supervisor-mode privilege level) * Sspmp extension (S-mode PMP, not ratified yet) diff --git a/arch/proc_cert_model/MC300-64.yaml b/arch/proc_cert_model/MC300-64.yaml new file mode 100644 index 000000000..72b7bd71e --- /dev/null +++ b/arch/proc_cert_model/MC300-64.yaml @@ -0,0 +1,20 @@ +# yaml-language-server: $schema=../../schemas/proc_cert_model_schema.json + +$schema: proc_cert_model_schema.json# +kind: Processor Certificate Model +name: MC300-64 +long_name: Advanced 64-bit Microcontroller Certificate +class: + $ref: proc_cert_class/MC.yaml# + +$inherits: "proc_cert_model/MC300-32.yaml#" + +# XLEN used by rakefile +base: 64 + +extensions: + Sm: + parameters: + XLEN: + schema: + const: 64 diff --git a/arch/certificate_model/MockCertificateModel.yaml b/arch/proc_cert_model/MockProcCertModel.yaml similarity index 95% rename from arch/certificate_model/MockCertificateModel.yaml rename to arch/proc_cert_model/MockProcCertModel.yaml index b9c072e70..a81b20dc7 100644 --- a/arch/certificate_model/MockCertificateModel.yaml +++ b/arch/proc_cert_model/MockProcCertModel.yaml @@ -1,11 +1,11 @@ -# yaml-language-server: $schema=../../schemas/cert_model_schema.json +# yaml-language-server: $schema=../../schemas/proc_cert_model_schema.json -$schema: cert_model_schema.json# -kind: certificate model -name: MockCertificateModel -long_name: Mock Certificate Model Long Name +$schema: proc_cert_model_schema.json# +kind: Processor Certificate Model +name: MockProcCertModel +long_name: Mock Processor Certificate Model Long Name class: - $ref: certificate_class/MockCertificateClass.yaml# + $ref: proc_cert_class/MockProcCertClass.yaml# # XLEN used by rakefile base: 64 diff --git a/backends/cfg_html_doc/templates/ext.adoc.erb b/backends/cfg_html_doc/templates/ext.adoc.erb index 089409ee0..26f367ee4 100644 --- a/backends/cfg_html_doc/templates/ext.adoc.erb +++ b/backends/cfg_html_doc/templates/ext.adoc.erb @@ -39,7 +39,7 @@ Implemented Version:: <%= ext_version.version_str %> <%- unless insts.empty? -%> == Instructions -The following instructions are added by this extension in the <%= ext.cfg_arch.name %> configuration: +The following instructions are added by this extension in the <%= cfg_arch.name %> configuration: [cols="1,3"] |=== diff --git a/backends/certificate_doc/tasks.rake b/backends/crd/tasks.rake similarity index 51% rename from backends/certificate_doc/tasks.rake rename to backends/crd/tasks.rake index b726aa88b..6f35d0657 100644 --- a/backends/certificate_doc/tasks.rake +++ b/backends/crd/tasks.rake @@ -7,44 +7,44 @@ require "asciidoctor-diagram" require_relative "#{$lib}/idl/passes/gen_adoc" -CERT_DOC_DIR = Pathname.new "#{$root}/backends/certificate_doc" +CERT_DOC_DIR = Pathname.new "#{$root}/backends/crd" -Dir.glob("#{$root}/arch/certificate_model/*.yaml") do |f| - cert_model_name = File.basename(f, ".yaml") - cert_model_obj = YAML.load_file(f, permitted_classes: [Date]) - cert_class_name = File.basename(cert_model_obj['class']['$ref'].split("#")[0], ".yaml") - raise "Ill-formed certificate model file #{f}: missing 'class' field" if cert_model_obj['class'].nil? +Dir.glob("#{$root}/arch/proc_cert_model/*.yaml") do |f| + proc_cert_model_name = File.basename(f, ".yaml") + proc_cert_model_obj = YAML.load_file(f, permitted_classes: [Date]) + proc_cert_class_name = File.basename(proc_cert_model_obj['class']['$ref'].split("#")[0], ".yaml") + raise "Ill-formed processor certificate model file #{f}: missing 'class' field" if proc_cert_model_obj['class'].nil? - base = cert_model_obj["base"] - raise "Missing certificate model base" if base.nil? + base = proc_cert_model_obj["base"] + raise "Missing processor certificate model base" if base.nil? base_isa_name = "rv#{base}" puts "UPDATE: Extracted base=#{base} from #{f}" - file "#{$root}/gen/certificate_doc/adoc/#{cert_model_name}.adoc" => [ + file "#{$root}/gen/crd/adoc/#{proc_cert_model_name}.adoc" => [ __FILE__, - "#{$root}/arch/certificate_class/#{cert_class_name}.yaml", - "#{$root}/arch/certificate_model/#{cert_model_name}.yaml", + "#{$root}/arch/proc_cert_class/#{proc_cert_class_name}.yaml", + "#{$root}/arch/proc_cert_model/#{proc_cert_model_name}.yaml", "#{$root}/lib/arch_obj_models/certificate.rb", "#{$root}/lib/arch_obj_models/portfolio.rb", "#{$root}/lib/portfolio_design.rb", "#{$root}/lib/design.rb", - "#{CERT_DOC_DIR}/templates/certificate.adoc.erb" + "#{CERT_DOC_DIR}/templates/crd.adoc.erb" ] do |t| # Create Architecture object. Function located in top-level Rakefile. puts "UPDATE: Creating Architecture #{base_isa_name} for #{t}" arch = arch_for(base_isa_name, base) - # Create CertModel for specific certificate model as specified in its arch YAML file. + # Create ProcCertModel for specific processor certificate model as specified in its arch YAML file. # The Architecture object also creates all other portfolio-related class instances from their arch YAML files. # None of these objects are provided with a Design object when created. - puts "UPDATE: Creating CertModel for #{cert_model_name} using base #{base_isa_name}" - cert_model = arch.cert_model(cert_model_name) + puts "UPDATE: Creating ProcCertModel for #{proc_cert_model_name} using base #{base_isa_name}" + proc_cert_model = arch.proc_cert_model(proc_cert_model_name) - puts "UPDATE: Creating PortfolioDesign using certificate model #{cert_model_name}" + puts "UPDATE: Creating PortfolioDesign using processor certificate model #{proc_cert_model_name}" # Create the one PortfolioDesign object required for the ERB evaluation. - portfolio_design = portfolio_design_for(cert_model_name, arch, base, [cert_model]) + portfolio_design = portfolio_design_for(proc_cert_model_name, arch, base, [proc_cert_model]) # Create empty binding and then specify explicitly which variables the ERB template can access. # Seems to use this method name in stack backtraces (hence its name). @@ -54,12 +54,12 @@ Dir.glob("#{$root}/arch/certificate_model/*.yaml") do |f| erb_binding = evaluate_erb erb_binding.local_variable_set(:arch, arch) erb_binding.local_variable_set(:design, portfolio_design) - erb_binding.local_variable_set(:cert_class, cert_model.cert_class) - erb_binding.local_variable_set(:portfolio_class, cert_model.cert_class) - erb_binding.local_variable_set(:cert_model, cert_model) - erb_binding.local_variable_set(:portfolio, cert_model) + erb_binding.local_variable_set(:proc_cert_class, proc_cert_model.proc_cert_class) + erb_binding.local_variable_set(:portfolio_class, proc_cert_model.proc_cert_class) + erb_binding.local_variable_set(:proc_cert_model, proc_cert_model) + erb_binding.local_variable_set(:portfolio, proc_cert_model) - template_path = Pathname.new("#{CERT_DOC_DIR}/templates/certificate.adoc.erb") + template_path = Pathname.new("#{CERT_DOC_DIR}/templates/crd.adoc.erb") erb = ERB.new(File.read(template_path), trim_mode: "-") erb.filename = template_path.to_s @@ -67,7 +67,7 @@ Dir.glob("#{$root}/arch/certificate_model/*.yaml") do |f| # Convert ERB to final ASCIIDOC. Note that this code is broken up into separate function calls # each with a variable name to aid in running a command-line debugger on this code. - puts "UPDATE: Converting ERB template to adoc for #{cert_model_name}" + puts "UPDATE: Converting ERB template to adoc for #{proc_cert_model_name}" erb_result = erb.result(erb_binding) erb_result_monospace_converted_to_links = portfolio_design.find_replace_links(erb_result) erb_result_with_links_added = portfolio_design.find_replace_links(erb_result_monospace_converted_to_links) @@ -77,11 +77,11 @@ Dir.glob("#{$root}/arch/certificate_model/*.yaml") do |f| puts "UPDATE: Generated adoc source at #{t.name}" end - file "#{$root}/gen/certificate_doc/pdf/#{cert_model_name}.pdf" => [ + file "#{$root}/gen/crd/pdf/#{proc_cert_model_name}.pdf" => [ __FILE__, - "#{$root}/gen/certificate_doc/adoc/#{cert_model_name}.adoc" + "#{$root}/gen/crd/adoc/#{proc_cert_model_name}.adoc" ] do |t| - adoc_file = "#{$root}/gen/certificate_doc/adoc/#{cert_model_name}.adoc" + adoc_file = "#{$root}/gen/crd/adoc/#{proc_cert_model_name}.adoc" FileUtils.mkdir_p File.dirname(t.name) sh [ "asciidoctor-pdf", @@ -100,11 +100,11 @@ Dir.glob("#{$root}/arch/certificate_model/*.yaml") do |f| puts "UPDATE: Generated PDF at #{t.name}" end - file "#{$root}/gen/certificate_doc/html/#{cert_model_name}.html" => [ + file "#{$root}/gen/crd/html/#{proc_cert_model_name}.html" => [ __FILE__, - "#{$root}/gen/certificate_doc/adoc/#{cert_model_name}.adoc" + "#{$root}/gen/crd/adoc/#{proc_cert_model_name}.adoc" ] do |t| - adoc_file = "#{$root}/gen/certificate_doc/adoc/#{cert_model_name}.adoc" + adoc_file = "#{$root}/gen/crd/adoc/#{proc_cert_model_name}.adoc" FileUtils.mkdir_p File.dirname(t.name) puts "UPDATE: Generating PDF at #{t.name}" @@ -129,34 +129,34 @@ namespace :gen do Generate certificate documentation for a specific version as a PDF. Required options: - cert_model_name - The key of the certification model under arch/certificate_model + proc_cert_model_name - The key of the certification model under arch/proc_cert_model DESC - task :cert_model_pdf, [:cert_model_name] do |_t, args| - cert_model_name = args[:cert_model_name] - if cert_model_name.nil? - warn "Missing required option: 'cert_model_name'" + task :proc_cert_model_pdf, [:proc_cert_model_name] do |_t, args| + proc_cert_model_name = args[:proc_cert_model_name] + if proc_cert_model_name.nil? + warn "Missing required option: 'proc_cert_model_name'" exit 1 end - unless File.exist?("#{$root}/arch/certificate_model/#{cert_model_name}.yaml") - warn "No certification model named '#{cert_model_name}' found in arch/certificate_model" + unless File.exist?("#{$root}/arch/proc_cert_model/#{proc_cert_model_name}.yaml") + warn "No certification model named '#{proc_cert_model_name}' found in arch/proc_cert_model" exit 1 end - Rake::Task["#{$root}/gen/certificate_doc/pdf/#{cert_model_name}.pdf"].invoke + Rake::Task["#{$root}/gen/crd/pdf/#{proc_cert_model_name}.pdf"].invoke end - task :cert_model_html, [:cert_model_name] do |_t, args| - if args[:cert_model_name].nil? - warn "Missing required option: 'cert_model_name'" + task :proc_cert_model_html, [:proc_cert_model_name] do |_t, args| + if args[:proc_cert_model_name].nil? + warn "Missing required option: 'proc_cert_model_name'" exit 1 end - unless File.exist?("#{$root}/arch/certificate_model/#{args[:cert_model_name]}.yaml") - warn "No certification model named '#{args[:cert_model_name]}' found in arch/certificate_model" + unless File.exist?("#{$root}/arch/proc_cert_model/#{args[:proc_cert_model_name]}.yaml") + warn "No certification model named '#{args[:proc_cert_model_name]}' found in arch/proc_cert_model" exit 1 end - Rake::Task["#{$root}/gen/certificate_doc/html/#{args[:cert_model_name]}.html"].invoke + Rake::Task["#{$root}/gen/crd/html/#{args[:proc_cert_model_name]}.html"].invoke end end diff --git a/backends/certificate_doc/templates/certificate.adoc.erb b/backends/crd/templates/crd.adoc.erb similarity index 90% rename from backends/certificate_doc/templates/certificate.adoc.erb rename to backends/crd/templates/crd.adoc.erb index e4ff70de3..1a5e39ff1 100644 --- a/backends/certificate_doc/templates/certificate.adoc.erb +++ b/backends/crd/templates/crd.adoc.erb @@ -15,7 +15,7 @@ // TODO: needs to be changed :imagesoutdir: images -= <%= cert_model.name %> Processor Certification Requirements Document += <%= proc_cert_model.name %> Processor Certification Requirements Document [Preface] == Revision History @@ -26,7 +26,7 @@ History of documentation changes that eventually lead to releases. |=== | Date | Revision | Changes -<% cert_model.revision_history.each do |rev| -%> +<% proc_cert_model.revision_history.each do |rev| -%> | <%= rev.date %> | <%= rev.revision %> a| <% rev.changes.each do |change| %> @@ -58,9 +58,9 @@ CSR field types:: == Introduction -<%= cert_model.introduction %> +<%= proc_cert_model.introduction %> -<%= cert_class.introduction %> +<%= proc_cert_class.introduction %> === What's a CRD? @@ -140,9 +140,9 @@ These documents augment information in the related TSC Profile when available an Only ratified extensions are candidates for certification. This implies all custom extensions are also OUT-OF-SCOPE. -==== Processor CRD Naming Scheme +==== Processor Certificate Class Naming Scheme -Processor CRD names have the following format: +Processor Certificate Class names have the following format: [<-base>] @@ -240,11 +240,11 @@ verification test suites anyways. |=== | Certificate Model | TSC Profile | Unpriv ISA Manual | Priv ISA Manual | Debug Manual -| <%= cert_model.name %> -| <%= cert_model.tsc_profile.nil? ? "No profile" : cert_model.tsc_profile.marketing_name %> -| <%= cert_model.unpriv_isa_manual_revision %> -| <%= cert_model.priv_isa_manual_revision %> -| <%= cert_model.debug_manual_revision %> +| <%= proc_cert_model.name %> +| <%= proc_cert_model.tsc_profile.nil? ? "No profile" : proc_cert_model.tsc_profile.marketing_name %> +| <%= proc_cert_model.unpriv_isa_manual_revision %> +| <%= proc_cert_model.priv_isa_manual_revision %> +| <%= proc_cert_model.debug_manual_revision %> |=== === Privileged Modes @@ -252,11 +252,11 @@ verification test suites anyways. |=== | M | S | U | VS | VU -| <% if cert_class.mandatory_priv_modes.include?('M') -%> MANDATORY <% else -%> OUT-OF-SCOPE <% end -%> -| <% if cert_class.mandatory_priv_modes.include?('S') -%> MANDATORY <% else -%> OUT-OF-SCOPE <% end -%> -| <% if cert_class.mandatory_priv_modes.include?('U') -%> MANDATORY <% else -%> OUT-OF-SCOPE <% end -%> -| <% if cert_class.mandatory_priv_modes.include?('VS') -%> MANDATORY <% else -%> OUT-OF-SCOPE <% end -%> -| <% if cert_class.mandatory_priv_modes.include?('VU') -%> MANDATORY <% else -%> OUT-OF-SCOPE <% end -%> +| <% if proc_cert_class.mandatory_priv_modes.include?('M') -%> MANDATORY <% else -%> OUT-OF-SCOPE <% end -%> +| <% if proc_cert_class.mandatory_priv_modes.include?('S') -%> MANDATORY <% else -%> OUT-OF-SCOPE <% end -%> +| <% if proc_cert_class.mandatory_priv_modes.include?('U') -%> MANDATORY <% else -%> OUT-OF-SCOPE <% end -%> +| <% if proc_cert_class.mandatory_priv_modes.include?('VS') -%> MANDATORY <% else -%> OUT-OF-SCOPE <% end -%> +| <% if proc_cert_class.mandatory_priv_modes.include?('VU') -%> MANDATORY <% else -%> OUT-OF-SCOPE <% end -%> |=== @@ -264,13 +264,13 @@ verification test suites anyways. == Extensions Any RISC-V extensions not listed in this section are OUT-OF-SCOPE. -The <%= cert_model.name %> certificate doesn't cover their behaviors. +The <%= proc_cert_model.name %> certificate doesn't cover their behaviors. <% ExtensionPresence.presence_types_obj.each do |presence_obj| -%> === <%= presence_obj.to_s.capitalize %> Extensions -<% ext_reqs = cert_model.in_scope_ext_reqs(presence_obj) -%> +<% ext_reqs = proc_cert_model.in_scope_ext_reqs(presence_obj) -%> <% if ext_reqs.empty? -%> None <% else -%> @@ -289,19 +289,19 @@ None |=== <% end # if empty ext_reqs -%> -<% cert_model.extra_notes_for_presence(presence_obj)&.each do |extra_note| -%> +<% proc_cert_model.extra_notes_for_presence(presence_obj)&.each do |extra_note| -%> NOTE: <%= extra_note.text %> <% end # each extra_note -%> <% end # each possible presence -%> -<% unless cert_model.recommendations.empty? -%> +<% unless proc_cert_model.recommendations.empty? -%> === Recommendations Recommendations are not strictly mandated but are included to guide implementers. -<% cert_model.recommendations.each do |recommendation| -%> +<% proc_cert_model.recommendations.each do |recommendation| -%> <%= recommendation.text %> <% end # each recommendation -%> <% end # unless recommendations empty -%> @@ -319,16 +319,16 @@ These implementation-dependent options defined by MANDATORY or OPTIONAL extensio An implementation must abide by the "Allowed Value(s)" to obtain a certificate. If the "Allowed Value(s)" is "Any" then any value allowed by the type is acceptable. -<% if cert_model.all_in_scope_ext_params.empty? -%> +<% if proc_cert_model.all_in_scope_ext_params.empty? -%> None <% else -%> [cols="4,2,1,1,2"] |=== | Parameter | Type | Allowed Value(s) | Extension(s) | Note -<% cert_model.all_in_scope_ext_params.each do |in_scope_ext_param| -%> +<% proc_cert_model.all_in_scope_ext_params.each do |in_scope_ext_param| -%> <% param = in_scope_ext_param.param -%> -<% exts = cert_model.all_in_scope_exts_with_param(param) -%> +<% exts = proc_cert_model.all_in_scope_exts_with_param(param) -%> | <%= param.name_potentially_with_link(exts) %> | <%= param.schema_type %> | <%= in_scope_ext_param.allowed_values %> @@ -344,15 +344,15 @@ These implementation-dependent options defined by MANDATORY or OPTIONAL extensio There are no restrictions on their values for certification purposes because the certificate doesn't cover the behavior of the associated RISC-V standard as a function of these parameters. -<% if cert_model.all_out_of_scope_params.empty? -%> +<% if proc_cert_model.all_out_of_scope_params.empty? -%> None <% else -%> [%autowidth] |=== | Parameters | Type | Extension(s) -<% cert_model.all_out_of_scope_params.each do |param| -%> -<% exts = cert_model.all_in_scope_exts_without_param(param) -%> +<% proc_cert_model.all_out_of_scope_params.each do |param| -%> +<% exts = proc_cert_model.all_in_scope_exts_without_param(param) -%> | <%= param.name_potentially_with_link(exts) %> | <%= param.schema_type %> | <% exts.each do |ext| -%><-param-<%= param.name %>-def,<%= ext.name %>>> <% end # do ext -%> @@ -364,7 +364,7 @@ None == Traps RISC-V supports both synchronous exceptions and asynchronous interrupts. -TODO: List only traps that exist in this certificate model (currently lists all possible in present extensions). +TODO: List only traps that exist in this processor certificate model (currently lists all possible in present extensions). See https://github.com/riscv-software-src/riscv-unified-db/issues/291 and https://github.com/riscv-software-src/riscv-unified-db/issues/324 TODO: Show traps per privilege mode @@ -388,7 +388,7 @@ TODO: Show traps per privilege mode == Instruction Summary -TODO: List only instructions that exist in this certificate model. +TODO: List only instructions that exist in this processor certificate model. Currently lists all possible in present extensions so the I extension is providing both RV32I and RV64I instructions. See https://github.com/riscv-software-src/riscv-unified-db/issues/291 and https://github.com/riscv-software-src/riscv-unified-db/issues/324 @@ -434,13 +434,13 @@ See https://github.com/riscv-software-src/riscv-unified-db/issues/291 and https: <% end # do -%> |=== -<% unless cert_model.requirement_groups.empty? -%> +<% unless proc_cert_model.requirement_groups.empty? -%> == Additional Requirements This section contains requirements in addition to those already specified related to extensions and parameters. These additional requirements are organized as groups of related requirements. -<% cert_model.requirement_groups.each do |group| -%> +<% proc_cert_model.requirement_groups.each do |group| -%> === <%= group.name %> <%= group.description %> @@ -470,7 +470,7 @@ Requirement <%= req.name %> only apply when <%= req.when_pretty %>. <<< [appendix] == Extension Details -<% cert_model.in_scope_ext_reqs.each do |ext_req| -%> +<% proc_cert_model.in_scope_ext_reqs.each do |ext_req| -%> <% ext = arch.extension(ext_req.name) -%> [[ext-<%= ext_req.name %>-def]] @@ -538,10 +538,10 @@ The following instructions are added by this extension: |=== <% end -%> -<% unless cert_model.in_scope_ext_params(ext_req).empty? -%> +<% unless proc_cert_model.in_scope_ext_params(ext_req).empty? -%> ==== IN-SCOPE Parameters -<% cert_model.in_scope_ext_params(ext_req).each do |ext_param| -%> +<% proc_cert_model.in_scope_ext_params(ext_req).each do |ext_param| -%> [[ext-<%= ext_req.name %>-param-<%= ext_param.name %>-def]] <%= ext_param.name %> ⇒ <%= ext_param.param.schema_type %>:: + @@ -551,10 +551,10 @@ The following instructions are added by this extension: <% end # do ext_param -%> <% end # unless table -%> -<% unless cert_model.out_of_scope_params(ext_req.name).empty? -%> +<% unless proc_cert_model.out_of_scope_params(ext_req.name).empty? -%> ==== OUT-OF-SCOPE Parameters -<% cert_model.out_of_scope_params(ext_req.name).each do |param| -%> +<% proc_cert_model.out_of_scope_params(ext_req.name).each do |param| -%> [[ext-<%= ext_req.name %>-param-<%= param.name %>-def]] <%= param.name %> ⇒ <%= param.schema_type %>:: + diff --git a/cert_flow.txt b/cert_flow.txt index 20d4be77a..56a330243 100644 --- a/cert_flow.txt +++ b/cert_flow.txt @@ -1,4 +1,4 @@ -backends/certificate_doc/tasks.rake +backends/crd/tasks.rake bootstrap_cfg_arch = cfg_arch_for("rv#{base}") # rv32 or rv64 Rakefile Calls ConfiguredArchitecture.new("gen/resolved_arch/rv32") @@ -8,24 +8,24 @@ bootstrap_cfg_arch = cfg_arch_for("rv#{base}") # rv32 or rv64 Calls PartialConfig.new(cfg path, @data) # Uses Ruby send() method @mxlen = @data["params"].xlen @name = "rv32" -bootstrap_cert_model = bootstrap_cfg_arch.cert_model(cert_model_name = "MC100-32") - Calls self.generate_obj_methods("cert_model", "certificate_model", CertModel) in Architecture # Magic - Calls define_method("cert_model") - Calls define_method("cert_models") - Creates @cert_models array and @cert_model_hash # Per arch_dir +bootstrap_proc_cert_model = bootstrap_cfg_arch.proc_cert_model(proc_cert_model_name = "MC100-32") + Calls self.generate_obj_methods("proc_cert_model", "proc_cert_model", ProcCertModel) in Architecture # Magic + Calls define_method("proc_cert_model") + Calls define_method("proc_cert_models") + Creates @proc_cert_models array and @proc_cert_model_hash # Per arch_dir For every certificiate model in the database - Loads yaml under gen/resolved_arch/rv32 into @cert_models hash - Calls cert_model.new() -> DatabaseObject.initialize(yaml, yaml_path, arch=base_cfg_arch) + Loads yaml under gen/resolved_arch/rv32 into @proc_cert_models hash + Calls proc_cert_model.new() -> DatabaseObject.initialize(yaml, yaml_path, arch=base_cfg_arch) @data = yaml @arch = arch # rv32 -cfg_arch = bootstrap_cert_model.to_cfg_arch # In Portfolio class +cfg_arch = bootstrap_proc_cert_model.to_cfg_arch # In Portfolio class Creates hash with mandatory extensions (with version requirement) and fully-constrained params (single_value?) Uses in_scope_ext_reqs() and all_in_scope_ext_params() in Portfolio Writes hash to yaml file in /tmp/.../MC100-32/cfg.yaml Passes yaml file to ConfiguredArchitecture.new() Creates Architecture (base class), Config, and PartialConfig again (see above) -cert_model = cfg_arch.cert_model(cert_model_name) - Creates CertModel for every model in the database and stores it in the real ConfiguredArchitecture object +proc_cert_model = cfg_arch.proc_cert_model(proc_cert_model_name) + Creates ProcCertModel for every model in the database and stores it in the real ConfiguredArchitecture object Calls ERB template Converts ERB result to ASCIIDOC diff --git a/lib/arch_obj_models/certificate.rb b/lib/arch_obj_models/certificate.rb index 6ac6ae66e..92669edc1 100644 --- a/lib/arch_obj_models/certificate.rb +++ b/lib/arch_obj_models/certificate.rb @@ -1,32 +1,32 @@ # Classes for certificates. -# Each certificate model is a member of a certificate class. +# Each processor certificate model is a member of a processor certificate class. require_relative "portfolio" ################### -# CertClass Class # +# ProcCertClass Class # ################### -# Holds information from certificate class YAML file. +# Holds information from processor certificate class YAML file. # The inherited "data" member is the database of extensions, instructions, CSRs, etc. -class CertClass < PortfolioClass +class ProcCertClass < PortfolioClass def mandatory_priv_modes = @data["mandatory_priv_modes"] end ################### -# CertModel Class # +# ProcCertModel Class # ################### -# Holds information about a certificate model YAML file. +# Holds information about a processor certificate model YAML file. # The inherited "data" member is the database of extensions, instructions, CSRs, etc. -class CertModel < Portfolio +class ProcCertModel < Portfolio # @param obj_yaml [Hash] Contains contents of Certificate Model yaml file (put in @data) # @param data_path [String] Path to yaml file # @param arch [Architecture] Database of RISC-V standards def initialize(obj_yaml, yaml_path, arch) super # Calls parent class with the same args I got - puts "UPDATE: Creating CertModel object for #{name} using arch #{arch.name}" + puts "UPDATE: Creating ProcCertModel object for #{name} using arch #{arch.name}" end def unpriv_isa_manual_revision = @data["unpriv_isa_manual_revision"] @@ -43,12 +43,12 @@ def tsc_profile profile end - # @return [CertClass] The certification class that this model belongs to. - def cert_class - cert_class = @arch.ref(@data["class"]['$ref']) - raise "No certificate class named '#{@data["class"]}'" if cert_class.nil? + # @return [ProcCertClass] The certification class that this model belongs to. + def proc_cert_class + proc_cert_class = @arch.ref(@data["class"]['$ref']) + raise "No processor certificate class named '#{@data["class"]}'" if proc_cert_class.nil? - cert_class + proc_cert_class end ##################### diff --git a/lib/arch_obj_models/portfolio.rb b/lib/arch_obj_models/portfolio.rb index a3c1c2396..fec926f18 100644 --- a/lib/arch_obj_models/portfolio.rb +++ b/lib/arch_obj_models/portfolio.rb @@ -19,7 +19,7 @@ # PortfolioClass # ################## -# Holds information from Portfolio class YAML file (certificate class or profile class). +# Holds information from Portfolio class YAML file (processor certificate class or profile class). # The inherited "data" member is the database of extensions, instructions, CSRs, etc. class PortfolioClass < DatabaseObject # @return [String] What kind of processor portfolio is this? diff --git a/lib/architecture.rb b/lib/architecture.rb index 8220af195..382a5baf5 100644 --- a/lib/architecture.rb +++ b/lib/architecture.rb @@ -14,8 +14,8 @@ # Extension extensions() extension_hash() extension(name) # Instruction instructions() instruction_hash() instruction(name) # Csr csrs() csr_hash() csr(name) -# CertClass cert_classes() cert_class_hash() cert_class(name) -# CertModel cert_models() cert_model_hash() cert_model(name) +# ProcCertClass proc_cert_classes() proc_cert_class_hash() proc_cert_class(name) +# ProcCertModel proc_cert_models() proc_cert_model_hash() proc_cert_model(name) # ProfileClass profile_classes() profile_class_hash() profile_class(name) # ProfileRelease profile_releases() profile_release_hash() profile_release(name) # Profile profiles() profile_hash() profile(name) @@ -159,14 +159,14 @@ def self.generate_obj_methods(fn_name, arch_dir, obj_class) klass: Csr }, { - fn_name: "cert_class", - arch_dir: "certificate_class", - klass: CertClass + fn_name: "proc_cert_class", + arch_dir: "proc_cert_class", + klass: ProcCertClass }, { - fn_name: "cert_model", - arch_dir: "certificate_model", - klass: CertModel + fn_name: "proc_cert_model", + arch_dir: "proc_cert_model", + klass: ProcCertModel }, { fn_name: "manual", @@ -238,7 +238,7 @@ def param(name) def portfolio_classes return @portfolio_classes unless @portfolio_classes.nil? - @portfolio_classes = profile_classes.concat(cert_classes).sort_by!(&:name) + @portfolio_classes = profile_classes.concat(proc_cert_classes).sort_by!(&:name) end # @return [Hash] Hash of all portfolio classes defined in the architecture @@ -330,12 +330,12 @@ def ref(uri) file_path, obj_path = uri.split("#") obj = case file_path - when /^certificate_class.*/ - cert_class_name = File.basename(file_path, ".yaml") - cert_class(cert_class_name) - when /^certificate_model.*/ - cert_model_name = File.basename(file_path, ".yaml") - cert_model(cert_model_name) + when /^proc_cert_class.*/ + proc_cert_class_name = File.basename(file_path, ".yaml") + proc_cert_class(proc_cert_class_name) + when /^proc_cert_model.*/ + proc_cert_model_name = File.basename(file_path, ".yaml") + proc_cert_model(proc_cert_model_name) when /^csr.*/ csr_name = File.basename(file_path, ".yaml") csr(csr_name) diff --git a/lib/portfolio_design.rb b/lib/portfolio_design.rb index 7ed475c58..a6d8bfb50 100644 --- a/lib/portfolio_design.rb +++ b/lib/portfolio_design.rb @@ -5,7 +5,7 @@ # Used in portfolio-based ERB templates to gather information about the "design". # The "design" corresponds to the file being created by the ERB template and facilitates # sharing ERB template fragments between different kinds of portfolios (mostly in the appendices). -# For example, a certificate model has one portfolio but a profile release has multiple portfolios +# For example, a processor certificate model has one portfolio but a profile release has multiple portfolios # but they both have just one PortfolioDesign object. require "ruby-prof" diff --git a/schemas/cert_class_schema.json b/schemas/proc_cert_class_schema.json similarity index 93% rename from schemas/cert_class_schema.json rename to schemas/proc_cert_class_schema.json index a816c4333..c174d8d6f 100644 --- a/schemas/cert_class_schema.json +++ b/schemas/proc_cert_class_schema.json @@ -7,11 +7,11 @@ "properties": { "$schema": { "type": "string", - "const": "cert_class_schema.json#" + "const": "proc_cert_class_schema.json#" }, "kind": { "type": "string", - "const": "Processor CRD" + "const": "Processor Certificate Class" }, "name": { "type": "string", diff --git a/schemas/cert_model_schema.json b/schemas/proc_cert_model_schema.json similarity index 86% rename from schemas/cert_model_schema.json rename to schemas/proc_cert_model_schema.json index 59369db12..6a4eed33a 100644 --- a/schemas/cert_model_schema.json +++ b/schemas/proc_cert_model_schema.json @@ -9,13 +9,13 @@ "oneOf": [ { "type": "string", - "pattern": "^(profile|certificate_model)/.*\\.yaml#.*" + "pattern": "^(profile|proc_cert_model)/.*\\.yaml#.*" }, { "type": "array", "items": { "type": "string", - "pattern": "^(profile|certificate_model)/.*\\.yaml#.*" + "pattern": "^(profile|proc_cert_model)/.*\\.yaml#.*" }, "uniqueItems": true } @@ -25,13 +25,13 @@ "oneOf": [ { "type": "string", - "pattern": "^(profile|certificate_model)/.*\\.yaml#.*" + "pattern": "^(profile|proc_cert_model)/.*\\.yaml#.*" }, { "type": "array", "items": { "type": "string", - "pattern": "^(profile|certificate_model)/.*\\.yaml#.*" + "pattern": "^(profile|proc_cert_model)/.*\\.yaml#.*" }, "uniqueItems": true } @@ -41,13 +41,13 @@ "oneOf": [ { "type": "string", - "pattern": "^(profile|certificate_model)/.*\\.yaml#.*" + "pattern": "^(profile|proc_cert_model)/.*\\.yaml#.*" }, { "type": "array", "items": { "type": "string", - "pattern": "^(profile|certificate_model)/.*\\.yaml#.*" + "pattern": "^(profile|proc_cert_model)/.*\\.yaml#.*" }, "uniqueItems": true } @@ -55,11 +55,11 @@ }, "$schema": { "type": "string", - "const": "cert_model_schema.json#" + "const": "proc_cert_model_schema.json#" }, "kind": { "type": "string", - "const": "certificate model" + "const": "Processor Certificate Model" }, "name": { "type": "string", @@ -75,7 +75,7 @@ "properties": { "$ref": { "type": "string", - "pattern": "^certificate_class/[A-Z][a-zA-Z0-9_]*\\.yaml#" + "pattern": "^proc_cert_class/[A-Z][a-zA-Z0-9_]*\\.yaml#" } }, "description": "Reference to the class this model belongs to" @@ -123,7 +123,7 @@ "properties": { "$ref": { "type": "string", - "pattern": "^profile_release|certificate_model/[A-Z][a-zA-Z0-9_]*\\.yaml#" + "pattern": "^profile_release|proc_cert_model/[A-Z][a-zA-Z0-9_]*\\.yaml#" } }, "additionalProperties": false @@ -148,13 +148,13 @@ "oneOf": [ { "type": "string", - "pattern": "^(profile|certificate_model)/.*\\.yaml#.*" + "pattern": "^(profile|proc_cert_model)/.*\\.yaml#.*" }, { "type": "array", "items": { "type": "string", - "pattern": "^(profile|certificate_model)/.*\\.yaml#.*" + "pattern": "^(profile|proc_cert_model)/.*\\.yaml#.*" }, "uniqueItems": true } @@ -164,13 +164,13 @@ "oneOf": [ { "type": "string", - "pattern": "^(profile|certificate_model)/.*\\.yaml#.*" + "pattern": "^(profile|proc_cert_model)/.*\\.yaml#.*" }, { "type": "array", "items": { "type": "string", - "pattern": "^(profile|certificate_model)/.*\\.yaml#.*" + "pattern": "^(profile|proc_cert_model)/.*\\.yaml#.*" }, "uniqueItems": true } From f7f823696ef69fcb413ba8e981d0b1a129113876 Mon Sep 17 00:00:00 2001 From: James Ball Date: Fri, 10 Jan 2025 21:18:46 +0000 Subject: [PATCH 21/21] Remove unneccessary concepts of base from architecture and portfolios (especially rakefiles). Now use cfg "_" instead of rv32 or rv64 since there really is no difference in the generated design. --- Rakefile | 50 ++++++--------------------------- backends/crd/tasks.rake | 22 +++++++-------- backends/profile_doc/tasks.rake | 38 ++++++++----------------- lib/architecture.rb | 12 +------- lib/cfg_arch.rb | 13 ++------- lib/design.rb | 10 ++++++- lib/portfolio_design.rb | 14 ++------- 7 files changed, 44 insertions(+), 115 deletions(-) diff --git a/Rakefile b/Rakefile index f2caaf94d..8f5be9a80 100644 --- a/Rakefile +++ b/Rakefile @@ -15,50 +15,17 @@ require_relative $root / "lib" / "portfolio_design" directory "#{$root}/.stamps" +# Load and execute Rakefile for each backend. Dir.glob("#{$root}/backends/*/tasks.rake") do |rakefile| + puts "UPDATE: Loading #{rakefile}" load rakefile end directory "#{$root}/.stamps" -# @param base_isa_name [String] rv32 or rv64 -# @param base [Integer] 32 or 64 -# @return [Architecture] -def arch_for(base_isa_name, base) - Rake::Task["#{$root}/.stamps/resolve-#{base_isa_name}.stamp"].invoke - - @archs ||= {} - return @archs[base_isa_name] if @archs.key?(base_isa_name) - - @archs[base_isa_name] = - Architecture.new( - base_isa_name, - base, - $root / "gen" / "resolved_arch" / base_isa_name, - ) -end - -# @param design_name [String] Profile release name for profiles and processor certificate model name for certificates -# @param arch [Architecture] The architecture database -# @param base [Integer] 32 or 64 -# @param portfolios [Array] Portfolios in this design -# @return [PortfolioDesign] -def portfolio_design_for(design_name, arch, base, portfolios) - Rake::Task["#{$root}/.stamps/resolve-#{design_name}.stamp"].invoke - - @portfolio_designs ||= {} - return @portfolio_designs[design_name] if @portfolio_designs.key?(design_name) - - @portfolio_designs[design_name] = - PortfolioDesign.new( - design_name, - arch, - base, - portfolios - ) -end - -def cfg_arch_for(config_name, base = nil) +# @param config_name [String] Name of configuration +# @return [ConfiguredArchitecture] +def cfg_arch_for(config_name) Rake::Task["#{$root}/.stamps/resolve-#{config_name}.stamp"].invoke @cfg_archs ||= {} @@ -67,7 +34,6 @@ def cfg_arch_for(config_name, base = nil) @cfg_archs[config_name] = ConfiguredArchitecture.new( config_name, - base, $root / "gen" / "resolved_arch" / config_name, overlay_path: $root / "cfgs" / config_name / "arch_overlay" ) @@ -157,18 +123,18 @@ namespace :test do end task schema: "gen:resolved_arch" do puts "Checking arch files against schema.." - Architecture.new("rv64", nil, "#{$root}/resolved_arch").validate(show_progress: true) + Architecture.new("rv64", "#{$root}/resolved_arch").validate(show_progress: true) puts "All files validate against their schema" end task idl: ["gen:resolved_arch", "#{$root}/.stamps/resolve-rv32.stamp", "#{$root}/.stamps/resolve-rv64.stamp"] do print "Parsing IDL code for RV32..." - cfg_arch32 = cfg_arch_for("rv32", 32) + cfg_arch32 = cfg_arch_for("rv32") puts "done" cfg_arch32.type_check print "Parsing IDL code for RV64..." - cfg_arch64 = cfg_arch_for("rv64", 64) + cfg_arch64 = cfg_arch_for("rv64") puts "done" cfg_arch64.type_check diff --git a/backends/crd/tasks.rake b/backends/crd/tasks.rake index 6f35d0657..7a990d6e4 100644 --- a/backends/crd/tasks.rake +++ b/backends/crd/tasks.rake @@ -7,6 +7,8 @@ require "asciidoctor-diagram" require_relative "#{$lib}/idl/passes/gen_adoc" +puts "UPDATE: Inside crd tasks.rake" + CERT_DOC_DIR = Pathname.new "#{$root}/backends/crd" Dir.glob("#{$root}/arch/proc_cert_model/*.yaml") do |f| @@ -15,13 +17,6 @@ Dir.glob("#{$root}/arch/proc_cert_model/*.yaml") do |f| proc_cert_class_name = File.basename(proc_cert_model_obj['class']['$ref'].split("#")[0], ".yaml") raise "Ill-formed processor certificate model file #{f}: missing 'class' field" if proc_cert_model_obj['class'].nil? - base = proc_cert_model_obj["base"] - raise "Missing processor certificate model base" if base.nil? - - base_isa_name = "rv#{base}" - - puts "UPDATE: Extracted base=#{base} from #{f}" - file "#{$root}/gen/crd/adoc/#{proc_cert_model_name}.adoc" => [ __FILE__, "#{$root}/arch/proc_cert_class/#{proc_cert_class_name}.yaml", @@ -32,19 +27,22 @@ Dir.glob("#{$root}/arch/proc_cert_model/*.yaml") do |f| "#{$root}/lib/design.rb", "#{CERT_DOC_DIR}/templates/crd.adoc.erb" ] do |t| - # Create Architecture object. Function located in top-level Rakefile. - puts "UPDATE: Creating Architecture #{base_isa_name} for #{t}" - arch = arch_for(base_isa_name, base) + # Ensure that unconfigured resolved architecture called "_" exists. + Rake::Task["#{$root}/.stamps/resolve-_.stamp"].invoke + + # Create architecture object so we can have it create the ProcCertModel. + # Use the unconfigured resolved architecture called "_". + arch = Architecture.new("RISC-V Architecture", $root / "gen" / "resolved_arch" / "_") # Create ProcCertModel for specific processor certificate model as specified in its arch YAML file. # The Architecture object also creates all other portfolio-related class instances from their arch YAML files. # None of these objects are provided with a Design object when created. - puts "UPDATE: Creating ProcCertModel for #{proc_cert_model_name} using base #{base_isa_name}" + puts "UPDATE: Creating ProcCertModel for #{proc_cert_model_name}" proc_cert_model = arch.proc_cert_model(proc_cert_model_name) puts "UPDATE: Creating PortfolioDesign using processor certificate model #{proc_cert_model_name}" # Create the one PortfolioDesign object required for the ERB evaluation. - portfolio_design = portfolio_design_for(proc_cert_model_name, arch, base, [proc_cert_model]) + portfolio_design = PortfolioDesign.new(proc_cert_model_name, arch, [proc_cert_model]) # Create empty binding and then specify explicitly which variables the ERB template can access. # Seems to use this method name in stack backtraces (hence its name). diff --git a/backends/profile_doc/tasks.rake b/backends/profile_doc/tasks.rake index 18ff79fa9..07232b2c2 100644 --- a/backends/profile_doc/tasks.rake +++ b/backends/profile_doc/tasks.rake @@ -7,6 +7,8 @@ require "asciidoctor-diagram" require_relative "#{$lib}/idl/passes/gen_adoc" +puts "UPDATE: Inside profile_doc tasks.rake" + PROFILE_DOC_DIR = Pathname.new "#{$root}/backends/profile_doc" Dir.glob("#{$root}/arch/profile_release/*.yaml") do |f| @@ -22,29 +24,8 @@ Dir.glob("#{$root}/arch/profile_release/*.yaml") do |f| profile_names = profile_release_obj['profiles'].map {|p| File.basename(p['$ref'].split("#")[0], ".yaml") } raise "Ill-formed profile release file #{f}: can't parse profile names" if profile_names.nil? - # Find maximum base across all profiles in the profile release. - max_base = nil - profile_names.each do |profile_name| - profile_pathname = "#{$root}/arch/profile/#{profile_name}.yaml" - profile_obj = YAML.load_file(profile_pathname, permitted_classes: [Date]) - raise "Can't parse #{profile_name}" if profile_obj.nil? - - base = profile_obj["base"] - raise "Missing profile base in #{profile}" if base.nil? - - puts "UPDATE: Extracted base=#{base} from #{f}" - - max_base = base if (max_base.nil? || base > max_base) - end - raise "Couldn't find max_base in the profiles #{profile_names}" if max_base.nil? - puts "UPDATE: Calculated max_base=#{max_base} across profiles in #{profile_release_name}" - profile_pathnames = profile_names.map {|profile_name| "#{$root}/arch/profile/#{profile_name}.yaml" } - # Just go with maximum base since it is the most inclusive. - base = max_base - base_isa_name = "rv#{base}" - file "#{$root}/gen/profile_doc/adoc/#{profile_release_name}.adoc" => [ __FILE__, "#{$root}/arch/profile_class/#{profile_class_name}.yaml", @@ -55,20 +36,23 @@ Dir.glob("#{$root}/arch/profile_release/*.yaml") do |f| "#{$root}/lib/design.rb", "#{PROFILE_DOC_DIR}/templates/profile.adoc.erb" ].concat(profile_pathnames) do |t| - # Create Architecture object. Function located in top-level Rakefile. - puts "UPDATE: Creating Architecture #{base_isa_name} for #{t}" - arch = arch_for(base_isa_name, base) + # Ensure that unconfigured resolved architecture called "_" exists. + Rake::Task["#{$root}/.stamps/resolve-_.stamp"].invoke + + # Create architecture object so we can have it create the ProcCertModel. + # Use the unconfigured resolved architecture called "_". + arch = Architecture.new("RISC-V Architecture", $root / "gen" / "resolved_arch" / "_") # Create PortfolioRelease for specific portfolio release as specified in its arch YAML file. # The Architecture object also creates all other portfolio-related class instances from their arch YAML files. # None of these objects are provided with a Design object when created. - puts "UPDATE: Creating Profile Release for #{profile_release_name} using #{base_isa_name}" + puts "UPDATE: Creating Profile Release for #{profile_release_name}" profile_release = arch.profile_release(profile_release_name) - puts "UPDATE: Creating PortfolioDesign using profile release #{profile_release_name}" # Create the one PortfolioDesign object required for the ERB evaluation. # Provide it with all the profiles in this ProfileRelease. - portfolio_design = portfolio_design_for(profile_release_name, arch, base, profile_release.profiles) + puts "UPDATE: Creating PortfolioDesign using profile release #{profile_release_name}" + portfolio_design = PortfolioDesign.new(profile_release_name, arch, profile_release.profiles) # Create empty binding and then specify explicitly which variables the ERB template can access. # Seems to use this method name in stack backtraces (hence its name). diff --git a/lib/architecture.rb b/lib/architecture.rb index 382a5baf5..5b78933b5 100644 --- a/lib/architecture.rb +++ b/lib/architecture.rb @@ -53,26 +53,16 @@ class Architecture # @return [String] Best name to identify architecture attr_reader :name - # @return [Integer] 32 for RV32I or 64 for RV64I - attr_reader :base - # @return [Pathname] Path to the directory containing YAML files defining the RISC-V standards attr_reader :path # Initialize a new architecture definition # # @param name [#to_s] The name associated with this architecture - # @param base [Integer] RISC-V base ISA width (32 for RV32I/RV32E, 64 for RV64I, nil if unknown) # @param arch_dir [String, Pathname] Path to a directory with a fully merged/resolved architecture definition - def initialize(name, base, arch_dir) + def initialize(name, arch_dir) @name = name.to_s.freeze - unless base.nil? - raise "Unsupported base ISA value of #{base}. Supported values are 32 or 64." unless base == 32 || base == 64 - end - @base = base - @base.freeze - @arch_dir = Pathname.new(arch_dir) raise "Architecture directory #{arch_dir} not found" unless @arch_dir.exist? diff --git a/lib/cfg_arch.rb b/lib/cfg_arch.rb index 0b7600d31..8dd2c2cbd 100644 --- a/lib/cfg_arch.rb +++ b/lib/cfg_arch.rb @@ -22,18 +22,12 @@ class ConfiguredArchitecture < Design :fully_configured?, :partially_configured?, :unconfigured?, :configured?, :param_values # @param config_name [#to_s] The configuration name which corresponds to a folder name under cfg_path - # @param base [Integer] RISC-V base ISA width (32 for RV32I/RV32E, 64 for RV64I, nil if unknown) # @param arch_dir [String,Pathname] Path to a directory with a fully merged/resolved architecture definition # @param overlay_path [String] Optional path to a directory that overlays the architecture # @param cfg_path [String] Optional path to where to find configuration file - def initialize(config_name, base, arch_dir, overlay_path: nil, cfg_path: "#{$root}/cfgs") + def initialize(config_name, arch_dir, overlay_path: nil, cfg_path: "#{$root}/cfgs") @config = Config.create("#{cfg_path}/#{config_name}/cfg.yaml") - @mxlen = @config.mxlen - @mxlen.freeze - - arch = Architecture.new(config_name, base, arch_dir) - - super(config_name, arch, overlay_path: overlay_path) + super(config_name, Architecture.new(config_name, arch_dir), @config.mxlen, overlay_path: overlay_path) end # Returns a string representation of the object, suitable for debugging. @@ -46,9 +40,6 @@ def inspect = "ConfiguredArchitecture##{name}" # These raise an error in the base class. # ########################################### - # @return [Integer] 32, 64, or nil if dynamic or undefined. - def mxlen = @mxlen - # Returns whether or not it may be possible to switch XLEN in +mode+ given this definition. # # There are three cases when this will return true: diff --git a/lib/design.rb b/lib/design.rb index 9e59c0ea6..e68b6e6bb 100644 --- a/lib/design.rb +++ b/lib/design.rb @@ -43,6 +43,9 @@ class Design < IDesign # @return [Architecture] The RISC-V architecture attr_reader :arch + # @return [Integer] 32, 64, or nil for dynamic + attr_reader :mxlen + # @return [Idl::Compiler] The IDL compiler attr_reader :idl_compiler @@ -58,12 +61,17 @@ def hash = @name_sym.hash # @param name [#to_s] The design name # @param arch [Architecture] The entire architecture + # @param mxlen [Integer] 32, 64, or nil for dynamic # @param overlay_path [String] Optional path to a directory that overlays the architecture - def initialize(name, arch, overlay_path: nil) + def initialize(name, arch, mxlen, overlay_path: nil) super(name) raise ArgumentError, "arch must be an Architecture but is a #{arch.class}" unless arch.is_a?(Architecture) @arch = arch + + @mxlen = mxlen + @mxlen.freeze + @idl_compiler = Idl::Compiler.new @symtab = Idl::SymbolTable.new(self) custom_globals_path = overlay_path.nil? ? Pathname.new("/does/not/exist") : overlay_path / "isa" / "globals.isa" diff --git a/lib/portfolio_design.rb b/lib/portfolio_design.rb index a6d8bfb50..cc2c3139a 100644 --- a/lib/portfolio_design.rb +++ b/lib/portfolio_design.rb @@ -21,23 +21,18 @@ class PortfolioDesign < Design # @param mxlen [Integer] Comes from portfolio YAML "base" (either 32 or 64) # @param portfolios [Array] Portfolios being converted to adoc # @param overlay_path [String] Optional path to a directory that overlays the architecture - def initialize(base_isa_name, arch, base, portfolios, overlay_path: nil) - raise ArgumentError, "base must be 32 or 64 but is #{base}" unless (base == 32 || base == 64) + def initialize(base_isa_name, arch, portfolios, overlay_path: nil) raise ArgumentError, "arch must be an Architecture but is a #{arch.class}" unless arch.is_a?(Architecture) raise ArgumentError, "portfolios must be an Array but is a #{portfolios.class}" unless portfolios.is_a?(Array) - @mxlen = base - @mxlen.freeze - # The PortfolioGroup has an Array inside it and forwards common Array methods to its internal Array. # Can call @portfolio_grp.each or @portfolio_grp.map and they are handled by the normal Array methods. @portfolio_grp = PortfolioGroup.new(portfolios) - # Sanity check base passed in to me. max_base = portfolios.map(&:base).max - raise ArgumentError, "Provided base of #{base} but maximum base across all provided portfolios is #{max_base}" unless base == max_base + raise ArgumentError, "Calculated maximum base of #{max_base} across portfolios is not 32 or 64" unless max_base == 32 || max_base == 64 - super(base_isa_name, arch, overlay_path: overlay_path) + super(base_isa_name, arch, max_base, overlay_path: overlay_path) end # Returns a string representation of the object, suitable for debugging. @@ -48,9 +43,6 @@ def inspect = "PortfolioDesign##{name}" # METHODS REQUIRED BY BASE CLASS # ################################## - # @return [Integer] 32 or 64. Might be nil if dynamic (not sure if dynamic required for portfolios). - def mxlen = @mxlen - # Returns whether or not it may be possible to switch XLEN in +mode+ given this definition. # # There are three cases when this will return true: