From d7e446802ba2529394b38902aab62412d584271f Mon Sep 17 00:00:00 2001 From: James Ball Date: Wed, 1 Jan 2025 00:49:05 +0000 Subject: [PATCH 01/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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/61] 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: From 49e6c4dbdfa4492d8714d6f779dea286edf7843f Mon Sep 17 00:00:00 2001 From: James Ball Date: Fri, 10 Jan 2025 21:48:37 +0000 Subject: [PATCH 22/61] Adding missing items to .gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index a9bdc6be4..8b1d1341e 100644 --- a/.gitignore +++ b/.gitignore @@ -11,7 +11,9 @@ diag-ditaa-* arch/manual/isa/**/riscv-isa-manual gen +resolved_arch node_modules _site images *.log +*.bak From 7e83edf64a27d0aeb13e33f8e9e852c5750ba0c9 Mon Sep 17 00:00:00 2001 From: James Ball Date: Sat, 11 Jan 2025 01:32:27 +0000 Subject: [PATCH 23/61] Created portfolio tasks.rake file and moved common Rake code from profile & CRD tasks.rake into it. --- .github/workflows/pages.yml | 8 +- .github/workflows/regress.yml | 2 +- .gitignore | 1 - .vscode/launch.json | 4 +- backends/crd/tasks.rake | 135 +++++++++-------------------- backends/portfolios/tasks.rake | 84 ++++++++++++++++++ backends/profile_doc/tasks.rake | 146 ++++++++++---------------------- 7 files changed, 180 insertions(+), 200 deletions(-) create mode 100644 backends/portfolios/tasks.rake diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml index 51469dc16..9fa8dbb5f 100644 --- a/.github/workflows/pages.yml +++ b/.github/workflows/pages.yml @@ -76,19 +76,19 @@ 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:proc_cert_model_pdf[MC100-32] + run: ./do gen:proc_crd_pdf[MC100-32] - name: Copy 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:proc_cert_model_html[MC100-32] + run: ./do gen:proc_crd_html[MC100-32] - name: Copy 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:proc_cert_model_pdf[MC100-64] + run: ./do gen:proc_crd_pdf[MC100-64] - name: Copy 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:proc_cert_model_html[MC100-64] + run: ./do gen:proc_crd_html[MC100-64] - name: Copy MC100-64 HTML un: cp gen/crd/html/MC100-64.html _site/htmls/MC100-64.html - name: Copy manual html diff --git a/.github/workflows/regress.yml b/.github/workflows/regress.yml index dc9ab17b7..00246e694 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:proc_cert_model_pdf[MockProcCertModel] + run: ./do gen:proc_crd_pdf[MockProcCertModel] regress-gen-profile: runs-on: ubuntu-latest needs: regress-smoke diff --git a/.gitignore b/.gitignore index 10b46a866..ce0c1b31c 100644 --- a/.gitignore +++ b/.gitignore @@ -17,4 +17,3 @@ _site images *.bak *.log -*.bak diff --git a/.vscode/launch.json b/.vscode/launch.json index 325f1cb86..953c38f92 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -6,7 +6,7 @@ "name": "MC100-32", "request": "launch", "command": "bundle exec rake", - "script": "gen:proc_cert_model_pdf[MC100-32]", + "script": "gen:proc_crd_pdf[MC100-32]", "args": [], "askParameters": false }, @@ -15,7 +15,7 @@ "name": "MC200-32", "request": "launch", "command": "bundle exec rake", - "script": "gen:proc_cert_model_pdf[MC200-32]", + "script": "gen:proc_crd_pdf[MC200-32]", "args": [], "askParameters": false }, diff --git a/backends/crd/tasks.rake b/backends/crd/tasks.rake index 7a990d6e4..a5122717f 100644 --- a/backends/crd/tasks.rake +++ b/backends/crd/tasks.rake @@ -1,48 +1,38 @@ # frozen_string_literal: true +# +# Contains Rake rules to generate adoc, PDF, and HTML for a CRD. require "pathname" -require "asciidoctor-pdf" -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| - 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? + model_name = File.basename(f, ".yaml") + model_obj = YAML.load_file(f, permitted_classes: [Date]) + class_name = File.basename(model_obj['class']['$ref'].split("#")[0], ".yaml") + raise "Ill-formed processor certificate model file #{f}: missing 'class' field" if model_obj['class'].nil? - file "#{$root}/gen/crd/adoc/#{proc_cert_model_name}.adoc" => [ + file "#{$root}/gen/crd/adoc/#{model_name}.adoc" => [ __FILE__, - "#{$root}/arch/proc_cert_class/#{proc_cert_class_name}.yaml", - "#{$root}/arch/proc_cert_model/#{proc_cert_model_name}.yaml", + "#{$root}/arch/proc_cert_class/#{class_name}.yaml", + "#{$root}/arch/proc_cert_model/#{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/crd.adoc.erb" ] do |t| - # 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" / "_") + arch = pf_create_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}" - proc_cert_model = arch.proc_cert_model(proc_cert_model_name) + puts "UPDATE: Creating ProcCertModel for #{model_name}" + proc_cert_model = arch.proc_cert_model(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 = PortfolioDesign.new(proc_cert_model_name, arch, [proc_cert_model]) + puts "UPDATE: Creating PortfolioDesign using processor certificate model #{model_name}" + portfolio_design = PortfolioDesign.new(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). @@ -57,104 +47,63 @@ Dir.glob("#{$root}/arch/proc_cert_model/*.yaml") do |f| 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/crd.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. - 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) - 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}" + pf_create_adoc("#{CERT_DOC_DIR}/templates/crd.adoc.erb", erb_binding, t.name, portfolio_design) end - file "#{$root}/gen/crd/pdf/#{proc_cert_model_name}.pdf" => [ + file "#{$root}/gen/crd/pdf/#{model_name}.pdf" => [ __FILE__, - "#{$root}/gen/crd/adoc/#{proc_cert_model_name}.adoc" + "#{$root}/gen/crd/adoc/#{model_name}.adoc" ] do |t| - adoc_file = "#{$root}/gen/crd/adoc/#{proc_cert_model_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_file - ].join(" ") - puts "UPDATE: Generated PDF at #{t.name}" + pf_adoc2pdf("#{$root}/gen/crd/adoc/#{model_name}.adoc", t.name) end - file "#{$root}/gen/crd/html/#{proc_cert_model_name}.html" => [ + file "#{$root}/gen/crd/html/#{model_name}.html" => [ __FILE__, - "#{$root}/gen/crd/adoc/#{proc_cert_model_name}.adoc" + "#{$root}/gen/crd/adoc/#{model_name}.adoc" ] do |t| - 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}" - sh [ - "asciidoctor", - "-w", - "-v", - "-a toc", - "-a imagesdir=#{$root}/ext/docs-resources/images", - "-b html5", - "-r asciidoctor-diagram", - "-r #{$root}/backends/ext_pdf_doc/idl_lexer", - "-o #{t.name}", - adoc_file - ].join(" ") - puts "UPDATE: Generated PDF at #{t.name}" + pf_adoc2html("#{$root}/gen/crd/adoc/#{model_name}.adoc", t.name) end end namespace :gen do desc <<~DESC - Generate certificate documentation for a specific version as a PDF. + Generate CRD (Certification Requirements Document) as a PDF. Required options: - proc_cert_model_name - The key of the certification model under arch/proc_cert_model + model_name - The name of the certification model under arch/proc_cert_model DESC - 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'" + task :proc_crd_pdf, [:model_name] do |_t, args| + model_name = args[:model_name] + if model_name.nil? + warn "Missing required option: 'model_name'" exit 1 end - 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" + unless File.exist?("#{$root}/arch/proc_cert_model/#{model_name}.yaml") + warn "No certification model named '#{model_name}' found in arch/proc_cert_model" exit 1 end - Rake::Task["#{$root}/gen/crd/pdf/#{proc_cert_model_name}.pdf"].invoke + Rake::Task["#{$root}/gen/crd/pdf/#{model_name}.pdf"].invoke end - 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'" + desc <<~DESC + Generate CRD (Certification Requirements Document) as an HTML file. + + Required options: + model_name - The name of the certification model under arch/proc_cert_model + DESC + task :proc_crd_html, [:model_name] do |_t, args| + if args[:model_name].nil? + warn "Missing required option: 'model_name'" exit 1 end - 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" + unless File.exist?("#{$root}/arch/proc_cert_model/#{args[:model_name]}.yaml") + warn "No certification model named '#{args[:model_name]}' found in arch/proc_cert_model" exit 1 end - Rake::Task["#{$root}/gen/crd/html/#{args[:proc_cert_model_name]}.html"].invoke + Rake::Task["#{$root}/gen/crd/html/#{args[:model_name]}.html"].invoke end end diff --git a/backends/portfolios/tasks.rake b/backends/portfolios/tasks.rake new file mode 100644 index 000000000..082dbb9ef --- /dev/null +++ b/backends/portfolios/tasks.rake @@ -0,0 +1,84 @@ +# frozen_string_literal: true +# +# Contains common methods called from portfolio-based tasks.rake files. + +require "pathname" +require "asciidoctor-pdf" +require "asciidoctor-diagram" +require_relative "#{$lib}/idl/passes/gen_adoc" + +# @return [Architecture] +def pf_create_arch + # 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 "_". + Architecture.new("RISC-V Architecture", $root / "gen" / "resolved_arch" / "_") +end + +# @param erb_template_pname [String] Path to ERB template file +# @param erb_binding [Binding] Path to ERB template file +# @param target_pname [String] Full name of adoc file being generated +# @param portfolio_design [PortfolioDesign] Portfolio design being generated +def pf_create_adoc(erb_template_pname, erb_binding, target_pname, portfolio_design) + template_path = Pathname.new(erb_template_pname) + erb = ERB.new(File.read(template_path), trim_mode: "-") + erb.filename = template_path.to_s + + FileUtils.mkdir_p File.dirname(target_pname) + + # 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 #{portfolio_design.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(target_pname, erb_result_with_links_resolved) + puts "UPDATE: Generated adoc in #{target_pname}" +end + +# @param adoc_file [String] Full name of source adoc file +# @param target_pname [String] Full name of PDF file being generated +def pf_adoc2pdf(adoc_file, target_pname) + FileUtils.mkdir_p File.dirname(target_pname) + + puts "UPDATE: Generating PDF in #{target_pname}" + 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 #{target_pname}", + adoc_file + ].join(" ") + puts "UPDATE: Generated PDF in #{target_pname}" +end + +# @param adoc_file [String] Full name of source adoc file +# @param target_pname [String] Full name of HTML file being generated +def pf_adoc2html(adoc_file, target_pname) + FileUtils.mkdir_p File.dirname(target_pname) + + puts "UPDATE: Generating HTML in #{target_pname}" + 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 #{target_pname}", + adoc_file + ].join(" ") + puts "UPDATE: Generated HTML in #{target_pname}" +end diff --git a/backends/profile_doc/tasks.rake b/backends/profile_doc/tasks.rake index 07232b2c2..2d74ef38d 100644 --- a/backends/profile_doc/tasks.rake +++ b/backends/profile_doc/tasks.rake @@ -1,58 +1,48 @@ # frozen_string_literal: true +# +# Contains Rake rules to generate adoc, PDF, and HTML for a profile release. require "pathname" -require "asciidoctor-pdf" -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| - 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? + release_name = File.basename(f, ".yaml") + release_obj = YAML.load_file(f, permitted_classes: [Date]) + raise "Can't parse #{f}" if 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 'class' field" if release_obj['class'].nil? + class_name = File.basename(release_obj['class']['$ref'].split("#")[0], ".yaml") + raise "Ill-formed profile release file #{f}: can't parse class name" if 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}: missing 'profiles' field" if release_obj['profiles'].nil? + profile_names = 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? profile_pathnames = profile_names.map {|profile_name| "#{$root}/arch/profile/#{profile_name}.yaml" } - file "#{$root}/gen/profile_doc/adoc/#{profile_release_name}.adoc" => [ + file "#{$root}/gen/profile_doc/adoc/#{release_name}.adoc" => [ __FILE__, - "#{$root}/arch/profile_class/#{profile_class_name}.yaml", - "#{$root}/arch/profile_release/#{profile_release_name}.yaml", + "#{$root}/arch/profile_class/#{class_name}.yaml", + "#{$root}/arch/profile_release/#{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", "#{PROFILE_DOC_DIR}/templates/profile.adoc.erb" ].concat(profile_pathnames) do |t| - # 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" / "_") + arch = pf_create_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}" - profile_release = arch.profile_release(profile_release_name) + puts "UPDATE: Creating Profile Release for #{release_name}" + profile_release = arch.profile_release(release_name) # Create the one PortfolioDesign object required for the ERB evaluation. # Provide it with all the profiles in this ProfileRelease. - puts "UPDATE: Creating PortfolioDesign using profile release #{profile_release_name}" - portfolio_design = PortfolioDesign.new(profile_release_name, arch, profile_release.profiles) + puts "UPDATE: Creating PortfolioDesign using profile release #{release_name}" + portfolio_design = PortfolioDesign.new(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). @@ -66,106 +56,64 @@ Dir.glob("#{$root}/arch/profile_release/*.yaml") do |f| 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 - - 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}" - 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}" + pf_create_adoc("#{PROFILE_DOC_DIR}/templates/profile.adoc.erb", erb_binding, t.name, portfolio_design) end - file "#{$root}/gen/profile_doc/pdf/#{profile_release_name}.pdf" => [ + file "#{$root}/gen/profile_doc/pdf/#{release_name}.pdf" => [ __FILE__, - "#{$root}/gen/profile_doc/adoc/#{profile_release_name}.adoc" + "#{$root}/gen/profile_doc/adoc/#{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}" + pf_adoc2pdf("#{$root}/gen/profile_doc/adoc/#{release_name}.adoc", t.name) end - file "#{$root}/gen/profile_doc/html/#{profile_release_name}.html" => [ + file "#{$root}/gen/profile_doc/html/#{release_name}.html" => [ __FILE__, - "#{$root}/gen/profile_doc/adoc/#{profile_release_name}.adoc" + "#{$root}/gen/profile_doc/adoc/#{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}" + pf_adoc2html("#{$root}/gen/profile_doc/adoc/#{release_name}.adoc", t.name) end end namespace :gen do desc <<~DESC - Generate profile documentation for a specific release as a PDF. + Generate profile documentation for a profile release as a PDF. Required options: - profile_release_name - The key of the profile release under arch/portfolio_release + release_name - The name of the profile release under arch/profile_release 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'" + task :profile_release_pdf, [:release_name] do |_t, args| + release_name = args[:release_name] + if release_name.nil? + warn "Missing required option: '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" + unless File.exist?("#{$root}/arch/profile_release/#{release_name}.yaml") + warn "No profile release named '#{release_name}' found in arch/profile_release" exit 1 end - Rake::Task["#{$root}/gen/profile_doc/pdf/#{profile_release_name}.pdf"].invoke + Rake::Task["#{$root}/gen/profile_doc/pdf/#{release_name}.pdf"].invoke end - 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'" + desc <<~DESC + Generate profile documentation for a profile release as an HTML. + + Required options: + release_name - The name of the profile release under arch/profile_release + DESC + task :profile_release_html, [:release_name] do |_t, args| + release_name = args[:release_name] + if release_name.nil? + warn "Missing required option: '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" + unless File.exist?("#{$root}/arch/profile_release/#{release_name}.yaml") + warn "No profile release named '#{release_name}' found in arch/profile_release" exit 1 end - Rake::Task["#{$root}/gen/profile_doc/html/#{profile_release_name}.html"].invoke + Rake::Task["#{$root}/gen/profile_doc/html/#{release_name}.html"].invoke end end From c3ffda10b83e6d29db33b7601aa48ace39c528db Mon Sep 17 00:00:00 2001 From: James Ball Date: Sat, 11 Jan 2025 16:25:44 +0000 Subject: [PATCH 24/61] Created portfolio backend with common tasks.rake code between portfolios. Also created include_erb method in PortfolioDesign that invokes ERB partial templates with a standard set of locals. --- .github/workflows/pages.yml | 10 +- Rakefile | 26 +- backends/common_templates/adoc/README.adoc | 1 - backends/crd/tasks.rake | 10 +- .../ext_pdf_doc/templates/ext_pdf.adoc.erb | 4 +- backends/portfolio/README.adoc | 2 + backends/{portfolios => portfolio}/tasks.rake | 0 backends/portfolio/templates/README.adoc | 1 + .../portfolio/templates/ext_appendix.adoc.erb | 864 ++++++++++++++++++ backends/{profile_doc => profile}/tasks.rake | 30 +- .../templates/profile.adoc.erb | 18 +- backends/templates/README.adoc | 1 + .../adoc => templates}/csr.adoc.erb | 0 .../adoc => templates}/inst.adoc.erb | 0 lib/arch_obj_models/portfolio.rb | 4 +- lib/cfg_arch.rb | 1 - lib/design.rb | 12 +- lib/portfolio_design.rb | 43 +- lib/template_helpers.rb | 17 +- 19 files changed, 975 insertions(+), 69 deletions(-) delete mode 100644 backends/common_templates/adoc/README.adoc create mode 100644 backends/portfolio/README.adoc rename backends/{portfolios => portfolio}/tasks.rake (100%) create mode 100644 backends/portfolio/templates/README.adoc create mode 100644 backends/portfolio/templates/ext_appendix.adoc.erb rename backends/{profile_doc => profile}/tasks.rake (78%) rename backends/{profile_doc => profile}/templates/profile.adoc.erb (97%) create mode 100644 backends/templates/README.adoc rename backends/{common_templates/adoc => templates}/csr.adoc.erb (100%) rename backends/{common_templates/adoc => templates}/inst.adoc.erb (100%) diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml index 9fa8dbb5f..9f3e3ef9f 100644 --- a/.github/workflows/pages.yml +++ b/.github/workflows/pages.yml @@ -62,19 +62,19 @@ jobs: - name: Create RVA20 Profile Release PDF Spec 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 + run: cp gen/profile/pdf/RVA20.pdf _site/pdfs/RVA20.pdf - name: Create RVA22 Profile Release PDF Spec 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 + run: cp gen/profile/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 + run: cp gen/profile/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 + run: cp gen/profile/pdf/RVB23.pdf _site/pdfs/RVB23.pdf - name: Create RVI20 Profile Release PDF Spec 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 + run: cp gen/profile/pdf/RVA20.pdf _site/pdfs/RVI20.pdf - name: Create MC100-32 PDF Spec run: ./do gen:proc_crd_pdf[MC100-32] - name: Copy MC100-32 PDF diff --git a/Rakefile b/Rakefile index 8f5be9a80..474dbdc87 100644 --- a/Rakefile +++ b/Rakefile @@ -324,7 +324,7 @@ namespace :test do Rake::Task["gen:html"].invoke("generic_rv64") Rake::Task["#{$root}/gen/crd/pdf/MockProcCertModel.pdf"].invoke - Rake::Task["#{$root}/gen/profile_doc/pdf/MockProfileRelease.pdf"].invoke + Rake::Task["#{$root}/gen/profile/pdf/MockProfileRelease.pdf"].invoke puts puts "Regression test PASSED" @@ -350,7 +350,7 @@ task :portfolios do 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 + Rake::Task["#{$root}/gen/profile/pdf/MockProfileRelease.pdf"].invoke portfolio_start_msg("MC100-32 CRD") Rake::Task["#{$root}/gen/crd/pdf/MC100-32.pdf"].invoke portfolio_start_msg("MC100-64 CRD") @@ -364,15 +364,15 @@ task :portfolios do 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 + Rake::Task["#{$root}/gen/profile/pdf/RVI20.pdf"].invoke portfolio_start_msg("RVA20 Profile Release") - Rake::Task["#{$root}/gen/profile_doc/pdf/RVA20.pdf"].invoke + Rake::Task["#{$root}/gen/profile/pdf/RVA20.pdf"].invoke portfolio_start_msg("RVA22 Profile Release") - Rake::Task["#{$root}/gen/profile_doc/pdf/RVA22.pdf"].invoke + Rake::Task["#{$root}/gen/profile/pdf/RVA22.pdf"].invoke portfolio_start_msg("RVA23 Profile Release") - Rake::Task["#{$root}/gen/profile_doc/pdf/RVA23.pdf"].invoke + Rake::Task["#{$root}/gen/profile/pdf/RVA23.pdf"].invoke portfolio_start_msg("RVB23 Profile Release") - Rake::Task["#{$root}/gen/profile_doc/pdf/RVB23.pdf"].invoke + Rake::Task["#{$root}/gen/profile/pdf/RVB23.pdf"].invoke end def portfolio_start_msg(name) @@ -391,9 +391,9 @@ 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" -task "RVA22": "#{$root}/gen/profile_doc/pdf/RVA22.pdf" -task "RVA23": "#{$root}/gen/profile_doc/pdf/RVA23.pdf" -task "RVB23": "#{$root}/gen/profile_doc/pdf/RVB23.pdf" +task "MockProfileRelease": "#{$root}/gen/profile/pdf/MockProfileRelease.pdf" +task "RVI20": "#{$root}/gen/profile/pdf/RVI20.pdf" +task "RVA20": "#{$root}/gen/profile/pdf/RVA20.pdf" +task "RVA22": "#{$root}/gen/profile/pdf/RVA22.pdf" +task "RVA23": "#{$root}/gen/profile/pdf/RVA23.pdf" +task "RVB23": "#{$root}/gen/profile/pdf/RVB23.pdf" diff --git a/backends/common_templates/adoc/README.adoc b/backends/common_templates/adoc/README.adoc deleted file mode 100644 index a17bf4ffe..000000000 --- a/backends/common_templates/adoc/README.adoc +++ /dev/null @@ -1 +0,0 @@ -This directory contains partial templates (e.g., a CSR description) to be used by document generators. diff --git a/backends/crd/tasks.rake b/backends/crd/tasks.rake index a5122717f..1391f628d 100644 --- a/backends/crd/tasks.rake +++ b/backends/crd/tasks.rake @@ -29,10 +29,11 @@ Dir.glob("#{$root}/arch/proc_cert_model/*.yaml") do |f| # None of these objects are provided with a Design object when created. puts "UPDATE: Creating ProcCertModel for #{model_name}" proc_cert_model = arch.proc_cert_model(model_name) + proc_cert_class = proc_cert_model.proc_cert_class # Create the one PortfolioDesign object required for the ERB evaluation. puts "UPDATE: Creating PortfolioDesign using processor certificate model #{model_name}" - portfolio_design = PortfolioDesign.new(model_name, arch, [proc_cert_model]) + portfolio_design = PortfolioDesign.new(model_name, arch, [proc_cert_model], proc_cert_class) # 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). @@ -40,12 +41,9 @@ Dir.glob("#{$root}/arch/proc_cert_model/*.yaml") do |f| binding end erb_binding = evaluate_erb - erb_binding.local_variable_set(:arch, arch) - erb_binding.local_variable_set(:design, portfolio_design) - 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) + portfolio_design.init_erb_binding(erb_binding) erb_binding.local_variable_set(:proc_cert_model, proc_cert_model) - erb_binding.local_variable_set(:portfolio, proc_cert_model) + erb_binding.local_variable_set(:proc_cert_class, proc_cert_class) pf_create_adoc("#{CERT_DOC_DIR}/templates/crd.adoc.erb", erb_binding, t.name, portfolio_design) end diff --git a/backends/ext_pdf_doc/templates/ext_pdf.adoc.erb b/backends/ext_pdf_doc/templates/ext_pdf.adoc.erb index 59f1f5690..58daaccfa 100644 --- a/backends/ext_pdf_doc/templates/ext_pdf.adoc.erb +++ b/backends/ext_pdf_doc/templates/ext_pdf.adoc.erb @@ -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, design: design } %> +<%= partial("templates/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, design: design } %> +<%= partial("templates/inst.adoc.erb", { inst: i, design: design }) %> :leveloffset: -2 <<< diff --git a/backends/portfolio/README.adoc b/backends/portfolio/README.adoc new file mode 100644 index 000000000..1f3ffee0f --- /dev/null +++ b/backends/portfolio/README.adoc @@ -0,0 +1,2 @@ +This portfolio backend isn't a real backend. +Instead, it contains common Rake code and ERB templates shared by multiple portfolio-based backends. diff --git a/backends/portfolios/tasks.rake b/backends/portfolio/tasks.rake similarity index 100% rename from backends/portfolios/tasks.rake rename to backends/portfolio/tasks.rake diff --git a/backends/portfolio/templates/README.adoc b/backends/portfolio/templates/README.adoc new file mode 100644 index 000000000..237833699 --- /dev/null +++ b/backends/portfolio/templates/README.adoc @@ -0,0 +1 @@ +This directory contains partial ERB templates shared by multiple portfolio-based backend. diff --git a/backends/portfolio/templates/ext_appendix.adoc.erb b/backends/portfolio/templates/ext_appendix.adoc.erb new file mode 100644 index 000000000..1a5e39ff1 --- /dev/null +++ b/backends/portfolio/templates/ext_appendix.adoc.erb @@ -0,0 +1,864 @@ +// Number heading sections (e.g., 1.0, 1.1, etc.) +:sectnums: + +// Add a table of contents for HTML (and VSCode adoc preview) +:toc: left + +// Include headings up to 3 levels deep (don't know why 5 gives you this). +:toclevels: 5 + +// +// Stuff to generate nice wavedrom drawings of instruction and CSR fields +// +:wavedrom: <%= $root %>/node_modules/.bin/wavedrom-cli + +// TODO: needs to be changed +:imagesoutdir: images + += <%= proc_cert_model.name %> Processor Certification Requirements Document + +[Preface] +== Revision History + +History of documentation changes that eventually lead to releases. + +[cols="1,1,5"] +|=== +| Date | Revision | Changes + +<% proc_cert_model.revision_history.each do |rev| -%> +| <%= rev.date %> +| <%= rev.revision %> +a| <% rev.changes.each do |change| %> +* <%= change %> +<% end -%> +<% end -%> +|=== + +[Preface] +== Typographic Conventions + +CSR field colors:: + +* Grey fields are reserved (WPRI) +* Green fields are present +* Red fields are defined by the RISC-V ISA but not present + +CSR field types:: + +[%autowidth] +|=== +| Abbreviation | Description + +<% CsrField::TYPE_DESC_MAP.each do |abbreviation, description| -%> +| <%= abbreviation %> +| <%= description %> +<% end -%> +|=== + +== Introduction + +<%= proc_cert_model.introduction %> + +<%= proc_cert_class.introduction %> + +=== What's a CRD? + +Certification Requirements Documents (CRDs) list requirements an implementation must meet +to obtain an associated RVI (RISC-V International) certificate. +CRDs are developed by the RVI CSC (Certification Steering Committee) organization in collaboration +with the RVI TSC (Technical Steering Committee) organization who creates RISC-V standards. + +The CRDs refer to and augment information provided in existing ratified RVI standards. + +There are a variety of certificates offered by RVI to accomodate the various RVI standards. +There are certificates for processors, non-processor system IP (e.g., IOMMU), +and system platforms (processor + system IP) hardware standards. +There are multiple classes of processor certificates available to accomodate the wide range of +RISC-V implementations from basic microcontrollers to advanced Applications-class processors. + +Each CRD has a list of mandatory behaviors along with a list of optional behaviors. +Note that not all behaviors allowed in RISC-V standards are supported by a particular CRD. + +=== CRD Naming Scheme + +CRDs have the following naming scheme: + + Format: [v] + +Where: + +* Left & right square braces denote optional. +* Less-than & greater-than signs just separate fields (i.e., they aren't present in the CRD name). +* identifies the type of RISC-V standard (processor, non-processor system IP, or platform) along with + any other information required to identify the variant of that standard. +* identifies a particular CRD release +** Format is [.[.]] +** Follows semantic versioning scheme (https://semver.org/) +** The release is updated when certification test changes are made that *could* cause a previously certified + implementation to now fail. + Examples are fixing a test bug, or increasing test coverage, or requiring a new version of a standard + A release of 0 is used for pre-release versions of a CRD and release versions start with 1. +** The release is updated when a CRD increases support for optional behaviors. + Examples are supporting for new optional standards or + 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 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) + +=== CRD Terminology + +.Requirement Types +[%autowidth] +|=== +| Term | Meaning + +| MANDATORY | You have to implement it to get a certificate and the certificate tests will cover it +| OPTIONAL | It's up to you if you implement or not. If you claim to implement it, certificate tests will cover it +| IN-SCOPE | Either MANDATORY or OPTIONAL +| OUT-OF-SCOPE | It's up to you if you implement or not. If you implement it, it won't be certified but make sure you don't mess up anything we are certifying. +| INCOMPATIBLE | If you implement it you won't get a certificate +|=== + +.Glossary +[%autowidth] +|=== +| Term | Meaning + +| CRD | Certification Requirements Document +| N/A | “Not Applicable” +| AKA | “Also Known As” +|=== + +=== Processor CRDs + +There are Processor CRDs for different classes of RISC-V processors. +These documents augment information in the related TSC Profile when available and/or other RVI standards documents +(e.g., Priv and Unpriv ISA manuals). +Only ratified extensions are candidates for certification. +This implies all custom extensions are also OUT-OF-SCOPE. + +==== Processor Certificate Class Naming Scheme + +Processor Certificate Class names have the following format: + + [<-base>] + +Where: + +* is MC for Microcontroller Class and AC for Apps-processor Class +* is 3-digit integer defined as follows: +** The hundreds's digit indicates the series +** The ten's digit identifies large differences in mandatory extensions (e.g., V, H) within the series +** The one's digit indentifies small/medium differences in mandatory extensions (e.g., Zicond, PMP) within the series +* is optional and is 32 for RV32I, 64 for RV64I, and 32E for RV32E +** If a CRD supports multiple bases and is omitted in a reference, it applies to all supported bases +** If a CRD only supports one base then is generally omitted + +[%autowidth] +|=== +| CRD | TSC Profile | Description + +| MC100-series | TBD | 32/64-bit minimal microcontroller that runs low-level software on an RTOS or bare-metal (no virtual memory) +| MC200-series | TBD | 32/64-bit intermediate microcontroller +| MC300-series | TBD | 32/64-bit advanced microcontroller +| AC100-series | RVB23 | 64-bit Apps-processor running Bespoke rich operating systems (e.g., Yocto Linux) +| AC200-series | RVA23 | 64-bit Apps-processor running standard rich operating systems (e.g., commercial Linux distributions, Android) +|=== + +==== CSR Field Terminology + +.Definition of CSR Fields +[%autowidth] +|=== +| Field Type | Read Value After Writing Illegal Value | Read Value Function Of | Illegal Instruction Exception | Priv ISA Manual Quote + +| WLRL | Any deterministic legal or illegal value | Value before write and illegal value written | Optional +| Implementations are permitted but not required to raise an illegal-instruction exception if an instruction attempts to write a non-supported value to a WLRL field. Implementations can return arbitrary bit patterns on the read of a WLRL field when the last write was of an illegal value, but the value returned should deterministically depend on the illegal written value and the value of the field prior to the write. +| WARL | Any deterministic legal value | Any architectural hart state | Prohibited +| Implementations will not raise an exception on writes of unsupported values to a WARL field. Implementations can return any legal value on the read of a WARL field when the last write was of an illegal value, but the legal value returned should deterministically depend on the illegal written value and the architectural state of the hart. +| WPRI | 0 | Nothing | Not specified +| Some whole read/write fields are reserved for future use. Implementations that do not furnish these fields must make them read-only zero. +|=== + +*WARL (Write Anything, Read Legal)*: + +The Priv ISA requires reads of WARL fields to return some implementation-dependent deterministic legal value +after the field is written with an illegal value. +Certifying such behaviors is expensive and provides low value for a certificate since software can't rely +on a particular behavior from one implementation to another. + +Processor CRDs define writes to WARL fields of illegal values to be OUT-OF-SCOPE unless otherwise stated +(i.e., certification tests will only ever write legal values to WARL fields except for the special cases listed below). +When not OUT-OF-SCOPE, the required behavior is defined as this might be more constrained in implementations than +in the standard. + +The following special cases for WARL are supported when explicitly listed in the corresponding CRD CSR field requirements: + +1. Probing for Field Width + +* Some WARL fields are variable length such as the ASID field in the virtual memory extension. +* Here's the algorithm recommended to discover the ASID width: +** The number of implemented ASID bits, termed ASIDLEN, may be determined by writing one to every bit position in + the ASID field, then reading back the value in the satp CSR to see which bit positions in the ASID field hold a one. +* The RVCP-provided certification materials (certification tests, certification reference models) can map writes of + illegal values to the ASID field to the corresponding read value as long as they are provided the ASIDLEN value + for an implementation. + +2. Probing for Options + +* E.g., Writable misa bits + +3. Allowed values are a function of extension presence and/or their parameters + +* E.g., satp.mode legal write values + +*WLRL (Write Legal, Read Legal)*: + +The Priv ISA requires reads of WLRL fields to return some implementation-dependent deterministic arbitrary value +after the field is written with an illegal value. +Certifying such behaviors is expensive and provides low value for a certificate since software can't rely +on a particular behavior. +Processor CRDs define writes to WLRL fields of illegal values to be OUT-OF-SCOPE unless otherwise stated +(i.e., certification tests will only ever write legal values to WLRL fields). + +*WPRI (Write Preserve, Read Ignore)*: + +The Priv ISA requires reads of WPRI fields to return a value of 0. +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 intended to be comprehensive +verification test suites anyways. + +=== Related Specifications + +[cols="2,2,3,3,3"] +|=== +| Certificate Model | TSC Profile | Unpriv ISA Manual | Priv ISA Manual | Debug Manual + +| <%= 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 + +|=== +| M | S | U | VS | VU + +| <% 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 -%> + +|=== + +<<< +== Extensions + +Any RISC-V extensions not listed in this section are OUT-OF-SCOPE. +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 = proc_cert_model.in_scope_ext_reqs(presence_obj) -%> +<% if ext_reqs.empty? -%> +None +<% else -%> +[%autowidth] +|=== +| Requirement ID | Extension | Version | Long Name | Note + +<% 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(", ") %> +| <%= ext.nil? ? "" : ext.long_name %> +| <%= ext_req.note.nil? ? "" : ext_req.note %> +<% end # each ext_req -%> +|=== +<% end # if empty ext_reqs -%> + +<% 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 proc_cert_model.recommendations.empty? -%> +=== Recommendations + +Recommendations are not strictly mandated but are included to guide implementers. + +<% proc_cert_model.recommendations.each do |recommendation| -%> +<%= recommendation.text %> +<% end # each recommendation -%> +<% end # unless recommendations empty -%> + +<<< +== Implementation-dependencies + +RISC-V standards support many implementation-defined parameters. In many cases, there +are no names associated with these parameters. Names are defined in this section when +not provided in the associated standard. + +=== IN-SCOPE Parameters + +These implementation-dependent options defined by MANDATORY or OPTIONAL extensions are IN-SCOPE. +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 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 + +<% proc_cert_model.all_in_scope_ext_params.each do |in_scope_ext_param| -%> +<% param = in_scope_ext_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 %> +| <% exts.each do |ext| -%><-param-<%= param.name %>-def,<%= ext.name %>>> <% end # do ext -%> +a| <%= in_scope_ext_param.note %> +<% end # do -%> +|=== +<% end # if table -%> + +=== OUT-OF-SCOPE Parameters + +These implementation-dependent options defined by MANDATORY or OPTIONAL extensions are OUT-OF-SCOPE. +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 proc_cert_model.all_out_of_scope_params.empty? -%> +None +<% else -%> +[%autowidth] +|=== +| Parameters | Type | Extension(s) + +<% 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 -%> + +<% end # do -%> +|=== +<% end # if table -%> + +== Traps + +RISC-V supports both synchronous exceptions and asynchronous interrupts. +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 + +=== Synchronous Exceptions + +|=== +| `xcause.CODE` CSR Field Value | Name +<% design.in_scope_exception_codes.sort_by{ |code| code.num }.each do |code| -%> +| <%= code.num %> | <%= code.name %> +<% end -%> +|=== + +=== Asynchronous Interrupts + +|=== +| `xcause.CODE` CSR Field Value | Name +<% design.in_scope_interrupt_codes.sort_by{ |code| code.num }.each do |code| -%> +| <%= code.num %> | <%= code.name %> +<% end -%> +|=== + +== Instruction Summary + +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 + +[%autowidth] +|=== +| Name | Long Name + +<% design.in_scope_instructions.each do |inst| -%> +| <%= link_to_inst(inst.name) %> +| <%= inst.long_name %> +<% end # do -%> +|=== + +== CSR Summary + +=== By Name + +[%autowidth] +|=== +| Name | Long Name | Address | Mode | Primary Extension + +<% design.in_scope_csrs.sort_by!(&:name).each do |csr| -%> +| <-def,<%= csr.name %>>> +| <%= csr.long_name %> +| <%= "0x#{csr.address.to_s(16)}" %> +| <%= csr.priv_mode %> +| <%= csr.primary_defined_by %> +<% end # do -%> +|=== + +=== By Address + +[%autowidth] +|=== +| Address | Mode | Name | Long Name | Primary Extension + +<% design.in_scope_csrs.sort_by!(&:address).each do |csr| -%> +| <%= "0x#{csr.address.to_s(16)}" %> +| <%= csr.priv_mode %> +| <-def,<%= csr.name %>>> +| <%= csr.long_name %> +| <%= csr.primary_defined_by %> +<% end # do -%> +|=== + +<% 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. + +<% proc_cert_model.requirement_groups.each do |group| -%> +=== <%= group.name %> + +<%= group.description %> + +<% unless group.when.nil? -%> +[IMPORTANT] +<%= group.name %> requirements only apply when <%= group.when_pretty %>. +<% end -%> + +[%autowidth] +|=== +| Req Number | Description + +<% group.requirements.each do |req| -%> +| <%= req.name %> +a| <%= req.description %> +<% unless req.when.nil? -%> +[IMPORTANT] +Requirement <%= req.name %> only apply when <%= req.when_pretty %>. +<% end -%> +<% end -%> +|=== + +<% end -%> +<% end # unless requirement_groups.empty? -%> + +<<< +[appendix] +== Extension Details +<% proc_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 %> + +<%= ext.nil? ? "" : "*Long Name*: " + ext.long_name + " +" %> + +*Version Requirement*: <%= ext_req.requirement_specs.map(&:to_s).join(", ") %> + + +<% ext.versions.each do |v| -%> +<%= v.version_spec %>:: + State::: + <%= v.state %> + <% if v.state == "ratified" -%> + Ratification date::: + <%= v.ratification_date %> + <% end # if %> + <% if v.changes.size > 0 -%> + Changes::: + + <% v.changes.each do |c| -%> + * <%= c %> + <% end -%> + + <% end -%> + <% unless v.url.nil? -%> + Ratification document::: + <%= v.url %> + <% end -%> + <% if v.implications.size > 0 -%> + Implies::: + <% v.implications.each do |i| -%> + * `<%= i.name %>` version <%= i.version_spec %> + <% end -%> + <% end -%> +<% end -%> + +==== Synopsis + +:leveloffset: +3 + +<%= ext.description %> + +:leveloffset: -3 + +<% unless ext_req.note.nil? -%> +[NOTE] +-- +<%= ext_req.note %> +-- +<% end -%> + +// TODO: GitHub issue 92: Use version specified by each profile. +<% insts = arch.instructions.select { |i| i.defined_by?(ext.min_version) } -%> +<% unless insts.empty? -%> +==== Instructions + +The following instructions are added by this extension: + +[cols="1,3"] +|=== +<% insts.each do |inst| -%> +| <%= link_to_inst(inst.name) %> +| *<%= inst.long_name %>* +<% end -%> +|=== +<% end -%> + +<% unless proc_cert_model.in_scope_ext_params(ext_req).empty? -%> +==== IN-SCOPE Parameters + +<% 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 %>:: ++ +-- +<%= ext_param.param.desc %> +-- +<% end # do ext_param -%> +<% end # unless table -%> + +<% unless proc_cert_model.out_of_scope_params(ext_req.name).empty? -%> +==== OUT-OF-SCOPE Parameters + +<% 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 %>:: ++ +-- +<%= param.desc %> +-- +<% end # do param -%> +<% end # unless table -%> +<% end # do ext_req -%> + +<<< +[appendix] +== Instruction Details + +<% design.in_scope_instructions.each do |inst| -%> +<<< +<%= anchor_for_inst(inst.name) %> +=== <%= inst.name %> + +*<%= inst.long_name %>* + +This instruction is defined by: + +<%= inst.defined_by_condition.to_asciidoc %> + +==== Encoding + +<% if inst.multi_encoding? -%> +[NOTE] +This instruction has different encodings in RV32 and RV64. + +==== +RV32:: ++ +[wavedrom, ,svg,subs='attributes',width="100%"] +.... +<%= JSON.dump inst.wavedrom_desc(32) %> +.... + +RV64:: ++ +[wavedrom, ,svg,subs='attributes',width="100%"] +.... +<%= JSON.dump inst.wavedrom_desc(64) %> +.... +==== +<% else -%> +[wavedrom, ,svg,subs='attributes',width="100%"] +.... +<%= JSON.dump inst.wavedrom_desc(inst.base.nil? ? 32 : inst.base) %> +.... +<% end -%> + +==== Synopsis + +<%= inst.description %> + +==== Access +<% if design.in_scope_extensions.any? { |e| e.name == "H" } -%> +[cols="^,^,^,^,^"] +<% else -%> +[cols="^,^,^"] +<% 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 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 %> +|=== + +<% if inst.access_detail? -%> +<%= inst.access_detail %> +<% end -%> + +==== Decode Variables + +<% if inst.multi_encoding? -%> +==== +RV32:: ++ +[source.idl] +---- +<% inst.decode_variables(32).each do |d| -%> +<%= d.sext? ? 'signed ' : '' %>Bits<<%= d.size %>> <%= d.name %> = <%= d.extract %>; +<% end -%> +---- + +RV64:: ++ +[source,idl] +---- +<% inst.decode_variables(64).each do |d| -%> +<%= d.sext? ? 'signed ' : '' %>Bits<<%= d.size %>> <%= d.name %> = <%= d.extract %>; +<% end -%> +---- +==== +<% else -%> +[source,idl] +---- +<% inst.decode_variables(inst.base.nil? ? 32 : inst.base).each do |d| -%> +<%= d.sext? ? 'signed ' : '' %>Bits<<%= d.size %>> <%= d.name %> = <%= d.extract %>; +<% end -%> +---- +<% end -%> + +==== Execution + +<% xlens = inst.base.nil? ? [32, 64] : [inst.base] -%> + +<% if inst.key?("operation()") -%> +[source,idl,subs="specialchars,macros"] +---- +<%= inst.operation_ast(design.symtab).gen_adoc %> +---- +<% end -%> + +==== Exceptions + +<%- exception_list = inst.reachable_exceptions_str(design.symtab) -%> +<%- if exception_list.empty? -%> +This instruction does not generate synchronous exceptions. +<%- else -%> +This instruction may result in the following synchronous exceptions: + + <%- exception_list.sort.each do |etype| -%> + * <%= etype %> + <%- end -%> + +<%- end -%> + + +<% end -%> + +<<< +[appendix] +== CSR Details + +<% design.in_scope_csrs.sort_by(&:name).each do |csr| -%> +<<< +[[csr-<%= csr.name %>-def]] +=== <%= csr.name %> + +*<%= csr.long_name %>* + +<% unless csr.base.nil? -%> +[NOTE] +-- +`<%= csr.name %>` is only defined in RV<%= csr.base %>. +-- +<% end -%> + +<%= csr.description %> + +==== Attributes +[%autowidth] +|=== +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| Defining extension a| <%= csr.defined_by_condition.to_asciidoc %> +<% if csr.dynamic_length?(design) -%> +h| Length | <%= csr.length_pretty(design) %> +<% else -%> +h| Length | <%= csr.length_pretty(design) %> +<% end -%> +h| Privilege Mode | <%= csr.priv_mode %> +|=== + + +==== Format +<% 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(design, csr.base.nil? ? 32 : csr.base, optional_type: 2) %> +.... +<% else -%> +<%# CSR has a dynamic length, or a field has a dynamic location, + so there is more than one format to display -%> +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(design, 32, optional_type: 2) %> +.... + +.<%= csr.name %> Format when <%= csr.length_cond64 %> +[wavedrom, ,svg,subs='attributes',width="100%"] +.... +<%= JSON.dump csr.wavedrom_desc(design, 64, optional_type: 2) %> +.... + + +<% end # unless dynamic length -%> + +==== Field Summary + +// use @ as a separator since IDL code can contain | +[%autowidth,separator=@,float="center",align="center",cols="^,<,<,<",options="header",role="stretch"] +|=== +@ Name @ Location @ Type @ Reset Value + +<%- csr.implemented_fields(design).each do |field| -%> +@ xref:<%=csr.name%>-<%=field.name%>-def[`<%= field.name %>`] +a@ +<%- if field.dynamic_location?(design) -%> + +[when,"<%= field.location_cond32 %>"] +-- +<%= field.location_pretty(design, 32) %> +-- + +[when,"<%= field.location_cond64 %>"] +-- +<%= field.location_pretty(design, 64) %> +-- + +<%- else -%> +<%= field.location_pretty(design) %> +<%- end -%> +a@ + +-- +<%= field.type_pretty(design.symtab) %> +-- + +a@ + +-- +<%= field.reset_value_pretty(design) %> +-- + +<%- end -%> +|=== + +==== Fields + +<%- 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(design).each do |field| -%> +[[<%=csr.name%>-<%=field.name%>-def]] +===== `<%= field.name %>` + +<%- if !field.defined_in_all_bases? -%> +IMPORTANT: <%= field.name %> is only defined in <%= field.base32_only? ? "RV32" : "RV64" %> (`<%= field.base32_only? ? field.location_cond32 : field.location_cond64 %>`) +<%- end -%> + +**** +Location:: +<%= field.location_pretty(design) %> + +Description:: +<%= field.description.gsub("\n", " +\n") %> + +Type:: +<%= field.type_pretty(design.symtab) %> + +Reset value:: +<%= field.reset_value_pretty(design) %> + +**** + +<%- end -%> +<%- end -%> + +<%- 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. + +When a software write occurs (_e.g._, through `csrrw`), the following determines the +written value: + +[idl] +---- +<%- csr.implemented_fields(design).each do |field| -%> +<%- if field.has_custom_sw_write? -%> +<%= field.name %> = <%= field["sw_write(csr_value)"] %> +<%- else -%> +<%= field.name %> = csr_value.<%= field.name %> +<%- end -%> +<%- end -%> +---- +<%- end -%> + +<%- if csr.has_custom_sw_read? -%> +==== Software read + +This CSR may return a value that is different from what is stored in hardware. + +[source,idl,subs="specialchars,macros"] +---- +<%= csr.sw_read_ast(design.symtab).gen_adoc %> +---- +<%- end -%> + +<% end # do in_scope_csrs -%> diff --git a/backends/profile_doc/tasks.rake b/backends/profile/tasks.rake similarity index 78% rename from backends/profile_doc/tasks.rake rename to backends/profile/tasks.rake index 2d74ef38d..3f3c9639d 100644 --- a/backends/profile_doc/tasks.rake +++ b/backends/profile/tasks.rake @@ -4,7 +4,7 @@ require "pathname" -PROFILE_DOC_DIR = Pathname.new "#{$root}/backends/profile_doc" +PROFILE_DOC_DIR = Pathname.new "#{$root}/backends/profile" Dir.glob("#{$root}/arch/profile_release/*.yaml") do |f| release_name = File.basename(f, ".yaml") @@ -21,7 +21,7 @@ Dir.glob("#{$root}/arch/profile_release/*.yaml") do |f| profile_pathnames = profile_names.map {|profile_name| "#{$root}/arch/profile/#{profile_name}.yaml" } - file "#{$root}/gen/profile_doc/adoc/#{release_name}.adoc" => [ + file "#{$root}/gen/profile/adoc/#{release_name}.adoc" => [ __FILE__, "#{$root}/arch/profile_class/#{class_name}.yaml", "#{$root}/arch/profile_release/#{release_name}.yaml", @@ -38,11 +38,13 @@ Dir.glob("#{$root}/arch/profile_release/*.yaml") do |f| # None of these objects are provided with a Design object when created. puts "UPDATE: Creating Profile Release for #{release_name}" profile_release = arch.profile_release(release_name) + profile_class = profile_release.profile_class # Create the one PortfolioDesign object required for the ERB evaluation. # Provide it with all the profiles in this ProfileRelease. puts "UPDATE: Creating PortfolioDesign using profile release #{release_name}" - portfolio_design = PortfolioDesign.new(release_name, arch, profile_release.profiles) + portfolio_design = + PortfolioDesign.new(release_name, arch, profile_release.profiles, profile_class) # 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). @@ -50,27 +52,25 @@ Dir.glob("#{$root}/arch/profile_release/*.yaml") do |f| binding end erb_binding = evaluate_erb - 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) + portfolio_design.init_erb_binding(erb_binding) erb_binding.local_variable_set(:profile_release, profile_release) + erb_binding.local_variable_set(:profile_class, profile_class) pf_create_adoc("#{PROFILE_DOC_DIR}/templates/profile.adoc.erb", erb_binding, t.name, portfolio_design) end - file "#{$root}/gen/profile_doc/pdf/#{release_name}.pdf" => [ + file "#{$root}/gen/profile/pdf/#{release_name}.pdf" => [ __FILE__, - "#{$root}/gen/profile_doc/adoc/#{release_name}.adoc" + "#{$root}/gen/profile/adoc/#{release_name}.adoc" ] do |t| - pf_adoc2pdf("#{$root}/gen/profile_doc/adoc/#{release_name}.adoc", t.name) + pf_adoc2pdf("#{$root}/gen/profile/adoc/#{release_name}.adoc", t.name) end - file "#{$root}/gen/profile_doc/html/#{release_name}.html" => [ + file "#{$root}/gen/profile/html/#{release_name}.html" => [ __FILE__, - "#{$root}/gen/profile_doc/adoc/#{release_name}.adoc" + "#{$root}/gen/profile/adoc/#{release_name}.adoc" ] do |t| - pf_adoc2html("#{$root}/gen/profile_doc/adoc/#{release_name}.adoc", t.name) + pf_adoc2html("#{$root}/gen/profile/adoc/#{release_name}.adoc", t.name) end end @@ -93,7 +93,7 @@ namespace :gen do exit 1 end - Rake::Task["#{$root}/gen/profile_doc/pdf/#{release_name}.pdf"].invoke + Rake::Task["#{$root}/gen/profile/pdf/#{release_name}.pdf"].invoke end desc <<~DESC @@ -114,6 +114,6 @@ namespace :gen do exit 1 end - Rake::Task["#{$root}/gen/profile_doc/html/#{release_name}.html"].invoke + Rake::Task["#{$root}/gen/profile/html/#{release_name}.html"].invoke end end diff --git a/backends/profile_doc/templates/profile.adoc.erb b/backends/profile/templates/profile.adoc.erb similarity index 97% rename from backends/profile_doc/templates/profile.adoc.erb rename to backends/profile/templates/profile.adoc.erb index 3f6339ee5..a2ffc1088 100644 --- a/backends/profile_doc/templates/profile.adoc.erb +++ b/backends/profile/templates/profile.adoc.erb @@ -312,10 +312,6 @@ are by definition non-profile extensions. For example, mandatory or optional profile extensions for a new profile might be prototyped as non-profile extensions on an earlier profile. -// XXX - Need to create render() Ruby function. -// See https://github.com/riscv-software-src/riscv-unified-db/issues/59 -// <%# render("#{$root}/backends/portfolio_doc/templates/family_intro.erb", portfolio_class: profile_class) %> - == <%= profile_class.name %> Profile Class <%= profile_class.introduction %> @@ -450,9 +446,11 @@ Extensions present in a profile are also present in higher-privileged profiles i <<< [appendix] +<%# XXX pf_design.include_erb("ext_appendix.adoc.erb") %> + == Extension Details -<% design.in_scope_extensions.each do |ext| -%> +<% pf_design.in_scope_extensions.each do |ext| -%> <<< === <%= ext.name %> Extension <%= ext.long_name %> @@ -538,7 +536,7 @@ This extension has the following implementation options: [appendix] == Instruction Details -<% design.in_scope_instructions.each do |inst| -%> +<% pf_design.in_scope_instructions.each do |inst| -%> <<< <%= anchor_for_inst(inst.name) %> === <%= inst.name %> @@ -583,18 +581,18 @@ RV64:: <%= inst.description %> ==== Access -<% if design.in_scope_extensions.any? { |e| e.name == "H" } -%> +<% if pf_design.in_scope_extensions.any? { |e| e.name == "H" } -%> [cols="^,^,^,^,^"] <% else -%> [cols="^,^,^"] <% 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 -%> +| M | <% if pf_design.in_scope_extensions.any? { |e| e.name == "H" } -%>HS<% else -%>S<% end -%> | U <% if pf_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 design.in_scope_extensions.any? { |e| e.name == "H" } %> +<% if pf_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 %> @@ -668,7 +666,7 @@ This instruction may result in the following synchronous exceptions: [appendix] == CSR Details -<% design.in_scope_csrs.sort_by!(&:name).each do |csr| -%> +<% pf_design.in_scope_csrs.sort_by!(&:name).each do |csr| -%> <<< [[csr-<%= csr.name %>-def]] === <%= csr.name %> diff --git a/backends/templates/README.adoc b/backends/templates/README.adoc new file mode 100644 index 000000000..9cf268e10 --- /dev/null +++ b/backends/templates/README.adoc @@ -0,0 +1 @@ +This directory contains partial ERB templates shared by multiple backends. diff --git a/backends/common_templates/adoc/csr.adoc.erb b/backends/templates/csr.adoc.erb similarity index 100% rename from backends/common_templates/adoc/csr.adoc.erb rename to backends/templates/csr.adoc.erb diff --git a/backends/common_templates/adoc/inst.adoc.erb b/backends/templates/inst.adoc.erb similarity index 100% rename from backends/common_templates/adoc/inst.adoc.erb rename to backends/templates/inst.adoc.erb diff --git a/lib/arch_obj_models/portfolio.rb b/lib/arch_obj_models/portfolio.rb index fec926f18..471fff3ea 100644 --- a/lib/arch_obj_models/portfolio.rb +++ b/lib/arch_obj_models/portfolio.rb @@ -55,7 +55,7 @@ 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.). + # Avoids having to call portfolio_grp.portfolios. (just call portfolio_grp.). def_delegators :@portfolios, :each, :map, :select # @param portfolios [Array] @@ -219,7 +219,7 @@ def extension_presence(ext_name) ############# # Holds information about a Portfolio (certificate or profile). -# The inherited "data" member is the database of extensions, instructions, CSRs, etc. +# The inherited "data" member is YAML data from the architecture for this portfolio object. class Portfolio < DatabaseObject # @param obj_yaml [Hash] Contains contents of Portfolio yaml file (put in @data) # @param data_path [String] Path to yaml file diff --git a/lib/cfg_arch.rb b/lib/cfg_arch.rb index 8dd2c2cbd..ef50a4018 100644 --- a/lib/cfg_arch.rb +++ b/lib/cfg_arch.rb @@ -6,7 +6,6 @@ require "forwardable" require "ruby-prof" -require "tilt" require_relative "architecture" require_relative "design" diff --git a/lib/design.rb b/lib/design.rb index e68b6e6bb..1b607715f 100644 --- a/lib/design.rb +++ b/lib/design.rb @@ -327,7 +327,7 @@ def find_replace_links(adoc) end end - # Returns an environment hash suitable for the render() function in ERB templates. + # Returns an environment hash suitable for the render_erb() function in ERB templates. # # @return [Hash] An environment hash suitable for use with ERb templates. def render_erb_env @@ -399,18 +399,20 @@ def implemented_interrupt_codes end private :render_erb_env - # Passes _erb_template_ through ERB within the content of this render_erb_env + # Passes _erb_template_ through ERB within the content of the render_erb_env # - # @param erb_template [String] ERB source + # @param erb_template [String] ERB template source string + # @param what [String] ??? # @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) + template = Tilt["erb"].new(t.path, trim: "-") + template.render(render_erb_env) rescue - warn "While rendering ERB template: #{what}" + warn "While rendering ERB template #{erb_template}: #{what}" raise ensure t.close diff --git a/lib/portfolio_design.rb b/lib/portfolio_design.rb index cc2c3139a..9ecea9b2b 100644 --- a/lib/portfolio_design.rb +++ b/lib/portfolio_design.rb @@ -9,26 +9,35 @@ # 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 + # @return [Array] All the portfolios in this portfolio design + attr_reader :portfolio_grp + + # @return [PortfolioClass] Portfolio class for all the portfolios in this design + attr_reader :portfolio_class + # @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 portfolio_class [PortfolioClass] PortfolioClass for all the Portfolios # @param overlay_path [String] Optional path to a directory that overlays the architecture - def initialize(base_isa_name, arch, portfolios, overlay_path: nil) + def initialize(base_isa_name, arch, portfolios, portfolio_class, 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) + raise ArgumentError, "portfolio_class must be a PortfolioClass but is a #{portfolio_class.class}" unless portfolio_class.is_a?(PortfolioClass) # 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) + @portfolio_class = portfolio_class + max_base = portfolios.map(&:base).max raise ArgumentError, "Calculated maximum base of #{max_base} across portfolios is not 32 or 64" unless max_base == 32 || max_base == 64 @@ -190,4 +199,34 @@ def in_scope_interrupt_codes = @portfolio_grp.in_scope_interrupt_codes(self) # 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) + + ################# + # EXTRA METHODS # + ################# + + # Called from tasks.rake file to add standard set of objects available to ERB templates. + def init_erb_binding(erb_binding) + raise ArgumentError, "Expected Binding object but got #{erb_binding.class}" unless erb_binding.is_a?(Binding) + + erb_binding.local_variable_set(:arch, arch) + erb_binding.local_variable_set(:design, self) + erb_binding.local_variable_set(:pf_design, self) + erb_binding.local_variable_set(:pf_class, self.portfolio_class) + end + + # Include a partial ERB template into a full ERB template. + # Provide the same objects as in init_erb_binding. + # + # @param template_path [String] Name of template file located in backends/portfolio/templates + # @return [String] Result of ERB evaluation of the template file + def include_erb(template_name) + partial("portfolio/templates/#{template_name}", + { + arch: arch, + design: self, + pf_design: self, + pf_class: self.portfolio_class + } + ) + end end diff --git a/lib/template_helpers.rb b/lib/template_helpers.rb index f42ef9603..2ca82658b 100644 --- a/lib/template_helpers.rb +++ b/lib/template_helpers.rb @@ -1,12 +1,10 @@ # frozen_string_literal: true - -# At this point, we insert a placeholder since it will be up -# to the backend to create a specific link. +# +# Collection of "helper" functions that can be called from ERB templates. require "erb" require "pathname" -# collection of functions that can be used inside ERB templates module TemplateHelpers # Insert a hyperlink to an extension. # @param name [#to_s] Name of the extension @@ -72,13 +70,18 @@ def anchor_for_csr_field(csr_name, field_name) "[[csr_field-#{csr_name.gsub(".", "_")}-#{field_name.gsub(".", "_")}-def]]" end - def partial(template_path, locals = {}) - template_path = Pathname.new($root / "backends" / "common_templates" / template_path) + # Include a partial ERB template into a full ERB template. + # + # @param template_pname [String] Path to template file relative to backends directory + # @param inputs [Hash] Input objects to pass into template + # @return [String] Result of ERB evaluation of the template file + def partial(template_pname, inputs = {}) + template_path = Pathname.new($root / "backends" / template_pname) raise ArgumentError, "Template '#{template_path} not found" unless template_path.exist? erb = ERB.new(template_path.read, trim_mode: "-") erb.filename = template_path.realpath.to_s - erb.result(OpenStruct.new(locals).instance_eval { binding }) + erb.result(OpenStruct.new(inputs).instance_eval { binding }) end end From b1652bece59bb00ad18875e76c41427307bde546 Mon Sep 17 00:00:00 2001 From: James Ball Date: Sun, 12 Jan 2025 01:14:17 +0000 Subject: [PATCH 25/61] Fix updated path to common templates --- Rakefile | 12 +- backends/manual/tasks.rake | 4 +- .../portfolio/templates/csr_appendix.adoc.erb | 169 ++++ .../portfolio/templates/ext_appendix.adoc.erb | 767 ------------------ .../templates/inst_appendix.adoc.erb | 126 +++ 5 files changed, 307 insertions(+), 771 deletions(-) create mode 100644 backends/portfolio/templates/csr_appendix.adoc.erb create mode 100644 backends/portfolio/templates/inst_appendix.adoc.erb diff --git a/Rakefile b/Rakefile index 474dbdc87..2cd940029 100644 --- a/Rakefile +++ b/Rakefile @@ -17,7 +17,6 @@ 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 @@ -295,6 +294,7 @@ namespace :test do These are basic but fast-running tests to check the database and tools DESC task :smoke do + puts "UPDATE: Starting test:smoke" puts "UPDATE: Running test:idl_compiler" Rake::Task["test:idl_compiler"].invoke puts "UPDATE: Running test:lib" @@ -303,6 +303,7 @@ namespace :test do Rake::Task["test:schema"].invoke puts "UPDATE: Running test:idl" Rake::Task["test:idl"].invoke + puts "UPDATE: Done test:smoke" end desc <<~DESC @@ -311,22 +312,29 @@ namespace :test do These tests must pass before a commit will be allowed in the main branch on GitHub DESC task :regress do + puts "UPDATE: Starting test:regress" Rake::Task["test:smoke"].invoke + puts "UPDATE: Running gen:html_manual MANUAL_NAME=isa VERSIONS=all" ENV["MANUAL_NAME"] = "isa" ENV["VERSIONS"] = "all" Rake::Task["gen:html_manual"].invoke + puts "UPDATE: Running gen:ext_pdf" ENV["EXT"] = "B" ENV["VERSION"] = "latest" Rake::Task["gen:ext_pdf"].invoke + puts "UPDATE: Running gen:html for generic_rv64" Rake::Task["gen:html"].invoke("generic_rv64") + puts "UPDATE: Generating MockProcCertModel.pdf" Rake::Task["#{$root}/gen/crd/pdf/MockProcCertModel.pdf"].invoke + + puts "UPDATE: Generating MockProfileRelease.pdf" Rake::Task["#{$root}/gen/profile/pdf/MockProfileRelease.pdf"].invoke - puts + puts "UPDATE: Done test:regress" puts "Regression test PASSED" end diff --git a/backends/manual/tasks.rake b/backends/manual/tasks.rake index 27b3e285e..a3363080c 100644 --- a/backends/manual/tasks.rake +++ b/backends/manual/tasks.rake @@ -206,7 +206,7 @@ end rule %r{#{MANUAL_GEN_DIR}/.*/.*/antora/modules/csrs/pages/.*\.adoc} => [ __FILE__, "gen:arch", - ($root / "backends" / "common_templates" / "adoc" / "csr.adoc.erb").to_s + ($root / "backends" / "templates" / "csr.adoc.erb").to_s ] do |t| csr_name = File.basename(t.name, ".adoc") @@ -214,7 +214,7 @@ rule %r{#{MANUAL_GEN_DIR}/.*/.*/antora/modules/csrs/pages/.*\.adoc} => [ csr = design.arch.csr(csr_name) raise "Can't find csr '#{csr_name}'" if csr.nil? - csr_template_path = $root / "backends" / "common_templates" / "adoc" / "csr.adoc.erb" + csr_template_path = $root / "backends" / "templates" / "csr.adoc.erb" erb = ERB.new(csr_template_path.read, trim_mode: "-") erb.filename = csr_template_path.to_s diff --git a/backends/portfolio/templates/csr_appendix.adoc.erb b/backends/portfolio/templates/csr_appendix.adoc.erb new file mode 100644 index 000000000..cc157196c --- /dev/null +++ b/backends/portfolio/templates/csr_appendix.adoc.erb @@ -0,0 +1,169 @@ +<<< +[appendix] +== CSR Details + +<% design.in_scope_csrs.sort_by(&:name).each do |csr| -%> +<<< +[[csr-<%= csr.name %>-def]] +=== <%= csr.name %> + +*<%= csr.long_name %>* + +<% unless csr.base.nil? -%> +[NOTE] +-- +`<%= csr.name %>` is only defined in RV<%= csr.base %>. +-- +<% end -%> + +<%= csr.description %> + +==== Attributes +[%autowidth] +|=== +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| Defining extension a| <%= csr.defined_by_condition.to_asciidoc %> +<% if csr.dynamic_length?(design) -%> +h| Length | <%= csr.length_pretty(design) %> +<% else -%> +h| Length | <%= csr.length_pretty(design) %> +<% end -%> +h| Privilege Mode | <%= csr.priv_mode %> +|=== + + +==== Format +<% 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(design, csr.base.nil? ? 32 : csr.base, optional_type: 2) %> +.... +<% else -%> +<%# CSR has a dynamic length, or a field has a dynamic location, + so there is more than one format to display -%> +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(design, 32, optional_type: 2) %> +.... + +.<%= csr.name %> Format when <%= csr.length_cond64 %> +[wavedrom, ,svg,subs='attributes',width="100%"] +.... +<%= JSON.dump csr.wavedrom_desc(design, 64, optional_type: 2) %> +.... + + +<% end # unless dynamic length -%> + +==== Field Summary + +// use @ as a separator since IDL code can contain | +[%autowidth,separator=@,float="center",align="center",cols="^,<,<,<",options="header",role="stretch"] +|=== +@ Name @ Location @ Type @ Reset Value + +<%- csr.implemented_fields(design).each do |field| -%> +@ xref:<%=csr.name%>-<%=field.name%>-def[`<%= field.name %>`] +a@ +<%- if field.dynamic_location?(design) -%> + +[when,"<%= field.location_cond32 %>"] +-- +<%= field.location_pretty(design, 32) %> +-- + +[when,"<%= field.location_cond64 %>"] +-- +<%= field.location_pretty(design, 64) %> +-- + +<%- else -%> +<%= field.location_pretty(design) %> +<%- end -%> +a@ + +-- +<%= field.type_pretty(design.symtab) %> +-- + +a@ + +-- +<%= field.reset_value_pretty(design) %> +-- + +<%- end -%> +|=== + +==== Fields + +<%- 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(design).each do |field| -%> +[[<%=csr.name%>-<%=field.name%>-def]] +===== `<%= field.name %>` + +<%- if !field.defined_in_all_bases? -%> +IMPORTANT: <%= field.name %> is only defined in <%= field.base32_only? ? "RV32" : "RV64" %> (`<%= field.base32_only? ? field.location_cond32 : field.location_cond64 %>`) +<%- end -%> + +**** +Location:: +<%= field.location_pretty(design) %> + +Description:: +<%= field.description.gsub("\n", " +\n") %> + +Type:: +<%= field.type_pretty(design.symtab) %> + +Reset value:: +<%= field.reset_value_pretty(design) %> + +**** + +<%- end -%> +<%- end -%> + +<%- 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. + +When a software write occurs (_e.g._, through `csrrw`), the following determines the +written value: + +[idl] +---- +<%- csr.implemented_fields(design).each do |field| -%> +<%- if field.has_custom_sw_write? -%> +<%= field.name %> = <%= field["sw_write(csr_value)"] %> +<%- else -%> +<%= field.name %> = csr_value.<%= field.name %> +<%- end -%> +<%- end -%> +---- +<%- end -%> + +<%- if csr.has_custom_sw_read? -%> +==== Software read + +This CSR may return a value that is different from what is stored in hardware. + +[source,idl,subs="specialchars,macros"] +---- +<%= csr.sw_read_ast(design.symtab).gen_adoc %> +---- +<%- end -%> + +<% end # do in_scope_csrs -%> diff --git a/backends/portfolio/templates/ext_appendix.adoc.erb b/backends/portfolio/templates/ext_appendix.adoc.erb index 1a5e39ff1..3df4f016e 100644 --- a/backends/portfolio/templates/ext_appendix.adoc.erb +++ b/backends/portfolio/templates/ext_appendix.adoc.erb @@ -1,472 +1,3 @@ -// Number heading sections (e.g., 1.0, 1.1, etc.) -:sectnums: - -// Add a table of contents for HTML (and VSCode adoc preview) -:toc: left - -// Include headings up to 3 levels deep (don't know why 5 gives you this). -:toclevels: 5 - -// -// Stuff to generate nice wavedrom drawings of instruction and CSR fields -// -:wavedrom: <%= $root %>/node_modules/.bin/wavedrom-cli - -// TODO: needs to be changed -:imagesoutdir: images - -= <%= proc_cert_model.name %> Processor Certification Requirements Document - -[Preface] -== Revision History - -History of documentation changes that eventually lead to releases. - -[cols="1,1,5"] -|=== -| Date | Revision | Changes - -<% proc_cert_model.revision_history.each do |rev| -%> -| <%= rev.date %> -| <%= rev.revision %> -a| <% rev.changes.each do |change| %> -* <%= change %> -<% end -%> -<% end -%> -|=== - -[Preface] -== Typographic Conventions - -CSR field colors:: - -* Grey fields are reserved (WPRI) -* Green fields are present -* Red fields are defined by the RISC-V ISA but not present - -CSR field types:: - -[%autowidth] -|=== -| Abbreviation | Description - -<% CsrField::TYPE_DESC_MAP.each do |abbreviation, description| -%> -| <%= abbreviation %> -| <%= description %> -<% end -%> -|=== - -== Introduction - -<%= proc_cert_model.introduction %> - -<%= proc_cert_class.introduction %> - -=== What's a CRD? - -Certification Requirements Documents (CRDs) list requirements an implementation must meet -to obtain an associated RVI (RISC-V International) certificate. -CRDs are developed by the RVI CSC (Certification Steering Committee) organization in collaboration -with the RVI TSC (Technical Steering Committee) organization who creates RISC-V standards. - -The CRDs refer to and augment information provided in existing ratified RVI standards. - -There are a variety of certificates offered by RVI to accomodate the various RVI standards. -There are certificates for processors, non-processor system IP (e.g., IOMMU), -and system platforms (processor + system IP) hardware standards. -There are multiple classes of processor certificates available to accomodate the wide range of -RISC-V implementations from basic microcontrollers to advanced Applications-class processors. - -Each CRD has a list of mandatory behaviors along with a list of optional behaviors. -Note that not all behaviors allowed in RISC-V standards are supported by a particular CRD. - -=== CRD Naming Scheme - -CRDs have the following naming scheme: - - Format: [v] - -Where: - -* Left & right square braces denote optional. -* Less-than & greater-than signs just separate fields (i.e., they aren't present in the CRD name). -* identifies the type of RISC-V standard (processor, non-processor system IP, or platform) along with - any other information required to identify the variant of that standard. -* identifies a particular CRD release -** Format is [.[.]] -** Follows semantic versioning scheme (https://semver.org/) -** The release is updated when certification test changes are made that *could* cause a previously certified - implementation to now fail. - Examples are fixing a test bug, or increasing test coverage, or requiring a new version of a standard - A release of 0 is used for pre-release versions of a CRD and release versions start with 1. -** The release is updated when a CRD increases support for optional behaviors. - Examples are supporting for new optional standards or - 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 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) - -=== CRD Terminology - -.Requirement Types -[%autowidth] -|=== -| Term | Meaning - -| MANDATORY | You have to implement it to get a certificate and the certificate tests will cover it -| OPTIONAL | It's up to you if you implement or not. If you claim to implement it, certificate tests will cover it -| IN-SCOPE | Either MANDATORY or OPTIONAL -| OUT-OF-SCOPE | It's up to you if you implement or not. If you implement it, it won't be certified but make sure you don't mess up anything we are certifying. -| INCOMPATIBLE | If you implement it you won't get a certificate -|=== - -.Glossary -[%autowidth] -|=== -| Term | Meaning - -| CRD | Certification Requirements Document -| N/A | “Not Applicable” -| AKA | “Also Known As” -|=== - -=== Processor CRDs - -There are Processor CRDs for different classes of RISC-V processors. -These documents augment information in the related TSC Profile when available and/or other RVI standards documents -(e.g., Priv and Unpriv ISA manuals). -Only ratified extensions are candidates for certification. -This implies all custom extensions are also OUT-OF-SCOPE. - -==== Processor Certificate Class Naming Scheme - -Processor Certificate Class names have the following format: - - [<-base>] - -Where: - -* is MC for Microcontroller Class and AC for Apps-processor Class -* is 3-digit integer defined as follows: -** The hundreds's digit indicates the series -** The ten's digit identifies large differences in mandatory extensions (e.g., V, H) within the series -** The one's digit indentifies small/medium differences in mandatory extensions (e.g., Zicond, PMP) within the series -* is optional and is 32 for RV32I, 64 for RV64I, and 32E for RV32E -** If a CRD supports multiple bases and is omitted in a reference, it applies to all supported bases -** If a CRD only supports one base then is generally omitted - -[%autowidth] -|=== -| CRD | TSC Profile | Description - -| MC100-series | TBD | 32/64-bit minimal microcontroller that runs low-level software on an RTOS or bare-metal (no virtual memory) -| MC200-series | TBD | 32/64-bit intermediate microcontroller -| MC300-series | TBD | 32/64-bit advanced microcontroller -| AC100-series | RVB23 | 64-bit Apps-processor running Bespoke rich operating systems (e.g., Yocto Linux) -| AC200-series | RVA23 | 64-bit Apps-processor running standard rich operating systems (e.g., commercial Linux distributions, Android) -|=== - -==== CSR Field Terminology - -.Definition of CSR Fields -[%autowidth] -|=== -| Field Type | Read Value After Writing Illegal Value | Read Value Function Of | Illegal Instruction Exception | Priv ISA Manual Quote - -| WLRL | Any deterministic legal or illegal value | Value before write and illegal value written | Optional -| Implementations are permitted but not required to raise an illegal-instruction exception if an instruction attempts to write a non-supported value to a WLRL field. Implementations can return arbitrary bit patterns on the read of a WLRL field when the last write was of an illegal value, but the value returned should deterministically depend on the illegal written value and the value of the field prior to the write. -| WARL | Any deterministic legal value | Any architectural hart state | Prohibited -| Implementations will not raise an exception on writes of unsupported values to a WARL field. Implementations can return any legal value on the read of a WARL field when the last write was of an illegal value, but the legal value returned should deterministically depend on the illegal written value and the architectural state of the hart. -| WPRI | 0 | Nothing | Not specified -| Some whole read/write fields are reserved for future use. Implementations that do not furnish these fields must make them read-only zero. -|=== - -*WARL (Write Anything, Read Legal)*: - -The Priv ISA requires reads of WARL fields to return some implementation-dependent deterministic legal value -after the field is written with an illegal value. -Certifying such behaviors is expensive and provides low value for a certificate since software can't rely -on a particular behavior from one implementation to another. - -Processor CRDs define writes to WARL fields of illegal values to be OUT-OF-SCOPE unless otherwise stated -(i.e., certification tests will only ever write legal values to WARL fields except for the special cases listed below). -When not OUT-OF-SCOPE, the required behavior is defined as this might be more constrained in implementations than -in the standard. - -The following special cases for WARL are supported when explicitly listed in the corresponding CRD CSR field requirements: - -1. Probing for Field Width - -* Some WARL fields are variable length such as the ASID field in the virtual memory extension. -* Here's the algorithm recommended to discover the ASID width: -** The number of implemented ASID bits, termed ASIDLEN, may be determined by writing one to every bit position in - the ASID field, then reading back the value in the satp CSR to see which bit positions in the ASID field hold a one. -* The RVCP-provided certification materials (certification tests, certification reference models) can map writes of - illegal values to the ASID field to the corresponding read value as long as they are provided the ASIDLEN value - for an implementation. - -2. Probing for Options - -* E.g., Writable misa bits - -3. Allowed values are a function of extension presence and/or their parameters - -* E.g., satp.mode legal write values - -*WLRL (Write Legal, Read Legal)*: - -The Priv ISA requires reads of WLRL fields to return some implementation-dependent deterministic arbitrary value -after the field is written with an illegal value. -Certifying such behaviors is expensive and provides low value for a certificate since software can't rely -on a particular behavior. -Processor CRDs define writes to WLRL fields of illegal values to be OUT-OF-SCOPE unless otherwise stated -(i.e., certification tests will only ever write legal values to WLRL fields). - -*WPRI (Write Preserve, Read Ignore)*: - -The Priv ISA requires reads of WPRI fields to return a value of 0. -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 intended to be comprehensive -verification test suites anyways. - -=== Related Specifications - -[cols="2,2,3,3,3"] -|=== -| Certificate Model | TSC Profile | Unpriv ISA Manual | Priv ISA Manual | Debug Manual - -| <%= 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 - -|=== -| M | S | U | VS | VU - -| <% 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 -%> - -|=== - -<<< -== Extensions - -Any RISC-V extensions not listed in this section are OUT-OF-SCOPE. -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 = proc_cert_model.in_scope_ext_reqs(presence_obj) -%> -<% if ext_reqs.empty? -%> -None -<% else -%> -[%autowidth] -|=== -| Requirement ID | Extension | Version | Long Name | Note - -<% 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(", ") %> -| <%= ext.nil? ? "" : ext.long_name %> -| <%= ext_req.note.nil? ? "" : ext_req.note %> -<% end # each ext_req -%> -|=== -<% end # if empty ext_reqs -%> - -<% 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 proc_cert_model.recommendations.empty? -%> -=== Recommendations - -Recommendations are not strictly mandated but are included to guide implementers. - -<% proc_cert_model.recommendations.each do |recommendation| -%> -<%= recommendation.text %> -<% end # each recommendation -%> -<% end # unless recommendations empty -%> - -<<< -== Implementation-dependencies - -RISC-V standards support many implementation-defined parameters. In many cases, there -are no names associated with these parameters. Names are defined in this section when -not provided in the associated standard. - -=== IN-SCOPE Parameters - -These implementation-dependent options defined by MANDATORY or OPTIONAL extensions are IN-SCOPE. -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 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 - -<% proc_cert_model.all_in_scope_ext_params.each do |in_scope_ext_param| -%> -<% param = in_scope_ext_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 %> -| <% exts.each do |ext| -%><-param-<%= param.name %>-def,<%= ext.name %>>> <% end # do ext -%> -a| <%= in_scope_ext_param.note %> -<% end # do -%> -|=== -<% end # if table -%> - -=== OUT-OF-SCOPE Parameters - -These implementation-dependent options defined by MANDATORY or OPTIONAL extensions are OUT-OF-SCOPE. -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 proc_cert_model.all_out_of_scope_params.empty? -%> -None -<% else -%> -[%autowidth] -|=== -| Parameters | Type | Extension(s) - -<% 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 -%> - -<% end # do -%> -|=== -<% end # if table -%> - -== Traps - -RISC-V supports both synchronous exceptions and asynchronous interrupts. -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 - -=== Synchronous Exceptions - -|=== -| `xcause.CODE` CSR Field Value | Name -<% design.in_scope_exception_codes.sort_by{ |code| code.num }.each do |code| -%> -| <%= code.num %> | <%= code.name %> -<% end -%> -|=== - -=== Asynchronous Interrupts - -|=== -| `xcause.CODE` CSR Field Value | Name -<% design.in_scope_interrupt_codes.sort_by{ |code| code.num }.each do |code| -%> -| <%= code.num %> | <%= code.name %> -<% end -%> -|=== - -== Instruction Summary - -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 - -[%autowidth] -|=== -| Name | Long Name - -<% design.in_scope_instructions.each do |inst| -%> -| <%= link_to_inst(inst.name) %> -| <%= inst.long_name %> -<% end # do -%> -|=== - -== CSR Summary - -=== By Name - -[%autowidth] -|=== -| Name | Long Name | Address | Mode | Primary Extension - -<% design.in_scope_csrs.sort_by!(&:name).each do |csr| -%> -| <-def,<%= csr.name %>>> -| <%= csr.long_name %> -| <%= "0x#{csr.address.to_s(16)}" %> -| <%= csr.priv_mode %> -| <%= csr.primary_defined_by %> -<% end # do -%> -|=== - -=== By Address - -[%autowidth] -|=== -| Address | Mode | Name | Long Name | Primary Extension - -<% design.in_scope_csrs.sort_by!(&:address).each do |csr| -%> -| <%= "0x#{csr.address.to_s(16)}" %> -| <%= csr.priv_mode %> -| <-def,<%= csr.name %>>> -| <%= csr.long_name %> -| <%= csr.primary_defined_by %> -<% end # do -%> -|=== - -<% 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. - -<% proc_cert_model.requirement_groups.each do |group| -%> -=== <%= group.name %> - -<%= group.description %> - -<% unless group.when.nil? -%> -[IMPORTANT] -<%= group.name %> requirements only apply when <%= group.when_pretty %>. -<% end -%> - -[%autowidth] -|=== -| Req Number | Description - -<% group.requirements.each do |req| -%> -| <%= req.name %> -a| <%= req.description %> -<% unless req.when.nil? -%> -[IMPORTANT] -Requirement <%= req.name %> only apply when <%= req.when_pretty %>. -<% end -%> -<% end -%> -|=== - -<% end -%> -<% end # unless requirement_groups.empty? -%> - <<< [appendix] == Extension Details @@ -564,301 +95,3 @@ The following instructions are added by this extension: <% end # do param -%> <% end # unless table -%> <% end # do ext_req -%> - -<<< -[appendix] -== Instruction Details - -<% design.in_scope_instructions.each do |inst| -%> -<<< -<%= anchor_for_inst(inst.name) %> -=== <%= inst.name %> - -*<%= inst.long_name %>* - -This instruction is defined by: - -<%= inst.defined_by_condition.to_asciidoc %> - -==== Encoding - -<% if inst.multi_encoding? -%> -[NOTE] -This instruction has different encodings in RV32 and RV64. - -==== -RV32:: -+ -[wavedrom, ,svg,subs='attributes',width="100%"] -.... -<%= JSON.dump inst.wavedrom_desc(32) %> -.... - -RV64:: -+ -[wavedrom, ,svg,subs='attributes',width="100%"] -.... -<%= JSON.dump inst.wavedrom_desc(64) %> -.... -==== -<% else -%> -[wavedrom, ,svg,subs='attributes',width="100%"] -.... -<%= JSON.dump inst.wavedrom_desc(inst.base.nil? ? 32 : inst.base) %> -.... -<% end -%> - -==== Synopsis - -<%= inst.description %> - -==== Access -<% if design.in_scope_extensions.any? { |e| e.name == "H" } -%> -[cols="^,^,^,^,^"] -<% else -%> -[cols="^,^,^"] -<% 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 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 %> -|=== - -<% if inst.access_detail? -%> -<%= inst.access_detail %> -<% end -%> - -==== Decode Variables - -<% if inst.multi_encoding? -%> -==== -RV32:: -+ -[source.idl] ----- -<% inst.decode_variables(32).each do |d| -%> -<%= d.sext? ? 'signed ' : '' %>Bits<<%= d.size %>> <%= d.name %> = <%= d.extract %>; -<% end -%> ----- - -RV64:: -+ -[source,idl] ----- -<% inst.decode_variables(64).each do |d| -%> -<%= d.sext? ? 'signed ' : '' %>Bits<<%= d.size %>> <%= d.name %> = <%= d.extract %>; -<% end -%> ----- -==== -<% else -%> -[source,idl] ----- -<% inst.decode_variables(inst.base.nil? ? 32 : inst.base).each do |d| -%> -<%= d.sext? ? 'signed ' : '' %>Bits<<%= d.size %>> <%= d.name %> = <%= d.extract %>; -<% end -%> ----- -<% end -%> - -==== Execution - -<% xlens = inst.base.nil? ? [32, 64] : [inst.base] -%> - -<% if inst.key?("operation()") -%> -[source,idl,subs="specialchars,macros"] ----- -<%= inst.operation_ast(design.symtab).gen_adoc %> ----- -<% end -%> - -==== Exceptions - -<%- exception_list = inst.reachable_exceptions_str(design.symtab) -%> -<%- if exception_list.empty? -%> -This instruction does not generate synchronous exceptions. -<%- else -%> -This instruction may result in the following synchronous exceptions: - - <%- exception_list.sort.each do |etype| -%> - * <%= etype %> - <%- end -%> - -<%- end -%> - - -<% end -%> - -<<< -[appendix] -== CSR Details - -<% design.in_scope_csrs.sort_by(&:name).each do |csr| -%> -<<< -[[csr-<%= csr.name %>-def]] -=== <%= csr.name %> - -*<%= csr.long_name %>* - -<% unless csr.base.nil? -%> -[NOTE] --- -`<%= csr.name %>` is only defined in RV<%= csr.base %>. --- -<% end -%> - -<%= csr.description %> - -==== Attributes -[%autowidth] -|=== -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| Defining extension a| <%= csr.defined_by_condition.to_asciidoc %> -<% if csr.dynamic_length?(design) -%> -h| Length | <%= csr.length_pretty(design) %> -<% else -%> -h| Length | <%= csr.length_pretty(design) %> -<% end -%> -h| Privilege Mode | <%= csr.priv_mode %> -|=== - - -==== Format -<% 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(design, csr.base.nil? ? 32 : csr.base, optional_type: 2) %> -.... -<% else -%> -<%# CSR has a dynamic length, or a field has a dynamic location, - so there is more than one format to display -%> -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(design, 32, optional_type: 2) %> -.... - -.<%= csr.name %> Format when <%= csr.length_cond64 %> -[wavedrom, ,svg,subs='attributes',width="100%"] -.... -<%= JSON.dump csr.wavedrom_desc(design, 64, optional_type: 2) %> -.... - - -<% end # unless dynamic length -%> - -==== Field Summary - -// use @ as a separator since IDL code can contain | -[%autowidth,separator=@,float="center",align="center",cols="^,<,<,<",options="header",role="stretch"] -|=== -@ Name @ Location @ Type @ Reset Value - -<%- csr.implemented_fields(design).each do |field| -%> -@ xref:<%=csr.name%>-<%=field.name%>-def[`<%= field.name %>`] -a@ -<%- if field.dynamic_location?(design) -%> - -[when,"<%= field.location_cond32 %>"] --- -<%= field.location_pretty(design, 32) %> --- - -[when,"<%= field.location_cond64 %>"] --- -<%= field.location_pretty(design, 64) %> --- - -<%- else -%> -<%= field.location_pretty(design) %> -<%- end -%> -a@ - --- -<%= field.type_pretty(design.symtab) %> --- - -a@ - --- -<%= field.reset_value_pretty(design) %> --- - -<%- end -%> -|=== - -==== Fields - -<%- 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(design).each do |field| -%> -[[<%=csr.name%>-<%=field.name%>-def]] -===== `<%= field.name %>` - -<%- if !field.defined_in_all_bases? -%> -IMPORTANT: <%= field.name %> is only defined in <%= field.base32_only? ? "RV32" : "RV64" %> (`<%= field.base32_only? ? field.location_cond32 : field.location_cond64 %>`) -<%- end -%> - -**** -Location:: -<%= field.location_pretty(design) %> - -Description:: -<%= field.description.gsub("\n", " +\n") %> - -Type:: -<%= field.type_pretty(design.symtab) %> - -Reset value:: -<%= field.reset_value_pretty(design) %> - -**** - -<%- end -%> -<%- end -%> - -<%- 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. - -When a software write occurs (_e.g._, through `csrrw`), the following determines the -written value: - -[idl] ----- -<%- csr.implemented_fields(design).each do |field| -%> -<%- if field.has_custom_sw_write? -%> -<%= field.name %> = <%= field["sw_write(csr_value)"] %> -<%- else -%> -<%= field.name %> = csr_value.<%= field.name %> -<%- end -%> -<%- end -%> ----- -<%- end -%> - -<%- if csr.has_custom_sw_read? -%> -==== Software read - -This CSR may return a value that is different from what is stored in hardware. - -[source,idl,subs="specialchars,macros"] ----- -<%= csr.sw_read_ast(design.symtab).gen_adoc %> ----- -<%- end -%> - -<% end # do in_scope_csrs -%> diff --git a/backends/portfolio/templates/inst_appendix.adoc.erb b/backends/portfolio/templates/inst_appendix.adoc.erb new file mode 100644 index 000000000..aaa755aa3 --- /dev/null +++ b/backends/portfolio/templates/inst_appendix.adoc.erb @@ -0,0 +1,126 @@ +[appendix] +== Instruction Details + +<% design.in_scope_instructions.each do |inst| -%> +<<< +<%= anchor_for_inst(inst.name) %> +=== <%= inst.name %> + +*<%= inst.long_name %>* + +This instruction is defined by: + +<%= inst.defined_by_condition.to_asciidoc %> + +==== Encoding + +<% if inst.multi_encoding? -%> +[NOTE] +This instruction has different encodings in RV32 and RV64. + +==== +RV32:: ++ +[wavedrom, ,svg,subs='attributes',width="100%"] +.... +<%= JSON.dump inst.wavedrom_desc(32) %> +.... + +RV64:: ++ +[wavedrom, ,svg,subs='attributes',width="100%"] +.... +<%= JSON.dump inst.wavedrom_desc(64) %> +.... +==== +<% else -%> +[wavedrom, ,svg,subs='attributes',width="100%"] +.... +<%= JSON.dump inst.wavedrom_desc(inst.base.nil? ? 32 : inst.base) %> +.... +<% end -%> + +==== Synopsis + +<%= inst.description %> + +==== Access +<% if design.in_scope_extensions.any? { |e| e.name == "H" } -%> +[cols="^,^,^,^,^"] +<% else -%> +[cols="^,^,^"] +<% 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 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 %> +|=== + +<% if inst.access_detail? -%> +<%= inst.access_detail %> +<% end -%> + +==== Decode Variables + +<% if inst.multi_encoding? -%> +==== +RV32:: ++ +[source.idl] +---- +<% inst.decode_variables(32).each do |d| -%> +<%= d.sext? ? 'signed ' : '' %>Bits<<%= d.size %>> <%= d.name %> = <%= d.extract %>; +<% end -%> +---- + +RV64:: ++ +[source,idl] +---- +<% inst.decode_variables(64).each do |d| -%> +<%= d.sext? ? 'signed ' : '' %>Bits<<%= d.size %>> <%= d.name %> = <%= d.extract %>; +<% end -%> +---- +==== +<% else -%> +[source,idl] +---- +<% inst.decode_variables(inst.base.nil? ? 32 : inst.base).each do |d| -%> +<%= d.sext? ? 'signed ' : '' %>Bits<<%= d.size %>> <%= d.name %> = <%= d.extract %>; +<% end -%> +---- +<% end -%> + +==== Execution + +<% xlens = inst.base.nil? ? [32, 64] : [inst.base] -%> + +<% if inst.key?("operation()") -%> +[source,idl,subs="specialchars,macros"] +---- +<%= inst.operation_ast(design.symtab).gen_adoc %> +---- +<% end -%> + +==== Exceptions + +<%- exception_list = inst.reachable_exceptions_str(design.symtab) -%> +<%- if exception_list.empty? -%> +This instruction does not generate synchronous exceptions. +<%- else -%> +This instruction may result in the following synchronous exceptions: + + <%- exception_list.sort.each do |etype| -%> + * <%= etype %> + <%- end -%> + +<%- end -%> + + +<% end -%> From b9aa16df8e6b9d05a6c9293bbdee01e12fe96515 Mon Sep 17 00:00:00 2001 From: James Ball Date: Sun, 12 Jan 2025 19:34:13 +0000 Subject: [PATCH 26/61] Finished --- arch/ext/Sha.yaml | 19 +- arch/ext/Shcounterenw.yaml | 21 + arch/ext/Shvsatpa.yaml | 16 + arch/profile/MP-S-64.yaml | 2 +- arch/profile/MP-U-64.yaml | 2 +- arch/profile/RVA20S64.yaml | 2 +- arch/profile/RVA20U64.yaml | 2 +- arch/profile/RVA22S64.yaml | 2 +- arch/profile/RVA22U64.yaml | 2 +- arch/profile/RVA23S64.yaml | 2 +- arch/profile/RVA23U64.yaml | 2 +- arch/profile/RVB23S64.yaml | 2 +- arch/profile/RVB23U64.yaml | 2 +- arch/profile/RVI20U32.yaml | 2 +- arch/profile/RVI20U64.yaml | 2 +- arch/profile_class/MockProfileClass.yaml | 2 +- arch/profile_class/RVA.yaml | 2 +- arch/profile_class/RVB.yaml | 2 +- arch/profile_class/RVI.yaml | 2 +- arch/profile_release/MockProfileRelease.yaml | 2 +- arch/profile_release/RVA20.yaml | 2 +- arch/profile_release/RVA22.yaml | 2 +- arch/profile_release/RVA23.yaml | 2 +- arch/profile_release/RVB23.yaml | 2 +- arch/profile_release/RVI20.yaml | 2 +- backends/crd/tasks.rake | 3 + backends/crd/templates/crd.adoc.erb | 400 +----------------- .../portfolio/templates/csr_appendix.adoc.erb | 2 +- .../portfolio/templates/ext_appendix.adoc.erb | 53 ++- .../templates/inst_appendix.adoc.erb | 12 +- backends/profile/tasks.rake | 3 + backends/profile/templates/profile.adoc.erb | 287 +------------ lib/arch_obj_models/certificate.rb | 158 +++++++ lib/arch_obj_models/portfolio.rb | 256 +++++------ lib/architecture.rb | 10 +- lib/design.rb | 16 +- lib/portfolio_design.rb | 43 +- schemas/profile_class_schema.json | 2 +- schemas/profile_release_schema.json | 2 +- schemas/profile_schema.json | 2 +- 40 files changed, 436 insertions(+), 913 deletions(-) create mode 100644 arch/ext/Shcounterenw.yaml create mode 100644 arch/ext/Shvsatpa.yaml diff --git a/arch/ext/Sha.yaml b/arch/ext/Sha.yaml index e0dc46d05..91a1d6d08 100644 --- a/arch/ext/Sha.yaml +++ b/arch/ext/Sha.yaml @@ -44,13 +44,12 @@ versions: - version: "1.0.0" state: ratified ratification_date: null - requires: - allOf: - - H - - Ssstateen - - Shcounterenw - - Shvstvala - - Shtvala - - Shvstvecd - - Shvsatpa - - Shgatpa + implies: + - [H, "1.0.0"] + - [Ssstateen, "1.0.0"] + - [Shcounterenw, "1.0.0"] + - [Shvstvala, "1.0.0"] + - [Shtvala, "1.0.0"] + - [Shvstvecd, "1.0.0"] + - [Shvsatpa, "1.0.0"] + - [Shgatpa, "1.0.0"] diff --git a/arch/ext/Shcounterenw.yaml b/arch/ext/Shcounterenw.yaml new file mode 100644 index 000000000..208ab6a3a --- /dev/null +++ b/arch/ext/Shcounterenw.yaml @@ -0,0 +1,21 @@ +# yaml-language-server: $schema=../../schemas/ext_schema.json + +$schema: "ext_schema.json#" +kind: extension +name: Shcounterenw +long_name: Hypervisor counter enable +description: | + For any hpmcounter that is not read-only zero, the corresponding bit in `hcounteren` must be writable. + + [NOTE] + This extension was ratified with the RVA22 profiles. +type: privileged +versions: + - version: "1.0.0" + state: ratified + ratification_date: 2023-08 + url: https://drive.google.com/file/d/1KcjgbLM5L1ZKY8934aJl8aQwGlMz6Cbo/view?usp=drive_link + param_constraints: + HCOUNTENABLE_EN: + extra_validation: | + HPM_COUNTER_EN.each_with_index { |hpm_exists, idx| assert(!hpm_exists || HCOUNTENABLE_EN[idx]) } diff --git a/arch/ext/Shvsatpa.yaml b/arch/ext/Shvsatpa.yaml new file mode 100644 index 000000000..97ca45647 --- /dev/null +++ b/arch/ext/Shvsatpa.yaml @@ -0,0 +1,16 @@ +# yaml-language-server: $schema=../../schemas/ext_schema.json + +$schema: "ext_schema.json#" +kind: extension +name: Shvsatpa +long_name: vstap translation mode requirements +description: | + All translation modes supported in the `satp` CSR must be supported in the `vsatp` CSR. + + [NOTE] + This extension was ratified with the RVA22 profiles. +type: privileged +versions: + - version: "1.0.0" + state: ratified + ratification_date: null diff --git a/arch/profile/MP-S-64.yaml b/arch/profile/MP-S-64.yaml index de2718e08..34cec3064 100644 --- a/arch/profile/MP-S-64.yaml +++ b/arch/profile/MP-S-64.yaml @@ -1,7 +1,7 @@ # yaml-language-server: $schema=../../schemas/profile_schema.json $schema: profile_schema.json# -kind: profile +kind: Profile name: MP-S-64 marketing_name: MockProfile 64-bit S-mode description: This is the Mock Profile Supervisor Mode description. diff --git a/arch/profile/MP-U-64.yaml b/arch/profile/MP-U-64.yaml index 3273c323b..a69a6ccbe 100644 --- a/arch/profile/MP-U-64.yaml +++ b/arch/profile/MP-U-64.yaml @@ -1,5 +1,5 @@ $schema: profile_schema.json# -kind: profile +kind: Profile name: MP-U-64 marketing_name: MockProfile 64-bit Unpriv mode: Unpriv diff --git a/arch/profile/RVA20S64.yaml b/arch/profile/RVA20S64.yaml index 939b820f0..5f8e59c87 100644 --- a/arch/profile/RVA20S64.yaml +++ b/arch/profile/RVA20S64.yaml @@ -1,5 +1,5 @@ $schema: profile_schema.json# -kind: profile +kind: Profile name: RVA20S64 marketing_name: RVA20S64 mode: S diff --git a/arch/profile/RVA20U64.yaml b/arch/profile/RVA20U64.yaml index 47d1cfa4b..66088a8aa 100644 --- a/arch/profile/RVA20U64.yaml +++ b/arch/profile/RVA20U64.yaml @@ -1,5 +1,5 @@ $schema: profile_schema.json# -kind: profile +kind: Profile name: RVA20U64 marketing_name: RVA20U64 mode: Unpriv diff --git a/arch/profile/RVA22S64.yaml b/arch/profile/RVA22S64.yaml index e2de79e2a..7f11ee519 100644 --- a/arch/profile/RVA22S64.yaml +++ b/arch/profile/RVA22S64.yaml @@ -1,5 +1,5 @@ $schema: profile_schema.json# -kind: profile +kind: Profile name: RVA22S64 marketing_name: RVA22S64 mode: S diff --git a/arch/profile/RVA22U64.yaml b/arch/profile/RVA22U64.yaml index 7e3b1c0f0..b30dae49b 100644 --- a/arch/profile/RVA22U64.yaml +++ b/arch/profile/RVA22U64.yaml @@ -1,5 +1,5 @@ $schema: profile_schema.json# -kind: profile +kind: Profile name: RVA22U64 marketing_name: RVA22U64 mode: Unpriv diff --git a/arch/profile/RVA23S64.yaml b/arch/profile/RVA23S64.yaml index 2484cafa9..cf0b50f0c 100644 --- a/arch/profile/RVA23S64.yaml +++ b/arch/profile/RVA23S64.yaml @@ -1,5 +1,5 @@ $schema: profile_schema.json# -kind: profile +kind: Profile name: RVA23S64 marketing_name: RVA23S64 mode: S diff --git a/arch/profile/RVA23U64.yaml b/arch/profile/RVA23U64.yaml index 00c9206bc..09d086259 100644 --- a/arch/profile/RVA23U64.yaml +++ b/arch/profile/RVA23U64.yaml @@ -1,5 +1,5 @@ $schema: profile_schema.json# -kind: profile +kind: Profile name: RVA23U64 marketing_name: RVA23U64 mode: Unpriv diff --git a/arch/profile/RVB23S64.yaml b/arch/profile/RVB23S64.yaml index 404096b7a..1a898dce1 100644 --- a/arch/profile/RVB23S64.yaml +++ b/arch/profile/RVB23S64.yaml @@ -1,5 +1,5 @@ $schema: profile_schema.json# -kind: profile +kind: Profile name: RVB23S64 marketing_name: RVB23S64 mode: S diff --git a/arch/profile/RVB23U64.yaml b/arch/profile/RVB23U64.yaml index b133ce78a..f9bae8faa 100644 --- a/arch/profile/RVB23U64.yaml +++ b/arch/profile/RVB23U64.yaml @@ -1,5 +1,5 @@ $schema: profile_schema.json# -kind: profile +kind: Profile name: RVB23U64 marketing_name: RVB23U64 mode: Unpriv diff --git a/arch/profile/RVI20U32.yaml b/arch/profile/RVI20U32.yaml index 4a1e17343..9c9b25e23 100644 --- a/arch/profile/RVI20U32.yaml +++ b/arch/profile/RVI20U32.yaml @@ -1,5 +1,5 @@ $schema: profile_schema.json# -kind: profile +kind: Profile name: RVI20U32 marketing_name: RVI20U32 mode: Unpriv diff --git a/arch/profile/RVI20U64.yaml b/arch/profile/RVI20U64.yaml index aa84d2e41..6d5adb983 100644 --- a/arch/profile/RVI20U64.yaml +++ b/arch/profile/RVI20U64.yaml @@ -1,5 +1,5 @@ $schema: profile_schema.json# -kind: profile +kind: Profile name: RVI20U64 $inherits: "profile/RVI20U32.yaml#" base: 64 diff --git a/arch/profile_class/MockProfileClass.yaml b/arch/profile_class/MockProfileClass.yaml index 36da53ffa..72dda72d6 100644 --- a/arch/profile_class/MockProfileClass.yaml +++ b/arch/profile_class/MockProfileClass.yaml @@ -1,5 +1,5 @@ $schema: profile_class_schema.json# -kind: profile class +kind: Profile Class processor_kind: Microcontroller name: MockProfileClass marketing_name: Mock Profile Class diff --git a/arch/profile_class/RVA.yaml b/arch/profile_class/RVA.yaml index f90ddfe13..ccff3ecaa 100644 --- a/arch/profile_class/RVA.yaml +++ b/arch/profile_class/RVA.yaml @@ -1,7 +1,7 @@ # yaml-language-server: $schema=../../schemas/profile_class_schema.json $schema: profile_class_schema.json# -kind: profile class +kind: Profile Class processor_kind: Apps Processor name: RVA marketing_name: RVA diff --git a/arch/profile_class/RVB.yaml b/arch/profile_class/RVB.yaml index 3aa2786fd..223c6e0e9 100644 --- a/arch/profile_class/RVB.yaml +++ b/arch/profile_class/RVB.yaml @@ -1,5 +1,5 @@ $schema: profile_class_schema.json# -kind: profile class +kind: Profile Class processor_kind: Apps Processor name: RVB marketing_name: RVB diff --git a/arch/profile_class/RVI.yaml b/arch/profile_class/RVI.yaml index 7bcf2aad3..d90b4c866 100644 --- a/arch/profile_class/RVI.yaml +++ b/arch/profile_class/RVI.yaml @@ -1,5 +1,5 @@ $schema: profile_class_schema.json# -kind: profile class +kind: Profile Class processor_kind: Generic Unprivileged name: RVI marketing_name: RVI diff --git a/arch/profile_release/MockProfileRelease.yaml b/arch/profile_release/MockProfileRelease.yaml index e3b56fbf6..5bd9b8848 100644 --- a/arch/profile_release/MockProfileRelease.yaml +++ b/arch/profile_release/MockProfileRelease.yaml @@ -1,5 +1,5 @@ $schema: profile_release_schema.json# -kind: profile release +kind: Profile Release name: MockProfileRelease marketing_name: MockProfileRelease Marketing Name class: diff --git a/arch/profile_release/RVA20.yaml b/arch/profile_release/RVA20.yaml index 59715da25..e5c4803fb 100644 --- a/arch/profile_release/RVA20.yaml +++ b/arch/profile_release/RVA20.yaml @@ -1,5 +1,5 @@ $schema: profile_release_schema.json# -kind: profile release +kind: Profile Release name: RVA20 marketing_name: RVA20 class: diff --git a/arch/profile_release/RVA22.yaml b/arch/profile_release/RVA22.yaml index 7a3350a15..659562724 100644 --- a/arch/profile_release/RVA22.yaml +++ b/arch/profile_release/RVA22.yaml @@ -1,5 +1,5 @@ $schema: profile_release_schema.json# -kind: profile release +kind: Profile Release name: RVA22 marketing_name: RVA22 class: diff --git a/arch/profile_release/RVA23.yaml b/arch/profile_release/RVA23.yaml index a883d8a8b..e7d7eec56 100644 --- a/arch/profile_release/RVA23.yaml +++ b/arch/profile_release/RVA23.yaml @@ -1,5 +1,5 @@ $schema: profile_release_schema.json# -kind: profile release +kind: Profile Release name: RVA23 marketing_name: RVA23 class: diff --git a/arch/profile_release/RVB23.yaml b/arch/profile_release/RVB23.yaml index 53eae651a..a6a679aa5 100644 --- a/arch/profile_release/RVB23.yaml +++ b/arch/profile_release/RVB23.yaml @@ -1,5 +1,5 @@ $schema: profile_release_schema.json# -kind: profile release +kind: Profile Release name: RVB23 marketing_name: RVB23 class: diff --git a/arch/profile_release/RVI20.yaml b/arch/profile_release/RVI20.yaml index a1a283f03..eb66ccc9b 100644 --- a/arch/profile_release/RVI20.yaml +++ b/arch/profile_release/RVI20.yaml @@ -1,5 +1,5 @@ $schema: profile_release_schema.json# -kind: profile release +kind: Profile Release name: RVI20 marketing_name: RVI20 class: diff --git a/backends/crd/tasks.rake b/backends/crd/tasks.rake index 1391f628d..9ac4fde94 100644 --- a/backends/crd/tasks.rake +++ b/backends/crd/tasks.rake @@ -20,6 +20,9 @@ Dir.glob("#{$root}/arch/proc_cert_model/*.yaml") do |f| "#{$root}/lib/arch_obj_models/portfolio.rb", "#{$root}/lib/portfolio_design.rb", "#{$root}/lib/design.rb", + "#{$root}/backends/portfolio/templates/ext_appendix.adoc.erb", + "#{$root}/backends/portfolio/templates/inst_appendix.adoc.erb", + "#{$root}/backends/portfolio/templates/csr_appendix.adoc.erb", "#{CERT_DOC_DIR}/templates/crd.adoc.erb" ] do |t| arch = pf_create_arch diff --git a/backends/crd/templates/crd.adoc.erb b/backends/crd/templates/crd.adoc.erb index 1a5e39ff1..ed1dca355 100644 --- a/backends/crd/templates/crd.adoc.erb +++ b/backends/crd/templates/crd.adoc.erb @@ -94,7 +94,7 @@ Where: any other information required to identify the variant of that standard. * identifies a particular CRD release ** Format is [.[.]] -** Follows semantic versioning scheme (https://semver.org/) +** Inspired by semantic versioning scheme (https://semver.org/) but doesn't follow it exactly ** The release is updated when certification test changes are made that *could* cause a previously certified implementation to now fail. Examples are fixing a test bug, or increasing test coverage, or requiring a new version of a standard @@ -467,398 +467,6 @@ Requirement <%= req.name %> only apply when <%= req.when_pretty %>. <% end -%> <% end # unless requirement_groups.empty? -%> -<<< -[appendix] -== Extension Details -<% proc_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 %> + -<%= ext.nil? ? "" : "*Long Name*: " + ext.long_name + " +" %> - -*Version Requirement*: <%= ext_req.requirement_specs.map(&:to_s).join(", ") %> + - -<% ext.versions.each do |v| -%> -<%= v.version_spec %>:: - State::: - <%= v.state %> - <% if v.state == "ratified" -%> - Ratification date::: - <%= v.ratification_date %> - <% end # if %> - <% if v.changes.size > 0 -%> - Changes::: - - <% v.changes.each do |c| -%> - * <%= c %> - <% end -%> - - <% end -%> - <% unless v.url.nil? -%> - Ratification document::: - <%= v.url %> - <% end -%> - <% if v.implications.size > 0 -%> - Implies::: - <% v.implications.each do |i| -%> - * `<%= i.name %>` version <%= i.version_spec %> - <% end -%> - <% end -%> -<% end -%> - -==== Synopsis - -:leveloffset: +3 - -<%= ext.description %> - -:leveloffset: -3 - -<% unless ext_req.note.nil? -%> -[NOTE] --- -<%= ext_req.note %> --- -<% end -%> - -// TODO: GitHub issue 92: Use version specified by each profile. -<% insts = arch.instructions.select { |i| i.defined_by?(ext.min_version) } -%> -<% unless insts.empty? -%> -==== Instructions - -The following instructions are added by this extension: - -[cols="1,3"] -|=== -<% insts.each do |inst| -%> -| <%= link_to_inst(inst.name) %> -| *<%= inst.long_name %>* -<% end -%> -|=== -<% end -%> - -<% unless proc_cert_model.in_scope_ext_params(ext_req).empty? -%> -==== IN-SCOPE Parameters - -<% 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 %>:: -+ --- -<%= ext_param.param.desc %> --- -<% end # do ext_param -%> -<% end # unless table -%> - -<% unless proc_cert_model.out_of_scope_params(ext_req.name).empty? -%> -==== OUT-OF-SCOPE Parameters - -<% 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 %>:: -+ --- -<%= param.desc %> --- -<% end # do param -%> -<% end # unless table -%> -<% end # do ext_req -%> - -<<< -[appendix] -== Instruction Details - -<% design.in_scope_instructions.each do |inst| -%> -<<< -<%= anchor_for_inst(inst.name) %> -=== <%= inst.name %> - -*<%= inst.long_name %>* - -This instruction is defined by: - -<%= inst.defined_by_condition.to_asciidoc %> - -==== Encoding - -<% if inst.multi_encoding? -%> -[NOTE] -This instruction has different encodings in RV32 and RV64. - -==== -RV32:: -+ -[wavedrom, ,svg,subs='attributes',width="100%"] -.... -<%= JSON.dump inst.wavedrom_desc(32) %> -.... - -RV64:: -+ -[wavedrom, ,svg,subs='attributes',width="100%"] -.... -<%= JSON.dump inst.wavedrom_desc(64) %> -.... -==== -<% else -%> -[wavedrom, ,svg,subs='attributes',width="100%"] -.... -<%= JSON.dump inst.wavedrom_desc(inst.base.nil? ? 32 : inst.base) %> -.... -<% end -%> - -==== Synopsis - -<%= inst.description %> - -==== Access -<% if design.in_scope_extensions.any? { |e| e.name == "H" } -%> -[cols="^,^,^,^,^"] -<% else -%> -[cols="^,^,^"] -<% 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 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 %> -|=== - -<% if inst.access_detail? -%> -<%= inst.access_detail %> -<% end -%> - -==== Decode Variables - -<% if inst.multi_encoding? -%> -==== -RV32:: -+ -[source.idl] ----- -<% inst.decode_variables(32).each do |d| -%> -<%= d.sext? ? 'signed ' : '' %>Bits<<%= d.size %>> <%= d.name %> = <%= d.extract %>; -<% end -%> ----- - -RV64:: -+ -[source,idl] ----- -<% inst.decode_variables(64).each do |d| -%> -<%= d.sext? ? 'signed ' : '' %>Bits<<%= d.size %>> <%= d.name %> = <%= d.extract %>; -<% end -%> ----- -==== -<% else -%> -[source,idl] ----- -<% inst.decode_variables(inst.base.nil? ? 32 : inst.base).each do |d| -%> -<%= d.sext? ? 'signed ' : '' %>Bits<<%= d.size %>> <%= d.name %> = <%= d.extract %>; -<% end -%> ----- -<% end -%> - -==== Execution - -<% xlens = inst.base.nil? ? [32, 64] : [inst.base] -%> - -<% if inst.key?("operation()") -%> -[source,idl,subs="specialchars,macros"] ----- -<%= inst.operation_ast(design.symtab).gen_adoc %> ----- -<% end -%> - -==== Exceptions - -<%- exception_list = inst.reachable_exceptions_str(design.symtab) -%> -<%- if exception_list.empty? -%> -This instruction does not generate synchronous exceptions. -<%- else -%> -This instruction may result in the following synchronous exceptions: - - <%- exception_list.sort.each do |etype| -%> - * <%= etype %> - <%- end -%> - -<%- end -%> - - -<% end -%> - -<<< -[appendix] -== CSR Details - -<% design.in_scope_csrs.sort_by(&:name).each do |csr| -%> -<<< -[[csr-<%= csr.name %>-def]] -=== <%= csr.name %> - -*<%= csr.long_name %>* - -<% unless csr.base.nil? -%> -[NOTE] --- -`<%= csr.name %>` is only defined in RV<%= csr.base %>. --- -<% end -%> - -<%= csr.description %> - -==== Attributes -[%autowidth] -|=== -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| Defining extension a| <%= csr.defined_by_condition.to_asciidoc %> -<% if csr.dynamic_length?(design) -%> -h| Length | <%= csr.length_pretty(design) %> -<% else -%> -h| Length | <%= csr.length_pretty(design) %> -<% end -%> -h| Privilege Mode | <%= csr.priv_mode %> -|=== - - -==== Format -<% 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(design, csr.base.nil? ? 32 : csr.base, optional_type: 2) %> -.... -<% else -%> -<%# CSR has a dynamic length, or a field has a dynamic location, - so there is more than one format to display -%> -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(design, 32, optional_type: 2) %> -.... - -.<%= csr.name %> Format when <%= csr.length_cond64 %> -[wavedrom, ,svg,subs='attributes',width="100%"] -.... -<%= JSON.dump csr.wavedrom_desc(design, 64, optional_type: 2) %> -.... - - -<% end # unless dynamic length -%> - -==== Field Summary - -// use @ as a separator since IDL code can contain | -[%autowidth,separator=@,float="center",align="center",cols="^,<,<,<",options="header",role="stretch"] -|=== -@ Name @ Location @ Type @ Reset Value - -<%- csr.implemented_fields(design).each do |field| -%> -@ xref:<%=csr.name%>-<%=field.name%>-def[`<%= field.name %>`] -a@ -<%- if field.dynamic_location?(design) -%> - -[when,"<%= field.location_cond32 %>"] --- -<%= field.location_pretty(design, 32) %> --- - -[when,"<%= field.location_cond64 %>"] --- -<%= field.location_pretty(design, 64) %> --- - -<%- else -%> -<%= field.location_pretty(design) %> -<%- end -%> -a@ - --- -<%= field.type_pretty(design.symtab) %> --- - -a@ - --- -<%= field.reset_value_pretty(design) %> --- - -<%- end -%> -|=== - -==== Fields - -<%- 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(design).each do |field| -%> -[[<%=csr.name%>-<%=field.name%>-def]] -===== `<%= field.name %>` - -<%- if !field.defined_in_all_bases? -%> -IMPORTANT: <%= field.name %> is only defined in <%= field.base32_only? ? "RV32" : "RV64" %> (`<%= field.base32_only? ? field.location_cond32 : field.location_cond64 %>`) -<%- end -%> - -**** -Location:: -<%= field.location_pretty(design) %> - -Description:: -<%= field.description.gsub("\n", " +\n") %> - -Type:: -<%= field.type_pretty(design.symtab) %> - -Reset value:: -<%= field.reset_value_pretty(design) %> - -**** - -<%- end -%> -<%- end -%> - -<%- 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. - -When a software write occurs (_e.g._, through `csrrw`), the following determines the -written value: - -[idl] ----- -<%- csr.implemented_fields(design).each do |field| -%> -<%- if field.has_custom_sw_write? -%> -<%= field.name %> = <%= field["sw_write(csr_value)"] %> -<%- else -%> -<%= field.name %> = csr_value.<%= field.name %> -<%- end -%> -<%- end -%> ----- -<%- end -%> - -<%- if csr.has_custom_sw_read? -%> -==== Software read - -This CSR may return a value that is different from what is stored in hardware. - -[source,idl,subs="specialchars,macros"] ----- -<%= csr.sw_read_ast(design.symtab).gen_adoc %> ----- -<%- end -%> - -<% end # do in_scope_csrs -%> +<%= portfolio_design.include_erb("ext_appendix.adoc.erb") %> +<%= portfolio_design.include_erb("inst_appendix.adoc.erb") %> +<%= portfolio_design.include_erb("csr_appendix.adoc.erb") %> diff --git a/backends/portfolio/templates/csr_appendix.adoc.erb b/backends/portfolio/templates/csr_appendix.adoc.erb index cc157196c..3e27067fe 100644 --- a/backends/portfolio/templates/csr_appendix.adoc.erb +++ b/backends/portfolio/templates/csr_appendix.adoc.erb @@ -2,7 +2,7 @@ [appendix] == CSR Details -<% design.in_scope_csrs.sort_by(&:name).each do |csr| -%> +<% portfolio_design.in_scope_csrs.sort_by(&:name).each do |csr| -%> <<< [[csr-<%= csr.name %>-def]] === <%= csr.name %> diff --git a/backends/portfolio/templates/ext_appendix.adoc.erb b/backends/portfolio/templates/ext_appendix.adoc.erb index 3df4f016e..5bda503cb 100644 --- a/backends/portfolio/templates/ext_appendix.adoc.erb +++ b/backends/portfolio/templates/ext_appendix.adoc.erb @@ -1,24 +1,36 @@ <<< [appendix] == Extension Details -<% proc_cert_model.in_scope_ext_reqs.each do |ext_req| -%> +<% portfolio_design.in_scope_ext_reqs.each do |ext_req| -%> <% ext = arch.extension(ext_req.name) -%> [[ext-<%= ext_req.name %>-def]] === Extension <%= ext_req.name %> + -<%= ext.nil? ? "" : "*Long Name*: " + ext.long_name + " +" %> +*Long Name*: <%= ext.long_name %> + *Version Requirement*: <%= ext_req.requirement_specs.map(&:to_s).join(", ") %> + +<% if portfolios.size > 1 -%> +.<%= ext.name %> Extension Presence +|=== +| <%= portfolio_kind %> | v<%= ext.versions.map { |ext_ver| ext_ver.canonical_version.to_s }.join(" | v") %> + +<% portfolios.each do |portfolio| -%> +| <%= portfolio.name %> | <%= portfolio.version_greatest_presence(ext.name, ext.versions).join(" | ") -%> +<% end -%> + +|=== +<% end # portfolios.size > 1 -%> + <% ext.versions.each do |v| -%> -<%= v.version_spec %>:: +<%= v.canonical_version %>:: State::: <%= v.state %> - <% if v.state == "ratified" -%> + <% if v.state == "ratified" && !v.ratification_date.nil? -%> Ratification date::: <%= v.ratification_date %> <% end # if %> - <% if v.changes.size > 0 -%> + <% if v.changes.empty? -%> Changes::: <% v.changes.each do |c| -%> @@ -30,7 +42,7 @@ Ratification document::: <%= v.url %> <% end -%> - <% if v.implications.size > 0 -%> + <% unless v.implications.empty? -%> Implies::: <% v.implications.each do |i| -%> * `<%= i.name %>` version <%= i.version_spec %> @@ -54,7 +66,7 @@ <% end -%> // TODO: GitHub issue 92: Use version specified by each profile. -<% insts = arch.instructions.select { |i| i.defined_by?(ext.min_version) } -%> +<% insts = portfolio_design.in_scope_instructions.select { |i| i.defined_by?(ext.min_version) } -%> <% unless insts.empty? -%> ==== Instructions @@ -62,17 +74,34 @@ The following instructions are added by this extension: [cols="1,3"] |=== -<% insts.each do |inst| -%> +<% insts.sort.each do |inst| -%> | <%= link_to_inst(inst.name) %> | *<%= inst.long_name %>* <% end -%> |=== <% end -%> -<% unless proc_cert_model.in_scope_ext_params(ext_req).empty? -%> +<% unless ext.params.empty? -%> +<% if portfolio_design.in_scope_ext_params(ext_req).empty? && portfolio_design.out_of_scope_params(ext_req.name).empty? -%> +==== Parameters + +This extension has the following parameters (AKA implementation options): + +<% ext.params.sort_by { |p| p.name }.each do |param| -%> +<%= param.name %>:: ++ +-- +<%= param.desc %> +-- +<% end # do param -%> + +<% end # if in_scope & out_of_scope empty -%> +<% end # unless table -%> + +<% unless portfolio_design.in_scope_ext_params(ext_req).empty? -%> ==== IN-SCOPE Parameters -<% proc_cert_model.in_scope_ext_params(ext_req).each do |ext_param| -%> +<% portfolio_design.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 %>:: + @@ -82,10 +111,10 @@ The following instructions are added by this extension: <% end # do ext_param -%> <% end # unless table -%> -<% unless proc_cert_model.out_of_scope_params(ext_req.name).empty? -%> +<% unless portfolio_design.out_of_scope_params(ext_req.name).empty? -%> ==== OUT-OF-SCOPE Parameters -<% proc_cert_model.out_of_scope_params(ext_req.name).each do |param| -%> +<% portfolio_design.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/backends/portfolio/templates/inst_appendix.adoc.erb b/backends/portfolio/templates/inst_appendix.adoc.erb index aaa755aa3..9054415cd 100644 --- a/backends/portfolio/templates/inst_appendix.adoc.erb +++ b/backends/portfolio/templates/inst_appendix.adoc.erb @@ -1,7 +1,8 @@ +<<< [appendix] == Instruction Details -<% design.in_scope_instructions.each do |inst| -%> +<% portfolio_design.in_scope_instructions.each do |inst| -%> <<< <%= anchor_for_inst(inst.name) %> === <%= inst.name %> @@ -18,6 +19,7 @@ This instruction is defined by: [NOTE] This instruction has different encodings in RV32 and RV64. +[tabs] ==== RV32:: + @@ -45,18 +47,18 @@ RV64:: <%= inst.description %> ==== Access -<% if design.in_scope_extensions.any? { |e| e.name == "H" } -%> +<% if portfolio_design.in_scope_extensions.any? { |e| e.name == "H" } -%> [cols="^,^,^,^,^"] <% else -%> [cols="^,^,^"] <% 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 -%> +| M | <% if portfolio_design.in_scope_extensions.any? { |e| e.name == "H" } -%>HS<% else -%>S<% end -%> | U <% if portfolio_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 design.in_scope_extensions.any? { |e| e.name == "H" } %> +<% if portfolio_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 %> @@ -69,6 +71,7 @@ RV64:: ==== Decode Variables <% if inst.multi_encoding? -%> +[tabs] ==== RV32:: + @@ -122,5 +125,4 @@ This instruction may result in the following synchronous exceptions: <%- end -%> - <% end -%> diff --git a/backends/profile/tasks.rake b/backends/profile/tasks.rake index 3f3c9639d..7867229f1 100644 --- a/backends/profile/tasks.rake +++ b/backends/profile/tasks.rake @@ -29,6 +29,9 @@ 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/portfolio/templates/ext_appendix.adoc.erb", + "#{$root}/backends/portfolio/templates/inst_appendix.adoc.erb", + "#{$root}/backends/portfolio/templates/csr_appendix.adoc.erb", "#{PROFILE_DOC_DIR}/templates/profile.adoc.erb" ].concat(profile_pathnames) do |t| arch = pf_create_arch diff --git a/backends/profile/templates/profile.adoc.erb b/backends/profile/templates/profile.adoc.erb index a2ffc1088..7eac891bb 100644 --- a/backends/profile/templates/profile.adoc.erb +++ b/backends/profile/templates/profile.adoc.erb @@ -444,287 +444,6 @@ Extensions present in a profile are also present in higher-privileged profiles i <% end -%> |=== -<<< -[appendix] -<%# XXX pf_design.include_erb("ext_appendix.adoc.erb") %> - -== Extension Details - -<% pf_design.in_scope_extensions.each do |ext| -%> -<<< -=== <%= ext.name %> Extension -<%= ext.long_name %> - -.<%= ext.name %> Extension Presence -|=== -| Profile | v<%= ext.versions.map { |ext_ver| ext_ver.canonical_version.to_s }.join(" | v") %> - -<% profile_release.profiles.each do |profile| -%> -| <%= profile.marketing_name %> | <%= profile.version_greatest_presence(ext.name, ext.versions).join(" | ") -%> -<% end -%> - -|=== - -<% ext.versions.each do |v| -%> -<%= v.canonical_version %>:: - State::: - <%= v.state %> - <% if v.state == "ratified" && !v.ratification_date.nil? -%> - Ratification date::: - <%= v.ratification_date %> - <% end # if %> - <% unless v.changes.empty? -%> - Changes::: - - <% v.changes.each do |c| -%> - * <%= c %> - <% end -%> - - <% end -%> - <% unless v.url.nil? -%> - Ratification document::: - <%= v.url %> - <% end -%> - <% unless v.implications -%> - Implies::: - <% v.implications.each do |i| -%> - * `<%= i.name %>` version <%= i.version %> - <% end -%> - <% end -%> -<% end -%> - -==== Synopsis - -:leveloffset: +3 - -<%= ext.description %> - -:leveloffset: -3 - -// TODO: GitHub issue 92: Use version specified by each profile and add version info to inst table below. -<%- insts = arch.instructions.select { |i| i.defined_by?(ext.min_version) } -%> -<%- unless insts.empty? -%> -==== Instructions - -The following instructions are added by this extension: - -[cols="1,3"] -|=== -<% insts.sort.each do |inst| -%> - | <%= "`#{inst.name}`" %> | *<%= inst.long_name %>* -<% end -%> -|=== -<% end -%> - -<% unless ext.params.empty? -%> -==== Parameters - -This extension has the following implementation options: - -<% ext.params.sort_by { |p| p.name }.each do |param| -%> -<%= param.name %>:: -+ --- -<%= param.desc %> --- -<% end -%> - -<% end -%> -<% end -%> - -<<< -[appendix] -== Instruction Details - -<% pf_design.in_scope_instructions.each do |inst| -%> -<<< -<%= anchor_for_inst(inst.name) %> -=== <%= inst.name %> - -*<%= inst.long_name %>* - -This instruction is defined by: - -<%= inst.defined_by_condition.to_asciidoc %> - -==== Encoding - -<% if inst.multi_encoding? -%> -[NOTE] -This instruction has different encodings in RV32 and RV64. - -[tabs] -==== -RV32:: -+ -[wavedrom, ,svg,subs='attributes',width="100%"] -.... -<%= JSON.dump inst.wavedrom_desc(32) %> -.... - -RV64:: -+ -[wavedrom, ,svg,subs='attributes',width="100%"] -.... -<%= JSON.dump inst.wavedrom_desc(64) %> -.... -==== -<% else -%> -[wavedrom, ,svg,subs='attributes',width="100%"] -.... -<%= JSON.dump inst.wavedrom_desc(inst.base.nil? ? 32 : inst.base) %> -.... -<% end -%> - -==== Synopsis - -<%= inst.description %> - -==== Access -<% if pf_design.in_scope_extensions.any? { |e| e.name == "H" } -%> -[cols="^,^,^,^,^"] -<% else -%> -[cols="^,^,^"] -<% end -%> -|=== -| M | <% if pf_design.in_scope_extensions.any? { |e| e.name == "H" } -%>HS<% else -%>S<% end -%> | U <% if pf_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 pf_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 %> -|=== - -<% if inst.access_detail? -%> -<%= inst.access_detail %> -<% end -%> - - -==== Decode Variables - -<% if inst.multi_encoding? -%> -[tabs] -==== -RV32:: -+ -[source.idl] ----- -<% inst.decode_variables(32).each do |d| -%> -<%= d.sext? ? 'signed ' : '' %>Bits<<%= d.size %>> <%= d.name %> = <%= d.extract %>; -<% end -%> ----- - -RV64:: -+ -[source,idl] ----- -<% inst.decode_variables(64).each do |d| -%> -<%= d.sext? ? 'signed ' : '' %>Bits<<%= d.size %>> <%= d.name %> = <%= d.extract %>; -<% end -%> ----- -==== -<% else -%> -[source,idl] ----- -<% inst.decode_variables(inst.base.nil? ? 32 : inst.base).each do |d| -%> -<%= d.sext? ? 'signed ' : '' %>Bits<<%= d.size %>> <%= d.name %> = <%= d.extract %>; -<% end -%> ----- -<% end -%> - -==== Execution - -<% xlens = inst.base.nil? ? [32, 64] : [inst.base] -%> - -<% if inst.key?("operation()") -%> -[source,idl,subs="specialchars,macros"] ----- -<%= inst.operation_ast(design.symtab).gen_adoc %> ----- -<% end -%> - -==== Exceptions - -<% exception_list = inst.reachable_exceptions_str(design.symtab) -%> -<% if exception_list.empty? -%> -This instruction does not generate synchronous exceptions. -<% else -%> -This instruction may result in the following synchronous exceptions: - - <% exception_list.sort.each do |etype| -%> - * <%= etype %> - <% end -%> - -<% end -%> - -<% end -%> - -<<< -[appendix] -== CSR Details - -<% pf_design.in_scope_csrs.sort_by!(&:name).each do |csr| -%> -<<< -[[csr-<%= csr.name %>-def]] -=== <%= csr.name %> - -*<%= csr.long_name %>* - -<% unless csr.base.nil? -%> -[NOTE] --- -`<%= csr.name %>` is only defined in RV<%= csr.base %>. --- -<% end -%> - -<%= csr.description %> - -==== Attributes -[%autowidth] -|=== -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| Defining extension a| <%= csr.defined_by_condition.to_asciidoc %> -<% if csr.dynamic_length?(design) -%> -h| Length | <%= csr.length_pretty(design) %> -<% else -%> -h| Length | <%= csr.length_pretty(design) %> -<% end -%> -h| Privilege Mode | <%= csr.priv_mode %> -|=== - - -==== Format -<% 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(design, csr.base.nil? ? 32 : csr.base) %> -.... -<% else -%> -<%# CSR has a dynamic length, or a field has a dynamic location, - so there is more than one format to display -%> -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(design, 32) %> -.... - -.<%= csr.name %> Format when <%= csr.length_cond64 %> -[wavedrom, ,svg,subs='attributes',width="100%"] -.... -<%= JSON.dump csr.wavedrom_desc(design, 64) %> -.... - - -<% end -%> - -<% end -%> +<%= portfolio_design.include_erb("ext_appendix.adoc.erb") %> +<%= portfolio_design.include_erb("inst_appendix.adoc.erb") %> +<%= portfolio_design.include_erb("csr_appendix.adoc.erb") %> diff --git a/lib/arch_obj_models/certificate.rb b/lib/arch_obj_models/certificate.rb index 92669edc1..8e5bc4382 100644 --- a/lib/arch_obj_models/certificate.rb +++ b/lib/arch_obj_models/certificate.rb @@ -137,4 +137,162 @@ def requirement_groups end @requirement_groups end + + ############################################ + # Routines using InScopeExtensionParameter # + ############################################ + + # @return [Array] Sorted list of parameters specified by any extension in portfolio. + # These are always IN-SCOPE by definition (since they are listed in the portfolio). + # Can have multiple array entries with the same parameter name since multiple extensions may define + # the same parameter. + def all_in_scope_ext_params + return @all_in_scope_ext_params unless @all_in_scope_ext_params.nil? + + @all_in_scope_ext_params = [] + + @data["extensions"].each do |ext_name, ext_data| + next if ext_name[-1] == "$" + + # Find Extension object from database + ext = @arch.extension(ext_name) + raise "Cannot find extension named #{ext_name}" if ext.nil? + + ext_data["parameters"]&.each do |param_name, param_data| + param = ext.params.find { |p| p.name == param_name } + raise "There is no param '#{param_name}' in extension '#{ext_name}" if param.nil? + + next unless ext.versions.any? do |ext_ver| + ver_req = ext_data["version"] || ">= #{ext.min_version.version_spec}" + ExtensionRequirement.new(ext_name, ver_req, @arch).satisfied_by?(ext_ver) && + param.defined_in_extension_version?(ext_ver) + end + + @all_in_scope_ext_params << + InScopeExtensionParameter.new(param, param_data["schema"], param_data["note"]) + end + end + @all_in_scope_ext_params.sort + end + + # @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) + + ext_params = [] # Local variable, no caching + + # Get extension information from portfolio YAML for passed in extension requirement. + ext_data = @data["extensions"][ext_req.name] + raise "Cannot find extension named #{ext_req.name}" if ext_data.nil? + + # Find Extension object from database + ext = @arch.extension(ext_req.name) + raise "Cannot find extension named #{ext_req.name}" if ext.nil? + + # Loop through an extension's parameter constraints (hash) from the certificate model. + # Note that "&" is the Ruby safe navigation operator (i.e., skip do loop if nil). + ext_data["parameters"]&.each do |param_name, param_data| + # Find ExtensionParameter object from database + ext_param = ext.params.find { |p| p.name == param_name } + raise "There is no param '#{param_name}' in extension '#{ext_req.name}" if ext_param.nil? + + next unless ext.versions.any? do |ext_ver| + ext_req.satisfied_by?(ext_ver) && + ext_param.defined_in_extension_version?(ext_ver) + end + + ext_params << + InScopeExtensionParameter.new(ext_param, param_data["schema"], param_data["note"]) + end + + ext_params.sort! + end + + # @return [Array] Sorted list of parameters out of scope across all in scope extensions + # (those listed as mandatory or optional in the certificate model). + def all_out_of_scope_params + return @all_out_of_scope_params unless @all_out_of_scope_params.nil? + + @all_out_of_scope_params = [] + in_scope_ext_reqs.each do |ext_req| + ext = @arch.extension(ext_req.name) + ext.params.each do |param| + next if all_in_scope_ext_params.any? { |c| c.param.name == param.name } + + next unless ext.versions.any? do |ext_ver| + ext_req.satisfied_by?(ext_ver) && + param.defined_in_extension_version?(ext_ver) + end + + @all_out_of_scope_params << param + end + end + @all_out_of_scope_params.sort! + end + + # @param ext_name [String] Extension name + # @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 } }.sort + end + + # @param param [ExtensionParameter] + # @return [Array] Sorted list of all in-scope extensions that define this parameter + # in the database and the parameter is in-scope. + def all_in_scope_exts_with_param(param) + raise ArgumentError, "Expecting ExtensionParameter" unless param.is_a?(ExtensionParameter) + + exts = [] + + # Iterate through all the extensions in the architecture database that define this parameter. + param.exts.each do |ext| + found = false + + in_scope_extensions.each do |potential_ext| + if ext.name == potential_ext.name + found = true + next + end + end + + if found + # Only add extensions that exist in this certificate model. + exts << ext + end + end + + # Return intersection of extension names + exts.sort_by!(&:name) + end + + # @param param [ExtensionParameter] + # @return [Array] List of all in-scope extensions that define this parameter in the + # database but the parameter is out-of-scope. + def all_in_scope_exts_without_param(param) + raise ArgumentError, "Expecting ExtensionParameter" unless param.is_a?(ExtensionParameter) + + exts = [] # Local variable, no caching + + # Iterate through all the extensions in the architecture database that define this parameter. + param.exts.each do |ext| + found = false + + in_scope_extensions.each do |potential_ext| + if ext.name == potential_ext.name + found = true + next + end + end + + if found + # Only add extensions that are in-scope (i.e., exist in this certificate model). + exts << ext + end + end + + # Return intersection of extension names + exts.sort_by!(&:name) + end end diff --git a/lib/arch_obj_models/portfolio.rb b/lib/arch_obj_models/portfolio.rb index 471fff3ea..a8c1b4a10 100644 --- a/lib/arch_obj_models/portfolio.rb +++ b/lib/arch_obj_models/portfolio.rb @@ -60,7 +60,8 @@ class PortfolioGroup # @param portfolios [Array] def initialize(portfolios) - @portfolios = portfolios + raise ArgumentError, "Need at least one portfolio" if portfolios.empty? + @portfolios = portfolios end # @return [Array] All portfolios in this portfolio group @@ -212,6 +213,72 @@ def extension_presence(ext_name) greatest_presence.nil? ? "-" : greatest_presence.to_s_concise end + + # @return [Array] Sorted list of parameters specified by any extension in portfolio. + def all_in_scope_ext_params + @ret = [] + portfolios.each do |portfolio| + @ret += portfolio.all_in_scope_ext_params + end + + @ret = @ret.uniq.sort + end + + # @param [ExtensionRequirement] + # @return [Array] Sorted list of extension parameters from portfolio for given extension. + def in_scope_ext_params(ext_req) + @ret = [] + portfolios.each do |portfolio| + @ret += portfolio.in_scope_ext_params(ext_req) + end + + @ret = @ret.uniq.sort + end + + # @return [Array] Sorted list of parameters out of scope across all in scope extensions. + def all_out_of_scope_params + @ret = [] + portfolios.each do |portfolio| + @ret += portfolio.all_out_of_scope_params + end + + @ret = @ret.uniq.sort + end + + # @param ext_name [String] Extension name + # @return [Array] Sorted list of parameters that are out of scope for named extension. + def out_of_scope_params(ext_name) + @ret = [] + portfolios.each do |portfolio| + @ret += portfolio.out_of_scope_params(ext_name) + end + + @ret = @ret.uniq.sort + end + + # @param param [ExtensionParameter] + # @return [Array] Sorted list of all in-scope extensions that define this parameter + # in the database and the parameter is in-scope. + def all_in_scope_exts_with_param(param) + @ret = [] + portfolios.each do |portfolio| + @ret += portfolio.all_in_scope_exts_with_param(param) + end + + @ret = @ret.uniq.sort + end + + # @param param [ExtensionParameter] + # @return [Array] List of all in-scope extensions that define this parameter in the + # database but the parameter is out-of-scope. + def all_in_scope_exts_without_param(param) + @ret = [] + portfolios.each do |portfolio| + @ret += portfolio.all_in_scope_exts_without_param(param) + end + + @ret = @ret.uniq.sort + end end ############# @@ -465,11 +532,39 @@ def uses_optional_types? @uses_optional_types end + ########################################################################### + # Portfolio types that supported the concept of in-scope and out-of-scope # + # parameter have to override the following methods. # + ########################################################################### + + # @return [Array] List of parameters specified by any extension in portfolio. + def all_in_scope_ext_params = [] + + # @param [ExtensionRequirement] + # @return [Array] Sorted list of extension parameters from portfolio for given extension. + def in_scope_ext_params(ext_req) = [] + + # @return [Array] Sorted list of parameters out of scope across all in scope extensions. + def all_out_of_scope_params = [] + + # @param ext_name [String] Extension name + # @return [Array] Sorted list of parameters that are out of scope for named extension. + def out_of_scope_params(ext_name) = [] + + # @param param [ExtensionParameter] + # @return [Array] Sorted list of all in-scope extensions that define this parameter + # in the database and the parameter is in-scope. + def all_in_scope_exts_with_param(param) = [] + + # @param param [ExtensionParameter] + # @return [Array] List of all in-scope extensions that define this parameter in the + # database but the parameter is out-of-scope. + def all_in_scope_exts_without_param(param) = [] + ################################### # InScopeExtensionParameter Class # ################################### - # Holds extension parameter information from the portfolio. class InScopeExtensionParameter attr_reader :param # ExtensionParameter object (from the architecture database) attr_reader :note @@ -521,163 +616,6 @@ def <=>(other) end end # class InScopeExtensionParameter - ############################################ - # Routines using InScopeExtensionParameter # - ############################################ - - # @return [Array] List of parameters specified by any extension in portfolio. - # These are always IN-SCOPE by definition (since they are listed in the portfolio). - # Can have multiple array entries with the same parameter name since multiple extensions may define - # the same parameter. - def all_in_scope_ext_params - return @all_in_scope_ext_params unless @all_in_scope_ext_params.nil? - - @all_in_scope_ext_params = [] - - @data["extensions"].each do |ext_name, ext_data| - next if ext_name[0] == "$" - - # Find Extension object from database - ext = @arch.extension(ext_name) - raise "Cannot find extension named #{ext_name}" if ext.nil? - - ext_data["parameters"]&.each do |param_name, param_data| - param = ext.params.find { |p| p.name == param_name } - raise "There is no param '#{param_name}' in extension '#{ext_name}" if param.nil? - - next unless ext.versions.any? do |ext_ver| - ver_req = ext_data["version"] || ">= #{ext.min_version.version_spec}" - ExtensionRequirement.new(ext_name, ver_req, @arch).satisfied_by?(ext_ver) && - param.defined_in_extension_version?(ext_ver) - end - - @all_in_scope_ext_params << - InScopeExtensionParameter.new(param, param_data["schema"], param_data["note"]) - end - end - @all_in_scope_ext_params - end - - # @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) - - ext_params = [] # Local variable, no caching - - # Get extension information from portfolio YAML for passed in extension requirement. - ext_data = @data["extensions"][ext_req.name] - raise "Cannot find extension named #{ext_req.name}" if ext_data.nil? - - # Find Extension object from database - ext = @arch.extension(ext_req.name) - raise "Cannot find extension named #{ext_req.name}" if ext.nil? - - # Loop through an extension's parameter constraints (hash) from the portfolio. - # Note that "&" is the Ruby safe navigation operator (i.e., skip do loop if nil). - ext_data["parameters"]&.each do |param_name, param_data| - # Find ExtensionParameter object from database - ext_param = ext.params.find { |p| p.name == param_name } - raise "There is no param '#{param_name}' in extension '#{ext_req.name}" if ext_param.nil? - - next unless ext.versions.any? do |ext_ver| - ext_req.satisfied_by?(ext_ver) && - ext_param.defined_in_extension_version?(ext_ver) - end - - ext_params << - InScopeExtensionParameter.new(ext_param, param_data["schema"], param_data["note"]) - end - - ext_params.sort! - end - - # @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? - - @all_out_of_scope_params = [] - in_scope_ext_reqs.each do |ext_req| - ext = @arch.extension(ext_req.name) - ext.params.each do |param| - next if all_in_scope_ext_params.any? { |c| c.param.name == param.name } - - next unless ext.versions.any? do |ext_ver| - ext_req.satisfied_by?(ext_ver) && - param.defined_in_extension_version?(ext_ver) - end - - @all_out_of_scope_params << param - end - end - @all_out_of_scope_params.sort! - end - - # @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 } }.sort - end - - # @return [Array] - # 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) - - exts = [] - - # Iterate through all the extensions in the architecture database that define this parameter. - param.exts.each do |ext| - found = false - - in_scope_extensions.each do |potential_ext| - if ext.name == potential_ext.name - found = true - next - end - end - - if found - # Only add extensions that exist in this portfolio. - exts << ext - end - end - - # Return intersection of extension names - exts.sort_by!(&:name) - end - - # @return [Array] - # 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) - - exts = [] # Local variable, no caching - - # Iterate through all the extensions in the architecture database that define this parameter. - param.exts.each do |ext| - found = false - - in_scope_extensions.each do |potential_ext| - if ext.name == potential_ext.name - found = true - next - end - end - - if found - # Only add extensions that are in-scope (i.e., exist in this portfolio). - exts << ext - end - end - - # Return intersection of extension names - exts.sort_by!(&:name) - end - ############################ # RevisionHistory Subclass # ############################ diff --git a/lib/architecture.rb b/lib/architecture.rb index 5b78933b5..9e8548c41 100644 --- a/lib/architecture.rb +++ b/lib/architecture.rb @@ -1,10 +1,12 @@ # frozen_string_literal: true -# Contains the "database" of RISC-V standards including extensions, instructions, -# CSRs, Profiles, and Certificates. Could be either the standard spec (defined by RISC-V International) -# of a custom spec (defined as an arch_overlay in /cfgs dir). +# The Architecture class is the API to the architecture database. +# The "database" contains RISC-V standards including extensions, instructions, +# CSRs, Profiles, and Certificates. +# The Architecture class is used by backends to export the information in the +# architecture database to create various outputs. # -# Creates Ruby functions at runtime (see generate_obj_methods() and OBJS array). +# The Architecture class creates Ruby functions at runtime (see generate_obj_methods() and OBJS array). # 1) Function to return Array (every klass in database) # 2) Function to return Hash (hash entry is nil if name doesn't exist) # 3) Function to return Klass given name (nil if name doesn't exist) diff --git a/lib/design.rb b/lib/design.rb index 1b607715f..24edc8644 100644 --- a/lib/design.rb +++ b/lib/design.rb @@ -1,18 +1,14 @@ # 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. +# The Design class assists backends when exporting information from the database. +# It contains common code such as IDL and ERB support used by multiple backends. # # 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 +# - Individual Extension +# - Profile Release +# - CRD (Certificate Requirements Document) +# - CTP (Certificate Test Plan) # # 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 diff --git a/lib/portfolio_design.rb b/lib/portfolio_design.rb index 9ecea9b2b..71c1f0e59 100644 --- a/lib/portfolio_design.rb +++ b/lib/portfolio_design.rb @@ -15,12 +15,12 @@ require_relative "arch_obj_models/portfolio" class PortfolioDesign < Design - # @return [Array] All the portfolios in this portfolio design - attr_reader :portfolio_grp - # @return [PortfolioClass] Portfolio class for all the portfolios in this design attr_reader :portfolio_class + # @return [String] Kind of portfolio for all portfolios in this design + attr_reader :portfolio_kind + # @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) @@ -37,6 +37,7 @@ def initialize(base_isa_name, arch, portfolios, portfolio_class, overlay_path: n @portfolio_grp = PortfolioGroup.new(portfolios) @portfolio_class = portfolio_class + @portfolio_kind = portfolios[0].kind max_base = portfolios.map(&:base).max raise ArgumentError, "Calculated maximum base of #{max_base} across portfolios is not 32 or 64" unless max_base == 32 || max_base == 64 @@ -200,6 +201,30 @@ def in_scope_interrupt_codes = @portfolio_grp.in_scope_interrupt_codes(self) # If the extension name isn't found in this design, return "-". def extension_presence(ext_name) = @portfolio_grp.extension_presence(ext_name) + # @return [Array] Sorted list of parameters specified by any extension in portfolio. + def all_in_scope_ext_params = @portfolio_grp.all_in_scope_ext_params + + # @param [ExtensionRequirement] + # @return [Array] Sorted list of extension parameters from portfolio for given extension. + def in_scope_ext_params(ext_req) = @portfolio_grp.in_scope_ext_params(ext_req) + + # @return [Array] Sorted list of parameters out of scope across all in scope extensions. + def all_out_of_scope_params = @portfolio_grp.all_out_of_scope_params + + # @param ext_name [String] Extension name + # @return [Array] Sorted list of parameters that are out of scope for named extension. + def out_of_scope_params(ext_name) = @portfolio_grp.out_of_scope_params(ext_name) + + # @param param [ExtensionParameter] + # @return [Array] Sorted list of all in-scope extensions that define this parameter + # in the database and the parameter is in-scope. + def all_in_scope_exts_with_param(param) = @portfolio_grp.all_in_scope_exts_with_param(param) + + # @param param [ExtensionParameter] + # @return [Array] List of all in-scope extensions that define this parameter in the + # database but the parameter is out-of-scope. + def all_in_scope_exts_without_param(param) = @portfolio_grp.all_in_scope_exts_without_param(param) + ################# # EXTRA METHODS # ################# @@ -210,8 +235,10 @@ def init_erb_binding(erb_binding) erb_binding.local_variable_set(:arch, arch) erb_binding.local_variable_set(:design, self) - erb_binding.local_variable_set(:pf_design, self) - erb_binding.local_variable_set(:pf_class, self.portfolio_class) + erb_binding.local_variable_set(:portfolio_design, self) + erb_binding.local_variable_set(:portfolio_class, @portfolio_class) + erb_binding.local_variable_set(:portfolio_kind, @portfolio_kind) + erb_binding.local_variable_set(:portfolios, @portfolio_grp.portfolios) end # Include a partial ERB template into a full ERB template. @@ -224,8 +251,10 @@ def include_erb(template_name) { arch: arch, design: self, - pf_design: self, - pf_class: self.portfolio_class + portfolio_design: self, + portfolio_class: @portfolio_class, + portfolio_kind: @portfolio_kind, + portfolios: @portfolio_grp.portfolios } ) end diff --git a/schemas/profile_class_schema.json b/schemas/profile_class_schema.json index 663b65cb3..00a9e4a22 100644 --- a/schemas/profile_class_schema.json +++ b/schemas/profile_class_schema.json @@ -10,7 +10,7 @@ }, "kind": { "type": "string", - "const": "profile class" + "const": "Profile Class" }, "processor_kind": { "type": "string", diff --git a/schemas/profile_release_schema.json b/schemas/profile_release_schema.json index 9d784c6d3..0c09411e2 100644 --- a/schemas/profile_release_schema.json +++ b/schemas/profile_release_schema.json @@ -10,7 +10,7 @@ }, "kind": { "type": "string", - "const": "profile release" + "const": "Profile Release" }, "name": { "type": "string", diff --git a/schemas/profile_schema.json b/schemas/profile_schema.json index 009a84953..a5460e27f 100644 --- a/schemas/profile_schema.json +++ b/schemas/profile_schema.json @@ -10,7 +10,7 @@ }, "kind": { "type": "string", - "const": "profile" + "const": "Profile" }, "name": { "type": "string", From 7f529c313893e025b05b5c387bb6935d9d6b4b2d Mon Sep 17 00:00:00 2001 From: James Ball Date: Sun, 12 Jan 2025 23:31:57 +0000 Subject: [PATCH 27/61] Portfolio name changes in preparation for CTP. --- .github/workflows/pages.yml | 34 +++---- .github/workflows/regress.yml | 2 +- .vscode/launch.json | 22 ++++- Rakefile | 93 ++++++++++--------- ...kProcCertClass.yaml => MockProcessor.yaml} | 2 +- ...kProcCertModel.yaml => MockProcessor.yaml} | 4 +- arch/profile/MP-S-64.yaml | 2 +- arch/profile/MP-U-64.yaml | 2 +- .../{MockProfileClass.yaml => Mock.yaml} | 2 +- .../{MockProfileRelease.yaml => Mock.yaml} | 6 +- backends/crd/tasks.rake | 26 +++--- backends/profile/tasks.rake | 20 ++-- lib/arch_obj_models/certificate.rb | 12 +-- 13 files changed, 125 insertions(+), 102 deletions(-) rename arch/proc_cert_class/{MockProcCertClass.yaml => MockProcessor.yaml} (93%) rename arch/proc_cert_model/{MockProcCertModel.yaml => MockProcessor.yaml} (98%) rename arch/profile_class/{MockProfileClass.yaml => Mock.yaml} (96%) rename arch/profile_release/{MockProfileRelease.yaml => Mock.yaml} (84%) diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml index 9f3e3ef9f..6962fbff3 100644 --- a/.github/workflows/pages.yml +++ b/.github/workflows/pages.yml @@ -62,35 +62,31 @@ jobs: - name: Create RVA20 Profile Release PDF Spec run: ./do gen:profile_release_pdf[RVA20] - name: Copy RVA20 Profile Release PDF - run: cp gen/profile/pdf/RVA20.pdf _site/pdfs/RVA20.pdf + run: cp gen/profile/pdf/RVA20ProfileRelease.pdf _site/pdfs - name: Create RVA22 Profile Release PDF Spec run: ./do gen:profile_release_pdf[RVA22] - name: Copy RVA22 Profile Release PDF - run: cp gen/profile/pdf/RVA22.pdf _site/pdfs/RVA22.pdf + run: cp gen/profile/pdf/RVA22ProfileRelease.pdf _site/pdfs + - name: Create RVA23 Profile Release PDF Spec + run: ./do gen:profile_release_pdf[RVA23] - name: Copy RVA23 Profile Release PDF - run: cp gen/profile/pdf/RVA23.pdf _site/pdfs/RVA23.pdf + run: cp gen/profile/pdf/RVA23ProfileRelease.pdf _site/pdfs + - name: Create RVB23 Profile Release PDF Spec + run: ./do gen:profile_release_pdf[RVB23] - name: Copy RVB23 Profile Release PDF - run: cp gen/profile/pdf/RVB23.pdf _site/pdfs/RVB23.pdf + run: cp gen/profile/pdf/RVB23ProfileRelease.pdf _site/pdfs - name: Create RVI20 Profile Release PDF Spec run: ./do gen:profile_release_pdf[RVI20] - name: Copy RVI20 Profile Release PDF - run: cp gen/profile/pdf/RVA20.pdf _site/pdfs/RVI20.pdf - - name: Create MC100-32 PDF Spec + run: cp gen/profile/pdf/RVA20ProfileRelease.pdf _site/pdfs + - name: Create MC100-32-CRD PDF Spec run: ./do gen:proc_crd_pdf[MC100-32] - - name: Copy 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:proc_crd_html[MC100-32] - - name: Copy MC100-32 HTML - run: cp gen/crd/html/MC100-32.html _site/htmls/MC100-32.html - - name: Create MC100-64 PDF Spec + - name: Copy MC100-32-CRD PDF + run: cp gen/crd/pdf/MC100-32-CRD.pdf _site/pdfs + - name: Create MC100-64-CRD PDF Spec run: ./do gen:proc_crd_pdf[MC100-64] - - name: Copy 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:proc_crd_html[MC100-64] - - name: Copy MC100-64 HTML - un: cp gen/crd/html/MC100-64.html _site/htmls/MC100-64.html + - name: Copy MC100-64-CRD PDF + run: cp gen/crd/pdf/MC100-64-CRD.pdf _site/pdfs - 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 00246e694..a7c3e512d 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:proc_crd_pdf[MockProcCertModel] + run: ./do gen:proc_crd_pdf[MockProcessor] regress-gen-profile: runs-on: ubuntu-latest needs: regress-smoke diff --git a/.vscode/launch.json b/.vscode/launch.json index 953c38f92..915e6a87c 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -3,7 +3,7 @@ "configurations": [ { "type": "rdbg", - "name": "MC100-32", + "name": "MC100-32-CRD", "request": "launch", "command": "bundle exec rake", "script": "gen:proc_crd_pdf[MC100-32]", @@ -12,13 +12,22 @@ }, { "type": "rdbg", - "name": "MC200-32", + "name": "MC200-32-CRD", "request": "launch", "command": "bundle exec rake", "script": "gen:proc_crd_pdf[MC200-32]", "args": [], "askParameters": false }, + { + "type": "rdbg", + "name": "MockCRD", + "request": "launch", + "command": "bundle exec rake", + "script": "gen:proc_crd_pdf[MockProcessor]", + "args": [], + "askParameters": false + }, { "type": "rdbg", "name": "portfolios", @@ -28,6 +37,15 @@ "args": [], "askParameters": false }, + { + "type": "rdbg", + "name": "MockProfile", + "request": "launch", + "command": "bundle exec rake", + "script": "gen:profile_release_pdf[Mock]", + "args": [], + "askParameters": false + }, { "type": "rdbg", "name": "RVA20", diff --git a/Rakefile b/Rakefile index 2cd940029..46ee708fa 100644 --- a/Rakefile +++ b/Rakefile @@ -122,7 +122,7 @@ namespace :test do end task schema: "gen:resolved_arch" do puts "Checking arch files against schema.." - Architecture.new("rv64", "#{$root}/resolved_arch").validate(show_progress: true) + Architecture.new("RISC-V Architecture", "#{$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 @@ -328,8 +328,11 @@ namespace :test do puts "UPDATE: Running gen:html for generic_rv64" Rake::Task["gen:html"].invoke("generic_rv64") - puts "UPDATE: Generating MockProcCertModel.pdf" - Rake::Task["#{$root}/gen/crd/pdf/MockProcCertModel.pdf"].invoke + puts "UPDATE: Generating MockProcessor-CRD.pdf" + Rake::Task["#{$root}/gen/crd/pdf/MockProcessor-CRD.pdf"].invoke + + #puts "UPDATE: Generating MockProcessor-CTP.pdf" + #Rake::Task["#{$root}/gen/ctp/pdf/MockProcessor-CTP.pdf"].invoke puts "UPDATE: Generating MockProfileRelease.pdf" Rake::Task["#{$root}/gen/profile/pdf/MockProfileRelease.pdf"].invoke @@ -355,32 +358,36 @@ desc <<~DESC Generate all portfolio-based PDF artifacts (certificates and profiles) DESC task :portfolios do - portfolio_start_msg("MockProcCertModel CRD") - Rake::Task["#{$root}/gen/crd/pdf/MockProcCertModel.pdf"].invoke - portfolio_start_msg("MockProfileRelease CRD") + portfolio_start_msg("MockProcessor-CRD") + Rake::Task["#{$root}/gen/crd/pdf/MockProcessor-CRD.pdf"].invoke + #portfolio_start_msg("MockProcessor-CTP") + #Rake::Task["#{$root}/gen/ctp/pdf/MockProcessor-CTP.pdf"].invoke + portfolio_start_msg("MockProfileRelease") Rake::Task["#{$root}/gen/profile/pdf/MockProfileRelease.pdf"].invoke - 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/pdf/RVI20.pdf"].invoke - portfolio_start_msg("RVA20 Profile Release") - Rake::Task["#{$root}/gen/profile/pdf/RVA20.pdf"].invoke - portfolio_start_msg("RVA22 Profile Release") - Rake::Task["#{$root}/gen/profile/pdf/RVA22.pdf"].invoke - portfolio_start_msg("RVA23 Profile Release") - Rake::Task["#{$root}/gen/profile/pdf/RVA23.pdf"].invoke - portfolio_start_msg("RVB23 Profile Release") - Rake::Task["#{$root}/gen/profile/pdf/RVB23.pdf"].invoke + #portfolio_start_msg("MC100-32-CTP") + #Rake::Task["#{$root}/gen/ctp/pdf/MC100-32-CTP.pdf"].invoke + portfolio_start_msg("MC100-32-CRD") + Rake::Task["#{$root}/gen/crd/pdf/MC100-32-CRD.pdf"].invoke + portfolio_start_msg("MC100-64-CRD") + Rake::Task["#{$root}/gen/crd/pdf/MC100-64-CRD.pdf"].invoke + portfolio_start_msg("MC200-32-CRD") + Rake::Task["#{$root}/gen/crd/pdf/MC200-32-CRD.pdf"].invoke + portfolio_start_msg("MC200-64-CRD") + Rake::Task["#{$root}/gen/crd/pdf/MC200-64-CRD.pdf"].invoke + portfolio_start_msg("MC300-32-CRD") + Rake::Task["#{$root}/gen/crd/pdf/MC300-32-CRD.pdf"].invoke + portfolio_start_msg("MC300-64-CRD") + Rake::Task["#{$root}/gen/crd/pdf/MC300-64-CRD.pdf"].invoke + portfolio_start_msg("RVI20ProfileRelease") + Rake::Task["#{$root}/gen/profile/pdf/RVI20ProfileRelease.pdf"].invoke + portfolio_start_msg("RVA20ProfileRelease") + Rake::Task["#{$root}/gen/profile/pdf/RVA20ProfileRelease.pdf"].invoke + portfolio_start_msg("RVA22ProfileRelease") + Rake::Task["#{$root}/gen/profile/pdf/RVA22ProfileRelease.pdf"].invoke + portfolio_start_msg("RVA23ProfileRelease") + Rake::Task["#{$root}/gen/profile/pdf/RVA23ProfileRelease.pdf"].invoke + portfolio_start_msg("RVB23ProfileRelease") + Rake::Task["#{$root}/gen/profile/pdf/RVB23ProfileRelease.pdf"].invoke end def portfolio_start_msg(name) @@ -391,17 +398,19 @@ def portfolio_start_msg(name) puts "" end -# Shortcut targets for building profiles and certificates. -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/pdf/MockProfileRelease.pdf" -task "RVI20": "#{$root}/gen/profile/pdf/RVI20.pdf" -task "RVA20": "#{$root}/gen/profile/pdf/RVA20.pdf" -task "RVA22": "#{$root}/gen/profile/pdf/RVA22.pdf" -task "RVA23": "#{$root}/gen/profile/pdf/RVA23.pdf" -task "RVB23": "#{$root}/gen/profile/pdf/RVB23.pdf" +# Shortcut targets for building CRDs, CTPs, and Profile Releases. +task "MockCRD": "#{$root}/gen/crd/pdf/MockProcessor-CRD.pdf" +#task "MockCTP": "#{$root}/gen/ctp/pdf/MockProcessor-CTP.pdf" +#task "MC100-32-CTP": "#{$root}/gen/ctp/pdf/MC100-32-CTP.pdf" +task "MC100-32-CRD": "#{$root}/gen/crd/pdf/MC100-32-CRD.pdf" +task "MC100-64-CRD": "#{$root}/gen/crd/pdf/MC100-64-CRD.pdf" +task "MC200-32-CRD": "#{$root}/gen/crd/pdf/MC200-32-CRD.pdf" +task "MC200-64-CRD": "#{$root}/gen/crd/pdf/MC200-64-CRD.pdf" +task "MC300-32-CRD": "#{$root}/gen/crd/pdf/MC300-32-CRD.pdf" +task "MC300-64-CRD": "#{$root}/gen/crd/pdf/MC300-64-CRD.pdf" +task "MockProfile": "#{$root}/gen/profile/pdf/MockProfileRelease.pdf" +task "RVI20": "#{$root}/gen/profile/pdf/RVI20ProfileRelease.pdf" +task "RVA20": "#{$root}/gen/profile/pdf/RVA20ProfileRelease.pdf" +task "RVA22": "#{$root}/gen/profile/pdf/RVA22ProfileRelease.pdf" +task "RVA23": "#{$root}/gen/profile/pdf/RVA23ProfileRelease.pdf" +task "RVB23": "#{$root}/gen/profile/pdf/RVB23ProfileRelease.pdf" diff --git a/arch/proc_cert_class/MockProcCertClass.yaml b/arch/proc_cert_class/MockProcessor.yaml similarity index 93% rename from arch/proc_cert_class/MockProcCertClass.yaml rename to arch/proc_cert_class/MockProcessor.yaml index 8e39ad897..be5f67909 100644 --- a/arch/proc_cert_class/MockProcCertClass.yaml +++ b/arch/proc_cert_class/MockProcessor.yaml @@ -3,7 +3,7 @@ $schema: proc_cert_class_schema.json# kind: Processor Certificate Class processor_kind: Apps Processor -name: MockProcCertClass +name: MockProcessor long_name: Mock Processor Certificate Class Long Name introduction: | diff --git a/arch/proc_cert_model/MockProcCertModel.yaml b/arch/proc_cert_model/MockProcessor.yaml similarity index 98% rename from arch/proc_cert_model/MockProcCertModel.yaml rename to arch/proc_cert_model/MockProcessor.yaml index a81b20dc7..abb5e4e70 100644 --- a/arch/proc_cert_model/MockProcCertModel.yaml +++ b/arch/proc_cert_model/MockProcessor.yaml @@ -2,10 +2,10 @@ $schema: proc_cert_model_schema.json# kind: Processor Certificate Model -name: MockProcCertModel +name: MockProcessor long_name: Mock Processor Certificate Model Long Name class: - $ref: proc_cert_class/MockProcCertClass.yaml# + $ref: proc_cert_class/MockProcessor.yaml# # XLEN used by rakefile base: 64 diff --git a/arch/profile/MP-S-64.yaml b/arch/profile/MP-S-64.yaml index 34cec3064..afa97f1b6 100644 --- a/arch/profile/MP-S-64.yaml +++ b/arch/profile/MP-S-64.yaml @@ -7,7 +7,7 @@ marketing_name: MockProfile 64-bit S-mode description: This is the Mock Profile Supervisor Mode description. mode: S base: 64 -release: { $ref: profile_release/MockProfileRelease.yaml# } +release: { $ref: profile_release/Mock.yaml# } contributors: - name: Micky Mouse email: micky@disney.com diff --git a/arch/profile/MP-U-64.yaml b/arch/profile/MP-U-64.yaml index a69a6ccbe..434dd100f 100644 --- a/arch/profile/MP-U-64.yaml +++ b/arch/profile/MP-U-64.yaml @@ -4,7 +4,7 @@ name: MP-U-64 marketing_name: MockProfile 64-bit Unpriv mode: Unpriv base: 64 -release: { $ref: profile_release/MockProfileRelease.yaml# } +release: { $ref: profile_release/Mock.yaml# } extensions: A: presence: optional diff --git a/arch/profile_class/MockProfileClass.yaml b/arch/profile_class/Mock.yaml similarity index 96% rename from arch/profile_class/MockProfileClass.yaml rename to arch/profile_class/Mock.yaml index 72dda72d6..037cda8c9 100644 --- a/arch/profile_class/MockProfileClass.yaml +++ b/arch/profile_class/Mock.yaml @@ -1,7 +1,7 @@ $schema: profile_class_schema.json# kind: Profile Class processor_kind: Microcontroller -name: MockProfileClass +name: Mock marketing_name: Mock Profile Class introduction: Here's the Mock Profile Class introduction. description: | diff --git a/arch/profile_release/MockProfileRelease.yaml b/arch/profile_release/Mock.yaml similarity index 84% rename from arch/profile_release/MockProfileRelease.yaml rename to arch/profile_release/Mock.yaml index 5bd9b8848..5d152071b 100644 --- a/arch/profile_release/MockProfileRelease.yaml +++ b/arch/profile_release/Mock.yaml @@ -1,9 +1,9 @@ $schema: profile_release_schema.json# kind: Profile Release -name: MockProfileRelease -marketing_name: MockProfileRelease Marketing Name +name: Mock +marketing_name: Mock Profile Release Marketing Name class: - $ref: profile_class/MockProfileClass.yaml# + $ref: profile_class/Mock.yaml# release: 20 state: ratified # current status ["ratified", "development"] versions: diff --git a/backends/crd/tasks.rake b/backends/crd/tasks.rake index 9ac4fde94..d69f1a452 100644 --- a/backends/crd/tasks.rake +++ b/backends/crd/tasks.rake @@ -12,7 +12,7 @@ Dir.glob("#{$root}/arch/proc_cert_model/*.yaml") do |f| class_name = File.basename(model_obj['class']['$ref'].split("#")[0], ".yaml") raise "Ill-formed processor certificate model file #{f}: missing 'class' field" if model_obj['class'].nil? - file "#{$root}/gen/crd/adoc/#{model_name}.adoc" => [ + file "#{$root}/gen/crd/adoc/#{model_name}-CRD.adoc" => [ __FILE__, "#{$root}/arch/proc_cert_class/#{class_name}.yaml", "#{$root}/arch/proc_cert_model/#{model_name}.yaml", @@ -30,12 +30,12 @@ Dir.glob("#{$root}/arch/proc_cert_model/*.yaml") do |f| # 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 #{model_name}" + puts "UPDATE: Creating ProcCertModel object for #{model_name}" proc_cert_model = arch.proc_cert_model(model_name) proc_cert_class = proc_cert_model.proc_cert_class # Create the one PortfolioDesign object required for the ERB evaluation. - puts "UPDATE: Creating PortfolioDesign using processor certificate model #{model_name}" + puts "UPDATE: Creating PortfolioDesign object using processor certificate model #{model_name}" portfolio_design = PortfolioDesign.new(model_name, arch, [proc_cert_model], proc_cert_class) # Create empty binding and then specify explicitly which variables the ERB template can access. @@ -51,24 +51,24 @@ Dir.glob("#{$root}/arch/proc_cert_model/*.yaml") do |f| pf_create_adoc("#{CERT_DOC_DIR}/templates/crd.adoc.erb", erb_binding, t.name, portfolio_design) end - file "#{$root}/gen/crd/pdf/#{model_name}.pdf" => [ + file "#{$root}/gen/crd/pdf/#{model_name}-CRD.pdf" => [ __FILE__, - "#{$root}/gen/crd/adoc/#{model_name}.adoc" + "#{$root}/gen/crd/adoc/#{model_name}-CRD.adoc" ] do |t| - pf_adoc2pdf("#{$root}/gen/crd/adoc/#{model_name}.adoc", t.name) + pf_adoc2pdf("#{$root}/gen/crd/adoc/#{model_name}-CRD.adoc", t.name) end - file "#{$root}/gen/crd/html/#{model_name}.html" => [ + file "#{$root}/gen/crd/html/#{model_name}-CRD.html" => [ __FILE__, - "#{$root}/gen/crd/adoc/#{model_name}.adoc" + "#{$root}/gen/crd/adoc/#{model_name}-CRD.adoc" ] do |t| - pf_adoc2html("#{$root}/gen/crd/adoc/#{model_name}.adoc", t.name) + pf_adoc2html("#{$root}/gen/crd/adoc/#{model_name}-CRD.adoc", t.name) end end namespace :gen do desc <<~DESC - Generate CRD (Certification Requirements Document) as a PDF. + Generate Processor CRD (Certification Requirements Document) as a PDF. Required options: model_name - The name of the certification model under arch/proc_cert_model @@ -85,11 +85,11 @@ namespace :gen do exit 1 end - Rake::Task["#{$root}/gen/crd/pdf/#{model_name}.pdf"].invoke + Rake::Task["#{$root}/gen/crd/pdf/#{model_name}-CRD.pdf"].invoke end desc <<~DESC - Generate CRD (Certification Requirements Document) as an HTML file. + Generate Processor CRD (Certification Requirements Document) as an HTML file. Required options: model_name - The name of the certification model under arch/proc_cert_model @@ -105,6 +105,6 @@ namespace :gen do exit 1 end - Rake::Task["#{$root}/gen/crd/html/#{args[:model_name]}.html"].invoke + Rake::Task["#{$root}/gen/crd/html/#{args[:model_name]}-CRD.html"].invoke end end diff --git a/backends/profile/tasks.rake b/backends/profile/tasks.rake index 7867229f1..28447add5 100644 --- a/backends/profile/tasks.rake +++ b/backends/profile/tasks.rake @@ -39,13 +39,13 @@ Dir.glob("#{$root}/arch/profile_release/*.yaml") do |f| # 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 #{release_name}" + puts "UPDATE: Creating ProfileRelease object for #{release_name}" profile_release = arch.profile_release(release_name) profile_class = profile_release.profile_class # Create the one PortfolioDesign object required for the ERB evaluation. # Provide it with all the profiles in this ProfileRelease. - puts "UPDATE: Creating PortfolioDesign using profile release #{release_name}" + puts "UPDATE: Creating PortfolioDesign object using profile release #{release_name}" portfolio_design = PortfolioDesign.new(release_name, arch, profile_release.profiles, profile_class) @@ -62,18 +62,18 @@ Dir.glob("#{$root}/arch/profile_release/*.yaml") do |f| pf_create_adoc("#{PROFILE_DOC_DIR}/templates/profile.adoc.erb", erb_binding, t.name, portfolio_design) end - file "#{$root}/gen/profile/pdf/#{release_name}.pdf" => [ + file "#{$root}/gen/profile/pdf/#{release_name}ProfileRelease.pdf" => [ __FILE__, - "#{$root}/gen/profile/adoc/#{release_name}.adoc" + "#{$root}/gen/profile/adoc/#{release_name}ProfileRelease.adoc" ] do |t| - pf_adoc2pdf("#{$root}/gen/profile/adoc/#{release_name}.adoc", t.name) + pf_adoc2pdf("#{$root}/gen/profile/adoc/#{release_name}ProfileRelease.adoc", t.name) end - file "#{$root}/gen/profile/html/#{release_name}.html" => [ + file "#{$root}/gen/profile/html/#{release_name}ProfileRelease.html" => [ __FILE__, - "#{$root}/gen/profile/adoc/#{release_name}.adoc" + "#{$root}/gen/profile/adoc/#{release_name}ProfileRelease.adoc" ] do |t| - pf_adoc2html("#{$root}/gen/profile/adoc/#{release_name}.adoc", t.name) + pf_adoc2html("#{$root}/gen/profile/adoc/#{release_name}ProfileRelease.adoc", t.name) end end @@ -96,7 +96,7 @@ namespace :gen do exit 1 end - Rake::Task["#{$root}/gen/profile/pdf/#{release_name}.pdf"].invoke + Rake::Task["#{$root}/gen/profile/pdf/#{release_name}ProfileRelease.pdf"].invoke end desc <<~DESC @@ -117,6 +117,6 @@ namespace :gen do exit 1 end - Rake::Task["#{$root}/gen/profile/html/#{release_name}.html"].invoke + Rake::Task["#{$root}/gen/profile/html/#{release_name}ProfileRelease.html"].invoke end end diff --git a/lib/arch_obj_models/certificate.rb b/lib/arch_obj_models/certificate.rb index 8e5bc4382..b5051bdcb 100644 --- a/lib/arch_obj_models/certificate.rb +++ b/lib/arch_obj_models/certificate.rb @@ -13,9 +13,9 @@ class ProcCertClass < PortfolioClass def mandatory_priv_modes = @data["mandatory_priv_modes"] end -################### +####################### # ProcCertModel Class # -################### +####################### # Holds information about a processor certificate model YAML file. # The inherited "data" member is the database of extensions, instructions, CSRs, etc. @@ -25,8 +25,6 @@ class ProcCertModel < Portfolio # @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 ProcCertModel object for #{name} using arch #{arch.name}" end def unpriv_isa_manual_revision = @data["unpriv_isa_manual_revision"] @@ -152,11 +150,13 @@ def all_in_scope_ext_params @all_in_scope_ext_params = [] @data["extensions"].each do |ext_name, ext_data| - next if ext_name[-1] == "$" + next if ext_name[0] == "$" # Find Extension object from database ext = @arch.extension(ext_name) - raise "Cannot find extension named #{ext_name}" if ext.nil? + if ext.nil? + raise "Cannot find extension named #{ext_name}" + end ext_data["parameters"]&.each do |param_name, param_data| param = ext.params.find { |p| p.name == param_name } From a74125599d0234a84db20d6bd41c4041771ab005 Mon Sep 17 00:00:00 2001 From: James Ball Date: Mon, 13 Jan 2025 00:40:00 +0000 Subject: [PATCH 28/61] One more typo apparently. --- backends/profile/tasks.rake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backends/profile/tasks.rake b/backends/profile/tasks.rake index 28447add5..d98bd2c79 100644 --- a/backends/profile/tasks.rake +++ b/backends/profile/tasks.rake @@ -21,7 +21,7 @@ Dir.glob("#{$root}/arch/profile_release/*.yaml") do |f| profile_pathnames = profile_names.map {|profile_name| "#{$root}/arch/profile/#{profile_name}.yaml" } - file "#{$root}/gen/profile/adoc/#{release_name}.adoc" => [ + file "#{$root}/gen/profile/adoc/#{release_name}ProfileRelease.adoc" => [ __FILE__, "#{$root}/arch/profile_class/#{class_name}.yaml", "#{$root}/arch/profile_release/#{release_name}.yaml", From a204d7cddbaf8aa9083528cb47068a940e9567eb Mon Sep 17 00:00:00 2001 From: James Ball Date: Mon, 13 Jan 2025 01:35:25 +0000 Subject: [PATCH 29/61] Update profile release name --- .github/workflows/regress.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/regress.yml b/.github/workflows/regress.yml index a7c3e512d..1acdbe64c 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_release_pdf[MockProfileRelease] + run: ./do gen:profile_release_pdf[Mock] From 4a1f00f4051ddda3c9817ca6e111367590e34f6a Mon Sep 17 00:00:00 2001 From: James Ball Date: Mon, 13 Jan 2025 17:22:03 +0000 Subject: [PATCH 30/61] Update UDB README to reflect updated Profile/CRD PDF names. --- README.adoc | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/README.adoc b/README.adoc index bca01db3b..a38b721ae 100644 --- a/README.adoc +++ b/README.adoc @@ -8,11 +8,13 @@ The following artifacts have been generated from the top of the `main` branch: * https://riscv-software-src.github.io/riscv-unified-db/example_cfg/html/index.html[configuration-specific documentation] * https://riscv-software-src.github.io/riscv-unified-db/ruby/arch_def/index.html[Ruby API documentation (database interface)] * https://riscv-software-src.github.io/riscv-unified-db/ruby/idl/index.html[Ruby IDL API documentation (IDL compiler)] -* https://riscv-software-src.github.io/riscv-unified-db/pdfs/RVI20.pdf[RVI20 Profile Release] -* https://riscv-software-src.github.io/riscv-unified-db/pdfs/RVA20.pdf[RVA20 Profile Release] -* https://riscv-software-src.github.io/riscv-unified-db/pdfs/RVA22.pdf[RVA22 Profile Release] -* https://riscv-software-src.github.io/riscv-unified-db/pdfs/MC100-32.pdf[MC100-32 Certification Requirements Document] -* https://riscv-software-src.github.io/riscv-unified-db/pdfs/MC100-64.pdf[MC100-64 Certification Requirements Document] +* https://riscv-software-src.github.io/riscv-unified-db/pdfs/RVI20ProfileRelease.pdf[RVI20 Profile Release] +* https://riscv-software-src.github.io/riscv-unified-db/pdfs/RVA20ProfileRelease.pdf[RVA20 Profile Release] +* https://riscv-software-src.github.io/riscv-unified-db/pdfs/RVA22ProfileRelease.pdf[RVA22 Profile Release] +* https://riscv-software-src.github.io/riscv-unified-db/pdfs/RVA23ProfileRelease.pdf[RVA23 Profile Release] +* https://riscv-software-src.github.io/riscv-unified-db/pdfs/RVB23ProfileRelease.pdf[RVB23 Profile Release] +* https://riscv-software-src.github.io/riscv-unified-db/pdfs/MC100-32-CRD.pdf[MC100-32 Certification Requirements Document] +* https://riscv-software-src.github.io/riscv-unified-db/pdfs/MC100-64-CRD.pdf[MC100-64 Certification Requirements Document] == Overview From 9d79d62069764cad17d8381e1f16e3455ca51a70 Mon Sep 17 00:00:00 2001 From: James Ball Date: Wed, 15 Jan 2025 01:12:36 +0000 Subject: [PATCH 31/61] Clean up adoc links before starting to mock up CTPs --- arch/proc_cert_model/MockProcessor.yaml | 12 +- backends/cfg_html_doc/adoc_gen.rake | 10 +- backends/cfg_html_doc/html_gen.rake | 40 +--- backends/crd/templates/crd.adoc.erb | 30 +-- backends/ext_pdf_doc/tasks.rake | 40 +--- backends/manual/tasks.rake | 10 +- backends/portfolio/tasks.rake | 5 +- .../portfolio/templates/csr_appendix.adoc.erb | 8 +- .../portfolio/templates/ext_appendix.adoc.erb | 18 +- backends/profile/tasks.rake | 1 + cert_flow.txt | 82 ------- lib/arch_obj_models/certificate.rb | 63 +++-- lib/arch_obj_models/extension.rb | 130 +--------- lib/arch_obj_models/parameter.rb | 137 +++++++++++ lib/arch_obj_models/portfolio.rb | 57 ++--- lib/architecture.rb | 8 +- lib/backend_helpers.rb | 224 ++++++++++++++++++ lib/cfg_arch.rb | 55 +++-- lib/design.rb | 26 +- lib/idesign.rb | 14 +- lib/idl/passes/gen_adoc.rb | 2 +- lib/portfolio_design.rb | 58 +++-- lib/template_helpers.rb | 87 ------- lib/test/test_backend_helpers.rb | 76 ++++++ 24 files changed, 648 insertions(+), 545 deletions(-) delete mode 100644 cert_flow.txt create mode 100644 lib/arch_obj_models/parameter.rb create mode 100644 lib/backend_helpers.rb delete mode 100644 lib/template_helpers.rb create mode 100644 lib/test/test_backend_helpers.rb diff --git a/arch/proc_cert_model/MockProcessor.yaml b/arch/proc_cert_model/MockProcessor.yaml index abb5e4e70..d75fb5903 100644 --- a/arch/proc_cert_model/MockProcessor.yaml +++ b/arch/proc_cert_model/MockProcessor.yaml @@ -169,18 +169,10 @@ extensions: CACHE_BLOCK_SIZE: schema: const: 64 - Zba: + B: presence: mandatory version: "~> 1.0" - note: "Added these as mandatory to see if bug in profiles not listing instructions in appendix is here in CRD too." - Zbb: - presence: mandatory - version: "~> 1.0" - note: "Added these as mandatory to see if bug in profiles not listing instructions in appendix is here in CRD too." - Zbs: - presence: mandatory - version: "~> 1.0" - note: "Added these as mandatory to see if bug in profiles not listing instructions in appendix is here in CRD too." + note: "Added this as mandatory to see if Zba, Zbb, and Zbs are included." requirement_groups: - name: Req-Grp-Any-XLEN diff --git a/backends/cfg_html_doc/adoc_gen.rake b/backends/cfg_html_doc/adoc_gen.rake index 0c4a67aae..f65a33a27 100644 --- a/backends/cfg_html_doc/adoc_gen.rake +++ b/backends/cfg_html_doc/adoc_gen.rake @@ -30,14 +30,14 @@ require "ruby-prof" cfg_arch.transitive_implemented_csrs.each do |csr| path = dir_path / "#{csr.name}.adoc" puts " Generating #{path}" - File.write(path, cfg_arch.find_replace_links(erb.result(binding))) + File.write(path, cfg_arch.convert_monospace_to_links(erb.result(binding))) end when "inst" cfg_arch.transitive_implemented_instructions.each do |inst| path = dir_path / "#{inst.name}.adoc" puts " Generating #{path}" # RubyProf.start - File.write(path, cfg_arch.find_replace_links(erb.result(binding))) + File.write(path, cfg_arch.convert_monospace_to_links(erb.result(binding))) # result = RubyProf.stop # RubyProf::FlatPrinter.new(result).print(STDOUT) end @@ -46,13 +46,13 @@ require "ruby-prof" 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))) + File.write(path, cfg_arch.convert_monospace_to_links(erb.result(binding))) end when "func" global_symtab = cfg_arch.symtab path = dir_path / "funcs.adoc" puts " Generating #{path}" - File.write(path, cfg_arch.find_replace_links(erb.result(binding))) + File.write(path, cfg_arch.convert_monospace_to_links(erb.result(binding))) else raise "todo" end @@ -107,7 +107,7 @@ require "ruby-prof" raise "Unsupported type" end - File.write t.name, cfg_arch.find_replace_links(lines.join("\n")) + File.write t.name, cfg_arch.convert_monospace_to_links(lines.join("\n")) end end diff --git a/backends/cfg_html_doc/html_gen.rake b/backends/cfg_html_doc/html_gen.rake index 1a2534fa2..908da1ec4 100644 --- a/backends/cfg_html_doc/html_gen.rake +++ b/backends/cfg_html_doc/html_gen.rake @@ -1,40 +1,6 @@ # frozen_string_literal: true -# Utilities for generating an Antora site out of an architecture def -module AntoraUtils - class << self - def resolve_links(path_or_str) - str = - if path_or_str.is_a?(Pathname) - path_or_str.read - else - path_or_str - end - str.gsub(/%%LINK%([^;%]+)\s*;\s*([^;%]+)\s*;\s*([^%]+)%%/) do - type = Regexp.last_match[1] - name = Regexp.last_match[2] - link_text = Regexp.last_match[3] - - case type - when "inst" - "xref:insts:#{name}.adoc##{name}-def[#{link_text.gsub(']', '\]')}]" - when "csr" - "xref:csrs:#{name}.adoc##{name}-def[#{link_text.gsub(']', '\]')}]" - when "csr_field" - csr_name, field_name = name.split('.') - "xref:csrs:#{csr_name}.adoc##{csr_name}-#{field_name}-def[#{link_text.gsub(']', '\]')}]" - when "ext" - "xref:exts:#{name}.adoc##{name}-def[#{link_text.gsub(']', '\]')}]" - when "func" - "xref:funcs:funcs.adoc##{name}-func-def[#{link_text.gsub(']', '\]')}]" - else - raise "Unhandled link type '#{type}' for '#{name}' #{match.captures}" - end - end - end - end -end - +# fill out templates for every csr, inst, ext, and func ["csr", "inst", "ext", "func"].each do |type| rule %r{#{$root}/gen/cfg_html_doc/.*/antora/modules/#{type}s/pages/.*\.adoc} => proc { |tname| config_name = Pathname.new(tname).relative_path_from("#{$root}/gen/cfg_html_doc").to_s.split("/")[0] @@ -86,7 +52,7 @@ rule %r{#{$root}/gen/cfg_html_doc/.*/antora/modules/ROOT/pages/config.adoc} => p cfg_arch = cfg_arch_for(config_name) 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(cfg_arch.convert_monospace_to_links(erb.result(binding))) end rule %r{#{$root}/gen/cfg_html_doc/.*/antora/modules/ROOT/pages/landing.adoc} => proc { |tname| @@ -100,7 +66,7 @@ rule %r{#{$root}/gen/cfg_html_doc/.*/antora/modules/ROOT/pages/landing.adoc} => cfg_arch = cfg_arch_for(config_name) FileUtils.mkdir_p File.dirname(t.name) - File.write t.name, AntoraUtils.resolve_links(cfg_arch.find_replace_links(File.read(t.prerequisites[0]))) + File.write t.name, AntoraUtils.resolve_links(cfg_arch.convert_monospace_to_links(File.read(t.prerequisites[0]))) end rule %r{#{$root}/gen/cfg_html_doc/.*/antora/antora.yml} => proc { |tname| diff --git a/backends/crd/templates/crd.adoc.erb b/backends/crd/templates/crd.adoc.erb index ed1dca355..02f847ed9 100644 --- a/backends/crd/templates/crd.adoc.erb +++ b/backends/crd/templates/crd.adoc.erb @@ -281,7 +281,7 @@ None <% ext_reqs.each do |ext_req| -%> <% ext = arch.extension(ext_req.name) -%> | <%= ext_req.req_id %> -| <-def,<%= ext_req.name %>>> +| <%= link_to_ext(ext_req.name) %> | <%= ext_req.requirement_specs.map(&:to_s).join(", ") %> | <%= ext.nil? ? "" : ext.long_name %> | <%= ext_req.note.nil? ? "" : ext_req.note %> @@ -319,21 +319,21 @@ 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 proc_cert_model.all_in_scope_ext_params.empty? -%> +<% if portfolio_design.all_in_scope_params.empty? -%> None <% else -%> [cols="4,2,1,1,2"] |=== | Parameter | Type | Allowed Value(s) | Extension(s) | Note -<% proc_cert_model.all_in_scope_ext_params.each do |in_scope_ext_param| -%> -<% param = in_scope_ext_param.param -%> -<% exts = proc_cert_model.all_in_scope_exts_with_param(param) -%> -| <%= param.name_potentially_with_link(exts) %> +<% design.all_in_scope_params.each do |in_scope_param| -%> +<% param = in_scope_param.param -%> +<% in_scope_exts = portfolio_design.all_in_scope_exts_with_param(param) -%> +| <%= param.name_potentially_with_link(in_scope_exts) %> | <%= param.schema_type %> -| <%= in_scope_ext_param.allowed_values %> -| <% exts.each do |ext| -%><-param-<%= param.name %>-def,<%= ext.name %>>> <% end # do ext -%> -a| <%= in_scope_ext_param.note %> +| <%= in_scope_param.allowed_values %> +| <%= in_scope_exts.each { |ext| param.name_with_link(ext) } %> +a| <%= in_scope_param.note %> <% end # do -%> |=== <% end # if table -%> @@ -344,18 +344,18 @@ 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 proc_cert_model.all_out_of_scope_params.empty? -%> +<% if portfolio_design.all_out_of_scope_params.empty? -%> None <% else -%> [%autowidth] |=== | Parameters | Type | Extension(s) -<% proc_cert_model.all_out_of_scope_params.each do |param| -%> -<% exts = proc_cert_model.all_in_scope_exts_without_param(param) -%> +<% portfolio_design.all_out_of_scope_params.each do |param| -%> +<% exts = portfolio_design.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 -%> +| <%= exts.each { |ext| param.name_with_link(ext) } %> <% end # do -%> |=== @@ -411,7 +411,7 @@ See https://github.com/riscv-software-src/riscv-unified-db/issues/291 and https: | Name | Long Name | Address | Mode | Primary Extension <% design.in_scope_csrs.sort_by!(&:name).each do |csr| -%> -| <-def,<%= csr.name %>>> +| <%= link_to_csr(csr.name) %> | <%= csr.long_name %> | <%= "0x#{csr.address.to_s(16)}" %> | <%= csr.priv_mode %> @@ -428,7 +428,7 @@ See https://github.com/riscv-software-src/riscv-unified-db/issues/291 and https: <% design.in_scope_csrs.sort_by!(&:address).each do |csr| -%> | <%= "0x#{csr.address.to_s(16)}" %> | <%= csr.priv_mode %> -| <-def,<%= csr.name %>>> +| <%= link_to_csr(csr.name) %> | <%= csr.long_name %> | <%= csr.primary_defined_by %> <% end # do -%> diff --git a/backends/ext_pdf_doc/tasks.rake b/backends/ext_pdf_doc/tasks.rake index ae10c3a4b..98342e977 100644 --- a/backends/ext_pdf_doc/tasks.rake +++ b/backends/ext_pdf_doc/tasks.rake @@ -9,44 +9,6 @@ require_relative "#{$lib}/idl/passes/gen_adoc" EXT_PDF_DOC_DIR = Pathname.new "#{$root}/backends/ext_pdf_doc" -# Utilities for generating an Antora site out of an architecture def -module AsciidocUtils - class << self - def resolve_links(path_or_str) - str = - if path_or_str.is_a?(Pathname) - path_or_str.read - else - path_or_str - end - str.gsub(/%%LINK%([^;%]+)\s*;\s*([^;%]+)\s*;\s*([^%]+)%%/) do - type = Regexp.last_match[1] - name = Regexp.last_match[2] - link_text = Regexp.last_match[3] - - case type - when "inst" - "xref:#inst-#{name.gsub('.', '_')}-def[#{link_text.gsub(']', '\]')}]" - when "csr" - "xref:#csr-#{name}-def[#{link_text.gsub(']', '\]')}]" - when "csr_field" - csr_name, field_name = name.split('.') - # "xref:csrs:#{csr_name}.adoc##{csr_name}-#{field_name}-def[#{link_text.gsub(']', '\]')}]" - link_text - when "ext" - # "xref:exts:#{name}.adoc##{name}-def[#{link_text.gsub(']', '\]')}]" - link_text - when "func" - # "xref:funcs:funcs.adoc##{name}-func-def[#{link_text.gsub(']', '\]')}]" - link_text - else - raise "Unhandled link type '#{type}' for '#{name}' #{match.captures}" - end - end - end - end -end - file "#{$root}/ext/docs-resources/themes/riscv-pdf.yml" => "#{$root}/.gitmodules" do |t| system "git submodule update --init ext/docs-resources" end @@ -153,7 +115,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(design.find_replace_links(erb.result(binding))) + File.write t.name, AsciidocUtils.resolve_links(design.convert_monospace_to_links(erb.result(binding))) end namespace :gen do diff --git a/backends/manual/tasks.rake b/backends/manual/tasks.rake index a3363080c..34e3a58cd 100644 --- a/backends/manual/tasks.rake +++ b/backends/manual/tasks.rake @@ -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(design.find_replace_links(erb.result(binding))) + File.write t.name, AntoraUtils.resolve_links(design.convert_monospace_to_links(erb.result(binding))) end # rule to create csr appendix page @@ -219,7 +219,7 @@ rule %r{#{MANUAL_GEN_DIR}/.*/.*/antora/modules/csrs/pages/.*\.adoc} => [ erb.filename = csr_template_path.to_s FileUtils.mkdir_p File.dirname(t.name) - File.write t.name, AntoraUtils.resolve_links(design.find_replace_links(erb.result(binding))) + File.write t.name, AntoraUtils.resolve_links(design.convert_monospace_to_links(erb.result(binding))) end # rule to create ext appendix page @@ -238,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(design.find_replace_links(erb.result(binding))) + File.write t.name, AntoraUtils.resolve_links(design.convert_monospace_to_links(erb.result(binding))) end # rule to create IDL function appendix page @@ -253,7 +253,7 @@ rule %r{#{MANUAL_GEN_DIR}/.*/.*/antora/modules/funcs/pages/funcs.adoc} => [ erb.filename = funcs_template_path.to_s FileUtils.mkdir_p File.dirname(t.name) - File.write t.name, AntoraUtils.resolve_links(design.find_replace_links(erb.result(binding))) + File.write t.name, AntoraUtils.resolve_links(design.convert_monospace_to_links(erb.result(binding))) end # rule to create IDL function appendix page @@ -270,7 +270,7 @@ rule %r{#{MANUAL_GEN_DIR}/.*/.*/antora/modules/params/pages/param_list.adoc} => erb.filename = param_list_template_path.to_s FileUtils.mkdir_p File.dirname(t.name) - File.write t.name, AntoraUtils.resolve_links(design.find_replace_links(erb.result(binding))) + File.write t.name, AntoraUtils.resolve_links(design.convert_monospace_to_links(erb.result(binding))) end rule %r{#{MANUAL_GEN_DIR}/.*/top/.*/antora/landing/antora.yml} => [ diff --git a/backends/portfolio/tasks.rake b/backends/portfolio/tasks.rake index 082dbb9ef..a1425cffa 100644 --- a/backends/portfolio/tasks.rake +++ b/backends/portfolio/tasks.rake @@ -32,9 +32,8 @@ def pf_create_adoc(erb_template_pname, erb_binding, target_pname, portfolio_desi # each with a variable name to aid in running a command-line debugger on this code. puts "UPDATE: Converting ERB template to adoc for #{portfolio_design.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) + erb_result_monospace_converted_to_links = portfolio_design.convert_monospace_to_links(erb_result) + erb_result_with_links_resolved = AsciidocUtils.resolve_links(erb_result_monospace_converted_to_links) File.write(target_pname, erb_result_with_links_resolved) puts "UPDATE: Generated adoc in #{target_pname}" diff --git a/backends/portfolio/templates/csr_appendix.adoc.erb b/backends/portfolio/templates/csr_appendix.adoc.erb index 3e27067fe..e950561da 100644 --- a/backends/portfolio/templates/csr_appendix.adoc.erb +++ b/backends/portfolio/templates/csr_appendix.adoc.erb @@ -4,7 +4,7 @@ <% portfolio_design.in_scope_csrs.sort_by(&:name).each do |csr| -%> <<< -[[csr-<%= csr.name %>-def]] +<%= anchor_for_csr(csr.name) %> === <%= csr.name %> *<%= csr.long_name %>* @@ -71,7 +71,7 @@ This CSR format changes dynamically with XLEN. @ Name @ Location @ Type @ Reset Value <%- csr.implemented_fields(design).each do |field| -%> -@ xref:<%=csr.name%>-<%=field.name%>-def[`<%= field.name %>`] +@ <%= link_to_csr_field(csr.name, field.name) %> a@ <%- if field.dynamic_location?(design) -%> @@ -110,8 +110,8 @@ This CSR has no fields. However, it must still exist (not cause an `Illegal Inst <%- else -%> <%- csr.implemented_fields(design).each do |field| -%> -[[<%=csr.name%>-<%=field.name%>-def]] -===== `<%= field.name %>` +<%= anchor_for_csr_field(csr.name, field.name) %> +===== `<%= field.name %>` Field <%- if !field.defined_in_all_bases? -%> IMPORTANT: <%= field.name %> is only defined in <%= field.base32_only? ? "RV32" : "RV64" %> (`<%= field.base32_only? ? field.location_cond32 : field.location_cond64 %>`) diff --git a/backends/portfolio/templates/ext_appendix.adoc.erb b/backends/portfolio/templates/ext_appendix.adoc.erb index 5bda503cb..ed122e439 100644 --- a/backends/portfolio/templates/ext_appendix.adoc.erb +++ b/backends/portfolio/templates/ext_appendix.adoc.erb @@ -4,7 +4,7 @@ <% portfolio_design.in_scope_ext_reqs.each do |ext_req| -%> <% ext = arch.extension(ext_req.name) -%> -[[ext-<%= ext_req.name %>-def]] +<%= anchor_for_ext(ext_req.name) %> === Extension <%= ext_req.name %> + *Long Name*: <%= ext.long_name %> + @@ -82,7 +82,7 @@ The following instructions are added by this extension: <% end -%> <% unless ext.params.empty? -%> -<% if portfolio_design.in_scope_ext_params(ext_req).empty? && portfolio_design.out_of_scope_params(ext_req.name).empty? -%> +<% if portfolio_design.in_scope_params(ext_req).empty? && portfolio_design.out_of_scope_params(ext_req.name).empty? -%> ==== Parameters This extension has the following parameters (AKA implementation options): @@ -98,24 +98,24 @@ This extension has the following parameters (AKA implementation options): <% end # if in_scope & out_of_scope empty -%> <% end # unless table -%> -<% unless portfolio_design.in_scope_ext_params(ext_req).empty? -%> +<% unless portfolio_design.in_scope_params(ext_req).empty? -%> ==== IN-SCOPE Parameters -<% portfolio_design.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 %>:: +<% portfolio_design.in_scope_params(ext_req).each do |param| -%> +<%= anchor_for_ext_param(ext_req.name, param.name) %> +<%= param.name %> ⇒ <%= param.param.schema_type %>:: + -- -<%= ext_param.param.desc %> +<%= param.param.desc %> -- -<% end # do ext_param -%> +<% end # do param -%> <% end # unless table -%> <% unless portfolio_design.out_of_scope_params(ext_req.name).empty? -%> ==== OUT-OF-SCOPE Parameters <% portfolio_design.out_of_scope_params(ext_req.name).each do |param| -%> -[[ext-<%= ext_req.name %>-param-<%= param.name %>-def]] +<%= anchor_for_ext_param(ext_req.name, param.name) %> <%= param.name %> ⇒ <%= param.schema_type %>:: + -- diff --git a/backends/profile/tasks.rake b/backends/profile/tasks.rake index d98bd2c79..25be87c69 100644 --- a/backends/profile/tasks.rake +++ b/backends/profile/tasks.rake @@ -29,6 +29,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}/lib/backend_helpers.rb", "#{$root}/backends/portfolio/templates/ext_appendix.adoc.erb", "#{$root}/backends/portfolio/templates/inst_appendix.adoc.erb", "#{$root}/backends/portfolio/templates/csr_appendix.adoc.erb", diff --git a/cert_flow.txt b/cert_flow.txt deleted file mode 100644 index 56a330243..000000000 --- a/cert_flow.txt +++ /dev/null @@ -1,82 +0,0 @@ -backends/crd/tasks.rake -bootstrap_cfg_arch = cfg_arch_for("rv#{base}") # rv32 or rv64 - Rakefile - Calls ConfiguredArchitecture.new("gen/resolved_arch/rv32") - Calls Architecture.new # Parent class - Calls Config.create("cfgs/rv32/cfg.yaml") # Factory class method - Loads config yaml file into @data # File specifies if fully, partially, or unconfigured - Calls PartialConfig.new(cfg path, @data) # Uses Ruby send() method - @mxlen = @data["params"].xlen - @name = "rv32" -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 @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_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) -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 - - -============================================================================================== -Actual rv32/cfg.yaml ---- -$schema: config_schema.json# -kind: architecture configuration -type: partially configured -name: rv32 -description: A generic RV32 system; only MXLEN is known -params: - XLEN: 32 -mandatory_extensions: - - name: "I" - version: ">= 0" - - name: "Sm" - version: ">= 0" -=============================================================================================== -Bootstrap /tmp/.../MC100-32/cfg.yaml ---- -"$schema": config_schema.json -type: partially configured -kind: architecture configuration -name: MC100-32 -description: A partially configured architecture definition corresponding to the MC100-32 - portfolio. -mandatory_extensions: -- name: I - version: - - "~> 2.1" -- name: C - version: - - "~> 2.2" -- name: M - version: - - "~> 2.0" -- name: Zicsr - version: - - "~> 2.0" -- name: Zicntr - version: - - "~> 2.0" -- name: Sm - version: - - "~> 1.11.0" -params: - MISALIGNED_SPLIT_STRATEGY: by_byte - PRECISE_SYNCHRONOUS_EXCEPTIONS: true - TRAP_ON_ECALL_FROM_M: true - TRAP_ON_EBREAK: true - M_MODE_ENDIANESS: little - XLEN: 32 diff --git a/lib/arch_obj_models/certificate.rb b/lib/arch_obj_models/certificate.rb index b5051bdcb..db51ca58a 100644 --- a/lib/arch_obj_models/certificate.rb +++ b/lib/arch_obj_models/certificate.rb @@ -136,18 +136,18 @@ def requirement_groups @requirement_groups end - ############################################ - # Routines using InScopeExtensionParameter # - ############################################ + ################################### + # Routines using InScopeParameter # + ################################### - # @return [Array] Sorted list of parameters specified by any extension in portfolio. + # @return [Array] Sorted list of parameters specified by any extension in portfolio. # These are always IN-SCOPE by definition (since they are listed in the portfolio). # Can have multiple array entries with the same parameter name since multiple extensions may define # the same parameter. - def all_in_scope_ext_params - return @all_in_scope_ext_params unless @all_in_scope_ext_params.nil? + def all_in_scope_params + return @all_in_scope_params unless @all_in_scope_params.nil? - @all_in_scope_ext_params = [] + @all_in_scope_params = [] @data["extensions"].each do |ext_name, ext_data| next if ext_name[0] == "$" @@ -163,25 +163,24 @@ def all_in_scope_ext_params raise "There is no param '#{param_name}' in extension '#{ext_name}" if param.nil? 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).satisfied_by?(ext_ver) && - param.defined_in_extension_version?(ext_ver) - end + ver_req = ext_data["version"] || ">= #{ext.min_version.version_spec}" + ExtensionRequirement.new(ext_name, ver_req, @arch).satisfied_by?(ext_ver) && + param.defined_in_extension_version?(ext_ver) + end - @all_in_scope_ext_params << - InScopeExtensionParameter.new(param, param_data["schema"], param_data["note"]) + @all_in_scope_params << InScopeParameter.new(param, param_data["schema"], param_data["note"]) end end - @all_in_scope_ext_params.sort + @all_in_scope_params.sort! end # @param [ExtensionRequirement] - # @return [Array] Sorted list of extension parameters from portfolio for given extension. + # @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) + def in_scope_params(ext_req) raise ArgumentError, "Expecting ExtensionRequirement" unless ext_req.is_a?(ExtensionRequirement) - ext_params = [] # Local variable, no caching + params = [] # Local variable, no caching # Get extension information from portfolio YAML for passed in extension requirement. ext_data = @data["extensions"][ext_req.name] @@ -194,23 +193,21 @@ def in_scope_ext_params(ext_req) # Loop through an extension's parameter constraints (hash) from the certificate model. # Note that "&" is the Ruby safe navigation operator (i.e., skip do loop if nil). ext_data["parameters"]&.each do |param_name, param_data| - # Find ExtensionParameter object from database - ext_param = ext.params.find { |p| p.name == param_name } - raise "There is no param '#{param_name}' in extension '#{ext_req.name}" if ext_param.nil? + # Find Parameter object from database + param = ext.params.find { |p| p.name == param_name } + raise "There is no param '#{param_name}' in extension '#{ext_req.name}" if param.nil? next unless ext.versions.any? do |ext_ver| - ext_req.satisfied_by?(ext_ver) && - ext_param.defined_in_extension_version?(ext_ver) - end + ext_req.satisfied_by?(ext_ver) && param.defined_in_extension_version?(ext_ver) + end - ext_params << - InScopeExtensionParameter.new(ext_param, param_data["schema"], param_data["note"]) + params << InScopeParameter.new(param, param_data["schema"], param_data["note"]) end - ext_params.sort! + params.sort! end - # @return [Array] Sorted list of parameters out of scope across all in scope extensions + # @return [Array] Sorted list of parameters out of scope across all in scope extensions # (those listed as mandatory or optional in the certificate model). def all_out_of_scope_params return @all_out_of_scope_params unless @all_out_of_scope_params.nil? @@ -219,7 +216,7 @@ def all_out_of_scope_params in_scope_ext_reqs.each do |ext_req| ext = @arch.extension(ext_req.name) ext.params.each do |param| - next if all_in_scope_ext_params.any? { |c| c.param.name == param.name } + next if all_in_scope_params.any? { |c| c.param.name == param.name } next unless ext.versions.any? do |ext_ver| ext_req.satisfied_by?(ext_ver) && @@ -233,16 +230,16 @@ def all_out_of_scope_params end # @param ext_name [String] Extension name - # @return [Array] Sorted list of 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 } }.sort end - # @param param [ExtensionParameter] + # @param param [Parameter] # @return [Array] Sorted list of all in-scope extensions that define this parameter # in the database and the parameter is in-scope. def all_in_scope_exts_with_param(param) - raise ArgumentError, "Expecting ExtensionParameter" unless param.is_a?(ExtensionParameter) + raise ArgumentError, "Expecting Parameter" unless param.is_a?(Parameter) exts = [] @@ -267,11 +264,11 @@ def all_in_scope_exts_with_param(param) exts.sort_by!(&:name) end - # @param param [ExtensionParameter] + # @param param [Parameter] # @return [Array] List of all in-scope extensions that define this parameter in the # database but the parameter is out-of-scope. def all_in_scope_exts_without_param(param) - raise ArgumentError, "Expecting ExtensionParameter" unless param.is_a?(ExtensionParameter) + raise ArgumentError, "Expecting Parameter" unless param.is_a?(Parameter) exts = [] # Local variable, no caching diff --git a/lib/arch_obj_models/extension.rb b/lib/arch_obj_models/extension.rb index ce1b0edee..b4d4821a5 100644 --- a/lib/arch_obj_models/extension.rb +++ b/lib/arch_obj_models/extension.rb @@ -1,132 +1,10 @@ # frozen_string_literal: true require_relative "database_obj" +require_relative "parameter" require_relative "schema" require_relative "../version" -# A parameter (AKA option, AKA implementation-defined value) supported by an extension -class ExtensionParameter - # @return [Architecture] The defining architecture - attr_reader :arch - - # @return [String] Parameter name - attr_reader :name - - # @return [String] Asciidoc description - attr_reader :desc - - # @return [Schema] JSON Schema for this param - attr_reader :schema - - # @return [String] Ruby code to perform validation above and beyond JSON schema - # @return [nil] If there is no extra validation - attr_reader :extra_validation - - # @return [Array] The extension(s) that define this parameter - # - # Some parameters are defined by multiple extensions (e.g., CACHE_BLOCK_SIZE by Zicbom and Zicboz). - # When defined in multiple places, the parameter *must* mean the exact same thing. - attr_reader :exts - - # @returns [Idl::Type] Type of the parameter - attr_reader :idl_type - - # Pretty convert extension schema to a string. - def schema_type - @schema.to_pretty_s - end - - # @param ext [Extension] - # @param name [String] - # @param data [Hash>" - else - name - end - end - - # sorts by name - def <=>(other) - raise ArgumentError, "ExtensionParameters are only comparable to other extension parameters" unless other.is_a?(ExtensionParameter) - - @name <=> other.name - end -end - -class ExtensionParameterWithValue - # @return [Object] The parameter value - attr_reader :value - - # @return [String] Parameter name - def name = @param.name - - # @return [String] Asciidoc description - def desc = @param.desc - - # @return [Hash] JSON Schema for the parameter value - def schema = @param.schema - - # @return [String] Ruby code to perform validation above and beyond JSON schema - # @return [nil] If there is no extra validatino - def extra_validation = @param.extra_validation - - # @return [Extension] The extension that defines this parameter - def exts = @param.exts - - def initialize(param, value) - @param = param - @value = value - end -end - # Extension definition class Extension < DatabaseObject # @return [String] Long name of the extension @@ -178,14 +56,14 @@ def min_ratified_version ratified_versions.min { |a, b| a.version_spec <=> b.version_spec } end - # @return [Array] List of parameters added by this extension + # @return [Array] List of parameters added by this extension def params return @params unless @params.nil? @params = [] if @data.key?("params") @data["params"].each do |param_name, param_data| - @params << ExtensionParameter.new(self, param_name, param_data) + @params << Parameter.new(self, param_name, param_data) end end @params @@ -363,7 +241,7 @@ def contributors @contributors end - # @return [Array] The list of parameters for this extension version + # @return [Array] The list of parameters for this extension version def params @ext.params.select { |p| p.defined_in_extension_version?(self) } end diff --git a/lib/arch_obj_models/parameter.rb b/lib/arch_obj_models/parameter.rb new file mode 100644 index 000000000..f55b4511b --- /dev/null +++ b/lib/arch_obj_models/parameter.rb @@ -0,0 +1,137 @@ +# frozen_string_literal: true + +require_relative "database_obj" +require_relative "schema" +require_relative "../version" + +# A parameter (AKA option, AKA implementation-defined value) supported by an extension +class Parameter + # @return [Architecture] The defining architecture + attr_reader :arch + + # @return [String] Parameter name + attr_reader :name + + # @return [String] Asciidoc description + attr_reader :desc + + # @return [Schema] JSON Schema for this param + attr_reader :schema + + # @return [String] Ruby code to perform validation above and beyond JSON schema + # @return [nil] If there is no extra validation + attr_reader :extra_validation + + # Some parameters are defined by multiple extensions (e.g., CACHE_BLOCK_SIZE by Zicbom and Zicboz). + # When defined in multiple places, the parameter *must* mean the exact same thing. + # + # @return [Array] The extension(s) that define this parameter + attr_reader :exts + + # @returns [Idl::Type] Type of the parameter + attr_reader :idl_type + + # Pretty convert extension schema to a string. + def schema_type + @schema.to_pretty_s + end + + # @param ext [Extension] + # @param name [String] + # @param data [Hash] List of all in-scope extensions that define this parameter. + # @return [String] Text that includes the parameter name and a link to the parameter definition + # if only one extension defines the parameter, otherwise just the parameter name. + def name_potentially_with_link(in_scope_exts) + raise ArgumentError, "Expecting Array" unless in_scope_exts.is_a?(Array) + raise ArgumentError, "Expecting Array[Extension]" unless in_scope_exts[0].is_a?(Extension) + + if in_scope_exts.size == 1 + link_to_ext_param(in_scope_exts[0].name, name) + else + name + end + end + + # sorts by name + def <=>(other) + raise ArgumentError, "Parameters are only comparable to other extension parameters" unless other.is_a?(Parameter) + + @name <=> other.name + end +end + +class ParameterWithValue + # @return [Object] The parameter value + attr_reader :value + + # @return [String] Parameter name + def name = @param.name + + # @return [String] Asciidoc description + def desc = @param.desc + + # @return [Hash] JSON Schema for the parameter value + def schema = @param.schema + + # @return [String] Ruby code to perform validation above and beyond JSON schema + # @return [nil] If there is no extra validatino + def extra_validation = @param.extra_validation + + # @return [Extension] The extension that defines this parameter + def exts = @param.exts + + def initialize(param, value) + @param = param + @value = value + end +end diff --git a/lib/arch_obj_models/portfolio.rb b/lib/arch_obj_models/portfolio.rb index a8c1b4a10..a5c3fc747 100644 --- a/lib/arch_obj_models/portfolio.rb +++ b/lib/arch_obj_models/portfolio.rb @@ -73,7 +73,7 @@ def param_values @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) + @param_values.merge!(portfolio.all_in_scope_params.select(&:single_value?).map { |p| [p.name, p.value] }.to_h) end @param_values @@ -214,28 +214,28 @@ def extension_presence(ext_name) greatest_presence.nil? ? "-" : greatest_presence.to_s_concise end - # @return [Array] Sorted list of parameters specified by any extension in portfolio. - def all_in_scope_ext_params + # @return [Array] Sorted list of parameters specified by any extension in portfolio. + def all_in_scope_params @ret = [] portfolios.each do |portfolio| - @ret += portfolio.all_in_scope_ext_params + @ret += portfolio.all_in_scope_params end @ret = @ret.uniq.sort end # @param [ExtensionRequirement] - # @return [Array] Sorted list of extension parameters from portfolio for given extension. - def in_scope_ext_params(ext_req) + # @return [Array] Sorted list of extension parameters from portfolio for given extension. + def in_scope_params(ext_req) @ret = [] portfolios.each do |portfolio| - @ret += portfolio.in_scope_ext_params(ext_req) + @ret += portfolio.in_scope_params(ext_req) end @ret = @ret.uniq.sort end - # @return [Array] Sorted list of parameters out of scope across all in scope extensions. + # @return [Array] Sorted list of parameters out of scope across all in scope extensions. def all_out_of_scope_params @ret = [] portfolios.each do |portfolio| @@ -246,7 +246,7 @@ def all_out_of_scope_params end # @param ext_name [String] Extension name - # @return [Array] Sorted list of 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) @ret = [] portfolios.each do |portfolio| @@ -256,7 +256,7 @@ def out_of_scope_params(ext_name) @ret = @ret.uniq.sort end - # @param param [ExtensionParameter] + # @param param [Parameter] # @return [Array] Sorted list of all in-scope extensions that define this parameter # in the database and the parameter is in-scope. def all_in_scope_exts_with_param(param) @@ -268,7 +268,7 @@ def all_in_scope_exts_with_param(param) @ret = @ret.uniq.sort end - # @param param [ExtensionParameter] + # @param param [Parameter] # @return [Array] List of all in-scope extensions that define this parameter in the # database but the parameter is out-of-scope. def all_in_scope_exts_without_param(param) @@ -537,40 +537,43 @@ def uses_optional_types? # parameter have to override the following methods. # ########################################################################### - # @return [Array] List of parameters specified by any extension in portfolio. - def all_in_scope_ext_params = [] + # @return [Array] List of parameters specified by any extension in portfolio. + def all_in_scope_params = [] # @param [ExtensionRequirement] - # @return [Array] Sorted list of extension parameters from portfolio for given extension. - def in_scope_ext_params(ext_req) = [] + # @return [Array] Sorted list of extension parameters from portfolio for given extension. + def in_scope_params(ext_req) = [] - # @return [Array] Sorted list of parameters out of scope across all in scope extensions. + # @return [Array] Sorted list of parameters out of scope across all in scope extensions. def all_out_of_scope_params = [] # @param ext_name [String] Extension name - # @return [Array] Sorted list of 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) = [] - # @param param [ExtensionParameter] + # @param param [Parameter] # @return [Array] Sorted list of all in-scope extensions that define this parameter # in the database and the parameter is in-scope. def all_in_scope_exts_with_param(param) = [] - # @param param [ExtensionParameter] + # @param param [Parameter] # @return [Array] List of all in-scope extensions that define this parameter in the # database but the parameter is out-of-scope. def all_in_scope_exts_without_param(param) = [] - ################################### - # InScopeExtensionParameter Class # - ################################### + ########################## + # InScopeParameter Class # + ########################## - class InScopeExtensionParameter - attr_reader :param # ExtensionParameter object (from the architecture database) + class InScopeParameter + # @return [Parameter] Parameter object (from the architecture database) + attr_reader :param + + # @return [String] Optional note associated with the parameter attr_reader :note def initialize(param, schema_hash, note) - raise ArgumentError, "Expecting ExtensionParameter" unless param.is_a?(ExtensionParameter) + raise ArgumentError, "Expecting Parameter" unless param.is_a?(Parameter) if schema_hash.nil? schema_hash = {} @@ -611,10 +614,10 @@ def allowed_values # sorts by name def <=>(other) raise ArgumentError, - "InScopeExtensionParameter are only comparable to other parameter constraints" unless other.is_a?(InScopeExtensionParameter) + "InScopeParameter are only comparable to other parameter constraints" unless other.is_a?(InScopeParameter) @param.name <=> other.param.name end - end # class InScopeExtensionParameter + end # class InScopeParameter ############################ # RevisionHistory Subclass # diff --git a/lib/architecture.rb b/lib/architecture.rb index 9e8548c41..fbfc3b030 100644 --- a/lib/architecture.rb +++ b/lib/architecture.rb @@ -28,7 +28,7 @@ # # klass Array Hash Klass func(String name) # ================== ================== ======================= ========================= -# ExtensionParameter params() param_hash() param(name) +# Parameter params() param_hash() param(name) # PortfolioClass portfolio_classes() portfolio_class_hash() portfolio_class(name) # Portfolio portfolios() portfolio_hash() portfolio(name) # ExceptionCodes exception_codes() @@ -202,14 +202,14 @@ def objs @objs.freeze end - # @return [Array] Alphabetical list of all parameters defined in the architecture + # @return [Array] Alphabetical list of all parameters defined in the architecture def params return @params unless @params.nil? @params = extensions.map(&:params).flatten.uniq(&:name).sort_by!(&:name) end - # @return [Hash] Hash of all extension parameters defined in the architecture + # @return [Hash] Hash of all extension parameters defined in the architecture def param_hash return @param_hash unless @param_hash.nil? @@ -220,7 +220,7 @@ def param_hash @param_hash end - # @return [ExtensionParameter] Parameter named +name+ + # @return [Parameter] Parameter named +name+ # @return [nil] if there is no parameter named +name+ def param(name) param_hash[name] diff --git a/lib/backend_helpers.rb b/lib/backend_helpers.rb new file mode 100644 index 000000000..cf82bbfba --- /dev/null +++ b/lib/backend_helpers.rb @@ -0,0 +1,224 @@ +# frozen_string_literal: true +# +# Collection of "helper" functions that can be called from backends and/or ERB templates. + +require "erb" +require "pathname" + +# Add to standard String class. +class String + # Should be called on all RISC-V extension, instruction, CSR, and CSR field names. + # Parameters never have periods in their names so they don't need to be sanitized. + # + # @param name [#to_s] Some RISC-V name which might have periods in it + # @return [String] Periods replaced with underscores + def sanitize = self.gsub(".", "_") +end + +# This module is included in the Design class so its methods are available to be called directly +# without having to prefix a method with the module name. +module TemplateHelpers + # Include a partial ERB template into a full ERB template. + # + # @param template_pname [String] Path to template file relative to backends directory + # @param inputs [Hash] Input objects to pass into template + # @return [String] Result of ERB evaluation of the template file + def partial(template_pname, inputs = {}) + template_path = Pathname.new($root / "backends" / template_pname) + raise ArgumentError, "Template '#{template_path} not found" unless template_path.exist? + + erb = ERB.new(template_path.read, trim_mode: "-") + erb.filename = template_path.realpath.to_s + + erb.result(OpenStruct.new(inputs).instance_eval { binding }) + end + + # Links are created with this proprietary format so that they can be converted + # later into either Asciidoc or Antora links (see the two implementations of "resolve_links"). + # %%LINK%;;%% + # + # Documentation: + # - How to make cross-references: https://docs.asciidoctor.org/asciidoc/latest/macros/xref/ + # - How to create anchors: https://docs.asciidoctor.org/asciidoc/latest/attributes/id/ + # - See https://github.com/riscv/riscv-isa-manual/issues/1397#issuecomment-2515109936 for + # discussion about using [#anchor] instead of [[anchor]] due to Antora's support. + + # @return [String] A hyperlink to an extension + # @param ext_name [#to_s] Name of the extension + def link_to_ext(ext_name) + "%%LINK%ext;#{ext_name.sanitize};#{ext_name.sanitize}%%" + end + + # @return [String] A hyperlink to a parameter defined by a particular extension. + # @param ext_name [#to_s] Name of the extension + # @param param_name [#to_s] Name of the parameter + def link_to_ext_param(ext_name, param_name) + check_no_periods(param_name) + "%%LINK%ext_param;#{ext_name.sanitize}.#{param_name};#{param_name}%%" + end + + # @return [String] A hyperlink to an instruction + # @param inst_name [#to_s] Name of the instruction + def link_to_inst(inst_name) + "%%LINK%inst;#{inst_name.sanitize};#{inst_name.sanitize}%%" + end + + # @return [String] A hyperlink to a CSR + # @param csr_name [#to_s] Name of the CSR + def link_to_csr(csr_name) + "%%LINK%csr;#{csr_name.sanitize};#{csr_name.sanitize}%%" + end + + # @return [String] A hyperlink to an IDL function + # @param func_name [#to_s] Name of the IDL function + def link_to_func(func_name) + "%%LINK%func;#{func_name.sanitize};#{func_name.sanitize}%%" + end + + # @return [String] A hyperlink to a CSR field + # @param csr_name [#to_s] Name of the CSR + # @param field_name [#to_s] Name of the CSR field + def link_to_csr_field(csr_name, field_name) + "%%LINK%csr_field;#{csr_name.sanitize}.#{field_name.sanitize};#{csr_name.sanitize}.#{field_name.sanitize}%%" + end + + # @return [String] An anchor for an extension + # @param ext_name [#to_s] Name of the extension + def anchor_for_ext(ext_name) + "[#ext-#{ext_name.sanitize}-def]" + end + + # @return [String] An anchor for a parameter defined by a particular extension. + # @param ext_name [#to_s] Name of the extension + # @param param_name [#to_s] Name of the parameter + def anchor_for_ext_param(ext_name, param_name) + check_no_periods(param_name) + "[#ext_param-#{ext_name.sanitize}-#{param_name}-def]" + end + + # Insert anchor to an instruction. + # @param name [#to_s] Name of the instruction + def anchor_for_inst(name) + "[#inst-#{name.sanitize}-def]" + end + + # Insert anchor to a CSR. + # @param name [#to_s] Name of the CSR + def anchor_for_csr(name) + "[#csr-#{name.sanitize}-def]" + end + + # Insert anchor to a CSR field. + # @param csr_name [#to_s] Name of the CSR + # @param field_name [#to_s] Name of the CSR field + def anchor_for_csr_field(csr_name, field_name) + "[#csr_field-#{csr_name.sanitize}-#{field_name.sanitize}-def]" + end + + # Insert anchor to an IDL function. + # @param name [#to_s] Name of the function + def anchor_for_func(name) + "[#func-#{name.sanitize}-def]" + end + + private + #@ param s [String] + def check_no_periods(s) + raise ArgumentError, "Periods are not allowed in '#{s}'" if s.include?(".") + end +end + +# Utilities for a backend to generate Asciidoc. +module AsciidocUtils + # The syntax "class << self" causes all methods to be treated as class methods. + class << self + # Convert proprietary link format to legal Asciidoc links. + # They are converted to standard xref:#anchor_name[link_text] format here. + # For example, + # %%LINK%inst;add;add instruction%% + # is converted to: + # xref:#inst-add-def[add instruction] + # + # @param path_or_str [Pathname or String] + # @return [String] + def resolve_links(path_or_str) + str = + if path_or_str.is_a?(Pathname) + path_or_str.read + else + path_or_str + end + str.gsub(/%%LINK%([^;%]+)\s*;\s*([^;%]+)\s*;\s*([^%]+)%%/) do + type = Regexp.last_match[1] + name = Regexp.last_match[2] + link_text = Regexp.last_match[3] + + case type + when "ext" + "xref:#ext-#{name}-def[#{link_text}]" + # link_text + when "ext_param" + ext_name, param_name = name.split('.') + "xref:#ext_param-#{ext_name}-#{param_name}-def[#{link_text}]" + when "inst" + #"xref:#inst-#{name}-def[#{link_text.gsub(']', '\]')}]" + # XXX: Is the gsub above necessary? + "xref:#inst-#{name}-def[#{link_text}]" + when "csr" + "xref:#csr-#{name}-def[#{link_text}]" + when "csr_field" + csr_name, field_name = name.split('.') + "xref:#csr_field-#{csr_name}-#{field_name}-def[#{link_text}]" + # link_text + when "func" + "xref:#func-#{name}-def[#{link_text}]" + else + raise "Unhandled link type '#{type}' for '#{name}' in '#{path_or_str}'" + end + end + end + end +end + +# Utilities for a backend to generate an Antora web-site. +module AntoraUtils + # The syntax "class << self" causes all methods to be treated as class methods. + class << self + # Convert proprietary link format to legal Asciidoc links. + # They are converted to standard xref:#anchor_name[link_text] format here. + # For example, + # %%LINK%inst;add;add instruction%% + # is converted to: + # xref:#inst-add-def[add instruction] + # + # @param path_or_str [Pathname or String] + # @return [String] + def resolve_links(path_or_str) + str = + if path_or_str.is_a?(Pathname) + path_or_str.read + else + path_or_str + end + str.gsub(/%%LINK%([^;%]+)\s*;\s*([^;%]+)\s*;\s*([^%]+)%%/) do + type = Regexp.last_match[-1] + name = Regexp.last_match[0] + link_text = Regexp.last_match[1] + + case type + when "inst" + "xref:insts:#{name}.adoc##{name}-def[#{link_text.gsub(']', '\]')}]" + when "csr" + "xref:csrs:#{name}.adoc##{name}-def[#{link_text.gsub(']', '\]')}]" + when "csr_field" + csr_name, field_name = name.split('.') + "xref:csrs:#{csr_name}.adoc##{csr_name}-#{field_name}-def[#{link_text.gsub(']', '\]')}]" + when "ext" + "xref:exts:#{name}.adoc##{name}-def[#{link_text.gsub(']', '\]')}]" + else + raise "Unhandled link type of '#{type}' for '#{name}' in '#{path_or_str}'" + end + end + end + end +end diff --git a/lib/cfg_arch.rb b/lib/cfg_arch.rb index ef50a4018..fffc100b8 100644 --- a/lib/cfg_arch.rb +++ b/lib/cfg_arch.rb @@ -126,7 +126,7 @@ def multi_xlen_in_mode?(mode) end end - # @return [Array] List of all parameters with one known value in the config + # @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? @@ -135,24 +135,24 @@ def params_with_value if @config.fully_configured? transitive_implemented_ext_vers.each do |ext_version| - ext_version.ext.params.each do |ext_param| - next unless @config.param_values.key?(ext_param.name) + ext_version.ext.params.each do |param| + next unless @config.param_values.key?(param.name) - @params_with_value << ExtensionParameterWithValue.new( - ext_param, - @config.param_values[ext_param.name] + @params_with_value << ParameterWithValue.new( + param, + @config.param_values[param.name] ) end end elsif @config.partially_configured? mandatory_ext_reqs.each do |ext_req| - ext_req.extension.params.each do |ext_param| + ext_req.extension.params.each do |param| # Params listed in the config always only have one value. - next unless @config.param_values.key?(ext_param.name) + next unless @config.param_values.key?(param.name) - @params_with_value << ExtensionParameterWithValue.new( - ext_param, - @config.param_values[ext_param.name] + @params_with_value << ParameterWithValue.new( + param, + @config.param_values[param.name] ) end end @@ -162,17 +162,17 @@ def params_with_value @params_with_value end - # @return [Array] List of all available parameters without one known value in the config + # @return [Array] List of all available parameters without one known value in the config 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| + ext.params.each do |param| # Params listed in the config always only have one value. - next if @config.param_values.key?(ext_param.name) + next if @config.param_values.key?(param.name) - @params_without_value << ext_param + @params_without_value << param end end @params_without_value @@ -311,6 +311,31 @@ def ext?(ext_name, *ext_version_requirements) @ext_cache[[ext_name, ext_version_requirements]] = result end + # Given an adoc string, find names of CSR/Instruction/Extension enclosed in `monospace` + # and replace them with links to the relevant object page. + # See backend_helpers.rb for a definition of the proprietary link format. + # + # @param adoc [String] Asciidoc source + # @return [String] Asciidoc source, with link placeholders + def convert_monospace_to_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_to_csr_field(csr_name, field_name) + elsif !csr.nil? + link_to_csr(csr_name) + elsif arch.instruction(name) + link_to_inst(name) + elsif arch.extension(name) + link_to_ext(name) + else + match + end + end + end + ##################################### # METHODS RESTRICTING PARENT METHOD # ##################################### diff --git a/lib/design.rb b/lib/design.rb index 24edc8644..f1d24ff52 100644 --- a/lib/design.rb +++ b/lib/design.rb @@ -31,7 +31,7 @@ require_relative "idl/passes/reachable_exceptions" require_relative "idl/passes/reachable_functions" -require_relative "template_helpers" +require_relative "backend_helpers" include TemplateHelpers @@ -299,30 +299,6 @@ def implemented_functions @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_erb() function in ERB templates. # # @return [Hash] An environment hash suitable for use with ERb templates. diff --git a/lib/idesign.rb b/lib/idesign.rb index 949b90a9f..d1fe0c0a0 100644 --- a/lib/idesign.rb +++ b/lib/idesign.rb @@ -40,12 +40,12 @@ 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 + # @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 + # @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 @@ -100,4 +100,14 @@ def unconfigured? def ext?(ext_name, *ext_version_requirements) raise "Abstract Method: Must be provided in child class" end + + # Given an adoc string, find names of CSR/Instruction/Extension enclosed in `monospace` + # and replace them with links to the relevant object page. + # See backend_helpers.rb for a definition of the proprietary link format. + # + # @param adoc [String] Asciidoc source + # @return [String] Asciidoc source, with link placeholders + def convert_monospace_to_links(adoc) + raise "Abstract Method: Must be provided in child class" + end end diff --git a/lib/idl/passes/gen_adoc.rb b/lib/idl/passes/gen_adoc.rb index 63a80a6cb..1c9cb794d 100644 --- a/lib/idl/passes/gen_adoc.rb +++ b/lib/idl/passes/gen_adoc.rb @@ -273,7 +273,7 @@ def gen_adoc(indent = 0, indent_spaces: 2) after_name = [] after_name << "<#{template_arg_nodes.map { |t| t.gen_adoc(0, indent_spaces:)}.join(', ')}>" unless template_arg_nodes.empty? after_name << "pass:[(]#{arg_nodes.map { |a| a.gen_adoc(0, indent_spaces: ) }.join(', ')})" - "#{' '*indent}%%LINK%func;#{name};#{name}%%#{after_name.join ''}" + "#{' '*indent}" + link_to_func(name) + "#{after_name.join ''}" end end diff --git a/lib/portfolio_design.rb b/lib/portfolio_design.rb index 71c1f0e59..deae13073 100644 --- a/lib/portfolio_design.rb +++ b/lib/portfolio_design.rb @@ -69,33 +69,33 @@ def inspect = "PortfolioDesign##{name}" # 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 + # @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) + ext_req.extension.params.each do |param| + next unless param_values.key?(param.name) - @params_with_value << ExtensionParameterWithValue.new(ext_param, param_values[ext_param.name]) + @params_with_value << ParameterWithValue.new(param, param_values[param.name]) end end @params_with_value end - # @return [Array] List of all available parameters not yet full-constrained to one specific value + # @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) + ext.params.each do |param| + next if param_values.key?(param.name) - @params_without_value << ext_param + @params_without_value << param end end @params_without_value @@ -149,6 +149,32 @@ def ext?(ext_name, *ext_version_requirements) @ext_cache[[ext_name, ext_version_requirements]] = result end + # Given an adoc string, find names of CSR/Instruction/Extension enclosed in `monospace` + # and replace them with links to the relevant object page. + # See backend_helpers.rb for a definition of the proprietary link format. + # + # @param adoc [String] Asciidoc source + # @return [String] Asciidoc source, with link placeholders + # XXX + def convert_monospace_to_links(adoc) + adoc.gsub(/`([\w.]+)`/) do |match| + name = Regexp.last_match(1) + csr_name, field_name = name.split(".") + csr = in_scope_csrs.find { |c| c.name == csr_name } + if !field_name.nil? && !csr.nil? && csr.field?(field_name) + link_to_csr_field(csr_name, field_name) + elsif !csr.nil? + link_to_csr(csr_name) + elsif in_scope_instructions.any? { |inst| inst.name == name } + link_to_inst(name) + elsif in_scope_extensions.any? { |ext| ext.name == name } + link_to_ext(name) + else + match + end + end + end + # # A Portfolio corresponds to a partially-configured design. # See the Config class for details. @@ -201,26 +227,26 @@ def in_scope_interrupt_codes = @portfolio_grp.in_scope_interrupt_codes(self) # If the extension name isn't found in this design, return "-". def extension_presence(ext_name) = @portfolio_grp.extension_presence(ext_name) - # @return [Array] Sorted list of parameters specified by any extension in portfolio. - def all_in_scope_ext_params = @portfolio_grp.all_in_scope_ext_params + # @return [Array] Sorted list of parameters specified by any extension in portfolio. + def all_in_scope_params = @portfolio_grp.all_in_scope_params # @param [ExtensionRequirement] - # @return [Array] Sorted list of extension parameters from portfolio for given extension. - def in_scope_ext_params(ext_req) = @portfolio_grp.in_scope_ext_params(ext_req) + # @return [Array] Sorted list of extension parameters from portfolio for given extension. + def in_scope_params(ext_req) = @portfolio_grp.in_scope_params(ext_req) - # @return [Array] Sorted list of parameters out of scope across all in scope extensions. + # @return [Array] Sorted list of parameters out of scope across all in scope extensions. def all_out_of_scope_params = @portfolio_grp.all_out_of_scope_params # @param ext_name [String] Extension name - # @return [Array] Sorted list of 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) = @portfolio_grp.out_of_scope_params(ext_name) - # @param param [ExtensionParameter] + # @param param [Parameter] # @return [Array] Sorted list of all in-scope extensions that define this parameter # in the database and the parameter is in-scope. def all_in_scope_exts_with_param(param) = @portfolio_grp.all_in_scope_exts_with_param(param) - # @param param [ExtensionParameter] + # @param param [Parameter] # @return [Array] List of all in-scope extensions that define this parameter in the # database but the parameter is out-of-scope. def all_in_scope_exts_without_param(param) = @portfolio_grp.all_in_scope_exts_without_param(param) diff --git a/lib/template_helpers.rb b/lib/template_helpers.rb deleted file mode 100644 index 2ca82658b..000000000 --- a/lib/template_helpers.rb +++ /dev/null @@ -1,87 +0,0 @@ -# frozen_string_literal: true -# -# Collection of "helper" functions that can be called from ERB templates. - -require "erb" -require "pathname" - -module TemplateHelpers - # Insert a hyperlink to an extension. - # @param name [#to_s] Name of the extension - def link_to_ext(name) - "%%LINK%ext;#{name};#{name}%%" - end - - # Insert a hyperlink to an extension parameter. - # @param ext_name [#to_s] Name of the extension - # @param param_name [#to_s] Name of the parameter - def link_to_ext_param(ext_name, param_name) - "<>" - end - - # Insert a hyperlink to an instruction. - # @param name [#to_s] Name of the instruction - def link_to_inst(name) - "%%LINK%inst;#{name};#{name}%%" - end - - # Insert a hyperlink to a CSR. - # @param name [#to_s] Name of the CSR - def link_to_csr(name) - "%%LINK%csr;#{name};#{name}%%" - end - - # Insert a hyperlink to a CSR field. - # @param csr_name [#to_s] Name of the CSR - # @param field_name [#to_s] Name of the CSR field - def link_to_csr_field(csr_name, field_name) - "%%LINK%csr_field;#{csr_name}.#{field_name};#{csr_name}.#{field_name}%%" - end - - # Insert anchor to an extension. - # @param name [#to_s] Name of the extension - def anchor_for_ext(name) - "[[ext-#{name.gsub(".", "_")}-def]]" - end - - # Insert anchor to an extension parameter. - # @param ext_name [#to_s] Name of the extension - # @param param_name [#to_s] Name of the parameter - def anchor_for_ext_param(ext_name, param_name) - "[[ext-#{ext_name.gsub(".", "_")}-param-#{param_name}-def]]" - end - - # Insert anchor to an instruction. - # @param name [#to_s] Name of the instruction - def anchor_for_inst(name) - "[[inst-#{name.gsub(".", "_")}-def]]" - end - - # Insert anchor to a CSR. - # @param name [#to_s] Name of the CSR - def anchor_for_csr(name) - "[[csr-#{name.gsub(".", "_")}-def]]" - end - - # Insert anchor to a CSR field. - # @param csr_name [#to_s] Name of the CSR - # @param field_name [#to_s] Name of the CSR field - def anchor_for_csr_field(csr_name, field_name) - "[[csr_field-#{csr_name.gsub(".", "_")}-#{field_name.gsub(".", "_")}-def]]" - end - - # Include a partial ERB template into a full ERB template. - # - # @param template_pname [String] Path to template file relative to backends directory - # @param inputs [Hash] Input objects to pass into template - # @return [String] Result of ERB evaluation of the template file - def partial(template_pname, inputs = {}) - template_path = Pathname.new($root / "backends" / template_pname) - raise ArgumentError, "Template '#{template_path} not found" unless template_path.exist? - - erb = ERB.new(template_path.read, trim_mode: "-") - erb.filename = template_path.realpath.to_s - - erb.result(OpenStruct.new(inputs).instance_eval { binding }) - end -end diff --git a/lib/test/test_backend_helpers.rb b/lib/test/test_backend_helpers.rb new file mode 100644 index 000000000..135f5525f --- /dev/null +++ b/lib/test/test_backend_helpers.rb @@ -0,0 +1,76 @@ +# frozen_string_literal: true + +require "English" +require "minitest/autorun" +require "backend_helpers" + +include TemplateHelpers + +$root ||= (Pathname.new(__FILE__) / ".." / ".." / "..").realpath + +class TestBackendHelpers < Minitest::Test + def test_ext + assert_equal("%%LINK%ext;foo;foo%%", link_to_ext("foo")) + assert_equal("[#ext-foo-def]", anchor_for_ext("foo")) + assert_equal("%%LINK%ext;fo_o;fo_o%%", link_to_ext("fo.o")) + assert_equal("[#ext-fo_o-def]", anchor_for_ext("fo.o")) + end + + def test_ext_param + assert_equal("%%LINK%ext_param;foo.bar;bar%%", link_to_ext_param("foo","bar")) + assert_equal("[#ext_param-foo-bar-def]", anchor_for_ext_param("foo","bar")) + assert_equal("%%LINK%ext_param;fo_o.bar;bar%%", link_to_ext_param("fo.o","bar")) + assert_equal("[#ext_param-fo_o-bar-def]", anchor_for_ext_param("fo.o","bar")) + assert_raises(ArgumentError) { link_to_ext_param("foo","ba.r") } + assert_raises(ArgumentError) { anchor_for_ext_param("foo","ba.r") } + end + + def test_inst + assert_equal("%%LINK%inst;foo;foo%%", link_to_inst("foo")) + assert_equal("[#inst-foo-def]", anchor_for_inst("foo")) + assert_equal("%%LINK%inst;fo_o;fo_o%%", link_to_inst("fo.o")) + assert_equal("[#inst-fo_o-def]", anchor_for_inst("fo.o")) + end + + def test_csr + assert_equal("%%LINK%csr;foo;foo%%", link_to_csr("foo")) + assert_equal("[#csr-foo-def]", anchor_for_csr("foo")) + assert_equal("%%LINK%csr;fo_o;fo_o%%", link_to_csr("fo.o")) + assert_equal("[#csr-fo_o-def]", anchor_for_csr("fo.o")) + end + + def test_csr_field + assert_equal("%%LINK%csr_field;foo.bar;foo.bar%%", link_to_csr_field("foo","bar")) + assert_equal("[#csr_field-foo-bar-def]", anchor_for_csr_field("foo","bar")) + assert_equal("%%LINK%csr_field;fo_o.ba_r;fo_o.ba_r%%", link_to_csr_field("fo.o","ba.r")) + assert_equal("[#csr_field-fo_o-ba_r-def]", anchor_for_csr_field("fo.o","ba.r")) + end +end + +class TestAsciidocUtils < Minitest::Test + def test_resolve_links_ext + assert_equal("xref:#ext-foo-def[bar]", AsciidocUtils.resolve_links("%%LINK%ext;foo;bar%%")) + assert_equal("xref:#ext-foo-def[foo]", AsciidocUtils.resolve_links(link_to_ext("foo"))) + end + + def test_resolve_links_ext_param + assert_equal("xref:#ext_param-foo-bar-def[zort]", AsciidocUtils.resolve_links("%%LINK%ext_param;foo.bar;zort%%")) + assert_equal("xref:#ext_param-foo-bar-def[bar]", AsciidocUtils.resolve_links(link_to_ext_param("foo","bar"))) + end + + def test_resolve_links_inst + assert_equal("xref:#inst-foo-def[bar]", AsciidocUtils.resolve_links("%%LINK%inst;foo;bar%%")) + assert_equal("xref:#inst-foo-def[foo]", AsciidocUtils.resolve_links(link_to_inst("foo"))) + end + + def test_resolve_links_csr + assert_equal("xref:#csr-foo-def[bar]", AsciidocUtils.resolve_links("%%LINK%csr;foo;bar%%")) + assert_equal("xref:#csr-foo-def[foo]", AsciidocUtils.resolve_links(link_to_csr("foo"))) + end + + def test_resolve_links_csr_field + assert_equal("xref:#csr_field-foo-bar-def[zort]", AsciidocUtils.resolve_links("%%LINK%csr_field;foo.bar;zort%%")) + assert_equal("xref:#csr_field-foo-bar-def[foo.bar]", AsciidocUtils.resolve_links(link_to_csr_field("foo","bar"))) + end + +end From 2787052fcb98deb619bba2ddc67e72af37078ea0 Mon Sep 17 00:00:00 2001 From: James Ball Date: Wed, 15 Jan 2025 05:48:23 +0000 Subject: [PATCH 32/61] Added IDL functions to new portfolio appendix in preparation for linking to IDL. Also found more places to use link_to_* and anchor_for_* helpers. --- backends/cfg_html_doc/templates/csr.adoc.erb | 4 +- backends/cfg_html_doc/templates/func.adoc.erb | 2 +- backends/cfg_html_doc/templates/inst.adoc.erb | 2 +- backends/crd/templates/crd.adoc.erb | 1 + .../ext_pdf_doc/templates/ext_pdf.adoc.erb | 2 +- backends/manual/templates/csr.adoc.erb | 6 +-- backends/manual/templates/func.adoc.erb | 2 +- .../manual/templates/instruction.adoc.erb | 2 +- .../templates/idl_func_appendix.adoc.erb | 40 +++++++++++++++++++ .../templates/inst_appendix.adoc.erb | 2 +- backends/profile/templates/profile.adoc.erb | 1 + backends/templates/csr.adoc.erb | 4 +- lib/backend_helpers.rb | 38 ++++++++---------- lib/design.rb | 12 +++--- lib/idl/ast.rb | 10 +++-- lib/idl/symbol_table.rb | 2 +- lib/portfolio_design.rb | 1 - 17 files changed, 86 insertions(+), 45 deletions(-) create mode 100644 backends/portfolio/templates/idl_func_appendix.adoc.erb diff --git a/backends/cfg_html_doc/templates/csr.adoc.erb b/backends/cfg_html_doc/templates/csr.adoc.erb index a2ef0c9ea..74d65b1b7 100644 --- a/backends/cfg_html_doc/templates/csr.adoc.erb +++ b/backends/cfg_html_doc/templates/csr.adoc.erb @@ -73,8 +73,8 @@ This CSR has no fields. However, it must still exist (not cause an `Illegal Inst <%- else -%> <%- csr.implemented_fields(cfg_arch).each do |field| -%> -[[<%=csr.name%>-<%=field.name%>-def]] -=== `<%= field.name %>` +<%= anchor_for_csr_field(csr.name, field.name) %> +=== `<%= field.name %>` Field [.csr-field-info] -- diff --git a/backends/cfg_html_doc/templates/func.adoc.erb b/backends/cfg_html_doc/templates/func.adoc.erb index 40b87e384..d96a40e75 100644 --- a/backends/cfg_html_doc/templates/func.adoc.erb +++ b/backends/cfg_html_doc/templates/func.adoc.erb @@ -5,7 +5,7 @@ = Functions <%- cfg_arch.implemented_functions.each do |f| -%> -[#<%= f.name %>-func-def] +<%= anchor_for_func(f.name) %> == <%= f.name %><%- if f.builtin? -%> (builtin)<%- end -%> <%= f.description %> diff --git a/backends/cfg_html_doc/templates/inst.adoc.erb b/backends/cfg_html_doc/templates/inst.adoc.erb index d34aa2124..e32b94be4 100644 --- a/backends/cfg_html_doc/templates/inst.adoc.erb +++ b/backends/cfg_html_doc/templates/inst.adoc.erb @@ -1,6 +1,6 @@ :tabs-sync-option: -[[inst:<%=inst.name.gsub('.', '_')%>-def]] +<%= anchor_for_inst(inst.name) %> = <%= inst.name %> *<%= inst.long_name %>* diff --git a/backends/crd/templates/crd.adoc.erb b/backends/crd/templates/crd.adoc.erb index 02f847ed9..4fa55f6db 100644 --- a/backends/crd/templates/crd.adoc.erb +++ b/backends/crd/templates/crd.adoc.erb @@ -470,3 +470,4 @@ Requirement <%= req.name %> only apply when <%= req.when_pretty %>. <%= portfolio_design.include_erb("ext_appendix.adoc.erb") %> <%= portfolio_design.include_erb("inst_appendix.adoc.erb") %> <%= portfolio_design.include_erb("csr_appendix.adoc.erb") %> +<%= portfolio_design.include_erb("idl_func_appendix.adoc.erb") %> diff --git a/backends/ext_pdf_doc/templates/ext_pdf.adoc.erb b/backends/ext_pdf_doc/templates/ext_pdf.adoc.erb index 58daaccfa..6da830cb7 100644 --- a/backends/ext_pdf_doc/templates/ext_pdf.adoc.erb +++ b/backends/ext_pdf_doc/templates/ext_pdf.adoc.erb @@ -311,7 +311,7 @@ The following <%= ext.csrs.size %> are added by this extension. == IDL Functions <%- ext.reachable_functions(design.symtab).sort { |a,b| a.name <=> b.name }.each do |f| -%> -[#<%= f.name %>-func-def] +<%= anchor_for_func(f.name) %> === <%= f.name %><%- if f.builtin? -%> (builtin)<%- end -%> <%= f.description %> diff --git a/backends/manual/templates/csr.adoc.erb b/backends/manual/templates/csr.adoc.erb index 3966a3668..dd3a9f34a 100644 --- a/backends/manual/templates/csr.adoc.erb +++ b/backends/manual/templates/csr.adoc.erb @@ -1,6 +1,6 @@ :tabs-sync-option: -[#csrs-<%= csr.name.gsub('.', '_') %>,reftext=<%= csr.name %>] +<%= anchor_for_csr(csr.name) %> = <%= csr.name %> *<%= csr.long_name %>* @@ -112,8 +112,8 @@ This CSR has no fields. However, it must still exist (not cause an `Illegal Inst <%- else -%> <%- csr.fields.each do |field| -%> -[[<%=csr.name%>-<%=field.name%>-def]] -=== `<%= field.name %>` +<%= anchor_for_csr_field(csr.name, field.name) %> +=== `<%= field.name %>` Field <%- if !field.defined_in_all_bases? -%> IMPORTANT: <%= field.name %> is only defined in <%= field.base32_only? ? "RV32" : "RV64" %> (`<%= field.base32_only? ? field.location_cond32 : field.location_cond64 %>`) diff --git a/backends/manual/templates/func.adoc.erb b/backends/manual/templates/func.adoc.erb index d435d316a..45b5abc4c 100644 --- a/backends/manual/templates/func.adoc.erb +++ b/backends/manual/templates/func.adoc.erb @@ -5,7 +5,7 @@ = Functions <%- design.functions.each do |f| -%> -[#<%= f.name %>-func-def] +<%= anchor_for_func(f.name) %> == <%= f.name %><%- if f.builtin? -%> (builtin)<%- end -%> <%= f.description %> diff --git a/backends/manual/templates/instruction.adoc.erb b/backends/manual/templates/instruction.adoc.erb index 8a52089f5..05e276026 100644 --- a/backends/manual/templates/instruction.adoc.erb +++ b/backends/manual/templates/instruction.adoc.erb @@ -1,6 +1,6 @@ :tabs-sync-option: -[[inst:<%=inst.name.gsub('.', '_')%>-def]] +<%= anchor_for_inst(inst.name) %> = <%= inst.name %> *<%= inst.long_name %>* diff --git a/backends/portfolio/templates/idl_func_appendix.adoc.erb b/backends/portfolio/templates/idl_func_appendix.adoc.erb new file mode 100644 index 000000000..7f2f518ca --- /dev/null +++ b/backends/portfolio/templates/idl_func_appendix.adoc.erb @@ -0,0 +1,40 @@ +<<< +[appendix] +== IDL Function Details + +<% design.functions.each do |f| -%> +<%= anchor_for_func(f.name) %> +=== <%= f.name %><% if f.builtin? -%> (builtin)<% end -%> + +<%= f.description %> + +[cols="1,2"] +|=== +h| Return Type +a| +[source,idl] +---- +<%= f.return_type_list_str.join(', ') %> +---- + +h| Arguments +a| +<% if f.arguments_list_str.empty? -%> +None +<% else -%> +[source,idl] +---- +<%= f.arguments_list_str.join (', ') %> +---- +<% end -%> +|=== + +<% unless f.builtin? -%> +<% body_ast = f.body -%> +[source,idl,subs="specialchars,macros"] +---- +<%= body_ast.gen_adoc %> +---- +<% end -%> + +<% end -%> diff --git a/backends/portfolio/templates/inst_appendix.adoc.erb b/backends/portfolio/templates/inst_appendix.adoc.erb index 9054415cd..0d569ec9b 100644 --- a/backends/portfolio/templates/inst_appendix.adoc.erb +++ b/backends/portfolio/templates/inst_appendix.adoc.erb @@ -100,7 +100,7 @@ RV64:: ---- <% end -%> -==== Execution +==== IDL Execution <% xlens = inst.base.nil? ? [32, 64] : [inst.base] -%> diff --git a/backends/profile/templates/profile.adoc.erb b/backends/profile/templates/profile.adoc.erb index 7eac891bb..5b454ccad 100644 --- a/backends/profile/templates/profile.adoc.erb +++ b/backends/profile/templates/profile.adoc.erb @@ -447,3 +447,4 @@ Extensions present in a profile are also present in higher-privileged profiles i <%= portfolio_design.include_erb("ext_appendix.adoc.erb") %> <%= portfolio_design.include_erb("inst_appendix.adoc.erb") %> <%= portfolio_design.include_erb("csr_appendix.adoc.erb") %> +<%= portfolio_design.include_erb("idl_func_appendix.adoc.erb") %> diff --git a/backends/templates/csr.adoc.erb b/backends/templates/csr.adoc.erb index 04d9fd1e6..c367c0ccc 100644 --- a/backends/templates/csr.adoc.erb +++ b/backends/templates/csr.adoc.erb @@ -66,8 +66,8 @@ This CSR has no fields. However, it must still exist (not cause an `Illegal Inst <%- else -%> <%- csr.fields.each do |field| -%> -[[<%=csr.name%>-<%=field.name%>-def]] -=== `<%= field.name %>` +<%= anchor_for_csr_field(csr.name, field.name) %> +=== `<%= field.name %>` Field [example] **** diff --git a/lib/backend_helpers.rb b/lib/backend_helpers.rb index cf82bbfba..3f3587643 100644 --- a/lib/backend_helpers.rb +++ b/lib/backend_helpers.rb @@ -10,7 +10,7 @@ class String # Should be called on all RISC-V extension, instruction, CSR, and CSR field names. # Parameters never have periods in their names so they don't need to be sanitized. # - # @param name [#to_s] Some RISC-V name which might have periods in it + # @param name [String] Some RISC-V name which might have periods in it # @return [String] Periods replaced with underscores def sanitize = self.gsub(".", "_") end @@ -44,79 +44,79 @@ def partial(template_pname, inputs = {}) # discussion about using [#anchor] instead of [[anchor]] due to Antora's support. # @return [String] A hyperlink to an extension - # @param ext_name [#to_s] Name of the extension + # @param ext_name [String] Name of the extension def link_to_ext(ext_name) "%%LINK%ext;#{ext_name.sanitize};#{ext_name.sanitize}%%" end # @return [String] A hyperlink to a parameter defined by a particular extension. - # @param ext_name [#to_s] Name of the extension - # @param param_name [#to_s] Name of the parameter + # @param ext_name [String] Name of the extension + # @param param_name [String] Name of the parameter def link_to_ext_param(ext_name, param_name) check_no_periods(param_name) "%%LINK%ext_param;#{ext_name.sanitize}.#{param_name};#{param_name}%%" end # @return [String] A hyperlink to an instruction - # @param inst_name [#to_s] Name of the instruction + # @param inst_name [String] Name of the instruction def link_to_inst(inst_name) "%%LINK%inst;#{inst_name.sanitize};#{inst_name.sanitize}%%" end # @return [String] A hyperlink to a CSR - # @param csr_name [#to_s] Name of the CSR + # @param csr_name [String] Name of the CSR def link_to_csr(csr_name) "%%LINK%csr;#{csr_name.sanitize};#{csr_name.sanitize}%%" end # @return [String] A hyperlink to an IDL function - # @param func_name [#to_s] Name of the IDL function + # @param func_name [String] Name of the IDL function def link_to_func(func_name) "%%LINK%func;#{func_name.sanitize};#{func_name.sanitize}%%" end # @return [String] A hyperlink to a CSR field - # @param csr_name [#to_s] Name of the CSR - # @param field_name [#to_s] Name of the CSR field + # @param csr_name [String] Name of the CSR + # @param field_name [String] Name of the CSR field def link_to_csr_field(csr_name, field_name) "%%LINK%csr_field;#{csr_name.sanitize}.#{field_name.sanitize};#{csr_name.sanitize}.#{field_name.sanitize}%%" end # @return [String] An anchor for an extension - # @param ext_name [#to_s] Name of the extension + # @param ext_name [String] Name of the extension def anchor_for_ext(ext_name) "[#ext-#{ext_name.sanitize}-def]" end # @return [String] An anchor for a parameter defined by a particular extension. - # @param ext_name [#to_s] Name of the extension - # @param param_name [#to_s] Name of the parameter + # @param ext_name [String] Name of the extension + # @param param_name [String] Name of the parameter def anchor_for_ext_param(ext_name, param_name) check_no_periods(param_name) "[#ext_param-#{ext_name.sanitize}-#{param_name}-def]" end # Insert anchor to an instruction. - # @param name [#to_s] Name of the instruction + # @param name [String] Name of the instruction def anchor_for_inst(name) "[#inst-#{name.sanitize}-def]" end # Insert anchor to a CSR. - # @param name [#to_s] Name of the CSR + # @param name [String] Name of the CSR def anchor_for_csr(name) "[#csr-#{name.sanitize}-def]" end # Insert anchor to a CSR field. - # @param csr_name [#to_s] Name of the CSR - # @param field_name [#to_s] Name of the CSR field + # @param csr_name [String] Name of the CSR + # @param field_name [String] Name of the CSR field def anchor_for_csr_field(csr_name, field_name) "[#csr_field-#{csr_name.sanitize}-#{field_name.sanitize}-def]" end # Insert anchor to an IDL function. - # @param name [#to_s] Name of the function + # @param name [String] Name of the function def anchor_for_func(name) "[#func-#{name.sanitize}-def]" end @@ -156,20 +156,16 @@ def resolve_links(path_or_str) case type when "ext" "xref:#ext-#{name}-def[#{link_text}]" - # link_text when "ext_param" ext_name, param_name = name.split('.') "xref:#ext_param-#{ext_name}-#{param_name}-def[#{link_text}]" when "inst" - #"xref:#inst-#{name}-def[#{link_text.gsub(']', '\]')}]" - # XXX: Is the gsub above necessary? "xref:#inst-#{name}-def[#{link_text}]" when "csr" "xref:#csr-#{name}-def[#{link_text}]" when "csr_field" csr_name, field_name = name.split('.') "xref:#csr_field-#{csr_name}-#{field_name}-def[#{link_text}]" - # link_text when "func" "xref:#func-#{name}-def[#{link_text}]" else diff --git a/lib/design.rb b/lib/design.rb index f1d24ff52..f99b10713 100644 --- a/lib/design.rb +++ b/lib/design.rb @@ -244,26 +244,26 @@ def implemented_interrupt_codes end end - # @return [Array] List of all functions defined by the architecture + # @return [Array] Sorted list of all functions defined by the architecture def functions return @functions unless @functions.nil? - @functions = @global_ast.functions + @functions = @global_ast.functions.sort end - # @return [Array] List of all implemented CSRs + # @return [Array] Sorted list of all implemented CSRs def transitive_implemented_csrs @transitive_implemented_csrs ||= transitive_implemented_ext_vers.map(&:implemented_csrs).flatten.uniq.sort end - # @return [Array] List of all implemented instructions + # @return [Array] Sorted list of all implemented instructions def transitive_implemented_instructions @transitive_implemented_instructions ||= transitive_implemented_ext_vers.map(&:implemented_instructions).flatten.uniq.sort end - # @return [Array] List of all reachable IDL functions for the design + # @return [Array] Sorted list of all reachable IDL functions for the design def implemented_functions return @implemented_functions unless @implemented_functions.nil? @@ -296,7 +296,7 @@ def implemented_functions end end - @implemented_functions + @implemented_functions.sort! end # Returns an environment hash suitable for the render_erb() function in ERB templates. diff --git a/lib/idl/ast.rb b/lib/idl/ast.rb index 0b048a670..cf62a22c3 100644 --- a/lib/idl/ast.rb +++ b/lib/idl/ast.rb @@ -731,13 +731,13 @@ def globals = definitions.select { |d| d.is_a?(GlobalWithInitializationAst) || d # @return {Array] List of all enum definitions def enums = definitions.select { |e| e.is_a?(EnumDefinitionAst) || e.is_a?(BuiltinEnumDefinitionAst) } - # @return {Array] List of all bitfield definitions + # @return {Array] List of all bitfield definitions def bitfields = definitions.select { |e| e.is_a?(BitfieldDefinitionAst) } - # @return [Array] List of all struct definitions + # @return [Array] List of all struct definitions def structs = definitions.select { |e| e.is_a?(StructDefinitionAst) } - # @return {Array] List of all function definitions + # @return {Array] List of all function definitions def functions = definitions.select { |e| e.is_a?(FunctionDefAst) } # Add all the global symbols to symtab @@ -4897,6 +4897,10 @@ def initialize(input, interval, name, targs, return_types, arguments, desc, body @reachable_functions_cache ||= {} end + def <=>(other) + name <=> other.name + end + attr_reader :reachable_functions_cache # @!macro freeze_tree diff --git a/lib/idl/symbol_table.rb b/lib/idl/symbol_table.rb index 5372a8610..ca4c58a49 100644 --- a/lib/idl/symbol_table.rb +++ b/lib/idl/symbol_table.rb @@ -46,7 +46,7 @@ def decode_var? @decode_var end - # @param function_name [#to_s] A function name + # @param function_name [String] A function name # @return [Boolean] whether or not this variable is a function template argument from a call site for the function 'function_name' def template_value_for?(function_name) !@template_index.nil? && (function_name.to_s == @function_name) diff --git a/lib/portfolio_design.rb b/lib/portfolio_design.rb index deae13073..430807ffc 100644 --- a/lib/portfolio_design.rb +++ b/lib/portfolio_design.rb @@ -155,7 +155,6 @@ def ext?(ext_name, *ext_version_requirements) # # @param adoc [String] Asciidoc source # @return [String] Asciidoc source, with link placeholders - # XXX def convert_monospace_to_links(adoc) adoc.gsub(/`([\w.]+)`/) do |match| name = Regexp.last_match(1) From 26e645a7dae809b91371349c800d23dea7c90fd7 Mon Sep 17 00:00:00 2001 From: James Ball Date: Wed, 15 Jan 2025 06:11:10 +0000 Subject: [PATCH 33/61] Found weird typo in backend_helper.rb causing manual regression test to fail. --- lib/backend_helpers.rb | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/lib/backend_helpers.rb b/lib/backend_helpers.rb index 3f3587643..e3eae5d56 100644 --- a/lib/backend_helpers.rb +++ b/lib/backend_helpers.rb @@ -169,7 +169,7 @@ def resolve_links(path_or_str) when "func" "xref:#func-#{name}-def[#{link_text}]" else - raise "Unhandled link type '#{type}' for '#{name}' in '#{path_or_str}'" + raise "Unhandled link type of '#{type}' for '#{name}' with link_text '#{link_text}'" end end end @@ -197,11 +197,13 @@ def resolve_links(path_or_str) path_or_str end str.gsub(/%%LINK%([^;%]+)\s*;\s*([^;%]+)\s*;\s*([^%]+)%%/) do - type = Regexp.last_match[-1] - name = Regexp.last_match[0] - link_text = Regexp.last_match[1] + type = Regexp.last_match[1] + name = Regexp.last_match[2] + link_text = Regexp.last_match[3] case type + when "ext" + "xref:exts:#{name}.adoc##{name}-def[#{link_text.gsub(']', '\]')}]" when "inst" "xref:insts:#{name}.adoc##{name}-def[#{link_text.gsub(']', '\]')}]" when "csr" @@ -209,10 +211,10 @@ def resolve_links(path_or_str) when "csr_field" csr_name, field_name = name.split('.') "xref:csrs:#{csr_name}.adoc##{csr_name}-#{field_name}-def[#{link_text.gsub(']', '\]')}]" - when "ext" - "xref:exts:#{name}.adoc##{name}-def[#{link_text.gsub(']', '\]')}]" + when "func" + "xref:func:#{name}.adoc##{name}-def[#{link_text.gsub(']', '\]')}]" else - raise "Unhandled link type of '#{type}' for '#{name}' in '#{path_or_str}'" + raise "Unhandled link type of '#{type}' for '#{name}' with link_text '#{link_text}'" end end end From 5520e2cd1921cc0a765e1ac230d113af3f50d2f8 Mon Sep 17 00:00:00 2001 From: James Ball Date: Thu, 16 Jan 2025 01:26:13 +0000 Subject: [PATCH 34/61] Changed anchors and links to use [[anchor]] and <> after learning more about the nuances in adoc and looking at what the ISA manual does. --- arch/profile/RVA22S64.yaml | 35 ------------------------- lib/arch_obj_models/extension.rb | 4 +-- lib/backend_helpers.rb | 44 ++++++++++++++++---------------- lib/design.rb | 9 ++++--- lib/test/test_backend_helpers.rb | 40 ++++++++++++++--------------- 5 files changed, 49 insertions(+), 83 deletions(-) diff --git a/arch/profile/RVA22S64.yaml b/arch/profile/RVA22S64.yaml index 7f11ee519..60b3c39cc 100644 --- a/arch/profile/RVA22S64.yaml +++ b/arch/profile/RVA22S64.yaml @@ -26,41 +26,6 @@ extensions: Svinval: presence: mandatory version: "~> 1.0" - Ssstateen: - presence: mandatory - version: "~> 1.0" - when: - implemented: H - note: | - Ssstateen is a new extension name introduced with RVA22. - Shvstvala: - presence: mandatory - version: "~> 1.0" - when: - implemented: H - note: | - Shvstvala is a new extension name introduced with RVA22. - Shtvala: - presence: mandatory - version: "~> 1.0" - when: - implemented: H - note: | - Shtvala is a new extension name introduced with RVA22. - Shvstvecd: - presence: mandatory - version: "~> 1.0" - when: - implemented: H - note: | - Shvstvecd is a new extension name introduced with RVA22. - Shgatpa: - presence: mandatory - version: "~> 1.0" - when: - implemented: H - note: | - Shgatpa is a new extension name introduced with RVA22. Sv57: presence: optional version: "~> 1.12" diff --git a/lib/arch_obj_models/extension.rb b/lib/arch_obj_models/extension.rb index b4d4821a5..1e404a68f 100644 --- a/lib/arch_obj_models/extension.rb +++ b/lib/arch_obj_models/extension.rb @@ -394,7 +394,7 @@ def implemented_instructions end # @param design [Design] The design - # @return [Array] List of CSRs in-scope for this design for this extension version (may be empty) + # @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 an IDesign object but got a #{design.class} object" unless design.is_a?(IDesign) @@ -408,7 +408,7 @@ def in_scope_csrs(design) end # @param design [Design] The design - # @return [Array] List of instructions in-scope for this design for this extension version (may be empty) + # @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 an IDesign object but got a #{design.class} object" unless design.is_a?(IDesign) diff --git a/lib/backend_helpers.rb b/lib/backend_helpers.rb index e3eae5d56..45eb8a5d4 100644 --- a/lib/backend_helpers.rb +++ b/lib/backend_helpers.rb @@ -34,14 +34,14 @@ def partial(template_pname, inputs = {}) end # Links are created with this proprietary format so that they can be converted - # later into either Asciidoc or Antora links (see the two implementations of "resolve_links"). + # later into either AsciiDoc or Antora links (see the two implementations of "resolve_links"). # %%LINK%;;%% # # Documentation: # - How to make cross-references: https://docs.asciidoctor.org/asciidoc/latest/macros/xref/ # - How to create anchors: https://docs.asciidoctor.org/asciidoc/latest/attributes/id/ - # - See https://github.com/riscv/riscv-isa-manual/issues/1397#issuecomment-2515109936 for - # discussion about using [#anchor] instead of [[anchor]] due to Antora's support. + # - See https://github.com/riscv/riscv-isa-manual/issues/1397 for a detailed + # discussion about how to put anchors and links into AsciiDoc. # @return [String] A hyperlink to an extension # @param ext_name [String] Name of the extension @@ -85,7 +85,7 @@ def link_to_csr_field(csr_name, field_name) # @return [String] An anchor for an extension # @param ext_name [String] Name of the extension def anchor_for_ext(ext_name) - "[#ext-#{ext_name.sanitize}-def]" + "[[ext-#{ext_name.sanitize}-def]]" end # @return [String] An anchor for a parameter defined by a particular extension. @@ -93,32 +93,32 @@ def anchor_for_ext(ext_name) # @param param_name [String] Name of the parameter def anchor_for_ext_param(ext_name, param_name) check_no_periods(param_name) - "[#ext_param-#{ext_name.sanitize}-#{param_name}-def]" + "[[ext_param-#{ext_name.sanitize}-#{param_name}-def]]" end # Insert anchor to an instruction. # @param name [String] Name of the instruction def anchor_for_inst(name) - "[#inst-#{name.sanitize}-def]" + "[[inst-#{name.sanitize}-def]]" end # Insert anchor to a CSR. # @param name [String] Name of the CSR def anchor_for_csr(name) - "[#csr-#{name.sanitize}-def]" + "[[csr-#{name.sanitize}-def]]" end # Insert anchor to a CSR field. # @param csr_name [String] Name of the CSR # @param field_name [String] Name of the CSR field def anchor_for_csr_field(csr_name, field_name) - "[#csr_field-#{csr_name.sanitize}-#{field_name.sanitize}-def]" + "[[csr_field-#{csr_name.sanitize}-#{field_name.sanitize}-def]]" end # Insert anchor to an IDL function. # @param name [String] Name of the function def anchor_for_func(name) - "[#func-#{name.sanitize}-def]" + "[[func-#{name.sanitize}-def]]" end private @@ -128,16 +128,16 @@ def check_no_periods(s) end end -# Utilities for a backend to generate Asciidoc. +# Utilities for a backend to generate AsciiDoc. module AsciidocUtils # The syntax "class << self" causes all methods to be treated as class methods. class << self - # Convert proprietary link format to legal Asciidoc links. - # They are converted to standard xref:#anchor_name[link_text] format here. + # Convert proprietary link format to legal AsciiDoc links. + # They are converted to AsciiDoc internal cross references (i.e., <>). # For example, # %%LINK%inst;add;add instruction%% # is converted to: - # xref:#inst-add-def[add instruction] + # <> # # @param path_or_str [Pathname or String] # @return [String] @@ -155,19 +155,19 @@ def resolve_links(path_or_str) case type when "ext" - "xref:#ext-#{name}-def[#{link_text}]" + "<>" when "ext_param" ext_name, param_name = name.split('.') - "xref:#ext_param-#{ext_name}-#{param_name}-def[#{link_text}]" + "<>" when "inst" - "xref:#inst-#{name}-def[#{link_text}]" + "<>" when "csr" - "xref:#csr-#{name}-def[#{link_text}]" + "<>" when "csr_field" csr_name, field_name = name.split('.') - "xref:#csr_field-#{csr_name}-#{field_name}-def[#{link_text}]" + "<>" when "func" - "xref:#func-#{name}-def[#{link_text}]" + "<>" else raise "Unhandled link type of '#{type}' for '#{name}' with link_text '#{link_text}'" end @@ -180,12 +180,12 @@ def resolve_links(path_or_str) module AntoraUtils # The syntax "class << self" causes all methods to be treated as class methods. class << self - # Convert proprietary link format to legal Asciidoc links. - # They are converted to standard xref:#anchor_name[link_text] format here. + # Convert proprietary link format to legal AsciiDoc links. + # They are converted to AsciiDoc external cross references (i.e., xref:filename:#anchor_name[link_text]). # For example, # %%LINK%inst;add;add instruction%% # is converted to: - # xref:#inst-add-def[add instruction] + # xref:insts:add.adoc#add-def[add instruction] # # @param path_or_str [Pathname or String] # @return [String] diff --git a/lib/design.rb b/lib/design.rb index f99b10713..96630a7d4 100644 --- a/lib/design.rb +++ b/lib/design.rb @@ -167,13 +167,14 @@ def type_check(show_progress: true, io: $stdout) puts "done" if show_progress end - # @return [Array] List of all extensions known to be implemented in this design, including transitive implications + # @return [Array] List of all extension versions known to be implemented in this design, + # including transitive implications. def transitive_implemented_ext_vers return @transitive_implemented_ext_vers unless @transitive_implemented_ext_vers.nil? list = implemented_ext_vers - list.each do |e| - implications = e.transitive_implications + list.each do |ext_ver| + implications = ext_ver.transitive_implications list.concat(implications) unless implications.empty? end @transitive_implemented_ext_vers = list.uniq.sort @@ -244,7 +245,7 @@ def implemented_interrupt_codes end end - # @return [Array] Sorted list of all functions defined by the architecture + # @return [Array] Sorted list of all IDL functions defined by the architecture def functions return @functions unless @functions.nil? diff --git a/lib/test/test_backend_helpers.rb b/lib/test/test_backend_helpers.rb index 135f5525f..1b2a4786c 100644 --- a/lib/test/test_backend_helpers.rb +++ b/lib/test/test_backend_helpers.rb @@ -11,66 +11,66 @@ class TestBackendHelpers < Minitest::Test def test_ext assert_equal("%%LINK%ext;foo;foo%%", link_to_ext("foo")) - assert_equal("[#ext-foo-def]", anchor_for_ext("foo")) + assert_equal("[[ext-foo-def]]", anchor_for_ext("foo")) assert_equal("%%LINK%ext;fo_o;fo_o%%", link_to_ext("fo.o")) - assert_equal("[#ext-fo_o-def]", anchor_for_ext("fo.o")) + assert_equal("[[ext-fo_o-def]]", anchor_for_ext("fo.o")) end def test_ext_param assert_equal("%%LINK%ext_param;foo.bar;bar%%", link_to_ext_param("foo","bar")) - assert_equal("[#ext_param-foo-bar-def]", anchor_for_ext_param("foo","bar")) + assert_equal("[[ext_param-foo-bar-def]]", anchor_for_ext_param("foo","bar")) assert_equal("%%LINK%ext_param;fo_o.bar;bar%%", link_to_ext_param("fo.o","bar")) - assert_equal("[#ext_param-fo_o-bar-def]", anchor_for_ext_param("fo.o","bar")) + assert_equal("[[ext_param-fo_o-bar-def]]", anchor_for_ext_param("fo.o","bar")) assert_raises(ArgumentError) { link_to_ext_param("foo","ba.r") } assert_raises(ArgumentError) { anchor_for_ext_param("foo","ba.r") } end def test_inst assert_equal("%%LINK%inst;foo;foo%%", link_to_inst("foo")) - assert_equal("[#inst-foo-def]", anchor_for_inst("foo")) + assert_equal("[[inst-foo-def]]", anchor_for_inst("foo")) assert_equal("%%LINK%inst;fo_o;fo_o%%", link_to_inst("fo.o")) - assert_equal("[#inst-fo_o-def]", anchor_for_inst("fo.o")) + assert_equal("[[inst-fo_o-def]]", anchor_for_inst("fo.o")) end def test_csr assert_equal("%%LINK%csr;foo;foo%%", link_to_csr("foo")) - assert_equal("[#csr-foo-def]", anchor_for_csr("foo")) + assert_equal("[[csr-foo-def]]", anchor_for_csr("foo")) assert_equal("%%LINK%csr;fo_o;fo_o%%", link_to_csr("fo.o")) - assert_equal("[#csr-fo_o-def]", anchor_for_csr("fo.o")) + assert_equal("[[csr-fo_o-def]]", anchor_for_csr("fo.o")) end def test_csr_field assert_equal("%%LINK%csr_field;foo.bar;foo.bar%%", link_to_csr_field("foo","bar")) - assert_equal("[#csr_field-foo-bar-def]", anchor_for_csr_field("foo","bar")) + assert_equal("[[csr_field-foo-bar-def]]", anchor_for_csr_field("foo","bar")) assert_equal("%%LINK%csr_field;fo_o.ba_r;fo_o.ba_r%%", link_to_csr_field("fo.o","ba.r")) - assert_equal("[#csr_field-fo_o-ba_r-def]", anchor_for_csr_field("fo.o","ba.r")) + assert_equal("[[csr_field-fo_o-ba_r-def]]", anchor_for_csr_field("fo.o","ba.r")) end end class TestAsciidocUtils < Minitest::Test def test_resolve_links_ext - assert_equal("xref:#ext-foo-def[bar]", AsciidocUtils.resolve_links("%%LINK%ext;foo;bar%%")) - assert_equal("xref:#ext-foo-def[foo]", AsciidocUtils.resolve_links(link_to_ext("foo"))) + assert_equal("<>", AsciidocUtils.resolve_links("%%LINK%ext;foo;bar%%")) + assert_equal("<>", AsciidocUtils.resolve_links(link_to_ext("foo"))) end def test_resolve_links_ext_param - assert_equal("xref:#ext_param-foo-bar-def[zort]", AsciidocUtils.resolve_links("%%LINK%ext_param;foo.bar;zort%%")) - assert_equal("xref:#ext_param-foo-bar-def[bar]", AsciidocUtils.resolve_links(link_to_ext_param("foo","bar"))) + assert_equal("<>", AsciidocUtils.resolve_links("%%LINK%ext_param;foo.bar;zort%%")) + assert_equal("<>", AsciidocUtils.resolve_links(link_to_ext_param("foo","bar"))) end def test_resolve_links_inst - assert_equal("xref:#inst-foo-def[bar]", AsciidocUtils.resolve_links("%%LINK%inst;foo;bar%%")) - assert_equal("xref:#inst-foo-def[foo]", AsciidocUtils.resolve_links(link_to_inst("foo"))) + assert_equal("<>", AsciidocUtils.resolve_links("%%LINK%inst;foo;bar%%")) + assert_equal("<>", AsciidocUtils.resolve_links(link_to_inst("foo"))) end def test_resolve_links_csr - assert_equal("xref:#csr-foo-def[bar]", AsciidocUtils.resolve_links("%%LINK%csr;foo;bar%%")) - assert_equal("xref:#csr-foo-def[foo]", AsciidocUtils.resolve_links(link_to_csr("foo"))) + assert_equal("<>", AsciidocUtils.resolve_links("%%LINK%csr;foo;bar%%")) + assert_equal("<>", AsciidocUtils.resolve_links(link_to_csr("foo"))) end def test_resolve_links_csr_field - assert_equal("xref:#csr_field-foo-bar-def[zort]", AsciidocUtils.resolve_links("%%LINK%csr_field;foo.bar;zort%%")) - assert_equal("xref:#csr_field-foo-bar-def[foo.bar]", AsciidocUtils.resolve_links(link_to_csr_field("foo","bar"))) + assert_equal("<>", AsciidocUtils.resolve_links("%%LINK%csr_field;foo.bar;zort%%")) + assert_equal("<>", AsciidocUtils.resolve_links(link_to_csr_field("foo","bar"))) end end From 8a74c2866a32ce5c29dbe3dcf22512894a087669 Mon Sep 17 00:00:00 2001 From: James Ball Date: Thu, 16 Jan 2025 01:32:10 +0000 Subject: [PATCH 35/61] Fixed bug I introduced in AntoraUtils (didn't realize that all IDL functions go to the same func.adoc file). --- lib/backend_helpers.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/backend_helpers.rb b/lib/backend_helpers.rb index 45eb8a5d4..b7d65d048 100644 --- a/lib/backend_helpers.rb +++ b/lib/backend_helpers.rb @@ -212,7 +212,8 @@ def resolve_links(path_or_str) csr_name, field_name = name.split('.') "xref:csrs:#{csr_name}.adoc##{csr_name}-#{field_name}-def[#{link_text.gsub(']', '\]')}]" when "func" - "xref:func:#{name}.adoc##{name}-def[#{link_text.gsub(']', '\]')}]" + # All functions are in the same file called "func.adoc". + "xref:func:func.adoc##{name}-def[#{link_text.gsub(']', '\]')}]" else raise "Unhandled link type of '#{type}' for '#{name}' with link_text '#{link_text}'" end From 048f4a80e408a89420fbb8d0341e5ab8ae16bd8f Mon Sep 17 00:00:00 2001 From: James Ball Date: Thu, 16 Jan 2025 01:55:34 +0000 Subject: [PATCH 36/61] Improve comment in AntoraUtils and add links to relevant Antora documentation. --- lib/backend_helpers.rb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/backend_helpers.rb b/lib/backend_helpers.rb index b7d65d048..54380fe13 100644 --- a/lib/backend_helpers.rb +++ b/lib/backend_helpers.rb @@ -187,6 +187,12 @@ class << self # is converted to: # xref:insts:add.adoc#add-def[add instruction] # + # Antora supports the module name after the "xref:". In the example above, it the module name is "insts" + # and corresponds to the directory name the add.adoc file is located in. For more details, see: + # https://docs.antora.org/antora/latest/page/xref/ + # and then + # https://docs.antora.org/antora/latest/page/resource-id-coordinates/ + # # @param path_or_str [Pathname or String] # @return [String] def resolve_links(path_or_str) From e050b6ca9a730d7b41305aac5e6c0c29093263e8 Mon Sep 17 00:00:00 2001 From: James Ball Date: Fri, 17 Jan 2025 05:23:34 +0000 Subject: [PATCH 37/61] Changed anchors to use #[foo] syntax. Added preliminary CTP information to mul.yaml. --- .github/workflows/pages.yml | 4 +- .vscode/launch.json | 18 + Rakefile | 48 +- arch/inst/M/mul.yaml | 38 +- .../portfolio/templates/beginning.adoc.erb | 54 +++ .../templates/inst_appendix.adoc.erb | 2 +- backends/{crd => proc_crd}/tasks.rake | 27 +- backends/proc_crd/templates/proc_crd.adoc.erb | 458 ++++++++++++++++++ backends/proc_ctp/tasks.rake | 111 +++++ .../templates/proc_ctp.adoc.erb} | 21 +- backends/profile/tasks.rake | 3 +- backends/profile/templates/profile.adoc.erb | 53 +- lib/backend_helpers.rb | 16 +- lib/portfolio_design.rb | 24 +- lib/test/test_backend_helpers.rb | 20 +- schemas/inst_schema.json | 68 +++ 16 files changed, 835 insertions(+), 130 deletions(-) create mode 100644 backends/portfolio/templates/beginning.adoc.erb rename backends/{crd => proc_crd}/tasks.rake (76%) create mode 100644 backends/proc_crd/templates/proc_crd.adoc.erb create mode 100644 backends/proc_ctp/tasks.rake rename backends/{crd/templates/crd.adoc.erb => proc_ctp/templates/proc_ctp.adoc.erb} (97%) diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml index 39e14b95a..0db28f0de 100644 --- a/.github/workflows/pages.yml +++ b/.github/workflows/pages.yml @@ -82,11 +82,11 @@ jobs: - name: Create MC100-32-CRD PDF Spec run: ./do gen:proc_crd_pdf[MC100-32] - name: Copy MC100-32-CRD PDF - run: cp gen/crd/pdf/MC100-32-CRD.pdf _site/pdfs + run: cp gen/proc_crd/pdf/MC100-32-CRD.pdf _site/pdfs - name: Create MC100-64-CRD PDF Spec run: ./do gen:proc_crd_pdf[MC100-64] - name: Copy MC100-64-CRD PDF - run: cp gen/crd/pdf/MC100-64-CRD.pdf _site/pdfs + run: cp gen/proc_crd/pdf/MC100-64-CRD.pdf _site/pdfs - name: Copy manual html run: cp -R gen/manual/isa/top/all/html _site/manual - name: Setup Pages diff --git a/.vscode/launch.json b/.vscode/launch.json index 915e6a87c..3fa985893 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -10,6 +10,15 @@ "args": [], "askParameters": false }, + { + "type": "rdbg", + "name": "MC100-32-CTP", + "request": "launch", + "command": "bundle exec rake", + "script": "gen:proc_ctp_pdf[MC100-32]", + "args": [], + "askParameters": false + }, { "type": "rdbg", "name": "MC200-32-CRD", @@ -28,6 +37,15 @@ "args": [], "askParameters": false }, + { + "type": "rdbg", + "name": "MockCTP", + "request": "launch", + "command": "bundle exec rake", + "script": "gen:proc_ctp_pdf[MockProcessor]", + "args": [], + "askParameters": false + }, { "type": "rdbg", "name": "portfolios", diff --git a/Rakefile b/Rakefile index 46ee708fa..6b7b0aeee 100644 --- a/Rakefile +++ b/Rakefile @@ -320,7 +320,7 @@ namespace :test do ENV["VERSIONS"] = "all" Rake::Task["gen:html_manual"].invoke - puts "UPDATE: Running gen:ext_pdf" + puts "UPDATE: Running gen:ext_pdf EXT=B VERSION=latest" ENV["EXT"] = "B" ENV["VERSION"] = "latest" Rake::Task["gen:ext_pdf"].invoke @@ -329,10 +329,10 @@ namespace :test do Rake::Task["gen:html"].invoke("generic_rv64") puts "UPDATE: Generating MockProcessor-CRD.pdf" - Rake::Task["#{$root}/gen/crd/pdf/MockProcessor-CRD.pdf"].invoke + Rake::Task["#{$root}/gen/proc_crd/pdf/MockProcessor-CRD.pdf"].invoke - #puts "UPDATE: Generating MockProcessor-CTP.pdf" - #Rake::Task["#{$root}/gen/ctp/pdf/MockProcessor-CTP.pdf"].invoke + puts "UPDATE: Generating MockProcessor-CTP.pdf" + Rake::Task["#{$root}/gen/proc_ctp/pdf/MockProcessor-CTP.pdf"].invoke puts "UPDATE: Generating MockProfileRelease.pdf" Rake::Task["#{$root}/gen/profile/pdf/MockProfileRelease.pdf"].invoke @@ -359,25 +359,25 @@ desc <<~DESC DESC task :portfolios do portfolio_start_msg("MockProcessor-CRD") - Rake::Task["#{$root}/gen/crd/pdf/MockProcessor-CRD.pdf"].invoke - #portfolio_start_msg("MockProcessor-CTP") - #Rake::Task["#{$root}/gen/ctp/pdf/MockProcessor-CTP.pdf"].invoke + Rake::Task["#{$root}/gen/proc_crd/pdf/MockProcessor-CRD.pdf"].invoke + portfolio_start_msg("MockProcessor-CTP") + Rake::Task["#{$root}/gen/proc_ctp/pdf/MockProcessor-CTP.pdf"].invoke portfolio_start_msg("MockProfileRelease") Rake::Task["#{$root}/gen/profile/pdf/MockProfileRelease.pdf"].invoke - #portfolio_start_msg("MC100-32-CTP") - #Rake::Task["#{$root}/gen/ctp/pdf/MC100-32-CTP.pdf"].invoke + portfolio_start_msg("MC100-32-CTP") + Rake::Task["#{$root}/gen/proc_ctp/pdf/MC100-32-CTP.pdf"].invoke portfolio_start_msg("MC100-32-CRD") - Rake::Task["#{$root}/gen/crd/pdf/MC100-32-CRD.pdf"].invoke + Rake::Task["#{$root}/gen/proc_crd/pdf/MC100-32-CRD.pdf"].invoke portfolio_start_msg("MC100-64-CRD") - Rake::Task["#{$root}/gen/crd/pdf/MC100-64-CRD.pdf"].invoke + Rake::Task["#{$root}/gen/proc_crd/pdf/MC100-64-CRD.pdf"].invoke portfolio_start_msg("MC200-32-CRD") - Rake::Task["#{$root}/gen/crd/pdf/MC200-32-CRD.pdf"].invoke + Rake::Task["#{$root}/gen/proc_crd/pdf/MC200-32-CRD.pdf"].invoke portfolio_start_msg("MC200-64-CRD") - Rake::Task["#{$root}/gen/crd/pdf/MC200-64-CRD.pdf"].invoke + Rake::Task["#{$root}/gen/proc_crd/pdf/MC200-64-CRD.pdf"].invoke portfolio_start_msg("MC300-32-CRD") - Rake::Task["#{$root}/gen/crd/pdf/MC300-32-CRD.pdf"].invoke + Rake::Task["#{$root}/gen/proc_crd/pdf/MC300-32-CRD.pdf"].invoke portfolio_start_msg("MC300-64-CRD") - Rake::Task["#{$root}/gen/crd/pdf/MC300-64-CRD.pdf"].invoke + Rake::Task["#{$root}/gen/proc_crd/pdf/MC300-64-CRD.pdf"].invoke portfolio_start_msg("RVI20ProfileRelease") Rake::Task["#{$root}/gen/profile/pdf/RVI20ProfileRelease.pdf"].invoke portfolio_start_msg("RVA20ProfileRelease") @@ -399,15 +399,15 @@ def portfolio_start_msg(name) end # Shortcut targets for building CRDs, CTPs, and Profile Releases. -task "MockCRD": "#{$root}/gen/crd/pdf/MockProcessor-CRD.pdf" -#task "MockCTP": "#{$root}/gen/ctp/pdf/MockProcessor-CTP.pdf" -#task "MC100-32-CTP": "#{$root}/gen/ctp/pdf/MC100-32-CTP.pdf" -task "MC100-32-CRD": "#{$root}/gen/crd/pdf/MC100-32-CRD.pdf" -task "MC100-64-CRD": "#{$root}/gen/crd/pdf/MC100-64-CRD.pdf" -task "MC200-32-CRD": "#{$root}/gen/crd/pdf/MC200-32-CRD.pdf" -task "MC200-64-CRD": "#{$root}/gen/crd/pdf/MC200-64-CRD.pdf" -task "MC300-32-CRD": "#{$root}/gen/crd/pdf/MC300-32-CRD.pdf" -task "MC300-64-CRD": "#{$root}/gen/crd/pdf/MC300-64-CRD.pdf" +task "MockCRD": "#{$root}/gen/proc_crd/pdf/MockProcessor-CRD.pdf" +task "MockCTP": "#{$root}/gen/proc_ctp/pdf/MockProcessor-CTP.pdf" +task "MC100-32-CTP": "#{$root}/gen/proc_ctp/pdf/MC100-32-CTP.pdf" +task "MC100-32-CRD": "#{$root}/gen/proc_crd/pdf/MC100-32-CRD.pdf" +task "MC100-64-CRD": "#{$root}/gen/proc_crd/pdf/MC100-64-CRD.pdf" +task "MC200-32-CRD": "#{$root}/gen/proc_crd/pdf/MC200-32-CRD.pdf" +task "MC200-64-CRD": "#{$root}/gen/proc_crd/pdf/MC200-64-CRD.pdf" +task "MC300-32-CRD": "#{$root}/gen/proc_crd/pdf/MC300-32-CRD.pdf" +task "MC300-64-CRD": "#{$root}/gen/proc_crd/pdf/MC300-64-CRD.pdf" task "MockProfile": "#{$root}/gen/profile/pdf/MockProfileRelease.pdf" task "RVI20": "#{$root}/gen/profile/pdf/RVI20ProfileRelease.pdf" task "RVA20": "#{$root}/gen/profile/pdf/RVA20ProfileRelease.pdf" diff --git a/arch/inst/M/mul.yaml b/arch/inst/M/mul.yaml index b4187b368..baa3633d7 100644 --- a/arch/inst/M/mul.yaml +++ b/arch/inst/M/mul.yaml @@ -34,7 +34,13 @@ access: u: always vs: always vu: always -data_independent_timing: true +data_independent_timing: + true + # Want to add something like this to IDL code. + # [#idl:inst:mul:exception-illegal_instruction]# + # if (implemented?(ExtensionName::M) && (CSR[misa].M == 1'b0)) { + # raise (ExceptionCode::IllegalInstruction, mode(), $encoding); + # }# operation(): | if (implemented?(ExtensionName::M) && (CSR[misa].M == 1'b0)) { raise (ExceptionCode::IllegalInstruction, mode(), $encoding); @@ -63,3 +69,33 @@ sail(): | RETIRE_FAIL } } + +certification-coverage-points: + - name: Encoding and basic operation + description: | + Encoding and basic operation for `mul` instruction + xrefs: + - norm:inst:mul:encoding + - norm:inst:mul:operation + - name: Illegal instruction exception + description: | + An illegal instruction exception is raised when the instruction is executed + and `misa.M` is 0. + xrefs: + - idl:inst:mul:exception-illegal_instruction + +certification-test-plan: + - name: Encoding + description: Verify the encoding of the mul instruction + coverage-points: + - Encoding and basic operation + setup: + - description: Load a variety of known values into rs1 & rs2 with a variety of rs1/rs2/rd values. + execution: + - description: Execute the mul instruction + validation: + - description: Check each result in rd + teardown: + - description: Clear the registers used for rd + - comment: | + Don't really need to clear the registers so this is a contrived example diff --git a/backends/portfolio/templates/beginning.adoc.erb b/backends/portfolio/templates/beginning.adoc.erb new file mode 100644 index 000000000..a99fb664b --- /dev/null +++ b/backends/portfolio/templates/beginning.adoc.erb @@ -0,0 +1,54 @@ +[[header]] +:description: <%= design.name %> <%= design.portfolio_design_type %> +// :revnumber: TODO +:revmark: "TODO: revmark" +:company: RISC-V +:url-riscv: https://riscv.org +:doctype: book +:preface-title: Licensing and Acknowledgements +:colophon: +:appendix-caption: Appendix +:title-logo-image: image:risc-v_logo.png["RISC-V International Logo",pdfwidth=3.25in,align=center] +:back-cover-image: image:riscv-horizontal-color.svg[opacity=25%] + +// Settings +:experimental: +:reproducible: +:wavedrom: <%= $root %>/node_modules/.bin/wavedrom-cli +// TODO: needs to be changed +:imagesoutdir: images +:icons: font +:lang: en +:example-caption: Example +:listing-caption: Listing +:table-caption: Table +:figure-caption: Figure +:xrefstyle: short +:chapter-refsig: Chapter +:section-refsig: Section +:appendix-refsig: Appendix +:sectnums: +:toc: left +:toclevels: 5 +// Determined that uncommenting this causes cross-references to IDL functions +// from instruction IDL code to not link. The IDL code uses this +// block tag to get "source" formatting: +// [source,idl,subs="specialchars,macros"] +// +// :source-highlighter: pygments +// ifdef::backend-pdf[] +// :source-highlighter: rouge +// endif::[] +:data-uri: +:hide-uri-scheme: +:stem: +:footnote: +:stem: latexmath +:footnote: +:le: ≤ +:ge: ≥ +:ne: ≠ +:approx: ≈ +:inf: ∞ +:csrname: envcfg +:imagesdir: images diff --git a/backends/portfolio/templates/inst_appendix.adoc.erb b/backends/portfolio/templates/inst_appendix.adoc.erb index 0d569ec9b..9b8187baa 100644 --- a/backends/portfolio/templates/inst_appendix.adoc.erb +++ b/backends/portfolio/templates/inst_appendix.adoc.erb @@ -100,7 +100,7 @@ RV64:: ---- <% end -%> -==== IDL Execution +==== IDL Operation <% xlens = inst.base.nil? ? [32, 64] : [inst.base] -%> diff --git a/backends/crd/tasks.rake b/backends/proc_crd/tasks.rake similarity index 76% rename from backends/crd/tasks.rake rename to backends/proc_crd/tasks.rake index d69f1a452..3fc51039b 100644 --- a/backends/crd/tasks.rake +++ b/backends/proc_crd/tasks.rake @@ -4,7 +4,7 @@ require "pathname" -CERT_DOC_DIR = Pathname.new "#{$root}/backends/crd" +PROC_CRD_DOC_DIR = Pathname.new "#{$root}/backends/proc_crd" Dir.glob("#{$root}/arch/proc_cert_model/*.yaml") do |f| model_name = File.basename(f, ".yaml") @@ -12,7 +12,7 @@ Dir.glob("#{$root}/arch/proc_cert_model/*.yaml") do |f| class_name = File.basename(model_obj['class']['$ref'].split("#")[0], ".yaml") raise "Ill-formed processor certificate model file #{f}: missing 'class' field" if model_obj['class'].nil? - file "#{$root}/gen/crd/adoc/#{model_name}-CRD.adoc" => [ + file "#{$root}/gen/proc_crd/adoc/#{model_name}-CRD.adoc" => [ __FILE__, "#{$root}/arch/proc_cert_class/#{class_name}.yaml", "#{$root}/arch/proc_cert_model/#{model_name}.yaml", @@ -23,7 +23,8 @@ Dir.glob("#{$root}/arch/proc_cert_model/*.yaml") do |f| "#{$root}/backends/portfolio/templates/ext_appendix.adoc.erb", "#{$root}/backends/portfolio/templates/inst_appendix.adoc.erb", "#{$root}/backends/portfolio/templates/csr_appendix.adoc.erb", - "#{CERT_DOC_DIR}/templates/crd.adoc.erb" + "#{$root}/backends/portfolio/templates/beginning.adoc.erb", + "#{PROC_CRD_DOC_DIR}/templates/proc_crd.adoc.erb" ] do |t| arch = pf_create_arch @@ -36,7 +37,7 @@ Dir.glob("#{$root}/arch/proc_cert_model/*.yaml") do |f| # Create the one PortfolioDesign object required for the ERB evaluation. puts "UPDATE: Creating PortfolioDesign object using processor certificate model #{model_name}" - portfolio_design = PortfolioDesign.new(model_name, arch, [proc_cert_model], proc_cert_class) + portfolio_design = PortfolioDesign.new(model_name, arch, PortfolioDesign.proc_crd_type, [proc_cert_model], proc_cert_class) # 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). @@ -48,21 +49,21 @@ Dir.glob("#{$root}/arch/proc_cert_model/*.yaml") do |f| erb_binding.local_variable_set(:proc_cert_model, proc_cert_model) erb_binding.local_variable_set(:proc_cert_class, proc_cert_class) - pf_create_adoc("#{CERT_DOC_DIR}/templates/crd.adoc.erb", erb_binding, t.name, portfolio_design) + pf_create_adoc("#{PROC_CRD_DOC_DIR}/templates/proc_crd.adoc.erb", erb_binding, t.name, portfolio_design) end - file "#{$root}/gen/crd/pdf/#{model_name}-CRD.pdf" => [ + file "#{$root}/gen/proc_crd/pdf/#{model_name}-CRD.pdf" => [ __FILE__, - "#{$root}/gen/crd/adoc/#{model_name}-CRD.adoc" + "#{$root}/gen/proc_crd/adoc/#{model_name}-CRD.adoc" ] do |t| - pf_adoc2pdf("#{$root}/gen/crd/adoc/#{model_name}-CRD.adoc", t.name) + pf_adoc2pdf("#{$root}/gen/proc_crd/adoc/#{model_name}-CRD.adoc", t.name) end - file "#{$root}/gen/crd/html/#{model_name}-CRD.html" => [ + file "#{$root}/gen/proc_crd/html/#{model_name}-CRD.html" => [ __FILE__, - "#{$root}/gen/crd/adoc/#{model_name}-CRD.adoc" + "#{$root}/gen/proc_crd/adoc/#{model_name}-CRD.adoc" ] do |t| - pf_adoc2html("#{$root}/gen/crd/adoc/#{model_name}-CRD.adoc", t.name) + pf_adoc2html("#{$root}/gen/proc_crd/adoc/#{model_name}-CRD.adoc", t.name) end end @@ -85,7 +86,7 @@ namespace :gen do exit 1 end - Rake::Task["#{$root}/gen/crd/pdf/#{model_name}-CRD.pdf"].invoke + Rake::Task["#{$root}/gen/proc_crd/pdf/#{model_name}-CRD.pdf"].invoke end desc <<~DESC @@ -105,6 +106,6 @@ namespace :gen do exit 1 end - Rake::Task["#{$root}/gen/crd/html/#{args[:model_name]}-CRD.html"].invoke + Rake::Task["#{$root}/gen/proc_crd/html/#{args[:model_name]}-CRD.html"].invoke end end diff --git a/backends/proc_crd/templates/proc_crd.adoc.erb b/backends/proc_crd/templates/proc_crd.adoc.erb new file mode 100644 index 000000000..462b3f5a3 --- /dev/null +++ b/backends/proc_crd/templates/proc_crd.adoc.erb @@ -0,0 +1,458 @@ +<%= portfolio_design.include_erb("beginning.adoc.erb") %> + += <%= proc_cert_model.name %> Processor Certification Requirements Document + +[Preface] +== Revision History + +History of documentation changes that eventually lead to releases. + +[cols="1,1,5"] +|=== +| Date | Revision | Changes + +<% proc_cert_model.revision_history.each do |rev| -%> +| <%= rev.date %> +| <%= rev.revision %> +a| <% rev.changes.each do |change| %> +* <%= change %> +<% end -%> +<% end -%> +|=== + +[Preface] +== Typographic Conventions + +CSR field colors:: + +* Grey fields are reserved (WPRI) +* Green fields are present +* Red fields are defined by the RISC-V ISA but not present + +CSR field types:: + +[%autowidth] +|=== +| Abbreviation | Description + +<% CsrField::TYPE_DESC_MAP.each do |abbreviation, description| -%> +| <%= abbreviation %> +| <%= description %> +<% end -%> +|=== + +== Introduction + +<%= proc_cert_model.introduction %> + +<%= proc_cert_class.introduction %> + +=== What's a CRD? + +Certification Requirements Documents (CRDs) list requirements an implementation must meet +to obtain an associated RVI (RISC-V International) certificate. +CRDs are developed by the RVI CSC (Certification Steering Committee) organization in collaboration +with the RVI TSC (Technical Steering Committee) organization who creates RISC-V standards. + +The CRDs refer to and augment information provided in existing ratified RVI standards. + +There are a variety of certificates offered by RVI to accomodate the various RVI standards. +There are certificates for processors, non-processor system IP (e.g., IOMMU), +and system platforms (processor + system IP) hardware standards. +There are multiple classes of processor certificates available to accomodate the wide range of +RISC-V implementations from basic microcontrollers to advanced Applications-class processors. + +Each CRD has a list of mandatory behaviors along with a list of optional behaviors. +Note that not all behaviors allowed in RISC-V standards are supported by a particular CRD. + +=== CRD Naming Scheme + +CRDs have the following naming scheme: + + Format: [v] + +Where: + +* Left & right square braces denote optional. +* Less-than & greater-than signs just separate fields (i.e., they aren't present in the CRD name). +* identifies the type of RISC-V standard (processor, non-processor system IP, or platform) along with + any other information required to identify the variant of that standard. +* identifies a particular CRD release +** Format is [.[.]] +** Inspired by semantic versioning scheme (https://semver.org/) but doesn't follow it exactly +** The release is updated when certification test changes are made that *could* cause a previously certified + implementation to now fail. + Examples are fixing a test bug, or increasing test coverage, or requiring a new version of a standard + A release of 0 is used for pre-release versions of a CRD and release versions start with 1. +** The release is updated when a CRD increases support for optional behaviors. + Examples are supporting for new optional standards or + 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 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) + +=== CRD Terminology + +.Requirement Types +[%autowidth] +|=== +| Term | Meaning + +| MANDATORY | You have to implement it to get a certificate and the certificate tests will cover it +| OPTIONAL | It's up to you if you implement or not. If you claim to implement it, certificate tests will cover it +| IN-SCOPE | Either MANDATORY or OPTIONAL +| OUT-OF-SCOPE | It's up to you if you implement or not. If you implement it, it won't be certified but make sure you don't mess up anything we are certifying. +| INCOMPATIBLE | If you implement it you won't get a certificate +|=== + +.Glossary +[%autowidth] +|=== +| Term | Meaning + +| CRD | Certification Requirements Document +| N/A | “Not Applicable” +| AKA | “Also Known As” +|=== + +=== Processor CRDs + +There are Processor CRDs for different classes of RISC-V processors. +These documents augment information in the related TSC Profile when available and/or other RVI standards documents +(e.g., Priv and Unpriv ISA manuals). +Only ratified extensions are candidates for certification. +This implies all custom extensions are also OUT-OF-SCOPE. + +==== Processor Certificate Class Naming Scheme + +Processor Certificate Class names have the following format: + + [<-base>] + +Where: + +* is MC for Microcontroller Class and AC for Apps-processor Class +* is 3-digit integer defined as follows: +** The hundreds's digit indicates the series +** The ten's digit identifies large differences in mandatory extensions (e.g., V, H) within the series +** The one's digit indentifies small/medium differences in mandatory extensions (e.g., Zicond, PMP) within the series +* is optional and is 32 for RV32I, 64 for RV64I, and 32E for RV32E +** If a CRD supports multiple bases and is omitted in a reference, it applies to all supported bases +** If a CRD only supports one base then is generally omitted + +[%autowidth] +|=== +| CRD | TSC Profile | Description + +| MC100-series | TBD | 32/64-bit minimal microcontroller that runs low-level software on an RTOS or bare-metal (no virtual memory) +| MC200-series | TBD | 32/64-bit intermediate microcontroller +| MC300-series | TBD | 32/64-bit advanced microcontroller +| AC100-series | RVB23 | 64-bit Apps-processor running Bespoke rich operating systems (e.g., Yocto Linux) +| AC200-series | RVA23 | 64-bit Apps-processor running standard rich operating systems (e.g., commercial Linux distributions, Android) +|=== + +==== CSR Field Terminology + +.Definition of CSR Fields +[%autowidth] +|=== +| Field Type | Read Value After Writing Illegal Value | Read Value Function Of | Illegal Instruction Exception | Priv ISA Manual Quote + +| WLRL | Any deterministic legal or illegal value | Value before write and illegal value written | Optional +| Implementations are permitted but not required to raise an illegal-instruction exception if an instruction attempts to write a non-supported value to a WLRL field. Implementations can return arbitrary bit patterns on the read of a WLRL field when the last write was of an illegal value, but the value returned should deterministically depend on the illegal written value and the value of the field prior to the write. +| WARL | Any deterministic legal value | Any architectural hart state | Prohibited +| Implementations will not raise an exception on writes of unsupported values to a WARL field. Implementations can return any legal value on the read of a WARL field when the last write was of an illegal value, but the legal value returned should deterministically depend on the illegal written value and the architectural state of the hart. +| WPRI | 0 | Nothing | Not specified +| Some whole read/write fields are reserved for future use. Implementations that do not furnish these fields must make them read-only zero. +|=== + +*WARL (Write Anything, Read Legal)*: + +The Priv ISA requires reads of WARL fields to return some implementation-dependent deterministic legal value +after the field is written with an illegal value. +Certifying such behaviors is expensive and provides low value for a certificate since software can't rely +on a particular behavior from one implementation to another. + +Processor CRDs define writes to WARL fields of illegal values to be OUT-OF-SCOPE unless otherwise stated +(i.e., certification tests will only ever write legal values to WARL fields except for the special cases listed below). +When not OUT-OF-SCOPE, the required behavior is defined as this might be more constrained in implementations than +in the standard. + +The following special cases for WARL are supported when explicitly listed in the corresponding CRD CSR field requirements: + +1. Probing for Field Width + +* Some WARL fields are variable length such as the ASID field in the virtual memory extension. +* Here's the algorithm recommended to discover the ASID width: +** The number of implemented ASID bits, termed ASIDLEN, may be determined by writing one to every bit position in + the ASID field, then reading back the value in the satp CSR to see which bit positions in the ASID field hold a one. +* The RVCP-provided certification materials (certification tests, certification reference models) can map writes of + illegal values to the ASID field to the corresponding read value as long as they are provided the ASIDLEN value + for an implementation. + +2. Probing for Options + +* E.g., Writable misa bits + +3. Allowed values are a function of extension presence and/or their parameters + +* E.g., satp.mode legal write values + +*WLRL (Write Legal, Read Legal)*: + +The Priv ISA requires reads of WLRL fields to return some implementation-dependent deterministic arbitrary value +after the field is written with an illegal value. +Certifying such behaviors is expensive and provides low value for a certificate since software can't rely +on a particular behavior. +Processor CRDs define writes to WLRL fields of illegal values to be OUT-OF-SCOPE unless otherwise stated +(i.e., certification tests will only ever write legal values to WLRL fields). + +*WPRI (Write Preserve, Read Ignore)*: + +The Priv ISA requires reads of WPRI fields to return a value of 0. +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 intended to be comprehensive +verification test suites anyways. + +=== Related Specifications + +[cols="2,2,3,3,3"] +|=== +| Certificate Model | TSC Profile | Unpriv ISA Manual | Priv ISA Manual | Debug Manual + +| <%= 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 + +|=== +| M | S | U | VS | VU + +| <% 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 -%> + +|=== + +<<< +== Extensions + +Any RISC-V extensions not listed in this section are OUT-OF-SCOPE. +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 = proc_cert_model.in_scope_ext_reqs(presence_obj) -%> +<% if ext_reqs.empty? -%> +None +<% else -%> +[%autowidth] +|=== +| Requirement ID | Extension | Version | Long Name | Note + +<% ext_reqs.each do |ext_req| -%> +<% ext = arch.extension(ext_req.name) -%> +| <%= ext_req.req_id %> +| <%= link_to_ext(ext_req.name) %> +| <%= ext_req.requirement_specs.map(&:to_s).join(", ") %> +| <%= ext.nil? ? "" : ext.long_name %> +| <%= ext_req.note.nil? ? "" : ext_req.note %> +<% end # each ext_req -%> +|=== +<% end # if empty ext_reqs -%> + +<% 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 proc_cert_model.recommendations.empty? -%> +=== Recommendations + +Recommendations are not strictly mandated but are included to guide implementers. + +<% proc_cert_model.recommendations.each do |recommendation| -%> +<%= recommendation.text %> +<% end # each recommendation -%> +<% end # unless recommendations empty -%> + +<<< +== Implementation-dependencies + +RISC-V standards support many implementation-defined parameters. In many cases, there +are no names associated with these parameters. Names are defined in this section when +not provided in the associated standard. + +=== IN-SCOPE Parameters + +These implementation-dependent options defined by MANDATORY or OPTIONAL extensions are IN-SCOPE. +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 portfolio_design.all_in_scope_params.empty? -%> +None +<% else -%> +[cols="4,2,1,1,2"] +|=== +| Parameter | Type | Allowed Value(s) | Extension(s) | Note + +<% design.all_in_scope_params.each do |in_scope_param| -%> +<% param = in_scope_param.param -%> +<% in_scope_exts = portfolio_design.all_in_scope_exts_with_param(param) -%> +| <%= param.name_potentially_with_link(in_scope_exts) %> +| <%= param.schema_type %> +| <%= in_scope_param.allowed_values %> +| <%= in_scope_exts.each { |ext| param.name_with_link(ext) } %> +a| <%= in_scope_param.note %> +<% end # do -%> +|=== +<% end # if table -%> + +=== OUT-OF-SCOPE Parameters + +These implementation-dependent options defined by MANDATORY or OPTIONAL extensions are OUT-OF-SCOPE. +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 portfolio_design.all_out_of_scope_params.empty? -%> +None +<% else -%> +[%autowidth] +|=== +| Parameters | Type | Extension(s) + +<% portfolio_design.all_out_of_scope_params.each do |param| -%> +<% exts = portfolio_design.all_in_scope_exts_without_param(param) -%> +| <%= param.name_potentially_with_link(exts) %> +| <%= param.schema_type %> +| <%= exts.each { |ext| param.name_with_link(ext) } %> + +<% end # do -%> +|=== +<% end # if table -%> + +== Traps + +RISC-V supports both synchronous exceptions and asynchronous interrupts. +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 + +=== Synchronous Exceptions + +|=== +| `xcause.CODE` CSR Field Value | Name +<% design.in_scope_exception_codes.sort_by{ |code| code.num }.each do |code| -%> +| <%= code.num %> | <%= code.name %> +<% end -%> +|=== + +=== Asynchronous Interrupts + +|=== +| `xcause.CODE` CSR Field Value | Name +<% design.in_scope_interrupt_codes.sort_by{ |code| code.num }.each do |code| -%> +| <%= code.num %> | <%= code.name %> +<% end -%> +|=== + +== Instruction Summary + +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 + +[%autowidth] +|=== +| Name | Long Name + +<% design.in_scope_instructions.each do |inst| -%> +| <%= link_to_inst(inst.name) %> +| <%= inst.long_name %> +<% end # do -%> +|=== + +== CSR Summary + +=== By Name + +[%autowidth] +|=== +| Name | Long Name | Address | Mode | Primary Extension + +<% design.in_scope_csrs.sort_by!(&:name).each do |csr| -%> +| <%= link_to_csr(csr.name) %> +| <%= csr.long_name %> +| <%= "0x#{csr.address.to_s(16)}" %> +| <%= csr.priv_mode %> +| <%= csr.primary_defined_by %> +<% end # do -%> +|=== + +=== By Address + +[%autowidth] +|=== +| Address | Mode | Name | Long Name | Primary Extension + +<% design.in_scope_csrs.sort_by!(&:address).each do |csr| -%> +| <%= "0x#{csr.address.to_s(16)}" %> +| <%= csr.priv_mode %> +| <%= link_to_csr(csr.name) %> +| <%= csr.long_name %> +| <%= csr.primary_defined_by %> +<% end # do -%> +|=== + +<% 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. + +<% proc_cert_model.requirement_groups.each do |group| -%> +=== <%= group.name %> + +<%= group.description %> + +<% unless group.when.nil? -%> +[IMPORTANT] +<%= group.name %> requirements only apply when <%= group.when_pretty %>. +<% end -%> + +[%autowidth] +|=== +| Req Number | Description + +<% group.requirements.each do |req| -%> +| <%= req.name %> +a| <%= req.description %> +<% unless req.when.nil? -%> +[IMPORTANT] +Requirement <%= req.name %> only apply when <%= req.when_pretty %>. +<% end -%> +<% end -%> +|=== + +<% end -%> +<% end # unless requirement_groups.empty? -%> + +<%= portfolio_design.include_erb("ext_appendix.adoc.erb") %> +<%= portfolio_design.include_erb("inst_appendix.adoc.erb") %> +<%= portfolio_design.include_erb("csr_appendix.adoc.erb") %> +<%= portfolio_design.include_erb("idl_func_appendix.adoc.erb") %> diff --git a/backends/proc_ctp/tasks.rake b/backends/proc_ctp/tasks.rake new file mode 100644 index 000000000..d04e0c45e --- /dev/null +++ b/backends/proc_ctp/tasks.rake @@ -0,0 +1,111 @@ +# frozen_string_literal: true +# +# Contains Rake rules to generate adoc, PDF, and HTML for a CTP (Certification Test Plan). + +require "pathname" + +PROC_CTP_DOC_DIR = Pathname.new "#{$root}/backends/proc_ctp" + +Dir.glob("#{$root}/arch/proc_cert_model/*.yaml") do |f| + model_name = File.basename(f, ".yaml") + model_obj = YAML.load_file(f, permitted_classes: [Date]) + class_name = File.basename(model_obj['class']['$ref'].split("#")[0], ".yaml") + raise "Ill-formed processor certificate model file #{f}: missing 'class' field" if model_obj['class'].nil? + + file "#{$root}/gen/proc_ctp/adoc/#{model_name}-CTP.adoc" => [ + __FILE__, + "#{$root}/arch/proc_cert_class/#{class_name}.yaml", + "#{$root}/arch/proc_cert_model/#{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", + "#{$root}/backends/portfolio/templates/ext_appendix.adoc.erb", + "#{$root}/backends/portfolio/templates/inst_appendix.adoc.erb", + "#{$root}/backends/portfolio/templates/csr_appendix.adoc.erb", + "#{$root}/backends/portfolio/templates/beginning.adoc.erb", + "#{PROC_CTP_DOC_DIR}/templates/proc_ctp.adoc.erb" + ] do |t| + arch = pf_create_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 object for #{model_name}" + proc_cert_model = arch.proc_cert_model(model_name) + proc_cert_class = proc_cert_model.proc_cert_class + + # Create the one PortfolioDesign object required for the ERB evaluation. + puts "UPDATE: Creating PortfolioDesign object using processor certificate model #{model_name}" + portfolio_design = PortfolioDesign.new(model_name, arch, PortfolioDesign.proc_ctp_type, [proc_cert_model], proc_cert_class) + + # 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). + def evaluate_erb + binding + end + erb_binding = evaluate_erb + portfolio_design.init_erb_binding(erb_binding) + erb_binding.local_variable_set(:proc_cert_model, proc_cert_model) + erb_binding.local_variable_set(:proc_cert_class, proc_cert_class) + + pf_create_adoc("#{PROC_CTP_DOC_DIR}/templates/proc_ctp.adoc.erb", erb_binding, t.name, portfolio_design) + end + + file "#{$root}/gen/proc_ctp/pdf/#{model_name}-CTP.pdf" => [ + __FILE__, + "#{$root}/gen/proc_ctp/adoc/#{model_name}-CTP.adoc" + ] do |t| + pf_adoc2pdf("#{$root}/gen/proc_ctp/adoc/#{model_name}-CTP.adoc", t.name) + end + + file "#{$root}/gen/proc_ctp/html/#{model_name}-CTP.html" => [ + __FILE__, + "#{$root}/gen/proc_ctp/adoc/#{model_name}-CTP.adoc" + ] do |t| + pf_adoc2html("#{$root}/gen/proc_ctp/adoc/#{model_name}-CTP.adoc", t.name) + end +end + +namespace :gen do + desc <<~DESC + Generate Processor CTP (Certification Test Plan) as a PDF. + + Required options: + model_name - The name of the certification model under arch/proc_cert_model + DESC + task :proc_ctp_pdf, [:model_name] do |_t, args| + model_name = args[:model_name] + if model_name.nil? + warn "Missing required option: 'model_name'" + exit 1 + end + + unless File.exist?("#{$root}/arch/proc_cert_model/#{model_name}.yaml") + warn "No certification model named '#{model_name}' found in arch/proc_cert_model" + exit 1 + end + + Rake::Task["#{$root}/gen/proc_ctp/pdf/#{model_name}-CTP.pdf"].invoke + end + + desc <<~DESC + Generate Processor CTP (Certification Test Plan) as an HTML file. + + Required options: + model_name - The name of the certification model under arch/proc_cert_model + DESC + task :proc_ctp_html, [:model_name] do |_t, args| + if args[:model_name].nil? + warn "Missing required option: 'model_name'" + exit 1 + end + + unless File.exist?("#{$root}/arch/proc_cert_model/#{args[:model_name]}.yaml") + warn "No certification model named '#{args[:model_name]}' found in arch/proc_cert_model" + exit 1 + end + + Rake::Task["#{$root}/gen/proc_ctp/html/#{args[:model_name]}-CTP.html"].invoke + end +end diff --git a/backends/crd/templates/crd.adoc.erb b/backends/proc_ctp/templates/proc_ctp.adoc.erb similarity index 97% rename from backends/crd/templates/crd.adoc.erb rename to backends/proc_ctp/templates/proc_ctp.adoc.erb index 4fa55f6db..a03bc245d 100644 --- a/backends/crd/templates/crd.adoc.erb +++ b/backends/proc_ctp/templates/proc_ctp.adoc.erb @@ -1,19 +1,4 @@ -// Number heading sections (e.g., 1.0, 1.1, etc.) -:sectnums: - -// Add a table of contents for HTML (and VSCode adoc preview) -:toc: left - -// Include headings up to 3 levels deep (don't know why 5 gives you this). -:toclevels: 5 - -// -// Stuff to generate nice wavedrom drawings of instruction and CSR fields -// -:wavedrom: <%= $root %>/node_modules/.bin/wavedrom-cli - -// TODO: needs to be changed -:imagesoutdir: images +<%= portfolio_design.include_erb("beginning.adoc.erb") %> = <%= proc_cert_model.name %> Processor Certification Requirements Document @@ -471,3 +456,7 @@ Requirement <%= req.name %> only apply when <%= req.when_pretty %>. <%= portfolio_design.include_erb("inst_appendix.adoc.erb") %> <%= portfolio_design.include_erb("csr_appendix.adoc.erb") %> <%= portfolio_design.include_erb("idl_func_appendix.adoc.erb") %> + +include::ext/riscv-isa-manual/src/riscv-unprivileged.adoc[] + +include::ext/riscv-isa-manual/src/riscv-privileged.adoc[] diff --git a/backends/profile/tasks.rake b/backends/profile/tasks.rake index 25be87c69..7322eae5b 100644 --- a/backends/profile/tasks.rake +++ b/backends/profile/tasks.rake @@ -33,6 +33,7 @@ Dir.glob("#{$root}/arch/profile_release/*.yaml") do |f| "#{$root}/backends/portfolio/templates/ext_appendix.adoc.erb", "#{$root}/backends/portfolio/templates/inst_appendix.adoc.erb", "#{$root}/backends/portfolio/templates/csr_appendix.adoc.erb", + "#{$root}/backends/portfolio/templates/beginning.adoc.erb", "#{PROFILE_DOC_DIR}/templates/profile.adoc.erb" ].concat(profile_pathnames) do |t| arch = pf_create_arch @@ -48,7 +49,7 @@ Dir.glob("#{$root}/arch/profile_release/*.yaml") do |f| # Provide it with all the profiles in this ProfileRelease. puts "UPDATE: Creating PortfolioDesign object using profile release #{release_name}" portfolio_design = - PortfolioDesign.new(release_name, arch, profile_release.profiles, profile_class) + PortfolioDesign.new(release_name, arch, PortfolioDesign.profile_release_type, profiles, profile_class) # 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/templates/profile.adoc.erb b/backends/profile/templates/profile.adoc.erb index 5b454ccad..ef03127f1 100644 --- a/backends/profile/templates/profile.adoc.erb +++ b/backends/profile/templates/profile.adoc.erb @@ -1,58 +1,9 @@ -[[header]] -:description: <%= profile_release.marketing_name %> Profile -:revdate: <%= profile_release.ratification_date.nil? ? Date.today : profile_release.ratification_date %> +<%= portfolio_design.include_erb("beginning.adoc.erb") %> -// TODO - Figure out what we really want here - Change percent hash to percent equals. -// :revnumber: <%# profile_release.map(&:version).sort.last %> - -:revmark: "TODO: revmark" -:company: <%= profile_class.company.name %> -:url-riscv: https://riscv.org -:doctype: book -:preface-title: Licensing and Acknowledgements -:colophon: -:appendix-caption: Appendix -:title-logo-image: image:risc-v_logo.png["RISC-V International Logo",pdfwidth=3.25in,align=center] +:revdate: <%= profile_release.ratification_date.nil? ? Date.today : profile_release.ratification_date %> <%# unless profile_release.state == "ratified" -%> :page-background-image: image:draft.png[opacity=20%] <%# end -%> -:back-cover-image: image:riscv-horizontal-color.svg[opacity=25%] -// Settings -:experimental: -:reproducible: -:wavedrom: <%= $root %>/node_modules/.bin/wavedrom-cli -// TODO: needs to be changed -:imagesoutdir: images -:icons: font -:lang: en -:example-caption: Example -:listing-caption: Listing -:table-caption: Table -:figure-caption: Figure -:xrefstyle: short -:chapter-refsig: Chapter -:section-refsig: Section -:appendix-refsig: Appendix -:sectnums: -:toc: left -:toclevels: 5 -:source-highlighter: pygments -ifdef::backend-pdf[] -:source-highlighter: rouge -endif::[] -:data-uri: -:hide-uri-scheme: -:stem: -:footnote: -:stem: latexmath -:footnote: -:le: ≤ -:ge: ≥ -:ne: ≠ -:approx: ≈ -:inf: ∞ -:csrname: envcfg -:imagesdir: images = <%= profile_release.name %> Profile Release diff --git a/lib/backend_helpers.rb b/lib/backend_helpers.rb index 54380fe13..0aa81f90b 100644 --- a/lib/backend_helpers.rb +++ b/lib/backend_helpers.rb @@ -40,8 +40,8 @@ def partial(template_pname, inputs = {}) # Documentation: # - How to make cross-references: https://docs.asciidoctor.org/asciidoc/latest/macros/xref/ # - How to create anchors: https://docs.asciidoctor.org/asciidoc/latest/attributes/id/ - # - See https://github.com/riscv/riscv-isa-manual/issues/1397 for a detailed - # discussion about how to put anchors and links into AsciiDoc. + # - See https://github.com/riscv/riscv-isa-manual/issues/1397#issuecomment-2515109936 for + # discussion about using [#anchor] instead of [[anchor]] due to Antora's support. # @return [String] A hyperlink to an extension # @param ext_name [String] Name of the extension @@ -85,7 +85,7 @@ def link_to_csr_field(csr_name, field_name) # @return [String] An anchor for an extension # @param ext_name [String] Name of the extension def anchor_for_ext(ext_name) - "[[ext-#{ext_name.sanitize}-def]]" + "[#ext-#{ext_name.sanitize}-def]" end # @return [String] An anchor for a parameter defined by a particular extension. @@ -93,32 +93,32 @@ def anchor_for_ext(ext_name) # @param param_name [String] Name of the parameter def anchor_for_ext_param(ext_name, param_name) check_no_periods(param_name) - "[[ext_param-#{ext_name.sanitize}-#{param_name}-def]]" + "[#ext_param-#{ext_name.sanitize}-#{param_name}-def]" end # Insert anchor to an instruction. # @param name [String] Name of the instruction def anchor_for_inst(name) - "[[inst-#{name.sanitize}-def]]" + "[#inst-#{name.sanitize}-def]" end # Insert anchor to a CSR. # @param name [String] Name of the CSR def anchor_for_csr(name) - "[[csr-#{name.sanitize}-def]]" + "[#csr-#{name.sanitize}-def]" end # Insert anchor to a CSR field. # @param csr_name [String] Name of the CSR # @param field_name [String] Name of the CSR field def anchor_for_csr_field(csr_name, field_name) - "[[csr_field-#{csr_name.sanitize}-#{field_name.sanitize}-def]]" + "[#csr_field-#{csr_name.sanitize}-#{field_name.sanitize}-def]" end # Insert anchor to an IDL function. # @param name [String] Name of the function def anchor_for_func(name) - "[[func-#{name.sanitize}-def]]" + "[#func-#{name.sanitize}-def]" end private diff --git a/lib/portfolio_design.rb b/lib/portfolio_design.rb index 430807ffc..b8b4c5305 100644 --- a/lib/portfolio_design.rb +++ b/lib/portfolio_design.rb @@ -21,17 +21,30 @@ class PortfolioDesign < Design # @return [String] Kind of portfolio for all portfolios in this design attr_reader :portfolio_kind - # @param base_isa_name [#to_s] The name of the base ISA configuration (rv32 or rv64) + # Class methods + def self.profile_release_type = "Profile Release" + def self.proc_crd_type = "Certification Requirements Document" + def self.proc_ctp_type = "Certification Test Plan" + def self.portfolio_design_types = [profile_release_type, proc_crd_type, proc_ctp_type] + + # @return [String] Type of design suitable for human readers. + attr_reader :portfolio_design_type + + # @param name [#to_s] The name of the portfolio design (i.e., backend filename without a suffix) # @param arch [Architecture] The database of RISC-V standards + # @param portfolio_design_type [String] Type of portfolio design associated with this design # @param mxlen [Integer] Comes from portfolio YAML "base" (either 32 or 64) # @param portfolios [Array] Portfolios being converted to adoc # @param portfolio_class [PortfolioClass] PortfolioClass for all the Portfolios # @param overlay_path [String] Optional path to a directory that overlays the architecture - def initialize(base_isa_name, arch, portfolios, portfolio_class, overlay_path: nil) + def initialize(name, arch, portfolio_design_type, portfolios, portfolio_class, overlay_path: nil) raise ArgumentError, "arch must be an Architecture but is a #{arch.class}" unless arch.is_a?(Architecture) + raise ArgumentError, "portfolio_design_type of #{portfolio_design_type} unknown" unless PortfolioDesign.portfolio_design_types.include?(portfolio_design_type) raise ArgumentError, "portfolios must be an Array but is a #{portfolios.class}" unless portfolios.is_a?(Array) raise ArgumentError, "portfolio_class must be a PortfolioClass but is a #{portfolio_class.class}" unless portfolio_class.is_a?(PortfolioClass) + @portfolio_design_type = portfolio_design_type + # 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) @@ -42,7 +55,7 @@ def initialize(base_isa_name, arch, portfolios, portfolio_class, overlay_path: n max_base = portfolios.map(&:base).max 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, max_base, overlay_path: overlay_path) + super(name, arch, max_base, overlay_path: overlay_path) end # Returns a string representation of the object, suitable for debugging. @@ -261,6 +274,7 @@ def init_erb_binding(erb_binding) erb_binding.local_variable_set(:arch, arch) erb_binding.local_variable_set(:design, self) erb_binding.local_variable_set(:portfolio_design, self) + erb_binding.local_variable_set(:portfolio_design_type, @portfolio_design_type) erb_binding.local_variable_set(:portfolio_class, @portfolio_class) erb_binding.local_variable_set(:portfolio_kind, @portfolio_kind) erb_binding.local_variable_set(:portfolios, @portfolio_grp.portfolios) @@ -272,11 +286,15 @@ def init_erb_binding(erb_binding) # @param template_path [String] Name of template file located in backends/portfolio/templates # @return [String] Result of ERB evaluation of the template file def include_erb(template_name) + template_pname = "portfolio/templates/#{template_name}" + + puts "UPDATE: #{portfolio_design_type} processing ERB partial template '#{template_pname}'" partial("portfolio/templates/#{template_name}", { arch: arch, design: self, portfolio_design: self, + portfolio_design_type: @portfolio_design_type, portfolio_class: @portfolio_class, portfolio_kind: @portfolio_kind, portfolios: @portfolio_grp.portfolios diff --git a/lib/test/test_backend_helpers.rb b/lib/test/test_backend_helpers.rb index 1b2a4786c..d79e016f1 100644 --- a/lib/test/test_backend_helpers.rb +++ b/lib/test/test_backend_helpers.rb @@ -11,39 +11,39 @@ class TestBackendHelpers < Minitest::Test def test_ext assert_equal("%%LINK%ext;foo;foo%%", link_to_ext("foo")) - assert_equal("[[ext-foo-def]]", anchor_for_ext("foo")) + assert_equal("[#ext-foo-def]", anchor_for_ext("foo")) assert_equal("%%LINK%ext;fo_o;fo_o%%", link_to_ext("fo.o")) - assert_equal("[[ext-fo_o-def]]", anchor_for_ext("fo.o")) + assert_equal("[#ext-fo_o-def]", anchor_for_ext("fo.o")) end def test_ext_param assert_equal("%%LINK%ext_param;foo.bar;bar%%", link_to_ext_param("foo","bar")) - assert_equal("[[ext_param-foo-bar-def]]", anchor_for_ext_param("foo","bar")) + assert_equal("[#ext_param-foo-bar-def]", anchor_for_ext_param("foo","bar")) assert_equal("%%LINK%ext_param;fo_o.bar;bar%%", link_to_ext_param("fo.o","bar")) - assert_equal("[[ext_param-fo_o-bar-def]]", anchor_for_ext_param("fo.o","bar")) + assert_equal("[#ext_param-fo_o-bar-def]", anchor_for_ext_param("fo.o","bar")) assert_raises(ArgumentError) { link_to_ext_param("foo","ba.r") } assert_raises(ArgumentError) { anchor_for_ext_param("foo","ba.r") } end def test_inst assert_equal("%%LINK%inst;foo;foo%%", link_to_inst("foo")) - assert_equal("[[inst-foo-def]]", anchor_for_inst("foo")) + assert_equal("[#inst-foo-def]", anchor_for_inst("foo")) assert_equal("%%LINK%inst;fo_o;fo_o%%", link_to_inst("fo.o")) - assert_equal("[[inst-fo_o-def]]", anchor_for_inst("fo.o")) + assert_equal("[#inst-fo_o-def]", anchor_for_inst("fo.o")) end def test_csr assert_equal("%%LINK%csr;foo;foo%%", link_to_csr("foo")) - assert_equal("[[csr-foo-def]]", anchor_for_csr("foo")) + assert_equal("[#csr-foo-def]", anchor_for_csr("foo")) assert_equal("%%LINK%csr;fo_o;fo_o%%", link_to_csr("fo.o")) - assert_equal("[[csr-fo_o-def]]", anchor_for_csr("fo.o")) + assert_equal("[#csr-fo_o-def]", anchor_for_csr("fo.o")) end def test_csr_field assert_equal("%%LINK%csr_field;foo.bar;foo.bar%%", link_to_csr_field("foo","bar")) - assert_equal("[[csr_field-foo-bar-def]]", anchor_for_csr_field("foo","bar")) + assert_equal("[#csr_field-foo-bar-def]", anchor_for_csr_field("foo","bar")) assert_equal("%%LINK%csr_field;fo_o.ba_r;fo_o.ba_r%%", link_to_csr_field("fo.o","ba.r")) - assert_equal("[[csr_field-fo_o-ba_r-def]]", anchor_for_csr_field("fo.o","ba.r")) + assert_equal("[#csr_field-fo_o-ba_r-def]", anchor_for_csr_field("fo.o","ba.r")) end end diff --git a/schemas/inst_schema.json b/schemas/inst_schema.json index 637543623..c51402bfc 100644 --- a/schemas/inst_schema.json +++ b/schemas/inst_schema.json @@ -149,6 +149,19 @@ }, "additionalProperties": false }, + "ctp-steps": { + "type": "array", + "items": { + "type": "object", + "required": ["description"], + "additionalProperties": false, + "properties": { + "description": { + "type": "string" + } + } + } + }, "inst_data": { "type": "object", "required": [ @@ -257,6 +270,61 @@ "type": "string", "description": "Functional description of the instruction using Sail" }, + "certification-coverage-points": { + "description": "Architecturally visible behaviors requiring validation by certification tests", + "type": "array", + "required": ["name", "xref", "description"], + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "xref": { + "type": "string", + "description": "Cross reference to ISA manual or IDL" + }, + "description": { + "type": "string" + } + } + }, + "certification-test-plan": { + "description": "How certification coverage points will be validated", + "type": "array", + "required": ["name", "description", "coverage-points"], + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "coverage-points": { + "type": "array", + "description": "List of certification coverage points to be validated", + "items": { + "type": "string" + } + }, + "setup": { + "$ref": "#/$defs/ctp-steps", + "description": "List of steps to setup test" + }, + "execution": { + "$ref": "#/$defs/ctp-steps", + "description": "List of steps to execute test after setup" + }, + "validation": { + "$ref": "#/$defs/ctp-steps", + "description": "List of steps to determine test pass/fail status" + }, + "teardown": { + "$ref": "#/$defs/ctp-steps", + "description": "List of steps to get ready for the next test" + } + } + }, "assembly": { "type": "string", "description": "Assembly format of the instruction. Can use decode variables" From e1b521ffe17fa62eff4d4fca9bee523fc395681c Mon Sep 17 00:00:00 2001 From: James Ball Date: Sun, 19 Jan 2025 13:14:26 +0000 Subject: [PATCH 38/61] Generating PDF for CTP with ISA manuals included in it. --- .gitmodules | 3 + Rakefile | 3 + arch/inst/M/mul.yaml | 8 +- backends/cfg_html_doc/templates/csr.adoc.erb | 2 +- backends/cfg_html_doc/templates/func.adoc.erb | 2 +- backends/cfg_html_doc/templates/inst.adoc.erb | 2 +- backends/cfg_html_doc/templates/toc.adoc.erb | 6 +- .../ext_pdf_doc/templates/ext_pdf.adoc.erb | 2 +- backends/manual/tasks.rake | 4 +- backends/manual/templates/csr.adoc.erb | 6 +- backends/manual/templates/ext.adoc.erb | 5 +- backends/manual/templates/func.adoc.erb | 2 +- .../manual/templates/instruction.adoc.erb | 2 +- backends/manual/templates/isa_nav.adoc.erb | 8 +- backends/manual/templates/param_list.adoc.erb | 4 +- backends/portfolio/tasks.rake | 6 +- .../portfolio/templates/csr_appendix.adoc.erb | 6 +- .../portfolio/templates/ext_appendix.adoc.erb | 8 +- .../templates/idl_func_appendix.adoc.erb | 2 +- .../templates/inst_appendix.adoc.erb | 2 +- backends/proc_cert/README.adoc | 2 + backends/proc_cert/tasks.rake | 37 ++++ backends/proc_cert/templates/README.adoc | 1 + backends/proc_crd/tasks.rake | 25 +-- backends/proc_crd/templates/proc_crd.adoc.erb | 8 +- backends/proc_ctp/tasks.rake | 63 ++++--- backends/proc_ctp/templates/proc_ctp.adoc.erb | 24 ++- backends/profile/tasks.rake | 2 +- backends/templates/csr.adoc.erb | 6 +- backends/templates/inst.adoc.erb | 2 +- ext/csc-riscv-isa-manual | 1 + lib/arch_obj_models/manual.rb | 27 +-- lib/arch_obj_models/parameter.rb | 4 +- lib/backend_helpers.rb | 169 +++++++++++------- lib/cfg_arch.rb | 8 +- lib/design.rb | 6 +- lib/idl/passes/gen_adoc.rb | 6 +- lib/portfolio_design.rb | 8 +- lib/test/test_backend_helpers.rb | 124 +++++++++---- 39 files changed, 387 insertions(+), 219 deletions(-) create mode 100644 backends/proc_cert/README.adoc create mode 100644 backends/proc_cert/tasks.rake create mode 100644 backends/proc_cert/templates/README.adoc create mode 160000 ext/csc-riscv-isa-manual diff --git a/.gitmodules b/.gitmodules index 7b74de08d..02151e5bd 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,3 +7,6 @@ [submodule "ext/riscv-isa-manual"] path = ext/riscv-isa-manual url = https://github.com/riscv/riscv-isa-manual +[submodule "ext/csc-riscv-isa-manual"] + path = ext/csc-riscv-isa-manual + url = https://github.com/RISC-V-Certification-Steering-Committee/riscv-isa-manual diff --git a/Rakefile b/Rakefile index 6b7b0aeee..6679c4cfa 100644 --- a/Rakefile +++ b/Rakefile @@ -400,7 +400,9 @@ end # Shortcut targets for building CRDs, CTPs, and Profile Releases. task "MockCRD": "#{$root}/gen/proc_crd/pdf/MockProcessor-CRD.pdf" +task "MockProcessorCRD": "#{$root}/gen/proc_crd/pdf/MockProcessor-CRD.pdf" task "MockCTP": "#{$root}/gen/proc_ctp/pdf/MockProcessor-CTP.pdf" +task "MockProcessorCTP": "#{$root}/gen/proc_ctp/pdf/MockProcessor-CTP.pdf" task "MC100-32-CTP": "#{$root}/gen/proc_ctp/pdf/MC100-32-CTP.pdf" task "MC100-32-CRD": "#{$root}/gen/proc_crd/pdf/MC100-32-CRD.pdf" task "MC100-64-CRD": "#{$root}/gen/proc_crd/pdf/MC100-64-CRD.pdf" @@ -409,6 +411,7 @@ task "MC200-64-CRD": "#{$root}/gen/proc_crd/pdf/MC200-64-CRD.pdf" task "MC300-32-CRD": "#{$root}/gen/proc_crd/pdf/MC300-32-CRD.pdf" task "MC300-64-CRD": "#{$root}/gen/proc_crd/pdf/MC300-64-CRD.pdf" task "MockProfile": "#{$root}/gen/profile/pdf/MockProfileRelease.pdf" +task "MockProfileRelease": "#{$root}/gen/profile/pdf/MockProfileRelease.pdf" task "RVI20": "#{$root}/gen/profile/pdf/RVI20ProfileRelease.pdf" task "RVA20": "#{$root}/gen/profile/pdf/RVA20ProfileRelease.pdf" task "RVA22": "#{$root}/gen/profile/pdf/RVA22ProfileRelease.pdf" diff --git a/arch/inst/M/mul.yaml b/arch/inst/M/mul.yaml index baa3633d7..6f55f8c90 100644 --- a/arch/inst/M/mul.yaml +++ b/arch/inst/M/mul.yaml @@ -37,7 +37,7 @@ access: data_independent_timing: true # Want to add something like this to IDL code. - # [#idl:inst:mul:exception-illegal_instruction]# + # [#idl:code:inst:mul:exception-illegal_instruction]# # if (implemented?(ExtensionName::M) && (CSR[misa].M == 1'b0)) { # raise (ExceptionCode::IllegalInstruction, mode(), $encoding); # }# @@ -75,14 +75,14 @@ certification-coverage-points: description: | Encoding and basic operation for `mul` instruction xrefs: - - norm:inst:mul:encoding - - norm:inst:mul:operation + - manual:inst:mul:encoding + - manual:inst:mul:operation - name: Illegal instruction exception description: | An illegal instruction exception is raised when the instruction is executed and `misa.M` is 0. xrefs: - - idl:inst:mul:exception-illegal_instruction + - idl:code:inst:mul:exception-illegal_instruction certification-test-plan: - name: Encoding diff --git a/backends/cfg_html_doc/templates/csr.adoc.erb b/backends/cfg_html_doc/templates/csr.adoc.erb index 74d65b1b7..1641c1c4d 100644 --- a/backends/cfg_html_doc/templates/csr.adoc.erb +++ b/backends/cfg_html_doc/templates/csr.adoc.erb @@ -73,7 +73,7 @@ This CSR has no fields. However, it must still exist (not cause an `Illegal Inst <%- else -%> <%- csr.implemented_fields(cfg_arch).each do |field| -%> -<%= anchor_for_csr_field(csr.name, field.name) %> +<%= anchor_for_udb_doc_csr_field(csr.name, field.name) %> === `<%= field.name %>` Field [.csr-field-info] diff --git a/backends/cfg_html_doc/templates/func.adoc.erb b/backends/cfg_html_doc/templates/func.adoc.erb index d96a40e75..75f04cf53 100644 --- a/backends/cfg_html_doc/templates/func.adoc.erb +++ b/backends/cfg_html_doc/templates/func.adoc.erb @@ -5,7 +5,7 @@ = Functions <%- cfg_arch.implemented_functions.each do |f| -%> -<%= anchor_for_func(f.name) %> +<%= anchor_for_udb_doc_idl_func(f.name) %> == <%= f.name %><%- if f.builtin? -%> (builtin)<%- end -%> <%= f.description %> diff --git a/backends/cfg_html_doc/templates/inst.adoc.erb b/backends/cfg_html_doc/templates/inst.adoc.erb index e32b94be4..2c3180aed 100644 --- a/backends/cfg_html_doc/templates/inst.adoc.erb +++ b/backends/cfg_html_doc/templates/inst.adoc.erb @@ -1,6 +1,6 @@ :tabs-sync-option: -<%= anchor_for_inst(inst.name) %> +<%= anchor_for_udb_doc_inst(inst.name) %> = <%= inst.name %> *<%= inst.long_name %>* diff --git a/backends/cfg_html_doc/templates/toc.adoc.erb b/backends/cfg_html_doc/templates/toc.adoc.erb index 3b53c6083..5aeff181b 100644 --- a/backends/cfg_html_doc/templates/toc.adoc.erb +++ b/backends/cfg_html_doc/templates/toc.adoc.erb @@ -2,17 +2,17 @@ .Extensions <%- cfg_arch.transitive_implemented_ext_vers.sort { |a, b| a.name <=> b.name }.each do |ext| -%> -* %%LINK%ext;<%= ext.name %>;<%= ext.name %>%% +* %%UDB_DOC_LINK%ext;<%= ext.name %>;<%= ext.name %>%% <%- end -%> .Control and Status Registers <%- cfg_arch.transitive_implemented_csrs.sort { |a, b| a.name <=> b.name }.each do |csr| -%> -* %%LINK%csr;<%= csr.name %>;<%= csr.name %>%% +* %%UDB_DOC_LINK%csr;<%= csr.name %>;<%= csr.name %>%% <%- end -%> .Instructions <%- cfg_arch.transitive_implemented_instructions.sort { |a, b| a.name <=> b.name }.each do |inst| -%> -* %%LINK%inst;<%= inst.name %>;<%= inst.name %>%% +* %%UDB_DOC_LINK%inst;<%= inst.name %>;<%= inst.name %>%% <%- end -%> .IDL functions diff --git a/backends/ext_pdf_doc/templates/ext_pdf.adoc.erb b/backends/ext_pdf_doc/templates/ext_pdf.adoc.erb index 6da830cb7..87b450e67 100644 --- a/backends/ext_pdf_doc/templates/ext_pdf.adoc.erb +++ b/backends/ext_pdf_doc/templates/ext_pdf.adoc.erb @@ -311,7 +311,7 @@ The following <%= ext.csrs.size %> are added by this extension. == IDL Functions <%- ext.reachable_functions(design.symtab).sort { |a,b| a.name <=> b.name }.each do |f| -%> -<%= anchor_for_func(f.name) %> +<%= anchor_for_udb_doc_idl_func(f.name) %> === <%= f.name %><%- if f.builtin? -%> (builtin)<%- end -%> <%= f.description %> diff --git a/backends/manual/tasks.rake b/backends/manual/tasks.rake index 34e3a58cd..024603794 100644 --- a/backends/manual/tasks.rake +++ b/backends/manual/tasks.rake @@ -256,7 +256,7 @@ rule %r{#{MANUAL_GEN_DIR}/.*/.*/antora/modules/funcs/pages/funcs.adoc} => [ File.write t.name, AntoraUtils.resolve_links(design.convert_monospace_to_links(erb.result(binding))) end -# rule to create IDL function appendix page +# rule to create parameter list appendix page rule %r{#{MANUAL_GEN_DIR}/.*/.*/antora/modules/params/pages/param_list.adoc} => [ __FILE__, ($root / "backends" / "manual" / "templates" / "param_list.adoc.erb").to_s @@ -432,7 +432,7 @@ namespace :gen do version_obj.instructions.each do |inst| Rake::Task[antora_path / "modules" / "insts" / "pages" / "#{inst.name}.adoc"].invoke end - version_obj.extensions.each do |ext| + version_obj.ext_vers.each do |ext| Rake::Task[antora_path / "modules" / "exts" / "pages" / "#{ext.name}.adoc"].invoke end Rake::Task[antora_path / "modules" / "params" / "pages" / "param_list.adoc"].invoke diff --git a/backends/manual/templates/csr.adoc.erb b/backends/manual/templates/csr.adoc.erb index dd3a9f34a..ad673adf6 100644 --- a/backends/manual/templates/csr.adoc.erb +++ b/backends/manual/templates/csr.adoc.erb @@ -1,6 +1,6 @@ :tabs-sync-option: -<%= anchor_for_csr(csr.name) %> +<%= anchor_for_udb_doc_csr(csr.name) %> = <%= csr.name %> *<%= csr.long_name %>* @@ -73,7 +73,7 @@ This CSR format changes dynamically. @ Name @ Location @ Type @ Reset Value <%- csr.fields.each do |field| -%> -@ xref:<%=csr.name%>-<%=field.name%>-def[`<%= field.name %>`] +@ <%= link_to_udb_doc_csr_field(csr.name, field.name) %> a@ <%- if field.dynamic_location?(design) -%> @@ -112,7 +112,7 @@ This CSR has no fields. However, it must still exist (not cause an `Illegal Inst <%- else -%> <%- csr.fields.each do |field| -%> -<%= anchor_for_csr_field(csr.name, field.name) %> +<%= anchor_for_udb_doc_csr_field(csr.name, field.name) %> === `<%= field.name %>` Field <%- if !field.defined_in_all_bases? -%> diff --git a/backends/manual/templates/ext.adoc.erb b/backends/manual/templates/ext.adoc.erb index 553d5ef22..e43ee06ea 100644 --- a/backends/manual/templates/ext.adoc.erb +++ b/backends/manual/templates/ext.adoc.erb @@ -1,4 +1,4 @@ -[ext:$<%= ext.name %>-def] +<%= anchor_for_udb_doc_ext(ext.name) %> = <%= ext.name %> Extension <%= ext.long_name %> @@ -53,9 +53,10 @@ The following instructions are defined by this extension: <%- unless ext.params.empty? -%> == Parameters -This extension has the following implementation options: +This extension has the following implementation options (AKA parameters): <%- ext.params.sort_by { |p| p.name }.each do |param| -%> +<%= anchor_for_udb_doc_ext_param(ext.name, param.name) %> <%= param.name %>:: + -- diff --git a/backends/manual/templates/func.adoc.erb b/backends/manual/templates/func.adoc.erb index 45b5abc4c..1fac9a513 100644 --- a/backends/manual/templates/func.adoc.erb +++ b/backends/manual/templates/func.adoc.erb @@ -5,7 +5,7 @@ = Functions <%- design.functions.each do |f| -%> -<%= anchor_for_func(f.name) %> +<%= anchor_for_udb_doc_idl_func(f.name) %> == <%= f.name %><%- if f.builtin? -%> (builtin)<%- end -%> <%= f.description %> diff --git a/backends/manual/templates/instruction.adoc.erb b/backends/manual/templates/instruction.adoc.erb index 05e276026..cb09ada2e 100644 --- a/backends/manual/templates/instruction.adoc.erb +++ b/backends/manual/templates/instruction.adoc.erb @@ -1,6 +1,6 @@ :tabs-sync-option: -<%= anchor_for_inst(inst.name) %> +<%= anchor_for_udb_doc_inst(inst.name) %> = <%= inst.name %> *<%= inst.long_name %>* diff --git a/backends/manual/templates/isa_nav.adoc.erb b/backends/manual/templates/isa_nav.adoc.erb index 93ebd8857..c75400e2c 100644 --- a/backends/manual/templates/isa_nav.adoc.erb +++ b/backends/manual/templates/isa_nav.adoc.erb @@ -8,17 +8,17 @@ * Alphabetical list of instructions <%- manual_version.instructions.sort { |a, b| a.name <=> b.name }.each do |inst| -%> -** xref:insts:<%= inst.name %>.adoc[<%= inst.name %>] +** <%= link_to_udb_doc_inst(inst.name) %> <%- end -%> * Alphabetical list of CSRs <%- manual_version.csrs.sort { |a, b| a.name <=> b.name }.each do |csr| -%> -** xref:csrs:<%= csr.name %>.adoc[<%= csr.name %>] +** <%= link_to_udb_doc_csr(csr.name) %> <%- end -%> * Alphabetical list of extensions -<%- manual_version.extensions.sort { |a, b| a.name <=> b.name }.each do |ext| -%> -** xref:exts:<%= ext.name %>.adoc[<%= ext.name %>] +<%- manual_version.ext_vers.sort { |a, b| a.name <=> b.name }.each do |ext_ver| -%> +** <%= link_to_udb_doc_ext(ext_ver.name) %> <%- end -%> * xref:params:param_list.adoc[Alphabetical list of parameters] diff --git a/backends/manual/templates/param_list.adoc.erb b/backends/manual/templates/param_list.adoc.erb index dd273b85b..d443ec46c 100644 --- a/backends/manual/templates/param_list.adoc.erb +++ b/backends/manual/templates/param_list.adoc.erb @@ -1,7 +1,7 @@ = Architectural Parameters <%- - params = manual_version.extensions.map{ |e| e.params }.flatten.uniq(&:name).sort_by!(&:name) + params = manual_version.ext_vers.map{ |e| e.params }.flatten.uniq(&:name).sort_by!(&:name) -%> The following <%= params.size %> parameters are defined in this manual: @@ -12,7 +12,7 @@ The following <%= params.size %> parameters are defined in this manual: <%- params.each do |param| -%> | <%= param.name %> | <%= param.schema.to_pretty_s %> -| <%= param.exts.map { |ext| "`#{ext.name}`"}.join(", ") %> +| <%= param.exts.map { |ext| link_to_udb_doc_ext_param(ext.name, param.name) }.join(", ") %> a| <%= param.desc %> <%- end -%> |=== diff --git a/backends/portfolio/tasks.rake b/backends/portfolio/tasks.rake index a1425cffa..5096eb74b 100644 --- a/backends/portfolio/tasks.rake +++ b/backends/portfolio/tasks.rake @@ -45,7 +45,7 @@ def pf_adoc2pdf(adoc_file, target_pname) FileUtils.mkdir_p File.dirname(target_pname) puts "UPDATE: Generating PDF in #{target_pname}" - sh [ + cmd = [ "asciidoctor-pdf", "-w", "-v", @@ -59,6 +59,10 @@ def pf_adoc2pdf(adoc_file, target_pname) "-o #{target_pname}", adoc_file ].join(" ") + + puts "UPDATE: #{cmd}" + sh cmd + puts "UPDATE: Generated PDF in #{target_pname}" end diff --git a/backends/portfolio/templates/csr_appendix.adoc.erb b/backends/portfolio/templates/csr_appendix.adoc.erb index e950561da..757b3e885 100644 --- a/backends/portfolio/templates/csr_appendix.adoc.erb +++ b/backends/portfolio/templates/csr_appendix.adoc.erb @@ -4,7 +4,7 @@ <% portfolio_design.in_scope_csrs.sort_by(&:name).each do |csr| -%> <<< -<%= anchor_for_csr(csr.name) %> +<%= anchor_for_udb_doc_csr(csr.name) %> === <%= csr.name %> *<%= csr.long_name %>* @@ -71,7 +71,7 @@ This CSR format changes dynamically with XLEN. @ Name @ Location @ Type @ Reset Value <%- csr.implemented_fields(design).each do |field| -%> -@ <%= link_to_csr_field(csr.name, field.name) %> +@ <%= link_to_udb_doc_csr_field(csr.name, field.name) %> a@ <%- if field.dynamic_location?(design) -%> @@ -110,7 +110,7 @@ This CSR has no fields. However, it must still exist (not cause an `Illegal Inst <%- else -%> <%- csr.implemented_fields(design).each do |field| -%> -<%= anchor_for_csr_field(csr.name, field.name) %> +<%= anchor_for_udb_doc_csr_field(csr.name, field.name) %> ===== `<%= field.name %>` Field <%- if !field.defined_in_all_bases? -%> diff --git a/backends/portfolio/templates/ext_appendix.adoc.erb b/backends/portfolio/templates/ext_appendix.adoc.erb index ed122e439..3f21041d1 100644 --- a/backends/portfolio/templates/ext_appendix.adoc.erb +++ b/backends/portfolio/templates/ext_appendix.adoc.erb @@ -4,7 +4,7 @@ <% portfolio_design.in_scope_ext_reqs.each do |ext_req| -%> <% ext = arch.extension(ext_req.name) -%> -<%= anchor_for_ext(ext_req.name) %> +<%= anchor_for_udb_doc_ext(ext_req.name) %> === Extension <%= ext_req.name %> + *Long Name*: <%= ext.long_name %> + @@ -75,7 +75,7 @@ The following instructions are added by this extension: [cols="1,3"] |=== <% insts.sort.each do |inst| -%> -| <%= link_to_inst(inst.name) %> +| <%= link_to_udb_doc_inst(inst.name) %> | *<%= inst.long_name %>* <% end -%> |=== @@ -102,7 +102,7 @@ This extension has the following parameters (AKA implementation options): ==== IN-SCOPE Parameters <% portfolio_design.in_scope_params(ext_req).each do |param| -%> -<%= anchor_for_ext_param(ext_req.name, param.name) %> +<%= anchor_for_udb_doc_ext_param(ext_req.name, param.name) %> <%= param.name %> ⇒ <%= param.param.schema_type %>:: + -- @@ -115,7 +115,7 @@ This extension has the following parameters (AKA implementation options): ==== OUT-OF-SCOPE Parameters <% portfolio_design.out_of_scope_params(ext_req.name).each do |param| -%> -<%= anchor_for_ext_param(ext_req.name, param.name) %> +<%= anchor_for_udb_doc_ext_param(ext_req.name, param.name) %> <%= param.name %> ⇒ <%= param.schema_type %>:: + -- diff --git a/backends/portfolio/templates/idl_func_appendix.adoc.erb b/backends/portfolio/templates/idl_func_appendix.adoc.erb index 7f2f518ca..88b2d45d2 100644 --- a/backends/portfolio/templates/idl_func_appendix.adoc.erb +++ b/backends/portfolio/templates/idl_func_appendix.adoc.erb @@ -3,7 +3,7 @@ == IDL Function Details <% design.functions.each do |f| -%> -<%= anchor_for_func(f.name) %> +<%= anchor_for_udb_doc_idl_func(f.name) %> === <%= f.name %><% if f.builtin? -%> (builtin)<% end -%> <%= f.description %> diff --git a/backends/portfolio/templates/inst_appendix.adoc.erb b/backends/portfolio/templates/inst_appendix.adoc.erb index 9b8187baa..d5d5882fa 100644 --- a/backends/portfolio/templates/inst_appendix.adoc.erb +++ b/backends/portfolio/templates/inst_appendix.adoc.erb @@ -4,7 +4,7 @@ <% portfolio_design.in_scope_instructions.each do |inst| -%> <<< -<%= anchor_for_inst(inst.name) %> +<%= anchor_for_udb_doc_inst(inst.name) %> === <%= inst.name %> *<%= inst.long_name %>* diff --git a/backends/proc_cert/README.adoc b/backends/proc_cert/README.adoc new file mode 100644 index 000000000..6bd20e832 --- /dev/null +++ b/backends/proc_cert/README.adoc @@ -0,0 +1,2 @@ +This certification backend isn't a real backend. +Instead, it contains common Rake code and ERB templates shared by multiple processor certification backends. diff --git a/backends/proc_cert/tasks.rake b/backends/proc_cert/tasks.rake new file mode 100644 index 000000000..f20fbc145 --- /dev/null +++ b/backends/proc_cert/tasks.rake @@ -0,0 +1,37 @@ +# frozen_string_literal: true +# +# Contains common methods called from certification backend tasks.rake files. + +require "pathname" +require "asciidoctor-pdf" +require "asciidoctor-diagram" + +# @param erb_template_pname [String] Path to ERB template file +# @param target_pname [String] Full name of adoc file being generated +# @param model_name [String] Name of the processor certificate model +def proc_cert_create_adoc(erb_template_pname, target_pname, model_name) + arch = pf_create_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 object for #{model_name}" + proc_cert_model = arch.proc_cert_model(model_name) + proc_cert_class = proc_cert_model.proc_cert_class + + # Create the one PortfolioDesign object required for the ERB evaluation. + puts "UPDATE: Creating PortfolioDesign object using processor certificate model #{model_name}" + portfolio_design = PortfolioDesign.new(model_name, arch, PortfolioDesign.proc_ctp_type, [proc_cert_model], proc_cert_class) + + # 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). + def evaluate_erb + binding + end + erb_binding = evaluate_erb + portfolio_design.init_erb_binding(erb_binding) + erb_binding.local_variable_set(:proc_cert_model, proc_cert_model) + erb_binding.local_variable_set(:proc_cert_class, proc_cert_class) + + pf_create_adoc(erb_template_pname, erb_binding, target_pname, portfolio_design) +end diff --git a/backends/proc_cert/templates/README.adoc b/backends/proc_cert/templates/README.adoc new file mode 100644 index 000000000..f577b4556 --- /dev/null +++ b/backends/proc_cert/templates/README.adoc @@ -0,0 +1 @@ +This directory contains partial ERB templates shared by multiple processor certification-based backends. diff --git a/backends/proc_crd/tasks.rake b/backends/proc_crd/tasks.rake index 3fc51039b..9975147bf 100644 --- a/backends/proc_crd/tasks.rake +++ b/backends/proc_crd/tasks.rake @@ -26,30 +26,7 @@ Dir.glob("#{$root}/arch/proc_cert_model/*.yaml") do |f| "#{$root}/backends/portfolio/templates/beginning.adoc.erb", "#{PROC_CRD_DOC_DIR}/templates/proc_crd.adoc.erb" ] do |t| - arch = pf_create_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 object for #{model_name}" - proc_cert_model = arch.proc_cert_model(model_name) - proc_cert_class = proc_cert_model.proc_cert_class - - # Create the one PortfolioDesign object required for the ERB evaluation. - puts "UPDATE: Creating PortfolioDesign object using processor certificate model #{model_name}" - portfolio_design = PortfolioDesign.new(model_name, arch, PortfolioDesign.proc_crd_type, [proc_cert_model], proc_cert_class) - - # 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). - def evaluate_erb - binding - end - erb_binding = evaluate_erb - portfolio_design.init_erb_binding(erb_binding) - erb_binding.local_variable_set(:proc_cert_model, proc_cert_model) - erb_binding.local_variable_set(:proc_cert_class, proc_cert_class) - - pf_create_adoc("#{PROC_CRD_DOC_DIR}/templates/proc_crd.adoc.erb", erb_binding, t.name, portfolio_design) + proc_cert_create_adoc("#{PROC_CRD_DOC_DIR}/templates/proc_crd.adoc.erb", t.name, model_name) end file "#{$root}/gen/proc_crd/pdf/#{model_name}-CRD.pdf" => [ diff --git a/backends/proc_crd/templates/proc_crd.adoc.erb b/backends/proc_crd/templates/proc_crd.adoc.erb index 462b3f5a3..46dbe0e8b 100644 --- a/backends/proc_crd/templates/proc_crd.adoc.erb +++ b/backends/proc_crd/templates/proc_crd.adoc.erb @@ -266,7 +266,7 @@ None <% ext_reqs.each do |ext_req| -%> <% ext = arch.extension(ext_req.name) -%> | <%= ext_req.req_id %> -| <%= link_to_ext(ext_req.name) %> +| <%= link_to_udb_doc_ext(ext_req.name) %> | <%= ext_req.requirement_specs.map(&:to_s).join(", ") %> | <%= ext.nil? ? "" : ext.long_name %> | <%= ext_req.note.nil? ? "" : ext_req.note %> @@ -382,7 +382,7 @@ See https://github.com/riscv-software-src/riscv-unified-db/issues/291 and https: | Name | Long Name <% design.in_scope_instructions.each do |inst| -%> -| <%= link_to_inst(inst.name) %> +| <%= link_to_udb_doc_inst(inst.name) %> | <%= inst.long_name %> <% end # do -%> |=== @@ -396,7 +396,7 @@ See https://github.com/riscv-software-src/riscv-unified-db/issues/291 and https: | Name | Long Name | Address | Mode | Primary Extension <% design.in_scope_csrs.sort_by!(&:name).each do |csr| -%> -| <%= link_to_csr(csr.name) %> +| <%= link_to_udb_doc_csr(csr.name) %> | <%= csr.long_name %> | <%= "0x#{csr.address.to_s(16)}" %> | <%= csr.priv_mode %> @@ -413,7 +413,7 @@ See https://github.com/riscv-software-src/riscv-unified-db/issues/291 and https: <% design.in_scope_csrs.sort_by!(&:address).each do |csr| -%> | <%= "0x#{csr.address.to_s(16)}" %> | <%= csr.priv_mode %> -| <%= link_to_csr(csr.name) %> +| <%= link_to_udb_doc_csr(csr.name) %> | <%= csr.long_name %> | <%= csr.primary_defined_by %> <% end # do -%> diff --git a/backends/proc_ctp/tasks.rake b/backends/proc_ctp/tasks.rake index d04e0c45e..c9313908f 100644 --- a/backends/proc_ctp/tasks.rake +++ b/backends/proc_ctp/tasks.rake @@ -5,6 +5,7 @@ require "pathname" PROC_CTP_DOC_DIR = Pathname.new "#{$root}/backends/proc_ctp" +PROC_CTP_GEN_DIR = $root / "gen" / "proc_ctp" Dir.glob("#{$root}/arch/proc_cert_model/*.yaml") do |f| model_name = File.basename(f, ".yaml") @@ -24,32 +25,11 @@ Dir.glob("#{$root}/arch/proc_cert_model/*.yaml") do |f| "#{$root}/backends/portfolio/templates/inst_appendix.adoc.erb", "#{$root}/backends/portfolio/templates/csr_appendix.adoc.erb", "#{$root}/backends/portfolio/templates/beginning.adoc.erb", - "#{PROC_CTP_DOC_DIR}/templates/proc_ctp.adoc.erb" + "#{PROC_CTP_DOC_DIR}/templates/proc_ctp.adoc.erb", + "#{PROC_CTP_GEN_DIR}/adoc/ext/riscv-isa-manual/README.md", + "#{PROC_CTP_GEN_DIR}/adoc/ext/riscv-isa-manual/docs-resources/README.md" ] do |t| - arch = pf_create_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 object for #{model_name}" - proc_cert_model = arch.proc_cert_model(model_name) - proc_cert_class = proc_cert_model.proc_cert_class - - # Create the one PortfolioDesign object required for the ERB evaluation. - puts "UPDATE: Creating PortfolioDesign object using processor certificate model #{model_name}" - portfolio_design = PortfolioDesign.new(model_name, arch, PortfolioDesign.proc_ctp_type, [proc_cert_model], proc_cert_class) - - # 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). - def evaluate_erb - binding - end - erb_binding = evaluate_erb - portfolio_design.init_erb_binding(erb_binding) - erb_binding.local_variable_set(:proc_cert_model, proc_cert_model) - erb_binding.local_variable_set(:proc_cert_class, proc_cert_class) - - pf_create_adoc("#{PROC_CTP_DOC_DIR}/templates/proc_ctp.adoc.erb", erb_binding, t.name, portfolio_design) + proc_cert_create_adoc("#{PROC_CTP_DOC_DIR}/templates/proc_ctp.adoc.erb", t.name, model_name) end file "#{$root}/gen/proc_ctp/pdf/#{model_name}-CTP.pdf" => [ @@ -67,6 +47,39 @@ Dir.glob("#{$root}/arch/proc_cert_model/*.yaml") do |f| end end +# Ensure that the riscv-isa-manual submodule repository is up-to-date. +file $root / "ext" / "csc-riscv-isa-manual" / "README.md" do + sh "git submodule update --init ext/csc-riscv-isa-manual 2>&1" +end + +# Ensure that the docs-resources submodule repository is up-to-date. +file $root / "ext" / "docs-resources" / "README.md" do + sh "git submodule update --init ext/docs-resources 2>&1" +end + +# Rule to copy the riscv-isa-manual submodule repository to the gen directory. +rule %r{#{PROC_CTP_GEN_DIR}/adoc/ext/riscv-isa-manual/README.md} => [ + "#{$root}/ext/csc-riscv-isa-manual/README.md" +] do |t| + FileUtils.mkdir_p File.dirname(t.name) + Dir.chdir($root / "ext" / "csc-riscv-isa-manual") do + sh "git archive --format=tar main | tar xvf - -C #{File.dirname(t.name)}" + end +end + +# Rule to copy the docs-resources submodule repository to the gen directory. +# Make the rule dependent on the riscv-isa-manual to ensure that gets copied +# to the gen directory first (because it has an empty docs-resources directory). +rule %r{#{PROC_CTP_GEN_DIR}/adoc/ext/riscv-isa-manual/docs-resources/README.md} => [ + "#{$root}/ext/docs-resources/README.md", + "#{$root}/ext/csc-riscv-isa-manual/README.md", +] do |t| + FileUtils.mkdir_p File.dirname(t.name) + Dir.chdir($root / "ext" / "docs-resources") do + sh "git archive --format=tar main | tar xvf - -C #{File.dirname(t.name)}" + end +end + namespace :gen do desc <<~DESC Generate Processor CTP (Certification Test Plan) as a PDF. diff --git a/backends/proc_ctp/templates/proc_ctp.adoc.erb b/backends/proc_ctp/templates/proc_ctp.adoc.erb index a03bc245d..8a8f742cc 100644 --- a/backends/proc_ctp/templates/proc_ctp.adoc.erb +++ b/backends/proc_ctp/templates/proc_ctp.adoc.erb @@ -2,6 +2,14 @@ = <%= proc_cert_model.name %> Processor Certification Requirements Document +Links to ISA manual anchors: + +* <> +* <> +* <> +* <> +* <> + [Preface] == Revision History @@ -266,7 +274,7 @@ None <% ext_reqs.each do |ext_req| -%> <% ext = arch.extension(ext_req.name) -%> | <%= ext_req.req_id %> -| <%= link_to_ext(ext_req.name) %> +| <%= link_to_udb_doc_ext(ext_req.name) %> | <%= ext_req.requirement_specs.map(&:to_s).join(", ") %> | <%= ext.nil? ? "" : ext.long_name %> | <%= ext_req.note.nil? ? "" : ext_req.note %> @@ -382,7 +390,7 @@ See https://github.com/riscv-software-src/riscv-unified-db/issues/291 and https: | Name | Long Name <% design.in_scope_instructions.each do |inst| -%> -| <%= link_to_inst(inst.name) %> +| <%= link_to_udb_doc_inst(inst.name) %> | <%= inst.long_name %> <% end # do -%> |=== @@ -396,7 +404,7 @@ See https://github.com/riscv-software-src/riscv-unified-db/issues/291 and https: | Name | Long Name | Address | Mode | Primary Extension <% design.in_scope_csrs.sort_by!(&:name).each do |csr| -%> -| <%= link_to_csr(csr.name) %> +| <%= link_to_udb_doc_csr(csr.name) %> | <%= csr.long_name %> | <%= "0x#{csr.address.to_s(16)}" %> | <%= csr.priv_mode %> @@ -413,7 +421,7 @@ See https://github.com/riscv-software-src/riscv-unified-db/issues/291 and https: <% design.in_scope_csrs.sort_by!(&:address).each do |csr| -%> | <%= "0x#{csr.address.to_s(16)}" %> | <%= csr.priv_mode %> -| <%= link_to_csr(csr.name) %> +| <%= link_to_udb_doc_csr(csr.name) %> | <%= csr.long_name %> | <%= csr.primary_defined_by %> <% end # do -%> @@ -457,6 +465,14 @@ Requirement <%= req.name %> only apply when <%= req.when_pretty %>. <%= portfolio_design.include_erb("csr_appendix.adoc.erb") %> <%= portfolio_design.include_erb("idl_func_appendix.adoc.erb") %> +<<< +<% puts "UPDATE: Including riscv-unprivileged.adoc" -%> +// Reset chapter numbering +:!chapter-number: include::ext/riscv-isa-manual/src/riscv-unprivileged.adoc[] +<<< +<% puts "UPDATE: Including riscv-privileged.adoc" -%> +// Reset chapter numbering +:!chapter-number: include::ext/riscv-isa-manual/src/riscv-privileged.adoc[] diff --git a/backends/profile/tasks.rake b/backends/profile/tasks.rake index 7322eae5b..7cd49fc63 100644 --- a/backends/profile/tasks.rake +++ b/backends/profile/tasks.rake @@ -49,7 +49,7 @@ Dir.glob("#{$root}/arch/profile_release/*.yaml") do |f| # Provide it with all the profiles in this ProfileRelease. puts "UPDATE: Creating PortfolioDesign object using profile release #{release_name}" portfolio_design = - PortfolioDesign.new(release_name, arch, PortfolioDesign.profile_release_type, profiles, profile_class) + PortfolioDesign.new(release_name, arch, PortfolioDesign.profile_release_type, profile_release.profiles, profile_class) # 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/templates/csr.adoc.erb b/backends/templates/csr.adoc.erb index c367c0ccc..9394d6b1d 100644 --- a/backends/templates/csr.adoc.erb +++ b/backends/templates/csr.adoc.erb @@ -1,4 +1,4 @@ -<%= anchor_for_csr(csr.name) %> +<%= anchor_for_udb_doc_csr(csr.name) %> = <%= csr.name %> *<%= csr.long_name %>* @@ -50,7 +50,7 @@ This CSR format changes dynamically. @Name @ Location @ Type @ Reset Value <%- csr.fields.each do |field| -%> -@ <%= link_to_csr_field(csr.name, field.name) %> +@ <%= link_to_udb_doc_csr_field(csr.name, field.name) %> @ <%= field.location_pretty(design) %> @ <%= field.type_pretty(design.symtab) %> @ <%= field.reset_value_pretty(design) %> @@ -66,7 +66,7 @@ This CSR has no fields. However, it must still exist (not cause an `Illegal Inst <%- else -%> <%- csr.fields.each do |field| -%> -<%= anchor_for_csr_field(csr.name, field.name) %> +<%= anchor_for_udb_doc_csr_field(csr.name, field.name) %> === `<%= field.name %>` Field [example] diff --git a/backends/templates/inst.adoc.erb b/backends/templates/inst.adoc.erb index 8c4218762..e49f32234 100644 --- a/backends/templates/inst.adoc.erb +++ b/backends/templates/inst.adoc.erb @@ -1,4 +1,4 @@ -<%= anchor_for_inst(inst.name) %> +<%= anchor_for_udb_doc_inst(inst.name) %> = <%= inst.name %> Synopsis:: diff --git a/ext/csc-riscv-isa-manual b/ext/csc-riscv-isa-manual new file mode 160000 index 000000000..f36b12739 --- /dev/null +++ b/ext/csc-riscv-isa-manual @@ -0,0 +1 @@ +Subproject commit f36b12739a1123ebb7e1a326f871850678080d3c diff --git a/lib/arch_obj_models/manual.rb b/lib/arch_obj_models/manual.rb index 473c0024e..29b67ec02 100644 --- a/lib/arch_obj_models/manual.rb +++ b/lib/arch_obj_models/manual.rb @@ -40,6 +40,9 @@ def name def title return @title unless @title.nil? + # See https://www.rubydoc.info/gems/asciidoctor for details on the Ruby API + # and https://www.rubydoc.info/gems/asciidoctor/Asciidoctor/Document for details on + # the Asciidoctor::Document object returned by Asciidoctor.load. @title = (Asciidoctor.load File.read(fullpath).scrub).doctitle.encode("US-ASCII") end @@ -88,11 +91,11 @@ def chapter(name) = chapters.find { |c| c.name == name } def title = @data["title"] # @return [Array] Array of extension versions in this volume - def extensions - return @extensions unless @extensions.nil? + def ext_vers + return @ext_vers unless @ext_vers.nil? - @extensions = [] - return @extensions if @data["extensions"].nil? + @ext_vers = [] + return @ext_vers if @data["extensions"].nil? @data["extensions"].each do |ext| ext_obj = arch.extension(ext[0]) @@ -107,9 +110,9 @@ def extensions next end - @extensions << ext_ver + @ext_vers << ext_ver end - @extensions + @ext_vers end def repo_path=(path) @@ -160,11 +163,11 @@ def volumes def state = @data["state"] - # @return [Array] Array of extension versions in this manual version - def extensions - return @extensions unless @extensions.nil? + # @return [Array] Array of extension versions in this manual version across all volumes + def ext_vers + return @ext_vers unless @ext_vers.nil? - @extensions = volumes.map(&:extensions).flatten.uniq + @ext_vers = volumes.map(&:ext_vers).flatten.uniq end # @return [Array] All instructions defined in this version @@ -172,7 +175,7 @@ def instructions return @instructions unless @instructions.nil? @instructions = [] - extensions.each do |ext| + ext_vers.each do |ext| ext_obj = @arch.extension(ext.name) ext_obj.instructions.each do |inst| @instructions << inst @@ -186,7 +189,7 @@ def csrs return @csrs unless @csrs.nil? @csrs = [] - extensions.each do |ext| + ext_vers.each do |ext| ext_obj = @arch.extension(ext.name) ext_obj.csrs.each do |csr| @csrs << csr diff --git a/lib/arch_obj_models/parameter.rb b/lib/arch_obj_models/parameter.rb index f55b4511b..19fef8bb6 100644 --- a/lib/arch_obj_models/parameter.rb +++ b/lib/arch_obj_models/parameter.rb @@ -85,7 +85,7 @@ def defined_in_extension_version?(version) # @return [String] Text that includes the parameter name and a link to the parameter definition. # Should only be called if there is only one in-scope extension that defines the parameter. def name_with_link(ext) - link_to_ext_param(ext.name, name) + link_to_udb_doc_ext_param(ext.name, name) end # @param exts [Array] List of all in-scope extensions that define this parameter. @@ -96,7 +96,7 @@ def name_potentially_with_link(in_scope_exts) raise ArgumentError, "Expecting Array[Extension]" unless in_scope_exts[0].is_a?(Extension) if in_scope_exts.size == 1 - link_to_ext_param(in_scope_exts[0].name, name) + link_to_udb_doc_ext_param(in_scope_exts[0].name, name) else name end diff --git a/lib/backend_helpers.rb b/lib/backend_helpers.rb index 0aa81f90b..c28cf12a7 100644 --- a/lib/backend_helpers.rb +++ b/lib/backend_helpers.rb @@ -11,8 +11,8 @@ class String # Parameters never have periods in their names so they don't need to be sanitized. # # @param name [String] Some RISC-V name which might have periods in it - # @return [String] Periods replaced with underscores - def sanitize = self.gsub(".", "_") + # @return [String] New String with periods replaced with underscores + def sanitize = String.new(self).gsub(".", "_") end # This module is included in the Design class so its methods are available to be called directly @@ -35,7 +35,7 @@ def partial(template_pname, inputs = {}) # Links are created with this proprietary format so that they can be converted # later into either AsciiDoc or Antora links (see the two implementations of "resolve_links"). - # %%LINK%;;%% + # %%UDB_DOC_LINK%;;%% # # Documentation: # - How to make cross-references: https://docs.asciidoctor.org/asciidoc/latest/macros/xref/ @@ -43,83 +43,99 @@ def partial(template_pname, inputs = {}) # - See https://github.com/riscv/riscv-isa-manual/issues/1397#issuecomment-2515109936 for # discussion about using [#anchor] instead of [[anchor]] due to Antora's support. - # @return [String] A hyperlink to an extension + # @return [String] A hyperlink to UDB extension documentation # @param ext_name [String] Name of the extension - def link_to_ext(ext_name) - "%%LINK%ext;#{ext_name.sanitize};#{ext_name.sanitize}%%" + def link_to_udb_doc_ext(ext_name) + "%%UDB_DOC_LINK%ext;#{ext_name.sanitize};#{ext_name}%%" end - # @return [String] A hyperlink to a parameter defined by a particular extension. + # @return [String] A hyperlink to UDB parameter documentation # @param ext_name [String] Name of the extension # @param param_name [String] Name of the parameter - def link_to_ext_param(ext_name, param_name) + def link_to_udb_doc_ext_param(ext_name, param_name) check_no_periods(param_name) - "%%LINK%ext_param;#{ext_name.sanitize}.#{param_name};#{param_name}%%" + "%%UDB_DOC_LINK%ext_param;#{ext_name.sanitize}.#{param_name};#{param_name}%%" end - # @return [String] A hyperlink to an instruction + # @return [String] A hyperlink to UDB instruction documentation # @param inst_name [String] Name of the instruction - def link_to_inst(inst_name) - "%%LINK%inst;#{inst_name.sanitize};#{inst_name.sanitize}%%" + def link_to_udb_doc_inst(inst_name) + "%%UDB_DOC_LINK%inst;#{inst_name.sanitize};#{inst_name}%%" end - # @return [String] A hyperlink to a CSR + # @return [String] A hyperlink to UDB CSR documentation # @param csr_name [String] Name of the CSR - def link_to_csr(csr_name) - "%%LINK%csr;#{csr_name.sanitize};#{csr_name.sanitize}%%" + def link_to_udb_doc_csr(csr_name) + "%%UDB_DOC_LINK%csr;#{csr_name.sanitize};#{csr_name}%%" end - # @return [String] A hyperlink to an IDL function + # @return [String] A hyperlink to UDB CSR field documentation + # @param csr_name [String] Name of the CSR + # @param field_name [String] Name of the CSR field + def link_to_udb_doc_csr_field(csr_name, field_name) + "%%UDB_DOC_LINK%csr_field;#{csr_name.sanitize}.#{field_name.sanitize};#{csr_name}.#{field_name}%%" + end + + # @return [String] A hyperlink to UDB IDL function documentation # @param func_name [String] Name of the IDL function - def link_to_func(func_name) - "%%LINK%func;#{func_name.sanitize};#{func_name.sanitize}%%" + def link_to_udb_doc_idl_func(func_name) + "%%UDB_DOC_LINK%func;#{func_name.sanitize};#{func_name}%%" end - # @return [String] A hyperlink to a CSR field - # @param csr_name [String] Name of the CSR - # @param field_name [String] Name of the CSR field - def link_to_csr_field(csr_name, field_name) - "%%LINK%csr_field;#{csr_name.sanitize}.#{field_name.sanitize};#{csr_name.sanitize}.#{field_name.sanitize}%%" + # @return [String] A hyperlink into IDL instruction code + # @param func_name [String] Name of the instruction + # @param func_name [String] Name of the location within the instruction code + def link_into_idl_inst_code(inst_name, loc_name) + "%%IDL_CODE_LINK%inst;#{inst_name.sanitize}.#{loc_name.sanitize};#{inst_name}.#{loc_name}%%" end + # TODO: Add csr and csr_field support - # @return [String] An anchor for an extension + # @return [String] An anchor for UDB extension documentation # @param ext_name [String] Name of the extension - def anchor_for_ext(ext_name) - "[#ext-#{ext_name.sanitize}-def]" + def anchor_for_udb_doc_ext(ext_name) + "[#udb:doc:ext:#{ext_name.sanitize}]" end - # @return [String] An anchor for a parameter defined by a particular extension. + # @return [String] An anchor for UDB parameter documentation # @param ext_name [String] Name of the extension # @param param_name [String] Name of the parameter - def anchor_for_ext_param(ext_name, param_name) + def anchor_for_udb_doc_ext_param(ext_name, param_name) check_no_periods(param_name) - "[#ext_param-#{ext_name.sanitize}-#{param_name}-def]" + "[#udb:doc:ext_param:#{ext_name.sanitize}:#{param_name}]" end - # Insert anchor to an instruction. + # @return [String] An anchor for UDB instruction documentation # @param name [String] Name of the instruction - def anchor_for_inst(name) - "[#inst-#{name.sanitize}-def]" + def anchor_for_udb_doc_inst(name) + "[#udb:doc:inst:#{name.sanitize}]" end - # Insert anchor to a CSR. + # @return [String] An anchor for UDB CSR documentation # @param name [String] Name of the CSR - def anchor_for_csr(name) - "[#csr-#{name.sanitize}-def]" + def anchor_for_udb_doc_csr(name) + "[#udb:doc:csr:#{name.sanitize}]" end - # Insert anchor to a CSR field. + # @return [String] An anchor for UDB CSR field documentation # @param csr_name [String] Name of the CSR # @param field_name [String] Name of the CSR field - def anchor_for_csr_field(csr_name, field_name) - "[#csr_field-#{csr_name.sanitize}-#{field_name.sanitize}-def]" + def anchor_for_udb_doc_csr_field(csr_name, field_name) + "[#udb:doc:csr_field:#{csr_name.sanitize}:#{field_name.sanitize}]" end - # Insert anchor to an IDL function. + # @return [String] An anchor for an IDL function documentation # @param name [String] Name of the function - def anchor_for_func(name) - "[#func-#{name.sanitize}-def]" + def anchor_for_udb_doc_idl_func(name) + "[#udb:doc:func:#{name.sanitize}]" + end + + # @return [String] A anchor inside IDL instruction code + # @param func_name [String] Name of the instruction + # @param func_name [String] Name of the location within the instruction code + def anchor_inside_idl_inst_code(inst_name, loc_name) + "[#idl:code:inst:#{inst_name.sanitize}:#{loc_name.sanitize}]" end + # TODO: Add csr and csr_field support private #@ param s [String] @@ -135,9 +151,9 @@ class << self # Convert proprietary link format to legal AsciiDoc links. # They are converted to AsciiDoc internal cross references (i.e., <>). # For example, - # %%LINK%inst;add;add instruction%% + # %%UDB_DOC_LINK%inst;add;add instruction%% # is converted to: - # <> + # <> # # @param path_or_str [Pathname or String] # @return [String] @@ -148,26 +164,39 @@ def resolve_links(path_or_str) else path_or_str end - str.gsub(/%%LINK%([^;%]+)\s*;\s*([^;%]+)\s*;\s*([^%]+)%%/) do + str.gsub(/%%UDB_DOC_LINK%([^;%]+)\s*;\s*([^;%]+)\s*;\s*([^%]+)%%/) do type = Regexp.last_match[1] name = Regexp.last_match[2] link_text = Regexp.last_match[3] case type when "ext" - "<>" + "<>" when "ext_param" ext_name, param_name = name.split('.') - "<>" + "<>" when "inst" - "<>" + "<>" when "csr" - "<>" + "<>" when "csr_field" csr_name, field_name = name.split('.') - "<>" + "<>" when "func" - "<>" + "<>" + else + raise "Unhandled link type of '#{type}' for '#{name}' with link_text '#{link_text}'" + end + end.gsub(/%%IDL_CODE_LINK%([^;%]+)\s*;\s*([^;%]+)\s*;\s*([^%]+)%%/) do + type = Regexp.last_match[1] + name = Regexp.last_match[2] + link_text = Regexp.last_match[3] + + case type + when "inst" + inst_name, loc_name = name.split('.') + "<>" + # TODO: Add csr and csr_field support else raise "Unhandled link type of '#{type}' for '#{name}' with link_text '#{link_text}'" end @@ -181,11 +210,15 @@ module AntoraUtils # The syntax "class << self" causes all methods to be treated as class methods. class << self # Convert proprietary link format to legal AsciiDoc links. - # They are converted to AsciiDoc external cross references (i.e., xref:filename:#anchor_name[link_text]). + # + # They are converted to AsciiDoc external cross references in the form: + # xref::.adoc:#[]) + # where <> don't appear in the actual cross reference (just there to indicate variable content). + # # For example, - # %%LINK%inst;add;add instruction%% + # %%UDB_DOC_LINK%inst;add;add instruction%% # is converted to: - # xref:insts:add.adoc#add-def[add instruction] + # xref:insts:add.adoc#udb:doc:add[add instruction] # # Antora supports the module name after the "xref:". In the example above, it the module name is "insts" # and corresponds to the directory name the add.adoc file is located in. For more details, see: @@ -202,24 +235,40 @@ def resolve_links(path_or_str) else path_or_str end - str.gsub(/%%LINK%([^;%]+)\s*;\s*([^;%]+)\s*;\s*([^%]+)%%/) do + str.gsub(/%%UDB_DOC_LINK%([^;%]+)\s*;\s*([^;%]+)\s*;\s*([^%]+)%%/) do type = Regexp.last_match[1] name = Regexp.last_match[2] link_text = Regexp.last_match[3] case type when "ext" - "xref:exts:#{name}.adoc##{name}-def[#{link_text.gsub(']', '\]')}]" + "xref:exts:#{name}.adoc#udb:doc:ext:#{name}[#{link_text.gsub(']', '\]')}]" + when "ext_param" + ext_name, param_name = name.split('.') + "xref:exts:#{ext_name}.adoc#udb:doc:ext_param:#{ext_name}:#{param_name}[#{link_text.gsub(']', '\]')}]" when "inst" - "xref:insts:#{name}.adoc##{name}-def[#{link_text.gsub(']', '\]')}]" + "xref:insts:#{name}.adoc#udb:doc:inst:#{name}[#{link_text.gsub(']', '\]')}]" when "csr" - "xref:csrs:#{name}.adoc##{name}-def[#{link_text.gsub(']', '\]')}]" + "xref:csrs:#{name}.adoc#udb:doc:csr:#{name}[#{link_text.gsub(']', '\]')}]" when "csr_field" csr_name, field_name = name.split('.') - "xref:csrs:#{csr_name}.adoc##{csr_name}-#{field_name}-def[#{link_text.gsub(']', '\]')}]" + "xref:csrs:#{csr_name}.adoc#udb:doc:csr_field:#{csr_name}:#{field_name}[#{link_text.gsub(']', '\]')}]" when "func" - # All functions are in the same file called "func.adoc". - "xref:func:func.adoc##{name}-def[#{link_text.gsub(']', '\]')}]" + # All functions are in the same file called "funcs.adoc". + "xref:funcs:funcs.adoc#udb:doc:func:#{name}[#{link_text.gsub(']', '\]')}]" + else + raise "Unhandled link type of '#{type}' for '#{name}' with link_text '#{link_text}'" + end + end.gsub(/%%IDL_CODE_LINK%([^;%]+)\s*;\s*([^;%]+)\s*;\s*([^%]+)%%/) do + type = Regexp.last_match[1] + name = Regexp.last_match[2] + link_text = Regexp.last_match[3] + + case type + when "inst" + inst_name, loc_name = name.split('.') + "xref:insts:#{inst_name}.adoc#idl:code:inst:#{inst_name}:#{loc_name}[#{link_text.gsub(']', '\]')}]" + # TODO: Add csr and csr_field support else raise "Unhandled link type of '#{type}' for '#{name}' with link_text '#{link_text}'" end diff --git a/lib/cfg_arch.rb b/lib/cfg_arch.rb index fffc100b8..9fd2e4191 100644 --- a/lib/cfg_arch.rb +++ b/lib/cfg_arch.rb @@ -323,13 +323,13 @@ def convert_monospace_to_links(adoc) csr_name, field_name = name.split(".") csr = arch.csr(csr_name) if !field_name.nil? && !csr.nil? && csr.field?(field_name) - link_to_csr_field(csr_name, field_name) + link_to_udb_doc_csr_field(csr_name, field_name) elsif !csr.nil? - link_to_csr(csr_name) + link_to_udb_doc_csr(csr_name) elsif arch.instruction(name) - link_to_inst(name) + link_to_udb_doc_inst(name) elsif arch.extension(name) - link_to_ext(name) + link_to_udb_doc_ext(name) else match end diff --git a/lib/design.rb b/lib/design.rb index 96630a7d4..afe481743 100644 --- a/lib/design.rb +++ b/lib/design.rb @@ -341,12 +341,10 @@ def possible_xlens # # @param type [Symbol] Type (:section, :csr, :inst, :ext) # @param name [#to_s] Name of the object - def link_to(type, name) - "%%LINK%#{type};#{name}%%" + def link_to_udb(type, name) + "%%UDB_DOC_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 diff --git a/lib/idl/passes/gen_adoc.rb b/lib/idl/passes/gen_adoc.rb index 1c9cb794d..8eedfa4f8 100644 --- a/lib/idl/passes/gen_adoc.rb +++ b/lib/idl/passes/gen_adoc.rb @@ -273,7 +273,7 @@ def gen_adoc(indent = 0, indent_spaces: 2) after_name = [] after_name << "<#{template_arg_nodes.map { |t| t.gen_adoc(0, indent_spaces:)}.join(', ')}>" unless template_arg_nodes.empty? after_name << "pass:[(]#{arg_nodes.map { |a| a.gen_adoc(0, indent_spaces: ) }.join(', ')})" - "#{' '*indent}" + link_to_func(name) + "#{after_name.join ''}" + "#{' '*indent}" + link_to_udb_doc_idl_func(name) + "#{after_name.join ''}" end end @@ -304,7 +304,7 @@ def gen_adoc(indent = 0, indent_spaces: 2) if @design.arch.csr(csr_text).nil? "#{' '*indent}#{csr_text}" else - "#{' '*indent}%%LINK%csr_field;#{idx_text}.#{@field_name};#{csr_text}%%" + "#{' '*indent}%%UDB_DOC_LINK%csr_field;#{idx_text}.#{@field_name};#{csr_text}%%" end end end @@ -327,7 +327,7 @@ def gen_adoc(indent = 0, indent_spaces: 2) if @design.arch.csr(csr_text).nil? "#{' '*indent}#{csr_text}" else - "#{' '*indent}%%LINK%csr;#{idx_text};#{csr_text}%%" + "#{' '*indent}%%UDB_DOC_LINK%csr;#{idx_text};#{csr_text}%%" end end end diff --git a/lib/portfolio_design.rb b/lib/portfolio_design.rb index b8b4c5305..73b3eb0a9 100644 --- a/lib/portfolio_design.rb +++ b/lib/portfolio_design.rb @@ -174,13 +174,13 @@ def convert_monospace_to_links(adoc) csr_name, field_name = name.split(".") csr = in_scope_csrs.find { |c| c.name == csr_name } if !field_name.nil? && !csr.nil? && csr.field?(field_name) - link_to_csr_field(csr_name, field_name) + link_to_udb_doc_csr_field(csr_name, field_name) elsif !csr.nil? - link_to_csr(csr_name) + link_to_udb_doc_csr(csr_name) elsif in_scope_instructions.any? { |inst| inst.name == name } - link_to_inst(name) + link_to_udb_doc_inst(name) elsif in_scope_extensions.any? { |ext| ext.name == name } - link_to_ext(name) + link_to_udb_doc_ext(name) else match end diff --git a/lib/test/test_backend_helpers.rb b/lib/test/test_backend_helpers.rb index d79e016f1..68e31c92f 100644 --- a/lib/test/test_backend_helpers.rb +++ b/lib/test/test_backend_helpers.rb @@ -10,67 +10,127 @@ class TestBackendHelpers < Minitest::Test def test_ext - assert_equal("%%LINK%ext;foo;foo%%", link_to_ext("foo")) - assert_equal("[#ext-foo-def]", anchor_for_ext("foo")) - assert_equal("%%LINK%ext;fo_o;fo_o%%", link_to_ext("fo.o")) - assert_equal("[#ext-fo_o-def]", anchor_for_ext("fo.o")) + assert_equal("%%UDB_DOC_LINK%ext;foo;foo%%", link_to_udb_doc_ext("foo")) + assert_equal("[#udb:doc:ext:foo]", anchor_for_udb_doc_ext("foo")) + assert_equal("%%UDB_DOC_LINK%ext;fo_o;fo.o%%", link_to_udb_doc_ext("fo.o")) + assert_equal("[#udb:doc:ext:fo_o]", anchor_for_udb_doc_ext("fo.o")) end def test_ext_param - assert_equal("%%LINK%ext_param;foo.bar;bar%%", link_to_ext_param("foo","bar")) - assert_equal("[#ext_param-foo-bar-def]", anchor_for_ext_param("foo","bar")) - assert_equal("%%LINK%ext_param;fo_o.bar;bar%%", link_to_ext_param("fo.o","bar")) - assert_equal("[#ext_param-fo_o-bar-def]", anchor_for_ext_param("fo.o","bar")) - assert_raises(ArgumentError) { link_to_ext_param("foo","ba.r") } - assert_raises(ArgumentError) { anchor_for_ext_param("foo","ba.r") } + assert_equal("%%UDB_DOC_LINK%ext_param;foo.bar;bar%%", link_to_udb_doc_ext_param("foo","bar")) + assert_equal("[#udb:doc:ext_param:foo:bar]", anchor_for_udb_doc_ext_param("foo","bar")) + assert_equal("%%UDB_DOC_LINK%ext_param;fo_o.bar;bar%%", link_to_udb_doc_ext_param("fo.o","bar")) + assert_equal("[#udb:doc:ext_param:fo_o:bar]", anchor_for_udb_doc_ext_param("fo.o","bar")) + assert_raises(ArgumentError) { link_to_udb_doc_ext_param("foo","ba.r") } + assert_raises(ArgumentError) { anchor_for_udb_doc_ext_param("foo","ba.r") } end def test_inst - assert_equal("%%LINK%inst;foo;foo%%", link_to_inst("foo")) - assert_equal("[#inst-foo-def]", anchor_for_inst("foo")) - assert_equal("%%LINK%inst;fo_o;fo_o%%", link_to_inst("fo.o")) - assert_equal("[#inst-fo_o-def]", anchor_for_inst("fo.o")) + assert_equal("%%UDB_DOC_LINK%inst;foo;foo%%", link_to_udb_doc_inst("foo")) + assert_equal("[#udb:doc:inst:foo]", anchor_for_udb_doc_inst("foo")) + assert_equal("%%UDB_DOC_LINK%inst;fo_o;fo.o%%", link_to_udb_doc_inst("fo.o")) + assert_equal("[#udb:doc:inst:fo_o]", anchor_for_udb_doc_inst("fo.o")) end def test_csr - assert_equal("%%LINK%csr;foo;foo%%", link_to_csr("foo")) - assert_equal("[#csr-foo-def]", anchor_for_csr("foo")) - assert_equal("%%LINK%csr;fo_o;fo_o%%", link_to_csr("fo.o")) - assert_equal("[#csr-fo_o-def]", anchor_for_csr("fo.o")) + assert_equal("%%UDB_DOC_LINK%csr;foo;foo%%", link_to_udb_doc_csr("foo")) + assert_equal("[#udb:doc:csr:foo]", anchor_for_udb_doc_csr("foo")) + assert_equal("%%UDB_DOC_LINK%csr;fo_o;fo.o%%", link_to_udb_doc_csr("fo.o")) + assert_equal("[#udb:doc:csr:fo_o]", anchor_for_udb_doc_csr("fo.o")) end def test_csr_field - assert_equal("%%LINK%csr_field;foo.bar;foo.bar%%", link_to_csr_field("foo","bar")) - assert_equal("[#csr_field-foo-bar-def]", anchor_for_csr_field("foo","bar")) - assert_equal("%%LINK%csr_field;fo_o.ba_r;fo_o.ba_r%%", link_to_csr_field("fo.o","ba.r")) - assert_equal("[#csr_field-fo_o-ba_r-def]", anchor_for_csr_field("fo.o","ba.r")) + assert_equal("%%UDB_DOC_LINK%csr_field;foo.bar;foo.bar%%", link_to_udb_doc_csr_field("foo","bar")) + assert_equal("[#udb:doc:csr_field:foo:bar]", anchor_for_udb_doc_csr_field("foo","bar")) + assert_equal("%%UDB_DOC_LINK%csr_field;fo_o.ba_r;fo.o.ba.r%%", link_to_udb_doc_csr_field("fo.o","ba.r")) + assert_equal("[#udb:doc:csr_field:fo_o:ba_r]", anchor_for_udb_doc_csr_field("fo.o","ba.r")) + end + + def test_idl_func + assert_equal("%%UDB_DOC_LINK%func;foo;foo%%", link_to_udb_doc_idl_func("foo")) + assert_equal("[#udb:doc:func:foo]", anchor_for_udb_doc_idl_func("foo")) + assert_equal("%%UDB_DOC_LINK%func;fo_o;fo.o%%", link_to_udb_doc_idl_func("fo.o")) + assert_equal("[#udb:doc:func:fo_o]", anchor_for_udb_doc_idl_func("fo.o")) + end + + def test_idl_code + assert_equal("%%IDL_CODE_LINK%inst;foo.bar;foo.bar%%", link_into_idl_inst_code("foo","bar")) + assert_equal("[#idl:code:inst:foo:bar]", anchor_inside_idl_inst_code("foo","bar")) + assert_equal("%%IDL_CODE_LINK%inst;fo_o.ba_r;fo.o.ba.r%%", link_into_idl_inst_code("fo.o","ba.r")) + assert_equal("[#idl:code:inst:fo_o:ba_r]", anchor_inside_idl_inst_code("fo.o","ba.r")) end end class TestAsciidocUtils < Minitest::Test def test_resolve_links_ext - assert_equal("<>", AsciidocUtils.resolve_links("%%LINK%ext;foo;bar%%")) - assert_equal("<>", AsciidocUtils.resolve_links(link_to_ext("foo"))) + assert_equal("<>", AsciidocUtils.resolve_links("%%UDB_DOC_LINK%ext;foo;bar%%")) + assert_equal("<>", AsciidocUtils.resolve_links(link_to_udb_doc_ext("foo"))) end def test_resolve_links_ext_param - assert_equal("<>", AsciidocUtils.resolve_links("%%LINK%ext_param;foo.bar;zort%%")) - assert_equal("<>", AsciidocUtils.resolve_links(link_to_ext_param("foo","bar"))) + assert_equal("<>", AsciidocUtils.resolve_links("%%UDB_DOC_LINK%ext_param;foo.bar;zort%%")) + assert_equal("<>", AsciidocUtils.resolve_links(link_to_udb_doc_ext_param("foo","bar"))) end def test_resolve_links_inst - assert_equal("<>", AsciidocUtils.resolve_links("%%LINK%inst;foo;bar%%")) - assert_equal("<>", AsciidocUtils.resolve_links(link_to_inst("foo"))) + assert_equal("<>", AsciidocUtils.resolve_links("%%UDB_DOC_LINK%inst;foo;bar%%")) + assert_equal("<>", AsciidocUtils.resolve_links(link_to_udb_doc_inst("foo"))) end def test_resolve_links_csr - assert_equal("<>", AsciidocUtils.resolve_links("%%LINK%csr;foo;bar%%")) - assert_equal("<>", AsciidocUtils.resolve_links(link_to_csr("foo"))) + assert_equal("<>", AsciidocUtils.resolve_links("%%UDB_DOC_LINK%csr;foo;bar%%")) + assert_equal("<>", AsciidocUtils.resolve_links(link_to_udb_doc_csr("foo"))) end def test_resolve_links_csr_field - assert_equal("<>", AsciidocUtils.resolve_links("%%LINK%csr_field;foo.bar;zort%%")) - assert_equal("<>", AsciidocUtils.resolve_links(link_to_csr_field("foo","bar"))) + assert_equal("<>", AsciidocUtils.resolve_links("%%UDB_DOC_LINK%csr_field;foo.bar;zort%%")) + assert_equal("<>", AsciidocUtils.resolve_links(link_to_udb_doc_csr_field("foo","bar"))) + end + + def test_resolve_links_func + assert_equal("<>", AsciidocUtils.resolve_links("%%UDB_DOC_LINK%func;foo;bar%%")) + assert_equal("<>", AsciidocUtils.resolve_links(link_to_udb_doc_idl_func("foo"))) + end + + def test_resolve_links_idl_code + assert_equal("<>", AsciidocUtils.resolve_links("%%IDL_CODE_LINK%inst;foo.bar;zort%%")) + assert_equal("<>", AsciidocUtils.resolve_links(link_into_idl_inst_code("foo","bar"))) end +end +class TestAntoraUtils < Minitest::Test + def test_resolve_links_ext + assert_equal("xref:exts:foo.adoc#udb:doc:ext:foo[bar\\]]", AntoraUtils.resolve_links("%%UDB_DOC_LINK%ext;foo;bar]%%")) + assert_equal("xref:exts:foo.adoc#udb:doc:ext:foo[foo]", AntoraUtils.resolve_links(link_to_udb_doc_ext("foo"))) + end + + def test_resolve_links_ext_param + assert_equal("xref:exts:foo.adoc#udb:doc:ext_param:foo:bar[[zort\\]]", AntoraUtils.resolve_links("%%UDB_DOC_LINK%ext_param;foo.bar;[zort]%%")) + assert_equal("xref:exts:foo.adoc#udb:doc:ext_param:foo:bar[bar]", AntoraUtils.resolve_links(link_to_udb_doc_ext_param("foo","bar"))) + end + + def test_resolve_links_inst + assert_equal("xref:insts:foo.adoc#udb:doc:inst:foo[[[bar\\]\\]]", AntoraUtils.resolve_links("%%UDB_DOC_LINK%inst;foo;[[bar]]%%")) + assert_equal("xref:insts:foo.adoc#udb:doc:inst:foo[foo]", AntoraUtils.resolve_links(link_to_udb_doc_inst("foo"))) + end + + def test_resolve_links_csr + assert_equal("xref:csrs:foo.adoc#udb:doc:csr:foo[\\]bar]", AntoraUtils.resolve_links("%%UDB_DOC_LINK%csr;foo;]bar%%")) + assert_equal("xref:csrs:foo.adoc#udb:doc:csr:foo[foo]", AntoraUtils.resolve_links(link_to_udb_doc_csr("foo"))) + end + + def test_resolve_links_csr_field + assert_equal("xref:csrs:foo.adoc#udb:doc:csr_field:foo:bar[zort\\]]", AntoraUtils.resolve_links("%%UDB_DOC_LINK%csr_field;foo.bar;zort]%%")) + assert_equal("xref:csrs:foo.adoc#udb:doc:csr_field:foo:bar[foo.bar]", AntoraUtils.resolve_links(link_to_udb_doc_csr_field("foo","bar"))) + end + + def test_resolve_links_func + assert_equal("xref:funcs:funcs.adoc#udb:doc:func:foo[\\]bar]", AntoraUtils.resolve_links("%%UDB_DOC_LINK%func;foo;]bar%%")) + assert_equal("xref:funcs:funcs.adoc#udb:doc:func:foo[foo]", AntoraUtils.resolve_links(link_to_udb_doc_idl_func("foo"))) + end + + def test_resolve_links_idl_code + assert_equal("xref:insts:foo.adoc#idl:code:inst:foo:bar[zort\\]]", AntoraUtils.resolve_links("%%IDL_CODE_LINK%inst;foo.bar;zort]%%")) + assert_equal("xref:insts:foo.adoc#idl:code:inst:foo:bar[foo.bar]", AntoraUtils.resolve_links(link_into_idl_inst_code("foo","bar"))) + end end From cd38e01c313402a6efbfb7da7e2ede5b25d99272 Mon Sep 17 00:00:00 2001 From: James Ball Date: Sun, 19 Jan 2025 15:18:59 +0000 Subject: [PATCH 39/61] Improve auto update of changes from submodules. --- backends/proc_ctp/tasks.rake | 12 ++++++++++-- ext/csc-riscv-isa-manual | 2 +- ext/docs-resources | 2 +- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/backends/proc_ctp/tasks.rake b/backends/proc_ctp/tasks.rake index c9313908f..419149265 100644 --- a/backends/proc_ctp/tasks.rake +++ b/backends/proc_ctp/tasks.rake @@ -61,9 +61,13 @@ end rule %r{#{PROC_CTP_GEN_DIR}/adoc/ext/riscv-isa-manual/README.md} => [ "#{$root}/ext/csc-riscv-isa-manual/README.md" ] do |t| + # Pull in the latest version of the csc-riscv-isa-manual. + sh "cd ext/csc-riscv-isa-manual; git fetch; git merge origin/main 2>&1" + + # Use git archive to extract the latest version of the csc-riscv-isa-manual. FileUtils.mkdir_p File.dirname(t.name) Dir.chdir($root / "ext" / "csc-riscv-isa-manual") do - sh "git archive --format=tar main | tar xvf - -C #{File.dirname(t.name)}" + sh "git archive --format=tar HEAD | tar xvf - -C #{File.dirname(t.name)}" end end @@ -74,9 +78,13 @@ rule %r{#{PROC_CTP_GEN_DIR}/adoc/ext/riscv-isa-manual/docs-resources/README.md} "#{$root}/ext/docs-resources/README.md", "#{$root}/ext/csc-riscv-isa-manual/README.md", ] do |t| + # Pull in the latest version of the docs-resources. + sh "cd ext/docs-resources; git fetch; git merge origin/main 2>&1" + + # Use git archive to extract the latest version of the docs-resources. FileUtils.mkdir_p File.dirname(t.name) Dir.chdir($root / "ext" / "docs-resources") do - sh "git archive --format=tar main | tar xvf - -C #{File.dirname(t.name)}" + sh "git archive --format=tar HEAD | tar xvf - -C #{File.dirname(t.name)}" end end diff --git a/ext/csc-riscv-isa-manual b/ext/csc-riscv-isa-manual index f36b12739..8d2e148a7 160000 --- a/ext/csc-riscv-isa-manual +++ b/ext/csc-riscv-isa-manual @@ -1 +1 @@ -Subproject commit f36b12739a1123ebb7e1a326f871850678080d3c +Subproject commit 8d2e148a77abb2d6f186bdd460e787ce28b7baaf diff --git a/ext/docs-resources b/ext/docs-resources index a76dd1d39..62576cdd2 160000 --- a/ext/docs-resources +++ b/ext/docs-resources @@ -1 +1 @@ -Subproject commit a76dd1d390cba28e3b1ce86313af03ce9b69399d +Subproject commit 62576cdd285615473c279eb9acecade9e0fe0e1f From b69a49a864a9e665cef3eefb71580fc8dee59e2f Mon Sep 17 00:00:00 2001 From: James Ball Date: Mon, 20 Jan 2025 18:53:57 +0000 Subject: [PATCH 40/61] Started to finally make progress on actual CTP generated doc. --- Rakefile | 4 + arch/inst/M/mul.yaml | 4 +- arch/proc_cert_model/MC100-32.yaml | 6 ++ backends/portfolio/tasks.rake | 7 +- backends/proc_cert/tasks.rake | 12 +-- .../proc_cert/templates/rev_history.adoc.erb | 16 +++ .../proc_cert/templates/typographic.adoc.erb | 19 ++++ backends/proc_crd/tasks.rake | 2 + backends/proc_crd/templates/proc_crd.adoc.erb | 37 +------ backends/proc_ctp/tasks.rake | 2 + backends/proc_ctp/templates/proc_ctp.adoc.erb | 98 ++++++------------- ext/csc-riscv-isa-manual | 2 +- lib/portfolio_design.rb | 44 ++++----- lib/proc_cert_design.rb | 70 +++++++++++++ schemas/inst_schema.json | 4 +- 15 files changed, 190 insertions(+), 137 deletions(-) create mode 100644 backends/proc_cert/templates/rev_history.adoc.erb create mode 100644 backends/proc_cert/templates/typographic.adoc.erb create mode 100644 lib/proc_cert_design.rb diff --git a/Rakefile b/Rakefile index 6679c4cfa..99073e29a 100644 --- a/Rakefile +++ b/Rakefile @@ -12,6 +12,7 @@ require "minitest/test_task" require_relative $root / "lib" / "architecture" require_relative $root / "lib" / "design" require_relative $root / "lib" / "portfolio_design" +require_relative $root / "lib" / "proc_cert_design" directory "#{$root}/.stamps" @@ -403,7 +404,10 @@ task "MockCRD": "#{$root}/gen/proc_crd/pdf/MockProcessor-CRD.pdf" task "MockProcessorCRD": "#{$root}/gen/proc_crd/pdf/MockProcessor-CRD.pdf" task "MockCTP": "#{$root}/gen/proc_ctp/pdf/MockProcessor-CTP.pdf" task "MockProcessorCTP": "#{$root}/gen/proc_ctp/pdf/MockProcessor-CTP.pdf" +task "MockCTP-HTML": "#{$root}/gen/proc_ctp/pdf/MockProcessor-CTP.html" +task "MockProcessorCTP-HTML": "#{$root}/gen/proc_ctp/pdf/MockProcessor-CTP.html" task "MC100-32-CTP": "#{$root}/gen/proc_ctp/pdf/MC100-32-CTP.pdf" +task "MC100-32-CTP-HTML": "#{$root}/gen/proc_ctp/pdf/MC100-32-CTP.html" task "MC100-32-CRD": "#{$root}/gen/proc_crd/pdf/MC100-32-CRD.pdf" task "MC100-64-CRD": "#{$root}/gen/proc_crd/pdf/MC100-64-CRD.pdf" task "MC200-32-CRD": "#{$root}/gen/proc_crd/pdf/MC200-32-CRD.pdf" diff --git a/arch/inst/M/mul.yaml b/arch/inst/M/mul.yaml index 6f55f8c90..04237ac00 100644 --- a/arch/inst/M/mul.yaml +++ b/arch/inst/M/mul.yaml @@ -70,7 +70,7 @@ sail(): | } } -certification-coverage-points: +cert-coverage-points: - name: Encoding and basic operation description: | Encoding and basic operation for `mul` instruction @@ -84,7 +84,7 @@ certification-coverage-points: xrefs: - idl:code:inst:mul:exception-illegal_instruction -certification-test-plan: +cert-test-plan: - name: Encoding description: Verify the encoding of the mul instruction coverage-points: diff --git a/arch/proc_cert_model/MC100-32.yaml b/arch/proc_cert_model/MC100-32.yaml index 4ebff9a41..63b5a7c70 100644 --- a/arch/proc_cert_model/MC100-32.yaml +++ b/arch/proc_cert_model/MC100-32.yaml @@ -14,7 +14,13 @@ versions: # XLEN used by rakefile base: 32 +# History of this certificate. revision_history: + - revision: "0.8.0" + date: "2025-01-19" + changes: + - Updated so that content can apply equally to all certificate-related documents + such as CRDs (Certification Requirement Documents) and CTPs (Certification Test Plans). - revision: "0.7.0" date: "2024-07-29" changes: diff --git a/backends/portfolio/tasks.rake b/backends/portfolio/tasks.rake index 5096eb74b..c152cc73c 100644 --- a/backends/portfolio/tasks.rake +++ b/backends/portfolio/tasks.rake @@ -60,7 +60,7 @@ def pf_adoc2pdf(adoc_file, target_pname) adoc_file ].join(" ") - puts "UPDATE: #{cmd}" + puts "UPDATE: bundle exec #{cmd}" sh cmd puts "UPDATE: Generated PDF in #{target_pname}" @@ -72,7 +72,7 @@ def pf_adoc2html(adoc_file, target_pname) FileUtils.mkdir_p File.dirname(target_pname) puts "UPDATE: Generating HTML in #{target_pname}" - sh [ + cmd = [ "asciidoctor", "-w", "-v", @@ -83,5 +83,8 @@ def pf_adoc2html(adoc_file, target_pname) "-o #{target_pname}", adoc_file ].join(" ") + + puts "UPDATE: bundle exec #{cmd}" + sh cmd puts "UPDATE: Generated HTML in #{target_pname}" end diff --git a/backends/proc_cert/tasks.rake b/backends/proc_cert/tasks.rake index f20fbc145..b2c80736e 100644 --- a/backends/proc_cert/tasks.rake +++ b/backends/proc_cert/tasks.rake @@ -19,9 +19,9 @@ def proc_cert_create_adoc(erb_template_pname, target_pname, model_name) proc_cert_model = arch.proc_cert_model(model_name) proc_cert_class = proc_cert_model.proc_cert_class - # Create the one PortfolioDesign object required for the ERB evaluation. - puts "UPDATE: Creating PortfolioDesign object using processor certificate model #{model_name}" - portfolio_design = PortfolioDesign.new(model_name, arch, PortfolioDesign.proc_ctp_type, [proc_cert_model], proc_cert_class) + # Create the one ProcCertDesign object required for the ERB evaluation. + puts "UPDATE: Creating ProcCertDesign object using processor certificate model #{model_name}" + proc_cert_design = ProcCertDesign.new(model_name, arch, ProcCertDesign.proc_ctp_type, proc_cert_model, proc_cert_class) # 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). @@ -29,9 +29,7 @@ def proc_cert_create_adoc(erb_template_pname, target_pname, model_name) binding end erb_binding = evaluate_erb - portfolio_design.init_erb_binding(erb_binding) - erb_binding.local_variable_set(:proc_cert_model, proc_cert_model) - erb_binding.local_variable_set(:proc_cert_class, proc_cert_class) + proc_cert_design.init_erb_binding(erb_binding) - pf_create_adoc(erb_template_pname, erb_binding, target_pname, portfolio_design) + pf_create_adoc(erb_template_pname, erb_binding, target_pname, proc_cert_design) end diff --git a/backends/proc_cert/templates/rev_history.adoc.erb b/backends/proc_cert/templates/rev_history.adoc.erb new file mode 100644 index 000000000..e42895bc1 --- /dev/null +++ b/backends/proc_cert/templates/rev_history.adoc.erb @@ -0,0 +1,16 @@ +== Certificate History + +History of documentation changes that eventually lead to releases. + +[cols="1,1,5"] +|=== +| Date | Revision | Changes + +<% proc_cert_model.revision_history.each do |rev| -%> +| <%= rev.date %> +| <%= rev.revision %> +a| <% rev.changes.each do |change| %> +* <%= change %> +<% end -%> +<% end -%> +|=== diff --git a/backends/proc_cert/templates/typographic.adoc.erb b/backends/proc_cert/templates/typographic.adoc.erb new file mode 100644 index 000000000..55e8cc24c --- /dev/null +++ b/backends/proc_cert/templates/typographic.adoc.erb @@ -0,0 +1,19 @@ +== Typographic Conventions + +CSR field colors:: + +* Grey fields are reserved (WPRI) +* Green fields are present +* Red fields are defined by the RISC-V ISA but not present + +CSR field types:: + +[%autowidth] +|=== +| Abbreviation | Description + +<% CsrField::TYPE_DESC_MAP.each do |abbreviation, description| -%> +| <%= abbreviation %> +| <%= description %> +<% end -%> +|=== diff --git a/backends/proc_crd/tasks.rake b/backends/proc_crd/tasks.rake index 9975147bf..d102a514a 100644 --- a/backends/proc_crd/tasks.rake +++ b/backends/proc_crd/tasks.rake @@ -24,6 +24,8 @@ Dir.glob("#{$root}/arch/proc_cert_model/*.yaml") do |f| "#{$root}/backends/portfolio/templates/inst_appendix.adoc.erb", "#{$root}/backends/portfolio/templates/csr_appendix.adoc.erb", "#{$root}/backends/portfolio/templates/beginning.adoc.erb", + "#{$root}/backends/proc_cert/templates/typographic.adoc.erb", + "#{$root}/backends/proc_cert/templates/rev_history.adoc.erb", "#{PROC_CRD_DOC_DIR}/templates/proc_crd.adoc.erb" ] do |t| proc_cert_create_adoc("#{PROC_CRD_DOC_DIR}/templates/proc_crd.adoc.erb", t.name, model_name) diff --git a/backends/proc_crd/templates/proc_crd.adoc.erb b/backends/proc_crd/templates/proc_crd.adoc.erb index 46dbe0e8b..22fc31867 100644 --- a/backends/proc_crd/templates/proc_crd.adoc.erb +++ b/backends/proc_crd/templates/proc_crd.adoc.erb @@ -3,43 +3,10 @@ = <%= proc_cert_model.name %> Processor Certification Requirements Document [Preface] -== Revision History - -History of documentation changes that eventually lead to releases. - -[cols="1,1,5"] -|=== -| Date | Revision | Changes - -<% proc_cert_model.revision_history.each do |rev| -%> -| <%= rev.date %> -| <%= rev.revision %> -a| <% rev.changes.each do |change| %> -* <%= change %> -<% end -%> -<% end -%> -|=== +<%= proc_cert_design.include_erb("rev_history.adoc.erb") %> [Preface] -== Typographic Conventions - -CSR field colors:: - -* Grey fields are reserved (WPRI) -* Green fields are present -* Red fields are defined by the RISC-V ISA but not present - -CSR field types:: - -[%autowidth] -|=== -| Abbreviation | Description - -<% CsrField::TYPE_DESC_MAP.each do |abbreviation, description| -%> -| <%= abbreviation %> -| <%= description %> -<% end -%> -|=== +<%= proc_cert_design.include_erb("typographic.adoc.erb") %> == Introduction diff --git a/backends/proc_ctp/tasks.rake b/backends/proc_ctp/tasks.rake index 419149265..06a9013c1 100644 --- a/backends/proc_ctp/tasks.rake +++ b/backends/proc_ctp/tasks.rake @@ -25,6 +25,8 @@ Dir.glob("#{$root}/arch/proc_cert_model/*.yaml") do |f| "#{$root}/backends/portfolio/templates/inst_appendix.adoc.erb", "#{$root}/backends/portfolio/templates/csr_appendix.adoc.erb", "#{$root}/backends/portfolio/templates/beginning.adoc.erb", + "#{$root}/backends/proc_cert/templates/typographic.adoc.erb", + "#{$root}/backends/proc_cert/templates/rev_history.adoc.erb", "#{PROC_CTP_DOC_DIR}/templates/proc_ctp.adoc.erb", "#{PROC_CTP_GEN_DIR}/adoc/ext/riscv-isa-manual/README.md", "#{PROC_CTP_GEN_DIR}/adoc/ext/riscv-isa-manual/docs-resources/README.md" diff --git a/backends/proc_ctp/templates/proc_ctp.adoc.erb b/backends/proc_ctp/templates/proc_ctp.adoc.erb index 8a8f742cc..148f69481 100644 --- a/backends/proc_ctp/templates/proc_ctp.adoc.erb +++ b/backends/proc_ctp/templates/proc_ctp.adoc.erb @@ -1,53 +1,12 @@ <%= portfolio_design.include_erb("beginning.adoc.erb") %> -= <%= proc_cert_model.name %> Processor Certification Requirements Document - -Links to ISA manual anchors: - -* <> -* <> -* <> -* <> -* <> += <%= proc_cert_model.name %> Processor Certification Test Plan [Preface] -== Revision History - -History of documentation changes that eventually lead to releases. - -[cols="1,1,5"] -|=== -| Date | Revision | Changes - -<% proc_cert_model.revision_history.each do |rev| -%> -| <%= rev.date %> -| <%= rev.revision %> -a| <% rev.changes.each do |change| %> -* <%= change %> -<% end -%> -<% end -%> -|=== +<%= proc_cert_design.include_erb("rev_history.adoc.erb") %> [Preface] -== Typographic Conventions - -CSR field colors:: - -* Grey fields are reserved (WPRI) -* Green fields are present -* Red fields are defined by the RISC-V ISA but not present - -CSR field types:: - -[%autowidth] -|=== -| Abbreviation | Description - -<% CsrField::TYPE_DESC_MAP.each do |abbreviation, description| -%> -| <%= abbreviation %> -| <%= description %> -<% end -%> -|=== +<%= proc_cert_design.include_erb("typographic.adoc.erb") %> == Introduction @@ -55,14 +14,21 @@ CSR field types:: <%= proc_cert_class.introduction %> -=== What's a CRD? +Links to ISA manual anchors: + +* <> +* <> +* <> +* <> + +=== What's a CTP? -Certification Requirements Documents (CRDs) list requirements an implementation must meet +Certification Test Plans (CTPs) list requirements an implementation must meet to obtain an associated RVI (RISC-V International) certificate. -CRDs are developed by the RVI CSC (Certification Steering Committee) organization in collaboration +CTPs are developed by the RVI CSC (Certification Steering Committee) organization in collaboration with the RVI TSC (Technical Steering Committee) organization who creates RISC-V standards. -The CRDs refer to and augment information provided in existing ratified RVI standards. +The CTPs refer to and augment information provided in existing ratified RVI standards. There are a variety of certificates offered by RVI to accomodate the various RVI standards. There are certificates for processors, non-processor system IP (e.g., IOMMU), @@ -70,29 +36,29 @@ and system platforms (processor + system IP) hardware standards. There are multiple classes of processor certificates available to accomodate the wide range of RISC-V implementations from basic microcontrollers to advanced Applications-class processors. -Each CRD has a list of mandatory behaviors along with a list of optional behaviors. -Note that not all behaviors allowed in RISC-V standards are supported by a particular CRD. +Each CTP has a list of mandatory behaviors along with a list of optional behaviors. +Note that not all behaviors allowed in RISC-V standards are supported by a particular CTP. -=== CRD Naming Scheme +=== CTP Naming Scheme -CRDs have the following naming scheme: +CTPs have the following naming scheme: Format: [v] Where: * Left & right square braces denote optional. -* Less-than & greater-than signs just separate fields (i.e., they aren't present in the CRD name). +* Less-than & greater-than signs just separate fields (i.e., they aren't present in the CTP name). * identifies the type of RISC-V standard (processor, non-processor system IP, or platform) along with any other information required to identify the variant of that standard. -* identifies a particular CRD release +* identifies a particular CTP release ** Format is [.[.]] ** Inspired by semantic versioning scheme (https://semver.org/) but doesn't follow it exactly ** The release is updated when certification test changes are made that *could* cause a previously certified implementation to now fail. Examples are fixing a test bug, or increasing test coverage, or requiring a new version of a standard - A release of 0 is used for pre-release versions of a CRD and release versions start with 1. -** The release is updated when a CRD increases support for optional behaviors. + A release of 0 is used for pre-release versions of a CTP and release versions start with 1. +** The release is updated when a CTP increases support for optional behaviors. Examples are supporting for new optional standards or 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 @@ -101,7 +67,7 @@ Where: ** If omitted, defaults to v1.0.0 ** Examples: v1, v1.1, v2.3.1, 0.3.4 (pre-release) -=== CRD Terminology +=== CTP Terminology .Requirement Types [%autowidth] @@ -120,14 +86,14 @@ Where: |=== | Term | Meaning -| CRD | Certification Requirements Document +| CTP | Certification Test Plan | N/A | “Not Applicable” | AKA | “Also Known As” |=== -=== Processor CRDs +=== Processor CTPs -There are Processor CRDs for different classes of RISC-V processors. +There are Processor CTPs for different classes of RISC-V processors. These documents augment information in the related TSC Profile when available and/or other RVI standards documents (e.g., Priv and Unpriv ISA manuals). Only ratified extensions are candidates for certification. @@ -147,12 +113,12 @@ Where: ** The ten's digit identifies large differences in mandatory extensions (e.g., V, H) within the series ** The one's digit indentifies small/medium differences in mandatory extensions (e.g., Zicond, PMP) within the series * is optional and is 32 for RV32I, 64 for RV64I, and 32E for RV32E -** If a CRD supports multiple bases and is omitted in a reference, it applies to all supported bases -** If a CRD only supports one base then is generally omitted +** If a CTP supports multiple bases and is omitted in a reference, it applies to all supported bases +** If a CTP only supports one base then is generally omitted [%autowidth] |=== -| CRD | TSC Profile | Description +| CTP | TSC Profile | Description | MC100-series | TBD | 32/64-bit minimal microcontroller that runs low-level software on an RTOS or bare-metal (no virtual memory) | MC200-series | TBD | 32/64-bit intermediate microcontroller @@ -183,12 +149,12 @@ after the field is written with an illegal value. Certifying such behaviors is expensive and provides low value for a certificate since software can't rely on a particular behavior from one implementation to another. -Processor CRDs define writes to WARL fields of illegal values to be OUT-OF-SCOPE unless otherwise stated +Processor CTPs define writes to WARL fields of illegal values to be OUT-OF-SCOPE unless otherwise stated (i.e., certification tests will only ever write legal values to WARL fields except for the special cases listed below). When not OUT-OF-SCOPE, the required behavior is defined as this might be more constrained in implementations than in the standard. -The following special cases for WARL are supported when explicitly listed in the corresponding CRD CSR field requirements: +The following special cases for WARL are supported when explicitly listed in the corresponding CTP CSR field requirements: 1. Probing for Field Width @@ -214,7 +180,7 @@ The Priv ISA requires reads of WLRL fields to return some implementation-depende after the field is written with an illegal value. Certifying such behaviors is expensive and provides low value for a certificate since software can't rely on a particular behavior. -Processor CRDs define writes to WLRL fields of illegal values to be OUT-OF-SCOPE unless otherwise stated +Processor CTPs define writes to WLRL fields of illegal values to be OUT-OF-SCOPE unless otherwise stated (i.e., certification tests will only ever write legal values to WLRL fields). *WPRI (Write Preserve, Read Ignore)*: diff --git a/ext/csc-riscv-isa-manual b/ext/csc-riscv-isa-manual index 8d2e148a7..d706dcedc 160000 --- a/ext/csc-riscv-isa-manual +++ b/ext/csc-riscv-isa-manual @@ -1 +1 @@ -Subproject commit 8d2e148a77abb2d6f186bdd460e787ce28b7baaf +Subproject commit d706dcedc46fb8d074288bdca9c0bc33497ef68f diff --git a/lib/portfolio_design.rb b/lib/portfolio_design.rb index 73b3eb0a9..c21076e06 100644 --- a/lib/portfolio_design.rb +++ b/lib/portfolio_design.rb @@ -21,15 +21,15 @@ class PortfolioDesign < Design # @return [String] Kind of portfolio for all portfolios in this design attr_reader :portfolio_kind + # @return [String] Type of design suitable for human readers. + attr_reader :portfolio_design_type + # Class methods def self.profile_release_type = "Profile Release" def self.proc_crd_type = "Certification Requirements Document" def self.proc_ctp_type = "Certification Test Plan" def self.portfolio_design_types = [profile_release_type, proc_crd_type, proc_ctp_type] - # @return [String] Type of design suitable for human readers. - attr_reader :portfolio_design_type - # @param name [#to_s] The name of the portfolio design (i.e., backend filename without a suffix) # @param arch [Architecture] The database of RISC-V standards # @param portfolio_design_type [String] Type of portfolio design associated with this design @@ -267,21 +267,31 @@ def all_in_scope_exts_without_param(param) = @portfolio_grp.all_in_scope_exts_wi # EXTRA METHODS # ################# + #@ return [Hash] Hash of objects available to ERB templates and + # ERB fragments included in the main ERB template. + # Put this in a method so it can be easily overridden by subclasses. + def erb_env + { + arch: arch, + design: self, + portfolio_design: self, + portfolio_design_type: @portfolio_design_type, + portfolio_class: @portfolio_class, + portfolio_kind: @portfolio_kind, + portfolios: @portfolio_grp.portfolios + } + end + # Called from tasks.rake file to add standard set of objects available to ERB templates. def init_erb_binding(erb_binding) raise ArgumentError, "Expected Binding object but got #{erb_binding.class}" unless erb_binding.is_a?(Binding) - erb_binding.local_variable_set(:arch, arch) - erb_binding.local_variable_set(:design, self) - erb_binding.local_variable_set(:portfolio_design, self) - erb_binding.local_variable_set(:portfolio_design_type, @portfolio_design_type) - erb_binding.local_variable_set(:portfolio_class, @portfolio_class) - erb_binding.local_variable_set(:portfolio_kind, @portfolio_kind) - erb_binding.local_variable_set(:portfolios, @portfolio_grp.portfolios) + erb_env.each do |key, obj| + erb_binding.local_variable_set(key, obj) + end end # Include a partial ERB template into a full ERB template. - # Provide the same objects as in init_erb_binding. # # @param template_path [String] Name of template file located in backends/portfolio/templates # @return [String] Result of ERB evaluation of the template file @@ -289,16 +299,6 @@ def include_erb(template_name) template_pname = "portfolio/templates/#{template_name}" puts "UPDATE: #{portfolio_design_type} processing ERB partial template '#{template_pname}'" - partial("portfolio/templates/#{template_name}", - { - arch: arch, - design: self, - portfolio_design: self, - portfolio_design_type: @portfolio_design_type, - portfolio_class: @portfolio_class, - portfolio_kind: @portfolio_kind, - portfolios: @portfolio_grp.portfolios - } - ) + partial(template_pname, erb_env) end end diff --git a/lib/proc_cert_design.rb b/lib/proc_cert_design.rb new file mode 100644 index 000000000..963560b8f --- /dev/null +++ b/lib/proc_cert_design.rb @@ -0,0 +1,70 @@ +# frozen_string_literal: true +# +# Inherits from PortfolioDesign and contains content shared by +# all processor certificate-based designs. + +require_relative "portfolio_design" + +class ProcCertDesign < PortfolioDesign + # @return [ProcCertModel] The processor certificate model object from the architecture database + attr_reader :proc_cert_model + + # @return [ProcCertClass] The processor certificate class object from the architecture database + attr_reader :proc_cert_class + + # @param name [#to_s] The name of the portfolio design (i.e., backend filename without a suffix) + # @param arch [Architecture] The database of RISC-V standards + # @param portfolio_design_type [String] Type of portfolio design associated with this design + # @param mxlen [Integer] Comes from portfolio YAML "base" (either 32 or 64) + # @param portfolios [Array] Portfolios being converted to adoc + # @param portfolio_class [PortfolioClass] PortfolioClass for all the Portfolios + # @param overlay_path [String] Optional path to a directory that overlays the architecture + def initialize(name, arch, portfolio_design_type, proc_cert_model, proc_cert_class, overlay_path: nil) + raise ArgumentError, "proc_cert_model must be a ProcCertModel" unless proc_cert_model.is_a?(ProcCertModel) + raise ArgumentError, "proc_cert_class must be a ProcCertClass" unless proc_cert_class.is_a?(ProcCertClass) + @proc_cert_model = proc_cert_model + @proc_cert_class = proc_cert_class + super(name, arch, portfolio_design_type, [proc_cert_model], proc_cert_class, 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 = "ProcCertDesign##{name}" + + # @return [Hash] Hash of objects to be used in ERB templates + # Add certificate-specific objects to the parent hash. + def erb_env + parent_hash = super + parent_hash[:proc_cert_design] = self + parent_hash[:proc_cert_model] = proc_cert_model + parent_hash[:proc_cert_class] = proc_cert_class + + parent_hash + end + + # Include a partial ERB template into a full ERB template. Can be either in + # the portfolio or proc_cert backends (but not both). + # + # @param template_path [String] Name of template file located in backends/portfolio/templates + # or in backends/proc_cert/templates + # @return [String] Result of ERB evaluation of the template file + def include_erb(template_name) + proc_cert_template_pname = "proc_cert/templates/#{template_name}" + proc_cert_template_path = Pathname.new($root / "backends" / proc_cert_template_pname) + + portfolio_template_pname = "portfolio/templates/#{template_name}" + portfolio_template_path = Pathname.new($root / "backends" / portfolio_template_pname) + + if proc_cert_template_path.exist? && portfolio_template_path.exist? + raise "Both #{proc_cert_template_pname} and #{portfolio_template_pname} exist. Need unique names." + elsif proc_cert_template_path.exist? + puts "UPDATE: #{portfolio_design_type} processing ERB partial template '#{proc_cert_template_pname}'" + partial(proc_cert_template_pname, erb_env) + elsif portfolio_template_path.exist? + puts "UPDATE: #{portfolio_design_type} processing ERB partial template '#{portfolio_template_pname}'" + partial(portfolio_template_pname, erb_env) + else + raise "Can't find file #{template_name} in either #{proc_cert_template_pname} or #{portfolio_template_pname}." + end + end +end diff --git a/schemas/inst_schema.json b/schemas/inst_schema.json index c51402bfc..ae4a2adc7 100644 --- a/schemas/inst_schema.json +++ b/schemas/inst_schema.json @@ -270,7 +270,7 @@ "type": "string", "description": "Functional description of the instruction using Sail" }, - "certification-coverage-points": { + "cert-coverage-points": { "description": "Architecturally visible behaviors requiring validation by certification tests", "type": "array", "required": ["name", "xref", "description"], @@ -288,7 +288,7 @@ } } }, - "certification-test-plan": { + "cert-test-plan": { "description": "How certification coverage points will be validated", "type": "array", "required": ["name", "description", "coverage-points"], From e090c735003978684fa4e44f66fdeb08bf0f8c67 Mon Sep 17 00:00:00 2001 From: James Ball Date: Tue, 21 Jan 2025 14:59:17 +0000 Subject: [PATCH 41/61] Added mock instruction to Xmock extension. For testing CTP. --- arch/inst/M/mul.yaml | 8 +- arch/inst/mock.yaml | 87 ++++ arch/proc_cert_model/MockProcessor.yaml | 4 +- .../proc_cert/templates/glossary.adoc.erb | 20 + .../templates/proc_naming_scheme.adoc.erb | 14 + .../proc_cert/templates/rev_history.adoc.erb | 2 - .../templates/rvcp_naming_scheme.adoc.erb | 22 + .../proc_cert/templates/typographic.adoc.erb | 2 - backends/proc_crd/templates/proc_crd.adoc.erb | 100 ++--- backends/proc_ctp/templates/proc_ctp.adoc.erb | 394 ++---------------- 10 files changed, 201 insertions(+), 452 deletions(-) create mode 100644 arch/inst/mock.yaml create mode 100644 backends/proc_cert/templates/glossary.adoc.erb create mode 100644 backends/proc_cert/templates/proc_naming_scheme.adoc.erb create mode 100644 backends/proc_cert/templates/rvcp_naming_scheme.adoc.erb diff --git a/arch/inst/M/mul.yaml b/arch/inst/M/mul.yaml index 04237ac00..975e8fd67 100644 --- a/arch/inst/M/mul.yaml +++ b/arch/inst/M/mul.yaml @@ -77,22 +77,22 @@ cert-coverage-points: xrefs: - manual:inst:mul:encoding - manual:inst:mul:operation - - name: Illegal instruction exception + - name: Illegal instruction exception when misa.M is 0 description: | An illegal instruction exception is raised when the instruction is executed and `misa.M` is 0. xrefs: - - idl:code:inst:mul:exception-illegal_instruction + - manual:priv:csr:misa:disabling-extension cert-test-plan: - name: Encoding - description: Verify the encoding of the mul instruction + description: Verify the encoding and basic operation of the `mul` instruction coverage-points: - Encoding and basic operation setup: - description: Load a variety of known values into rs1 & rs2 with a variety of rs1/rs2/rd values. execution: - - description: Execute the mul instruction + - description: Execute the `mul` instruction validation: - description: Check each result in rd teardown: diff --git a/arch/inst/mock.yaml b/arch/inst/mock.yaml new file mode 100644 index 000000000..2cdd0afd1 --- /dev/null +++ b/arch/inst/mock.yaml @@ -0,0 +1,87 @@ +# yaml-language-server: $schema=../../schemas/inst_schema.json + +$schema: "inst_schema.json#" +kind: instruction +name: mock +long_name: Mock Instruction (Just for testing UDB) +description: | + The mock instruction computes the value of PI to an infinite number of decimal places. + Okay, actually it performs the equivalent of the `mul` instruction. + + [NOTE] + Computing PI to an infinite number of decicial places is impossible, but hey, why not? + +definedBy: Xmock +assembly: xd, xs1, xs2 +encoding: + match: 0000001----------000-----0110011 + variables: + - name: rs2 + location: 24-20 + - name: rs1 + location: 19-15 + - name: rd + location: 11-7 +access: + s: always + u: always + vs: always + vu: always +data_independent_timing: + true + # Want to add something like this to IDL code. + # [#idl:code:inst:mul:exception-illegal_instruction]# + # if (implemented?(ExtensionName::M) && (CSR[misa].M == 1'b0)) { + # raise (ExceptionCode::IllegalInstruction, mode(), $encoding); + # }# +operation(): | + if (implemented?(ExtensionName::M) && (CSR[misa].M == 1'b0)) { + raise (ExceptionCode::IllegalInstruction, mode(), $encoding); + } + + XReg src1 = X[rs1]; + XReg src2 = X[rs2]; + + X[rd] = (src1 * src2)[XLEN-1:0]; + +sail(): | + { + if extension("M") | haveZmmul() then { + let rs1_val = X(rs1); + let rs2_val = X(rs2); + let rs1_int : int = if signed1 then signed(rs1_val) else unsigned(rs1_val); + let rs2_int : int = if signed2 then signed(rs2_val) else unsigned(rs2_val); + let result_wide = to_bits(2 * sizeof(xlen), rs1_int * rs2_int); + let result = if high + then result_wide[(2 * sizeof(xlen) - 1) .. sizeof(xlen)] + else result_wide[(sizeof(xlen) - 1) .. 0]; + X(rd) = result; + RETIRE_SUCCESS + } else { + handle_illegal(); + RETIRE_FAIL + } + } + +cert-coverage-points: + - name: Encoding and basic operation + description: | + Encoding and basic operation for `mock` instruction + xrefs: + - udb:doc:inst:mock + +cert-test-plan: + - name: Encoding + description: Verify the encoding and basic operation of the `mock` instruction + coverage-points: + - Encoding and basic operation + setup: + - description: Load a variety of known values into rs1 & rs2 with a variety of rs1/rs2/rd values. + execution: + - description: Execute the `mock` instruction + validation: + - description: Check each result in rd + teardown: + - description: Clear the registers used for rd + - comment: | + Don't really need to clear the registers so this is a contrived example diff --git a/arch/proc_cert_model/MockProcessor.yaml b/arch/proc_cert_model/MockProcessor.yaml index d75fb5903..be396ecf9 100644 --- a/arch/proc_cert_model/MockProcessor.yaml +++ b/arch/proc_cert_model/MockProcessor.yaml @@ -26,7 +26,7 @@ revision_history: - Also created to test CRDs introduction: | - Mock CRD introduction: + Mock Certificate Model introduction: * Hello * Bob! @@ -83,6 +83,8 @@ extensions: note: | Here's a multi-line note + for the C extension. + M: + presence: mandatory Zicsr: version: "~> 2.0" presence: mandatory diff --git a/backends/proc_cert/templates/glossary.adoc.erb b/backends/proc_cert/templates/glossary.adoc.erb new file mode 100644 index 000000000..ec2566df2 --- /dev/null +++ b/backends/proc_cert/templates/glossary.adoc.erb @@ -0,0 +1,20 @@ +.Glossary +[%autowidth] +|=== +| Term | Meaning + +| RISC-V | (Reduced Instruction Set Computer) architecture, version 5 +| RVI | RISC-V International (organization that oversees RISC-V) +| RVCP | RISC-V Certification Program +| TSC | Technical Steering Committee (branch of RVI that creates standards) +| CSC | Certification Steering Committee (branch of RVI that oversees the RVCP) +| CRD | Certification Requirements Document +| CTP | Certification Test Plan +| CSR | Control & Status Register (located inside processor, not memory-mapped) +| TBD | To Be Determined +| N/A | “Not Applicable” +| AKA | “Also Known As” +| Must | Indicates a mandatory requirement +| Should | Indicates a recommended requirement +| May | Indicates an optional requirement +|=== diff --git a/backends/proc_cert/templates/proc_naming_scheme.adoc.erb b/backends/proc_cert/templates/proc_naming_scheme.adoc.erb new file mode 100644 index 000000000..2cb012054 --- /dev/null +++ b/backends/proc_cert/templates/proc_naming_scheme.adoc.erb @@ -0,0 +1,14 @@ +RVCP components for processors have the following format for their : + + [<-base>] + +Where: + +* is MC for Microcontroller Class and AC for Apps-processor Class +* is 3-digit integer defined as follows: +** The hundreds's digit indicates the series +** The ten's digit identifies large differences in mandatory extensions (e.g., V, H) within the series +** The one's digit indentifies small/medium differences in mandatory extensions (e.g., Zicond, PMP) within the series +* is optional and is 32 for RV32I, 64 for RV64I, and 32E for RV32E +** If multiple bases are supported and is omitted in a reference, the reference applies to all bases +** If only one base is supported then is generally omitted diff --git a/backends/proc_cert/templates/rev_history.adoc.erb b/backends/proc_cert/templates/rev_history.adoc.erb index e42895bc1..f8da71d24 100644 --- a/backends/proc_cert/templates/rev_history.adoc.erb +++ b/backends/proc_cert/templates/rev_history.adoc.erb @@ -1,5 +1,3 @@ -== Certificate History - History of documentation changes that eventually lead to releases. [cols="1,1,5"] diff --git a/backends/proc_cert/templates/rvcp_naming_scheme.adoc.erb b/backends/proc_cert/templates/rvcp_naming_scheme.adoc.erb new file mode 100644 index 000000000..d0ad65289 --- /dev/null +++ b/backends/proc_cert/templates/rvcp_naming_scheme.adoc.erb @@ -0,0 +1,22 @@ +All components of the RVCP share the following naming scheme: + + Format: [v] + +Where: + +* Left & right square braces denote optional. +* Less-than & greater-than signs just separate fields (i.e., they aren't present in the CRD name). +* identifies the type of RISC-V standard (processor, non-processor system IP, or platform) along with + any other information required to identify the variant of that standard. +* identifies a particular release +** Format is [.[.]] +** Inspired by semantic versioning scheme (https://semver.org/) but doesn't follow it exactly + +The rules for updating , , and are defined by the type of RVCP component. +However, the follow these general guidelines: + +* A release of 0 is used for pre-release versions and release versions start with 1. +* The release number is updated when mandatory changes are made. +* The release number is updated when optional changes are made. +* The release number is updated for documentation fixes/improvements that don't affect + certificates already obtained for a particular implementation. diff --git a/backends/proc_cert/templates/typographic.adoc.erb b/backends/proc_cert/templates/typographic.adoc.erb index 55e8cc24c..7b1aacdde 100644 --- a/backends/proc_cert/templates/typographic.adoc.erb +++ b/backends/proc_cert/templates/typographic.adoc.erb @@ -1,5 +1,3 @@ -== Typographic Conventions - CSR field colors:: * Grey fields are reserved (WPRI) diff --git a/backends/proc_crd/templates/proc_crd.adoc.erb b/backends/proc_crd/templates/proc_crd.adoc.erb index 22fc31867..bc5fb34fb 100644 --- a/backends/proc_crd/templates/proc_crd.adoc.erb +++ b/backends/proc_crd/templates/proc_crd.adoc.erb @@ -3,11 +3,20 @@ = <%= proc_cert_model.name %> Processor Certification Requirements Document [Preface] +== CRD Revision History + <%= proc_cert_design.include_erb("rev_history.adoc.erb") %> [Preface] +== Typographic Conventions + <%= proc_cert_design.include_erb("typographic.adoc.erb") %> +[Preface] +== Glossary + +<%= proc_cert_design.include_erb("glossary.adoc.erb") %> + == Introduction <%= proc_cert_model.introduction %> @@ -32,35 +41,25 @@ RISC-V implementations from basic microcontrollers to advanced Applications-clas Each CRD has a list of mandatory behaviors along with a list of optional behaviors. Note that not all behaviors allowed in RISC-V standards are supported by a particular CRD. -=== CRD Naming Scheme +=== Naming Scheme + +==== CRD Naming + +<%= proc_cert_design.include_erb("rvcp_naming_scheme.adoc.erb") %> -CRDs have the following naming scheme: +The specific rules for updating the version number of a CRD are as follows: - Format: [v] +* The release number is updated for changes that *could* cause a previously certified + implementation to no longer meet requirements. An example is requiring a new version of a standard. +* The release number is updated for increased support of optional behaviors. +* The release number is updated for documentation fixes/improvements. + These changes *cannot* cause a previously certified implementation to no longer meet requirements. -Where: +==== Processor Naming -* Left & right square braces denote optional. -* Less-than & greater-than signs just separate fields (i.e., they aren't present in the CRD name). -* identifies the type of RISC-V standard (processor, non-processor system IP, or platform) along with - any other information required to identify the variant of that standard. -* identifies a particular CRD release -** Format is [.[.]] -** Inspired by semantic versioning scheme (https://semver.org/) but doesn't follow it exactly -** The release is updated when certification test changes are made that *could* cause a previously certified - implementation to now fail. - Examples are fixing a test bug, or increasing test coverage, or requiring a new version of a standard - A release of 0 is used for pre-release versions of a CRD and release versions start with 1. -** The release is updated when a CRD increases support for optional behaviors. - Examples are supporting for new optional standards or - 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 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) +<%= proc_cert_design.include_erb("proc_naming_scheme.adoc.erb") %> -=== CRD Terminology +=== Requirements Terminology .Requirement Types [%autowidth] @@ -74,54 +73,6 @@ Where: | INCOMPATIBLE | If you implement it you won't get a certificate |=== -.Glossary -[%autowidth] -|=== -| Term | Meaning - -| CRD | Certification Requirements Document -| N/A | “Not Applicable” -| AKA | “Also Known As” -|=== - -=== Processor CRDs - -There are Processor CRDs for different classes of RISC-V processors. -These documents augment information in the related TSC Profile when available and/or other RVI standards documents -(e.g., Priv and Unpriv ISA manuals). -Only ratified extensions are candidates for certification. -This implies all custom extensions are also OUT-OF-SCOPE. - -==== Processor Certificate Class Naming Scheme - -Processor Certificate Class names have the following format: - - [<-base>] - -Where: - -* is MC for Microcontroller Class and AC for Apps-processor Class -* is 3-digit integer defined as follows: -** The hundreds's digit indicates the series -** The ten's digit identifies large differences in mandatory extensions (e.g., V, H) within the series -** The one's digit indentifies small/medium differences in mandatory extensions (e.g., Zicond, PMP) within the series -* is optional and is 32 for RV32I, 64 for RV64I, and 32E for RV32E -** If a CRD supports multiple bases and is omitted in a reference, it applies to all supported bases -** If a CRD only supports one base then is generally omitted - -[%autowidth] -|=== -| CRD | TSC Profile | Description - -| MC100-series | TBD | 32/64-bit minimal microcontroller that runs low-level software on an RTOS or bare-metal (no virtual memory) -| MC200-series | TBD | 32/64-bit intermediate microcontroller -| MC300-series | TBD | 32/64-bit advanced microcontroller -| AC100-series | RVB23 | 64-bit Apps-processor running Bespoke rich operating systems (e.g., Yocto Linux) -| AC200-series | RVA23 | 64-bit Apps-processor running standard rich operating systems (e.g., commercial Linux distributions, Android) -|=== - -==== CSR Field Terminology - .Definition of CSR Fields [%autowidth] |=== @@ -340,10 +291,6 @@ TODO: Show traps per privilege mode == Instruction Summary -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 - [%autowidth] |=== | Name | Long Name @@ -419,6 +366,7 @@ Requirement <%= req.name %> only apply when <%= req.when_pretty %>. <% end -%> <% end # unless requirement_groups.empty? -%> +// Appendices <%= portfolio_design.include_erb("ext_appendix.adoc.erb") %> <%= portfolio_design.include_erb("inst_appendix.adoc.erb") %> <%= portfolio_design.include_erb("csr_appendix.adoc.erb") %> diff --git a/backends/proc_ctp/templates/proc_ctp.adoc.erb b/backends/proc_ctp/templates/proc_ctp.adoc.erb index 148f69481..ad735d1fd 100644 --- a/backends/proc_ctp/templates/proc_ctp.adoc.erb +++ b/backends/proc_ctp/templates/proc_ctp.adoc.erb @@ -3,11 +3,20 @@ = <%= proc_cert_model.name %> Processor Certification Test Plan [Preface] +== CTP Revision History + <%= proc_cert_design.include_erb("rev_history.adoc.erb") %> [Preface] +== Typographic Conventions + <%= proc_cert_design.include_erb("typographic.adoc.erb") %> +[Preface] +== Glossary + +<%= proc_cert_design.include_erb("glossary.adoc.erb") %> + == Introduction <%= proc_cert_model.introduction %> @@ -23,333 +32,33 @@ Links to ISA manual anchors: === What's a CTP? -Certification Test Plans (CTPs) list requirements an implementation must meet -to obtain an associated RVI (RISC-V International) certificate. +Certification Test Plans (CTPs) list certification coverage points and how they will be tested. CTPs are developed by the RVI CSC (Certification Steering Committee) organization in collaboration with the RVI TSC (Technical Steering Committee) organization who creates RISC-V standards. -The CTPs refer to and augment information provided in existing ratified RVI standards. +Each certificated offered has a corresponding CRD and CTP. The CRD defines the certification requirements +that an implementation must meet and what behaviors are covered by the certificate. The CTP uses the CRD +to determine the required coverage points. -There are a variety of certificates offered by RVI to accomodate the various RVI standards. -There are certificates for processors, non-processor system IP (e.g., IOMMU), -and system platforms (processor + system IP) hardware standards. -There are multiple classes of processor certificates available to accomodate the wide range of -RISC-V implementations from basic microcontrollers to advanced Applications-class processors. +=== Naming Scheme -Each CTP has a list of mandatory behaviors along with a list of optional behaviors. -Note that not all behaviors allowed in RISC-V standards are supported by a particular CTP. +==== CTP Naming -=== CTP Naming Scheme +<%= proc_cert_design.include_erb("rvcp_naming_scheme.adoc.erb") %> -CTPs have the following naming scheme: +The specific rules for updating the version number for a CTP are as follows: - Format: [v] +* The release number is updated for changes that *could* cause a previously certified + implementation to now fail. An example is increasing coverage. +* The release number is updated when the CRD referenced by a CTP adds support for new optional behaviors. +* The release number is updated for documentation fixes/improvements. + These changes *cannot* cause a previously certified implementation to no longer pass certification. -Where: +==== Processor Naming Scheme -* Left & right square braces denote optional. -* Less-than & greater-than signs just separate fields (i.e., they aren't present in the CTP name). -* identifies the type of RISC-V standard (processor, non-processor system IP, or platform) along with - any other information required to identify the variant of that standard. -* identifies a particular CTP release -** Format is [.[.]] -** Inspired by semantic versioning scheme (https://semver.org/) but doesn't follow it exactly -** The release is updated when certification test changes are made that *could* cause a previously certified - implementation to now fail. - Examples are fixing a test bug, or increasing test coverage, or requiring a new version of a standard - A release of 0 is used for pre-release versions of a CTP and release versions start with 1. -** The release is updated when a CTP increases support for optional behaviors. - Examples are supporting for new optional standards or - 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 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) +<%= proc_cert_design.include_erb("proc_naming_scheme.adoc.erb") %> -=== CTP Terminology - -.Requirement Types -[%autowidth] -|=== -| Term | Meaning - -| MANDATORY | You have to implement it to get a certificate and the certificate tests will cover it -| OPTIONAL | It's up to you if you implement or not. If you claim to implement it, certificate tests will cover it -| IN-SCOPE | Either MANDATORY or OPTIONAL -| OUT-OF-SCOPE | It's up to you if you implement or not. If you implement it, it won't be certified but make sure you don't mess up anything we are certifying. -| INCOMPATIBLE | If you implement it you won't get a certificate -|=== - -.Glossary -[%autowidth] -|=== -| Term | Meaning - -| CTP | Certification Test Plan -| N/A | “Not Applicable” -| AKA | “Also Known As” -|=== - -=== Processor CTPs - -There are Processor CTPs for different classes of RISC-V processors. -These documents augment information in the related TSC Profile when available and/or other RVI standards documents -(e.g., Priv and Unpriv ISA manuals). -Only ratified extensions are candidates for certification. -This implies all custom extensions are also OUT-OF-SCOPE. - -==== Processor Certificate Class Naming Scheme - -Processor Certificate Class names have the following format: - - [<-base>] - -Where: - -* is MC for Microcontroller Class and AC for Apps-processor Class -* is 3-digit integer defined as follows: -** The hundreds's digit indicates the series -** The ten's digit identifies large differences in mandatory extensions (e.g., V, H) within the series -** The one's digit indentifies small/medium differences in mandatory extensions (e.g., Zicond, PMP) within the series -* is optional and is 32 for RV32I, 64 for RV64I, and 32E for RV32E -** If a CTP supports multiple bases and is omitted in a reference, it applies to all supported bases -** If a CTP only supports one base then is generally omitted - -[%autowidth] -|=== -| CTP | TSC Profile | Description - -| MC100-series | TBD | 32/64-bit minimal microcontroller that runs low-level software on an RTOS or bare-metal (no virtual memory) -| MC200-series | TBD | 32/64-bit intermediate microcontroller -| MC300-series | TBD | 32/64-bit advanced microcontroller -| AC100-series | RVB23 | 64-bit Apps-processor running Bespoke rich operating systems (e.g., Yocto Linux) -| AC200-series | RVA23 | 64-bit Apps-processor running standard rich operating systems (e.g., commercial Linux distributions, Android) -|=== - -==== CSR Field Terminology - -.Definition of CSR Fields -[%autowidth] -|=== -| Field Type | Read Value After Writing Illegal Value | Read Value Function Of | Illegal Instruction Exception | Priv ISA Manual Quote - -| WLRL | Any deterministic legal or illegal value | Value before write and illegal value written | Optional -| Implementations are permitted but not required to raise an illegal-instruction exception if an instruction attempts to write a non-supported value to a WLRL field. Implementations can return arbitrary bit patterns on the read of a WLRL field when the last write was of an illegal value, but the value returned should deterministically depend on the illegal written value and the value of the field prior to the write. -| WARL | Any deterministic legal value | Any architectural hart state | Prohibited -| Implementations will not raise an exception on writes of unsupported values to a WARL field. Implementations can return any legal value on the read of a WARL field when the last write was of an illegal value, but the legal value returned should deterministically depend on the illegal written value and the architectural state of the hart. -| WPRI | 0 | Nothing | Not specified -| Some whole read/write fields are reserved for future use. Implementations that do not furnish these fields must make them read-only zero. -|=== - -*WARL (Write Anything, Read Legal)*: - -The Priv ISA requires reads of WARL fields to return some implementation-dependent deterministic legal value -after the field is written with an illegal value. -Certifying such behaviors is expensive and provides low value for a certificate since software can't rely -on a particular behavior from one implementation to another. - -Processor CTPs define writes to WARL fields of illegal values to be OUT-OF-SCOPE unless otherwise stated -(i.e., certification tests will only ever write legal values to WARL fields except for the special cases listed below). -When not OUT-OF-SCOPE, the required behavior is defined as this might be more constrained in implementations than -in the standard. - -The following special cases for WARL are supported when explicitly listed in the corresponding CTP CSR field requirements: - -1. Probing for Field Width - -* Some WARL fields are variable length such as the ASID field in the virtual memory extension. -* Here's the algorithm recommended to discover the ASID width: -** The number of implemented ASID bits, termed ASIDLEN, may be determined by writing one to every bit position in - the ASID field, then reading back the value in the satp CSR to see which bit positions in the ASID field hold a one. -* The RVCP-provided certification materials (certification tests, certification reference models) can map writes of - illegal values to the ASID field to the corresponding read value as long as they are provided the ASIDLEN value - for an implementation. - -2. Probing for Options - -* E.g., Writable misa bits - -3. Allowed values are a function of extension presence and/or their parameters - -* E.g., satp.mode legal write values - -*WLRL (Write Legal, Read Legal)*: - -The Priv ISA requires reads of WLRL fields to return some implementation-dependent deterministic arbitrary value -after the field is written with an illegal value. -Certifying such behaviors is expensive and provides low value for a certificate since software can't rely -on a particular behavior. -Processor CTPs define writes to WLRL fields of illegal values to be OUT-OF-SCOPE unless otherwise stated -(i.e., certification tests will only ever write legal values to WLRL fields). - -*WPRI (Write Preserve, Read Ignore)*: - -The Priv ISA requires reads of WPRI fields to return a value of 0. -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 intended to be comprehensive -verification test suites anyways. - -=== Related Specifications - -[cols="2,2,3,3,3"] -|=== -| Certificate Model | TSC Profile | Unpriv ISA Manual | Priv ISA Manual | Debug Manual - -| <%= 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 - -|=== -| M | S | U | VS | VU - -| <% 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 -%> - -|=== - -<<< -== Extensions - -Any RISC-V extensions not listed in this section are OUT-OF-SCOPE. -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 = proc_cert_model.in_scope_ext_reqs(presence_obj) -%> -<% if ext_reqs.empty? -%> -None -<% else -%> -[%autowidth] -|=== -| Requirement ID | Extension | Version | Long Name | Note - -<% ext_reqs.each do |ext_req| -%> -<% ext = arch.extension(ext_req.name) -%> -| <%= ext_req.req_id %> -| <%= link_to_udb_doc_ext(ext_req.name) %> -| <%= ext_req.requirement_specs.map(&:to_s).join(", ") %> -| <%= ext.nil? ? "" : ext.long_name %> -| <%= ext_req.note.nil? ? "" : ext_req.note %> -<% end # each ext_req -%> -|=== -<% end # if empty ext_reqs -%> - -<% 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 proc_cert_model.recommendations.empty? -%> -=== Recommendations - -Recommendations are not strictly mandated but are included to guide implementers. - -<% proc_cert_model.recommendations.each do |recommendation| -%> -<%= recommendation.text %> -<% end # each recommendation -%> -<% end # unless recommendations empty -%> - -<<< -== Implementation-dependencies - -RISC-V standards support many implementation-defined parameters. In many cases, there -are no names associated with these parameters. Names are defined in this section when -not provided in the associated standard. - -=== IN-SCOPE Parameters - -These implementation-dependent options defined by MANDATORY or OPTIONAL extensions are IN-SCOPE. -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 portfolio_design.all_in_scope_params.empty? -%> -None -<% else -%> -[cols="4,2,1,1,2"] -|=== -| Parameter | Type | Allowed Value(s) | Extension(s) | Note - -<% design.all_in_scope_params.each do |in_scope_param| -%> -<% param = in_scope_param.param -%> -<% in_scope_exts = portfolio_design.all_in_scope_exts_with_param(param) -%> -| <%= param.name_potentially_with_link(in_scope_exts) %> -| <%= param.schema_type %> -| <%= in_scope_param.allowed_values %> -| <%= in_scope_exts.each { |ext| param.name_with_link(ext) } %> -a| <%= in_scope_param.note %> -<% end # do -%> -|=== -<% end # if table -%> - -=== OUT-OF-SCOPE Parameters - -These implementation-dependent options defined by MANDATORY or OPTIONAL extensions are OUT-OF-SCOPE. -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 portfolio_design.all_out_of_scope_params.empty? -%> -None -<% else -%> -[%autowidth] -|=== -| Parameters | Type | Extension(s) - -<% portfolio_design.all_out_of_scope_params.each do |param| -%> -<% exts = portfolio_design.all_in_scope_exts_without_param(param) -%> -| <%= param.name_potentially_with_link(exts) %> -| <%= param.schema_type %> -| <%= exts.each { |ext| param.name_with_link(ext) } %> - -<% end # do -%> -|=== -<% end # if table -%> - -== Traps - -RISC-V supports both synchronous exceptions and asynchronous interrupts. -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 - -=== Synchronous Exceptions - -|=== -| `xcause.CODE` CSR Field Value | Name -<% design.in_scope_exception_codes.sort_by{ |code| code.num }.each do |code| -%> -| <%= code.num %> | <%= code.name %> -<% end -%> -|=== - -=== Asynchronous Interrupts - -|=== -| `xcause.CODE` CSR Field Value | Name -<% design.in_scope_interrupt_codes.sort_by{ |code| code.num }.each do |code| -%> -| <%= code.num %> | <%= code.name %> -<% end -%> -|=== - -== Instruction Summary - -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 +== Instruction Coverage Points [%autowidth] |=== @@ -361,9 +70,7 @@ See https://github.com/riscv-software-src/riscv-unified-db/issues/291 and https: <% end # do -%> |=== -== CSR Summary - -=== By Name +== CSR Coverage Points [%autowidth] |=== @@ -378,54 +85,7 @@ See https://github.com/riscv-software-src/riscv-unified-db/issues/291 and https: <% end # do -%> |=== -=== By Address - -[%autowidth] -|=== -| Address | Mode | Name | Long Name | Primary Extension - -<% design.in_scope_csrs.sort_by!(&:address).each do |csr| -%> -| <%= "0x#{csr.address.to_s(16)}" %> -| <%= csr.priv_mode %> -| <%= link_to_udb_doc_csr(csr.name) %> -| <%= csr.long_name %> -| <%= csr.primary_defined_by %> -<% end # do -%> -|=== - -<% 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. - -<% proc_cert_model.requirement_groups.each do |group| -%> -=== <%= group.name %> - -<%= group.description %> - -<% unless group.when.nil? -%> -[IMPORTANT] -<%= group.name %> requirements only apply when <%= group.when_pretty %>. -<% end -%> - -[%autowidth] -|=== -| Req Number | Description - -<% group.requirements.each do |req| -%> -| <%= req.name %> -a| <%= req.description %> -<% unless req.when.nil? -%> -[IMPORTANT] -Requirement <%= req.name %> only apply when <%= req.when_pretty %>. -<% end -%> -<% end -%> -|=== - -<% end -%> -<% end # unless requirement_groups.empty? -%> - +// Appendices <%= portfolio_design.include_erb("ext_appendix.adoc.erb") %> <%= portfolio_design.include_erb("inst_appendix.adoc.erb") %> <%= portfolio_design.include_erb("csr_appendix.adoc.erb") %> From dd9f6db8e47f53ff31d4510d9d74a43dee7c35f9 Mon Sep 17 00:00:00 2001 From: James Ball Date: Tue, 21 Jan 2025 15:32:47 +0000 Subject: [PATCH 42/61] Changed CTP rake to always sync to latest of csc-riscv-isa-manual. --- backends/proc_ctp/tasks.rake | 65 +++++++++++++----------------------- ext/csc-riscv-isa-manual | 2 +- 2 files changed, 25 insertions(+), 42 deletions(-) diff --git a/backends/proc_ctp/tasks.rake b/backends/proc_ctp/tasks.rake index 06a9013c1..630ba1563 100644 --- a/backends/proc_ctp/tasks.rake +++ b/backends/proc_ctp/tasks.rake @@ -31,6 +31,30 @@ Dir.glob("#{$root}/arch/proc_cert_model/*.yaml") do |f| "#{PROC_CTP_GEN_DIR}/adoc/ext/riscv-isa-manual/README.md", "#{PROC_CTP_GEN_DIR}/adoc/ext/riscv-isa-manual/docs-resources/README.md" ] do |t| + # Ensure that the required submodule repositories are up-to-date. + # TODO: Commented this out since it puts the submodule in the HEADless state + # and doesn't contain the latest updates to "main" from the submodules. + #sh "git submodule update --init ext/csc-riscv-isa-manual 2>&1" + #sh "git submodule update --init ext/docs-resources 2>&1" + + # Pull in the latest version of the csc-riscv-isa-manual. + sh "cd ext/csc-riscv-isa-manual; git fetch; git merge origin/main 2>&1" + + # Use git archive to extract the latest version of the csc-riscv-isa-manual. + FileUtils.mkdir_p File.dirname(t.name) + Dir.chdir($root / "ext" / "csc-riscv-isa-manual") do + sh "git archive --format=tar HEAD | tar xvf - -C #{File.dirname(t.name)}" + end + + # Pull in the latest version of the docs-resources. + sh "cd ext/docs-resources; git fetch; git merge origin/main 2>&1" + + # Use git archive to extract the latest version of the docs-resources. + FileUtils.mkdir_p File.dirname(t.name) + Dir.chdir($root / "ext" / "docs-resources") do + sh "git archive --format=tar HEAD | tar xvf - -C #{File.dirname(t.name)}" + end + proc_cert_create_adoc("#{PROC_CTP_DOC_DIR}/templates/proc_ctp.adoc.erb", t.name, model_name) end @@ -49,47 +73,6 @@ Dir.glob("#{$root}/arch/proc_cert_model/*.yaml") do |f| end end -# Ensure that the riscv-isa-manual submodule repository is up-to-date. -file $root / "ext" / "csc-riscv-isa-manual" / "README.md" do - sh "git submodule update --init ext/csc-riscv-isa-manual 2>&1" -end - -# Ensure that the docs-resources submodule repository is up-to-date. -file $root / "ext" / "docs-resources" / "README.md" do - sh "git submodule update --init ext/docs-resources 2>&1" -end - -# Rule to copy the riscv-isa-manual submodule repository to the gen directory. -rule %r{#{PROC_CTP_GEN_DIR}/adoc/ext/riscv-isa-manual/README.md} => [ - "#{$root}/ext/csc-riscv-isa-manual/README.md" -] do |t| - # Pull in the latest version of the csc-riscv-isa-manual. - sh "cd ext/csc-riscv-isa-manual; git fetch; git merge origin/main 2>&1" - - # Use git archive to extract the latest version of the csc-riscv-isa-manual. - FileUtils.mkdir_p File.dirname(t.name) - Dir.chdir($root / "ext" / "csc-riscv-isa-manual") do - sh "git archive --format=tar HEAD | tar xvf - -C #{File.dirname(t.name)}" - end -end - -# Rule to copy the docs-resources submodule repository to the gen directory. -# Make the rule dependent on the riscv-isa-manual to ensure that gets copied -# to the gen directory first (because it has an empty docs-resources directory). -rule %r{#{PROC_CTP_GEN_DIR}/adoc/ext/riscv-isa-manual/docs-resources/README.md} => [ - "#{$root}/ext/docs-resources/README.md", - "#{$root}/ext/csc-riscv-isa-manual/README.md", -] do |t| - # Pull in the latest version of the docs-resources. - sh "cd ext/docs-resources; git fetch; git merge origin/main 2>&1" - - # Use git archive to extract the latest version of the docs-resources. - FileUtils.mkdir_p File.dirname(t.name) - Dir.chdir($root / "ext" / "docs-resources") do - sh "git archive --format=tar HEAD | tar xvf - -C #{File.dirname(t.name)}" - end -end - namespace :gen do desc <<~DESC Generate Processor CTP (Certification Test Plan) as a PDF. diff --git a/ext/csc-riscv-isa-manual b/ext/csc-riscv-isa-manual index d706dcedc..323fbe8e4 160000 --- a/ext/csc-riscv-isa-manual +++ b/ext/csc-riscv-isa-manual @@ -1 +1 @@ -Subproject commit d706dcedc46fb8d074288bdca9c0bc33497ef68f +Subproject commit 323fbe8e4b4e5ca7aae41819d4112a7f4a0e8ee9 From d2a8615fa860b2656bcbdcacf86e83704bd10d83 Mon Sep 17 00:00:00 2001 From: James Ball Date: Wed, 22 Jan 2025 01:44:49 +0000 Subject: [PATCH 43/61] Starting to generate CTP coverage points --- arch/inst/M/mul.yaml | 6 +- arch/inst/mock.yaml | 30 +++++---- .../portfolio/templates/csr_appendix.adoc.erb | 42 ++++++------- .../templates/inst_appendix.adoc.erb | 34 +++++++--- backends/proc_ctp/tasks.rake | 4 +- ext/csc-riscv-isa-manual | 2 +- lib/arch_obj_models/instruction.rb | 62 +++++++++++++++++++ lib/backend_helpers.rb | 8 +++ 8 files changed, 141 insertions(+), 47 deletions(-) diff --git a/arch/inst/M/mul.yaml b/arch/inst/M/mul.yaml index 975e8fd67..62723e0f7 100644 --- a/arch/inst/M/mul.yaml +++ b/arch/inst/M/mul.yaml @@ -74,15 +74,15 @@ cert-coverage-points: - name: Encoding and basic operation description: | Encoding and basic operation for `mul` instruction - xrefs: + links: - manual:inst:mul:encoding - manual:inst:mul:operation - name: Illegal instruction exception when misa.M is 0 description: | An illegal instruction exception is raised when the instruction is executed and `misa.M` is 0. - xrefs: - - manual:priv:csr:misa:disabling-extension + links: + - manual:csr:misa:disabling-extension cert-test-plan: - name: Encoding diff --git a/arch/inst/mock.yaml b/arch/inst/mock.yaml index 2cdd0afd1..1e0048a48 100644 --- a/arch/inst/mock.yaml +++ b/arch/inst/mock.yaml @@ -27,22 +27,20 @@ access: u: always vs: always vu: always -data_independent_timing: - true - # Want to add something like this to IDL code. - # [#idl:code:inst:mul:exception-illegal_instruction]# - # if (implemented?(ExtensionName::M) && (CSR[misa].M == 1'b0)) { - # raise (ExceptionCode::IllegalInstruction, mode(), $encoding); - # }# +data_independent_timing: true operation(): | - if (implemented?(ExtensionName::M) && (CSR[misa].M == 1'b0)) { - raise (ExceptionCode::IllegalInstruction, mode(), $encoding); - } + #anchor("illegal-inst-exc-misa-disabled") { + if (implemented?(ExtensionName::M) && (CSR[misa].M == 1'b0)) { + raise (ExceptionCode::IllegalInstruction, mode(), $encoding); + } + #} XReg src1 = X[rs1]; XReg src2 = X[rs2]; - X[rd] = (src1 * src2)[XLEN-1:0]; + #anchor("calculation") { + X[rd] = (src1 * src2)[XLEN-1:0]; + #} sail(): | { @@ -67,8 +65,16 @@ cert-coverage-points: - name: Encoding and basic operation description: | Encoding and basic operation for `mock` instruction - xrefs: + links: + - manual:inst:mul:encoding - udb:doc:inst:mock + - name: Illegal instruction exception when misa.M is 0 + description: | + An illegal instruction exception is raised when the instruction is executed + and `misa.M` is 0. + links: + - manual:csr:misa:disabling-extension + # - idl:code:inst:mock:illegal-inst-exc-misa-disabled cert-test-plan: - name: Encoding diff --git a/backends/portfolio/templates/csr_appendix.adoc.erb b/backends/portfolio/templates/csr_appendix.adoc.erb index 757b3e885..82fb02767 100644 --- a/backends/portfolio/templates/csr_appendix.adoc.erb +++ b/backends/portfolio/templates/csr_appendix.adoc.erb @@ -70,10 +70,10 @@ This CSR format changes dynamically with XLEN. |=== @ Name @ Location @ Type @ Reset Value -<%- csr.implemented_fields(design).each do |field| -%> +<% csr.implemented_fields(design).each do |field| -%> @ <%= link_to_udb_doc_csr_field(csr.name, field.name) %> a@ -<%- if field.dynamic_location?(design) -%> +<% if field.dynamic_location?(design) -%> [when,"<%= field.location_cond32 %>"] -- @@ -85,9 +85,9 @@ a@ <%= field.location_pretty(design, 64) %> -- -<%- else -%> +<% else -%> <%= field.location_pretty(design) %> -<%- end -%> +<% end -%> a@ -- @@ -100,22 +100,22 @@ a@ <%= field.reset_value_pretty(design) %> -- -<%- end -%> +<% end -%> |=== ==== Fields -<%- if csr.implemented_fields(design).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 -%> +<% else -%> -<%- csr.implemented_fields(design).each do |field| -%> +<% csr.implemented_fields(design).each do |field| -%> <%= anchor_for_udb_doc_csr_field(csr.name, field.name) %> ===== `<%= field.name %>` Field -<%- if !field.defined_in_all_bases? -%> +<% if !field.defined_in_all_bases? -%> IMPORTANT: <%= field.name %> is only defined in <%= field.base32_only? ? "RV32" : "RV64" %> (`<%= field.base32_only? ? field.location_cond32 : field.location_cond64 %>`) -<%- end -%> +<% end -%> **** Location:: @@ -132,10 +132,10 @@ Reset value:: **** -<%- end -%> -<%- end -%> +<% end -%> +<% end -%> -<%- if csr.implemented_fields(design).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. @@ -145,17 +145,17 @@ written value: [idl] ---- -<%- csr.implemented_fields(design).each do |field| -%> -<%- if field.has_custom_sw_write? -%> +<% csr.implemented_fields(design).each do |field| -%> +<% if field.has_custom_sw_write? -%> <%= field.name %> = <%= field["sw_write(csr_value)"] %> -<%- else -%> +<% else -%> <%= field.name %> = csr_value.<%= field.name %> -<%- end -%> -<%- end -%> +<% end -%> +<% end -%> ---- -<%- end -%> +<% end -%> -<%- if csr.has_custom_sw_read? -%> +<% if csr.has_custom_sw_read? -%> ==== Software read This CSR may return a value that is different from what is stored in hardware. @@ -164,6 +164,6 @@ This CSR may return a value that is different from what is stored in hardware. ---- <%= csr.sw_read_ast(design.symtab).gen_adoc %> ---- -<%- end -%> +<% end -%> <% end # do in_scope_csrs -%> diff --git a/backends/portfolio/templates/inst_appendix.adoc.erb b/backends/portfolio/templates/inst_appendix.adoc.erb index d5d5882fa..61292e56e 100644 --- a/backends/portfolio/templates/inst_appendix.adoc.erb +++ b/backends/portfolio/templates/inst_appendix.adoc.erb @@ -113,16 +113,36 @@ RV64:: ==== Exceptions -<%- exception_list = inst.reachable_exceptions_str(design.symtab) -%> -<%- if exception_list.empty? -%> +<% exception_list = inst.reachable_exceptions_str(design.symtab) -%> +<% if exception_list.empty? -%> This instruction does not generate synchronous exceptions. -<%- else -%> +<% else -%> This instruction may result in the following synchronous exceptions: - <%- exception_list.sort.each do |etype| -%> + <% exception_list.sort.each do |etype| -%> * <%= etype %> - <%- end -%> - -<%- end -%> + <% end -%> <% end -%> + +==== Coverage Points + +<% if inst.cert_coverage_points.empty? -%> +None defined yet. +<% else -%> +[%autowidth] +|=== +| Name | Description | Links + +<% inst.cert_coverage_points.each do |cp| -%> +| <%= cp.name %> +| <%= cp.description %> +a| <% cp.cert_links.each do |link| -%> +* <%= link.to_adoc %> +<% end # each link -%> +<% end # each coverage point -%> +|=== + +<% end # if -%> + +<% end # each in_scope instruction -%> diff --git a/backends/proc_ctp/tasks.rake b/backends/proc_ctp/tasks.rake index 630ba1563..e7869bab4 100644 --- a/backends/proc_ctp/tasks.rake +++ b/backends/proc_ctp/tasks.rake @@ -27,9 +27,7 @@ Dir.glob("#{$root}/arch/proc_cert_model/*.yaml") do |f| "#{$root}/backends/portfolio/templates/beginning.adoc.erb", "#{$root}/backends/proc_cert/templates/typographic.adoc.erb", "#{$root}/backends/proc_cert/templates/rev_history.adoc.erb", - "#{PROC_CTP_DOC_DIR}/templates/proc_ctp.adoc.erb", - "#{PROC_CTP_GEN_DIR}/adoc/ext/riscv-isa-manual/README.md", - "#{PROC_CTP_GEN_DIR}/adoc/ext/riscv-isa-manual/docs-resources/README.md" + "#{PROC_CTP_DOC_DIR}/templates/proc_ctp.adoc.erb" ] do |t| # Ensure that the required submodule repositories are up-to-date. # TODO: Commented this out since it puts the submodule in the HEADless state diff --git a/ext/csc-riscv-isa-manual b/ext/csc-riscv-isa-manual index 323fbe8e4..15e6cb577 160000 --- a/ext/csc-riscv-isa-manual +++ b/ext/csc-riscv-isa-manual @@ -1 +1 @@ -Subproject commit 323fbe8e4b4e5ca7aae41819d4112a7f4a0e8ee9 +Subproject commit 15e6cb5778b53c9ebe9a8b2fb852fa570db00503 diff --git a/lib/arch_obj_models/instruction.rb b/lib/arch_obj_models/instruction.rb index 72bed1eb6..683a78537 100644 --- a/lib/arch_obj_models/instruction.rb +++ b/lib/arch_obj_models/instruction.rb @@ -757,4 +757,66 @@ def exists_in_design?(design) design.mandatory_ext_reqs.none? { |ext_req| excluded_by?(ext_req) } end end + + # @return [Array] + def cert_coverage_points + return @cert_coverage_points unless @cert_coverage_points.nil? + + @cert_coverage_points = [] + @data["cert-coverage-points"]&.each do |cert_coverage_point| + @cert_coverage_points << CertCoveragePoint.new(cert_coverage_point) + end + @cert_coverage_points + end + + class CertCoveragePoint + # @param data [Hash] Data from YAML file + def initialize(data) + raise ArgumentError, "Need Hash but was passed a #{data.class}" unless data.is_a?(Hash) + + @data = data + end + + # @return [String] Name of the coverage point + def name = @data["name"] + + # @return [String] Description of coverage point (could be multiple lines) + def description = @data["description"] + + # @return [Array] Sorted list of certification point links (cross references) + def cert_links + return @cert_links unless @cert_links.nil? + + @cert_links = [] + @data["links"]&.each do |link_data| + @cert_links << CertLink.new(link_data) + end + @cert_links.sort! + end + end + + class CertLink + # @return [String] YAML data string + attr_reader :data + + # @param data [String] The cross reference link provided in the YAML + def initialize(data) + raise ArgumentError, "Need String but was passed a #{data.class}" unless data.is_a?(String) + @data = data + end + + # @return [String] Asciidoc to create desired link. + def to_adoc + "<<#{data},#{data}>>" + end + + # For sorting ... + def <=>(other) + if other.is_a?(CertLink) + @data <=> other.data + else + raise ArgumentError, "CertLink is not comparable to a #{other.class.name}" + end + end + end end diff --git a/lib/backend_helpers.rb b/lib/backend_helpers.rb index c28cf12a7..6b47f8ad9 100644 --- a/lib/backend_helpers.rb +++ b/lib/backend_helpers.rb @@ -33,6 +33,10 @@ def partial(template_pname, inputs = {}) erb.result(OpenStruct.new(inputs).instance_eval { binding }) end + ######### + # LINKS # + ######### + # Links are created with this proprietary format so that they can be converted # later into either AsciiDoc or Antora links (see the two implementations of "resolve_links"). # %%UDB_DOC_LINK%;;%% @@ -90,6 +94,10 @@ def link_into_idl_inst_code(inst_name, loc_name) end # TODO: Add csr and csr_field support + ########### + # ANCHORS # + ########### + # @return [String] An anchor for UDB extension documentation # @param ext_name [String] Name of the extension def anchor_for_udb_doc_ext(ext_name) From 1050eebbb809a63848aeceddfe332d0eee77da44 Mon Sep 17 00:00:00 2001 From: James Ball Date: Thu, 23 Jan 2025 01:24:09 +0000 Subject: [PATCH 44/61] Added support for instructions, extensions, CSRs, and CSR fields to have certification test plan information. --- arch/inst/M/mul.yaml | 33 ++-- arch/inst/mock.yaml | 21 +-- backends/portfolio/tasks.rake | 12 ++ .../templates/inst_appendix.adoc.erb | 31 +++- backends/proc_ctp/tasks.rake | 19 ++- backends/proc_ctp/templates/proc_ctp.adoc.erb | 4 +- ext/csc-riscv-isa-manual | 2 +- lib/arch_obj_models/database_obj.rb | 157 ++++++++++++++++++ lib/arch_obj_models/instruction.rb | 62 ------- schemas/csr_schema.json | 12 ++ schemas/ext_schema.json | 6 + schemas/inst_schema.json | 68 +------- schemas/profile_schema.json | 6 + schemas/schema_defs.json | 59 +++++++ 14 files changed, 325 insertions(+), 167 deletions(-) diff --git a/arch/inst/M/mul.yaml b/arch/inst/M/mul.yaml index 62723e0f7..b982b8d74 100644 --- a/arch/inst/M/mul.yaml +++ b/arch/inst/M/mul.yaml @@ -71,11 +71,15 @@ sail(): | } cert-coverage-points: - - name: Encoding and basic operation + - name: Encoding description: | - Encoding and basic operation for `mul` instruction + Encoding of `mul` instruction links: - manual:inst:mul:encoding + - name: Basic operation + description: | + Basic operation of `mul` instruction + links: - manual:inst:mul:operation - name: Illegal instruction exception when misa.M is 0 description: | @@ -84,18 +88,19 @@ cert-coverage-points: links: - manual:csr:misa:disabling-extension -cert-test-plan: +cert-test-procedures: - name: Encoding - description: Verify the encoding and basic operation of the `mul` instruction + description: Verify the encoding of the `mul` instruction coverage-points: - - Encoding and basic operation - setup: - - description: Load a variety of known values into rs1 & rs2 with a variety of rs1/rs2/rd values. - execution: - - description: Execute the `mul` instruction - validation: - - description: Check each result in rd - teardown: - - description: Clear the registers used for rd - - comment: | + - Encoding + steps: + - name: setup + description: Load a variety of known values into rs1 & rs2 with a variety of rs1/rs2/rd values. + - name: execution + description: Execute the `mock` instruction + - name: validation + description: Check each result in rd + - name: teardown + description: Clear the registers used for rd + note: | Don't really need to clear the registers so this is a contrived example diff --git a/arch/inst/mock.yaml b/arch/inst/mock.yaml index 1e0048a48..ea6c1acb2 100644 --- a/arch/inst/mock.yaml +++ b/arch/inst/mock.yaml @@ -76,18 +76,19 @@ cert-coverage-points: - manual:csr:misa:disabling-extension # - idl:code:inst:mock:illegal-inst-exc-misa-disabled -cert-test-plan: +cert-test-procedures: - name: Encoding description: Verify the encoding and basic operation of the `mock` instruction coverage-points: - Encoding and basic operation - setup: - - description: Load a variety of known values into rs1 & rs2 with a variety of rs1/rs2/rd values. - execution: - - description: Execute the `mock` instruction - validation: - - description: Check each result in rd - teardown: - - description: Clear the registers used for rd - - comment: | + steps: + - name: setup + description: Load a variety of known values into rs1 & rs2 with a variety of rs1/rs2/rd values. + - name: execution + description: Execute the `mock` instruction + - name: validation + description: Check each result in rd + - name: teardown + description: Clear the registers used for rd + note: | Don't really need to clear the registers so this is a contrived example diff --git a/backends/portfolio/tasks.rake b/backends/portfolio/tasks.rake index c152cc73c..ebd0c9012 100644 --- a/backends/portfolio/tasks.rake +++ b/backends/portfolio/tasks.rake @@ -61,6 +61,17 @@ def pf_adoc2pdf(adoc_file, target_pname) ].join(" ") puts "UPDATE: bundle exec #{cmd}" + + # Write out command used to convert adoc to PDF to allow running this + # manually during development. + run_pname = File.dirname(adoc_file) + "/run.sh" + sh "rm -f #{run_pname}" + sh "echo '#!/bin/bash' >#{run_pname}" + sh "echo >>#{run_pname}" + sh "echo #{cmd} >>#{run_pname}" + sh "chmod +x #{run_pname}" + + # Now run the actual command. sh cmd puts "UPDATE: Generated PDF in #{target_pname}" @@ -86,5 +97,6 @@ def pf_adoc2html(adoc_file, target_pname) puts "UPDATE: bundle exec #{cmd}" sh cmd + puts "UPDATE: Generated HTML in #{target_pname}" end diff --git a/backends/portfolio/templates/inst_appendix.adoc.erb b/backends/portfolio/templates/inst_appendix.adoc.erb index 61292e56e..8145ec0be 100644 --- a/backends/portfolio/templates/inst_appendix.adoc.erb +++ b/backends/portfolio/templates/inst_appendix.adoc.erb @@ -125,11 +125,9 @@ This instruction may result in the following synchronous exceptions: <% end -%> -==== Coverage Points +<% unless inst.cert_coverage_points.empty? -%> +==== Certification Coverage Points -<% if inst.cert_coverage_points.empty? -%> -None defined yet. -<% else -%> [%autowidth] |=== | Name | Description | Links @@ -143,6 +141,29 @@ a| <% cp.cert_links.each do |link| -%> <% end # each coverage point -%> |=== -<% end # if -%> +<% end # unless no coverage points -%> + +<% unless inst.cert_test_procedures.empty? -%> +==== Certification Test Procedures + +[%autowidth] +|=== +| Name | Description | Coverage Points | Steps + +<% inst.cert_test_procedures.each do |tp| -%> +| <%= tp.name %> +| <%= tp.description %> +a| <% tp.cert_coverage_points.each do |cp| -%> +* <%= cp.name %> +<% end # each cp -%> +a| <% tp.cert_steps.each do |step| -%> +. <%= step.name %> +* <%= step.description %> +<%= step.note.nil? ? "" : "* " + step.note %> +<% end # each step -%> +<% end # each test plan -%> +|=== + +<% end # unless no test plans -%> <% end # each in_scope instruction -%> diff --git a/backends/proc_ctp/tasks.rake b/backends/proc_ctp/tasks.rake index e7869bab4..5d0f0245f 100644 --- a/backends/proc_ctp/tasks.rake +++ b/backends/proc_ctp/tasks.rake @@ -6,6 +6,8 @@ require "pathname" PROC_CTP_DOC_DIR = Pathname.new "#{$root}/backends/proc_ctp" PROC_CTP_GEN_DIR = $root / "gen" / "proc_ctp" +PROC_CTP_ISA_MAN_DIR = "#{PROC_CTP_GEN_DIR}/adoc/ext/riscv-isa-manual" +PROC_CTP_DOCS_RESOURCES_DIR = "#{PROC_CTP_ISA_MAN_DIR}/docs-resources" Dir.glob("#{$root}/arch/proc_cert_model/*.yaml") do |f| model_name = File.basename(f, ".yaml") @@ -13,7 +15,7 @@ Dir.glob("#{$root}/arch/proc_cert_model/*.yaml") do |f| class_name = File.basename(model_obj['class']['$ref'].split("#")[0], ".yaml") raise "Ill-formed processor certificate model file #{f}: missing 'class' field" if model_obj['class'].nil? - file "#{$root}/gen/proc_ctp/adoc/#{model_name}-CTP.adoc" => [ + file "#{PROC_CTP_GEN_DIR}/adoc/#{model_name}-CTP.adoc" => [ __FILE__, "#{$root}/arch/proc_cert_class/#{class_name}.yaml", "#{$root}/arch/proc_cert_model/#{model_name}.yaml", @@ -38,19 +40,20 @@ Dir.glob("#{$root}/arch/proc_cert_model/*.yaml") do |f| # Pull in the latest version of the csc-riscv-isa-manual. sh "cd ext/csc-riscv-isa-manual; git fetch; git merge origin/main 2>&1" + # Pull in the latest version of the docs-resources. + #sh "cd ext/docs-resources; git fetch; git merge origin/main 2>&1" + + # Use git archive to extract the latest version of the csc-riscv-isa-manual. - FileUtils.mkdir_p File.dirname(t.name) + FileUtils.mkdir_p "#{PROC_CTP_ISA_MAN_DIR}" Dir.chdir($root / "ext" / "csc-riscv-isa-manual") do - sh "git archive --format=tar HEAD | tar xvf - -C #{File.dirname(t.name)}" + sh "git archive --format=tar HEAD | tar xf - -C #{PROC_CTP_ISA_MAN_DIR}" end - # Pull in the latest version of the docs-resources. - sh "cd ext/docs-resources; git fetch; git merge origin/main 2>&1" - # Use git archive to extract the latest version of the docs-resources. - FileUtils.mkdir_p File.dirname(t.name) + FileUtils.mkdir_p "#{PROC_CTP_DOCS_RESOURCES_DIR}" Dir.chdir($root / "ext" / "docs-resources") do - sh "git archive --format=tar HEAD | tar xvf - -C #{File.dirname(t.name)}" + sh "git archive --format=tar HEAD | tar xf - -C #{PROC_CTP_DOCS_RESOURCES_DIR}" end proc_cert_create_adoc("#{PROC_CTP_DOC_DIR}/templates/proc_ctp.adoc.erb", t.name, model_name) diff --git a/backends/proc_ctp/templates/proc_ctp.adoc.erb b/backends/proc_ctp/templates/proc_ctp.adoc.erb index ad735d1fd..d9fea1e5e 100644 --- a/backends/proc_ctp/templates/proc_ctp.adoc.erb +++ b/backends/proc_ctp/templates/proc_ctp.adoc.erb @@ -58,7 +58,7 @@ The specific rules for updating the version number for a CTP are as follows: <%= proc_cert_design.include_erb("proc_naming_scheme.adoc.erb") %> -== Instruction Coverage Points +== Instruction Summary [%autowidth] |=== @@ -70,7 +70,7 @@ The specific rules for updating the version number for a CTP are as follows: <% end # do -%> |=== -== CSR Coverage Points +== CSR Summary [%autowidth] |=== diff --git a/ext/csc-riscv-isa-manual b/ext/csc-riscv-isa-manual index 15e6cb577..97992e293 160000 --- a/ext/csc-riscv-isa-manual +++ b/ext/csc-riscv-isa-manual @@ -1 +1 @@ -Subproject commit 15e6cb5778b53c9ebe9a8b2fb852fa570db00503 +Subproject commit 97992e29382102911efaf26c5fb3f7101320e6d8 diff --git a/lib/arch_obj_models/database_obj.rb b/lib/arch_obj_models/database_obj.rb index a074a45a3..b66bb6c40 100644 --- a/lib/arch_obj_models/database_obj.rb +++ b/lib/arch_obj_models/database_obj.rb @@ -47,6 +47,62 @@ def initialize(data, data_path, arch) @description = data["description"] end + # @return [Array] + def cert_coverage_points + return @cert_coverage_points unless @cert_coverage_points.nil? + + @cert_coverage_points = [] + @data["cert-coverage-points"]&.each do |cert_data| + @cert_coverage_points << CertCoveragePoint.new(cert_data, self) + end + @cert_coverage_points + end + + # @return [Hash] Hash of all coverage points defined by database object + def cert_coverage_point_hash + return @cert_coverage_point_hash unless @cert_coverage_point_hash.nil? + + @cert_coverage_point_hash = {} + cert_coverage_points.each do |cp| + @cert_coverage_point_hash[cp.name] = cp + end + @cert_coverage_point_hash + end + + # @return [CertCoveragePoint] + # @return [nil] if there is no certification coverage pointed named +name+ + def cert_coverage_point(name) + cert_coverage_point_hash[name] + end + + # @return [Array] + def cert_test_procedures + return @cert_test_procedures unless @cert_test_procedures.nil? + + @cert_test_procedures = [] + @data["cert-test-procedures"]&.each do |cert_data| + @cert_test_procedures << CertTestProcedure.new(cert_data, self) + end + @cert_test_procedures + end + + # @return [Hash] Hash of all coverage points defined by database object + def cert_test_procedure_hash + return @cert_test_procedure_hash unless @cert_test_procedure_hash.nil? + + @cert_test_procedure_hash = {} + cert_test_procedures.each do |tp| + @cert_test_procedure_hash[tp.name] = tp + end + @cert_test_procedure_hash + end + + # @return [CertTestProcedure] + # @return [nil] if there is no certification test plan named +name+ + def cert_test_procedure(name) + cert_test_procedure_hash[name] + end + # Exception raised when there is a problem with a schema file class SchemaError < ::StandardError # result from JsonSchemer.validate @@ -628,3 +684,104 @@ def empty? = true def to_h = {} def minimize = {} end + +class CertCoveragePoint + # @param data [Hash] Data from YAML file + # @param db_obj [DatabaseObject] + def initialize(data, db_obj) + raise ArgumentError, "Need Hash but was passed a #{data.class}" unless data.is_a?(Hash) + raise ArgumentError, "Need DatabaseObject but was passed a #{db_obj.class}" unless db_obj.is_a?(DatabaseObject) + + @data = data + @db_obj = db_obj + end + + # @return [String] Name of the coverage point + def name = @data["name"] + + # @return [String] Description of coverage point (could be multiple lines) + def description = @data["description"] + + # @return [Array] List of certification point links (cross references) + def cert_links + return @cert_links unless @cert_links.nil? + + @cert_links = [] + @data["links"]&.each do |link_data| + @cert_links << CertLink.new(link_data) + end + @cert_links + end +end + +class CertLink + # @param data [String] The cross reference link provided in the YAML + def initialize(data) + raise ArgumentError, "Need String but was passed a #{data.class}" unless data.is_a?(String) + @data = data + end + + # @return [String] Asciidoc to create desired link. + def to_adoc + "<<#{@data},#{@data}>>" + end +end + +class CertTestProcedure + # @param data [Hash] Data from YAML file + # @param db_obj [DatabaseObject] + def initialize(data, db_obj) + raise ArgumentError, "Need Hash but was passed a #{data.class}" unless data.is_a?(Hash) + raise ArgumentError, "Need DatabaseObject but was passed a #{db_obj.class}" unless db_obj.is_a?(DatabaseObject) + + @data = data + @db_obj = db_obj + end + + # @return [String] Name of the test plan + def name = @data["name"] + + # @return [String] Description of test plan (could be multiple lines) + def description = @data["description"] + + # @return [Array] + def cert_coverage_points + return @cert_coverage_points unless @cert_coverage_points.nil? + + @cert_coverage_points = [] + @data["coverage-points"]&.each do |name| + cp = @db_obj.cert_coverage_point(name) + raise ArgumentError, "CertTestProcedure named '#{name}' for '#{@db_obj.name}' of kind #{@db_obj.kind} can't be found" if cp.nil? + @cert_coverage_points << cp + end + @cert_coverage_points + end + + # @return [Array] List of certification test plan steps + def cert_steps + return @cert_steps unless @cert_steps.nil? + + @cert_steps = [] + @data["steps"]&.each do |step_data| + @cert_steps << CertStep.new(step_data) + end + @cert_steps + end +end + +class CertStep + # @param data [Hash] The step information from the YAML + def initialize(data) + raise ArgumentError, "Need Hash but was passed a #{data.class}" unless data.is_a?(Hash) + @data = data + end + + # @return [String] Name of the step + def name = @data["name"] + + # @return [String] Description of the step + def description = @data["description"] + + # @return [String] Optional note (can be nil) + def note = @data["note"] +end diff --git a/lib/arch_obj_models/instruction.rb b/lib/arch_obj_models/instruction.rb index 683a78537..72bed1eb6 100644 --- a/lib/arch_obj_models/instruction.rb +++ b/lib/arch_obj_models/instruction.rb @@ -757,66 +757,4 @@ def exists_in_design?(design) design.mandatory_ext_reqs.none? { |ext_req| excluded_by?(ext_req) } end end - - # @return [Array] - def cert_coverage_points - return @cert_coverage_points unless @cert_coverage_points.nil? - - @cert_coverage_points = [] - @data["cert-coverage-points"]&.each do |cert_coverage_point| - @cert_coverage_points << CertCoveragePoint.new(cert_coverage_point) - end - @cert_coverage_points - end - - class CertCoveragePoint - # @param data [Hash] Data from YAML file - def initialize(data) - raise ArgumentError, "Need Hash but was passed a #{data.class}" unless data.is_a?(Hash) - - @data = data - end - - # @return [String] Name of the coverage point - def name = @data["name"] - - # @return [String] Description of coverage point (could be multiple lines) - def description = @data["description"] - - # @return [Array] Sorted list of certification point links (cross references) - def cert_links - return @cert_links unless @cert_links.nil? - - @cert_links = [] - @data["links"]&.each do |link_data| - @cert_links << CertLink.new(link_data) - end - @cert_links.sort! - end - end - - class CertLink - # @return [String] YAML data string - attr_reader :data - - # @param data [String] The cross reference link provided in the YAML - def initialize(data) - raise ArgumentError, "Need String but was passed a #{data.class}" unless data.is_a?(String) - @data = data - end - - # @return [String] Asciidoc to create desired link. - def to_adoc - "<<#{data},#{data}>>" - end - - # For sorting ... - def <=>(other) - if other.is_a?(CertLink) - @data <=> other.data - else - raise ArgumentError, "CertLink is not comparable to a #{other.class.name}" - end - end - end end diff --git a/schemas/csr_schema.json b/schemas/csr_schema.json index e32cb20db..16c8c5084 100644 --- a/schemas/csr_schema.json +++ b/schemas/csr_schema.json @@ -105,6 +105,12 @@ } ], "description": "Extension(s) that affect the definition of the field beyond the extension (or base) the field is originally defined in" + }, + "cert-coverage-points": { + "$ref": "schema_defs.json#/$defs/cert-coverage-points" + }, + "cert-test-procedures": { + "$ref": "schema_defs.json#/$defs/cert-test-procedures" } }, @@ -269,6 +275,12 @@ "$source": { "description": "Path to the source file this definition came from; used by downstream tooling -- not expected to be in handwritten files", "type": "string" + }, + "cert-coverage-points": { + "$ref": "schema_defs.json#/$defs/cert-coverage-points" + }, + "cert-test-procedures": { + "$ref": "schema_defs.json#/$defs/cert-test-procedures" } }, "additionalProperties": false, diff --git a/schemas/ext_schema.json b/schemas/ext_schema.json index 40d6d485c..1a3709324 100644 --- a/schemas/ext_schema.json +++ b/schemas/ext_schema.json @@ -290,6 +290,12 @@ "$source": { "type": "string", "description": "Source file where this extension was defined" + }, + "cert-coverage-points": { + "$ref": "schema_defs.json#/$defs/cert-coverage-points" + }, + "cert-test-procedures": { + "$ref": "schema_defs.json#/$defs/cert-test-procedures" } }, "additionalProperties": false diff --git a/schemas/inst_schema.json b/schemas/inst_schema.json index ae4a2adc7..60bfa42b0 100644 --- a/schemas/inst_schema.json +++ b/schemas/inst_schema.json @@ -149,19 +149,6 @@ }, "additionalProperties": false }, - "ctp-steps": { - "type": "array", - "items": { - "type": "object", - "required": ["description"], - "additionalProperties": false, - "properties": { - "description": { - "type": "string" - } - } - } - }, "inst_data": { "type": "object", "required": [ @@ -271,59 +258,10 @@ "description": "Functional description of the instruction using Sail" }, "cert-coverage-points": { - "description": "Architecturally visible behaviors requiring validation by certification tests", - "type": "array", - "required": ["name", "xref", "description"], - "additionalProperties": false, - "properties": { - "name": { - "type": "string" - }, - "xref": { - "type": "string", - "description": "Cross reference to ISA manual or IDL" - }, - "description": { - "type": "string" - } - } + "$ref": "schema_defs.json#/$defs/cert-coverage-points" }, - "cert-test-plan": { - "description": "How certification coverage points will be validated", - "type": "array", - "required": ["name", "description", "coverage-points"], - "additionalProperties": false, - "properties": { - "name": { - "type": "string" - }, - "description": { - "type": "string" - }, - "coverage-points": { - "type": "array", - "description": "List of certification coverage points to be validated", - "items": { - "type": "string" - } - }, - "setup": { - "$ref": "#/$defs/ctp-steps", - "description": "List of steps to setup test" - }, - "execution": { - "$ref": "#/$defs/ctp-steps", - "description": "List of steps to execute test after setup" - }, - "validation": { - "$ref": "#/$defs/ctp-steps", - "description": "List of steps to determine test pass/fail status" - }, - "teardown": { - "$ref": "#/$defs/ctp-steps", - "description": "List of steps to get ready for the next test" - } - } + "cert-test-procedures": { + "$ref": "schema_defs.json#/$defs/cert-test-procedures" }, "assembly": { "type": "string", diff --git a/schemas/profile_schema.json b/schemas/profile_schema.json index a5460e27f..129653bb4 100644 --- a/schemas/profile_schema.json +++ b/schemas/profile_schema.json @@ -19,6 +19,12 @@ "base": { "type": "integer", "description": "32 for RV32I or 64 for RV64I" + }, + "cert-coverage-points": { + "$ref": "schema_defs.json#/$defs/cert-coverage-points" + }, + "cert-test-procedures": { + "$ref": "schema_defs.json#/$defs/cert-test-procedures" } } } diff --git a/schemas/schema_defs.json b/schemas/schema_defs.json index a5ee77adc..251a03c2b 100644 --- a/schemas/schema_defs.json +++ b/schemas/schema_defs.json @@ -267,6 +267,65 @@ "$ref": "#/$defs/when_condition" } } + }, + "cert-coverage-points": { + "description": "Architecturally visible behaviors requiring validation by certification tests", + "type": "array", + "required": ["name", "link", "description"], + "properties": { + "name": { + "type": "string" + }, + "link": { + "type": "string", + "description": "Cross reference to UDB documentation, ISA manual, or IDL code" + }, + "description": { + "type": "string" + } + }, + "additionalProperties": false + }, + "cert-test-procedures": { + "description": "Procedure test must follow to test certification coverage points", + "type": "array", + "required": ["name", "description", "coverage-points"], + "properties": { + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "coverage-points": { + "type": "array", + "description": "List of certification coverage points to be validated", + "items": { + "type": "string" + } + }, + "steps": { + "description": "List of steps to be followed in order by test", + "type": "array", + "items": { + "type": "object", + "required": ["name", "description"], + "properties": { + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "note": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } } } } From 3a74e69d3ebdf5661a2159aae787cd3d3e042bbb Mon Sep 17 00:00:00 2001 From: James Ball Date: Thu, 23 Jan 2025 04:48:50 +0000 Subject: [PATCH 45/61] Only add CTP content to CTP docs (was going in CRDs and Profiles too). --- .../templates/inst_appendix.adoc.erb | 4 ++++ backends/proc_ctp/templates/proc_ctp.adoc.erb | 6 ++--- lib/portfolio_design.rb | 17 +++++++++----- lib/proc_cert_design.rb | 23 +++++++++++-------- 4 files changed, 32 insertions(+), 18 deletions(-) diff --git a/backends/portfolio/templates/inst_appendix.adoc.erb b/backends/portfolio/templates/inst_appendix.adoc.erb index 8145ec0be..47c721d27 100644 --- a/backends/portfolio/templates/inst_appendix.adoc.erb +++ b/backends/portfolio/templates/inst_appendix.adoc.erb @@ -125,6 +125,8 @@ This instruction may result in the following synchronous exceptions: <% end -%> +<% if defined?(gen_ctp_content) && gen_ctp_content -%> + <% unless inst.cert_coverage_points.empty? -%> ==== Certification Coverage Points @@ -166,4 +168,6 @@ a| <% tp.cert_steps.each do |step| -%> <% end # unless no test plans -%> +<% end # if gen_ctp_content -%> + <% end # each in_scope instruction -%> diff --git a/backends/proc_ctp/templates/proc_ctp.adoc.erb b/backends/proc_ctp/templates/proc_ctp.adoc.erb index d9fea1e5e..b384ff726 100644 --- a/backends/proc_ctp/templates/proc_ctp.adoc.erb +++ b/backends/proc_ctp/templates/proc_ctp.adoc.erb @@ -86,9 +86,9 @@ The specific rules for updating the version number for a CTP are as follows: |=== // Appendices -<%= portfolio_design.include_erb("ext_appendix.adoc.erb") %> -<%= portfolio_design.include_erb("inst_appendix.adoc.erb") %> -<%= portfolio_design.include_erb("csr_appendix.adoc.erb") %> +<%= portfolio_design.include_erb("ext_appendix.adoc.erb", { "gen_ctp_content" => true }) %> +<%= portfolio_design.include_erb("inst_appendix.adoc.erb", { "gen_ctp_content" => true }) %> +<%= portfolio_design.include_erb("csr_appendix.adoc.erb", { "gen_ctp_content" => true }) %> <%= portfolio_design.include_erb("idl_func_appendix.adoc.erb") %> <<< diff --git a/lib/portfolio_design.rb b/lib/portfolio_design.rb index c21076e06..fc11284ef 100644 --- a/lib/portfolio_design.rb +++ b/lib/portfolio_design.rb @@ -267,11 +267,14 @@ def all_in_scope_exts_without_param(param) = @portfolio_grp.all_in_scope_exts_wi # EXTRA METHODS # ################# - #@ return [Hash] Hash of objects available to ERB templates and + # @param extra_inputs [Hash] Any extra inputs to be passed to ERB template. + # @return [Hash] Hash of objects available to ERB templates and # ERB fragments included in the main ERB template. # Put this in a method so it can be easily overridden by subclasses. - def erb_env - { + def erb_env(extra_inputs = {}) + raise ArgumentError, "extra_inputs must be an Hash but is a #{extra_inputs.class}" unless extra_inputs.is_a?(Hash) + + h = { arch: arch, design: self, portfolio_design: self, @@ -280,6 +283,8 @@ def erb_env portfolio_kind: @portfolio_kind, portfolios: @portfolio_grp.portfolios } + + h.merge!(extra_inputs) end # Called from tasks.rake file to add standard set of objects available to ERB templates. @@ -294,11 +299,11 @@ def init_erb_binding(erb_binding) # Include a partial ERB template into a full ERB template. # # @param template_path [String] Name of template file located in backends/portfolio/templates + # @param extra_inputs [Hash] Any extra inputs to be passed to ERB template. # @return [String] Result of ERB evaluation of the template file - def include_erb(template_name) + def include_erb(template_name, extra_inputs = {}) template_pname = "portfolio/templates/#{template_name}" - puts "UPDATE: #{portfolio_design_type} processing ERB partial template '#{template_pname}'" - partial(template_pname, erb_env) + partial(template_pname, erb_env(extra_inputs)) end end diff --git a/lib/proc_cert_design.rb b/lib/proc_cert_design.rb index 963560b8f..2396b7130 100644 --- a/lib/proc_cert_design.rb +++ b/lib/proc_cert_design.rb @@ -31,15 +31,19 @@ def initialize(name, arch, portfolio_design_type, proc_cert_model, proc_cert_cla # @return [String] A string representation of the object. def inspect = "ProcCertDesign##{name}" + # @param extra_inputs [Array] Any extra inputs to be passed to ERB template. # @return [Hash] Hash of objects to be used in ERB templates # Add certificate-specific objects to the parent hash. - def erb_env - parent_hash = super - parent_hash[:proc_cert_design] = self - parent_hash[:proc_cert_model] = proc_cert_model - parent_hash[:proc_cert_class] = proc_cert_class + def erb_env(*extra_inputs) + raise ArgumentError, "extra_inputs must be an Array but is a #{extra_inputs.class}" unless extra_inputs.is_a?(Array) - parent_hash + h = super # Call parent method with whatever args I got + + h[:proc_cert_design] = self + h[:proc_cert_model] = proc_cert_model + h[:proc_cert_class] = proc_cert_class + + h end # Include a partial ERB template into a full ERB template. Can be either in @@ -47,8 +51,9 @@ def erb_env # # @param template_path [String] Name of template file located in backends/portfolio/templates # or in backends/proc_cert/templates + # @param extra_inputs [Hash] Any extra inputs to be passed to ERB template. # @return [String] Result of ERB evaluation of the template file - def include_erb(template_name) + def include_erb(template_name, extra_inputs = {}) proc_cert_template_pname = "proc_cert/templates/#{template_name}" proc_cert_template_path = Pathname.new($root / "backends" / proc_cert_template_pname) @@ -59,10 +64,10 @@ def include_erb(template_name) raise "Both #{proc_cert_template_pname} and #{portfolio_template_pname} exist. Need unique names." elsif proc_cert_template_path.exist? puts "UPDATE: #{portfolio_design_type} processing ERB partial template '#{proc_cert_template_pname}'" - partial(proc_cert_template_pname, erb_env) + partial(proc_cert_template_pname, erb_env(extra_inputs)) elsif portfolio_template_path.exist? puts "UPDATE: #{portfolio_design_type} processing ERB partial template '#{portfolio_template_pname}'" - partial(portfolio_template_pname, erb_env) + partial(portfolio_template_pname, erb_env(extra_inputs)) else raise "Can't find file #{template_name} in either #{proc_cert_template_pname} or #{portfolio_template_pname}." end From 57ace39470c5837e7e49bce11d62512b1b6062b1 Mon Sep 17 00:00:00 2001 From: James Ball Date: Thu, 23 Jan 2025 05:48:40 +0000 Subject: [PATCH 46/61] Switch display of test procedures from table to list. --- .../portfolio/templates/ext_appendix.adoc.erb | 2 -- .../templates/inst_appendix.adoc.erb | 23 +++++++++---------- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/backends/portfolio/templates/ext_appendix.adoc.erb b/backends/portfolio/templates/ext_appendix.adoc.erb index 3f21041d1..08a0f7ff7 100644 --- a/backends/portfolio/templates/ext_appendix.adoc.erb +++ b/backends/portfolio/templates/ext_appendix.adoc.erb @@ -7,9 +7,7 @@ <%= anchor_for_udb_doc_ext(ext_req.name) %> === Extension <%= ext_req.name %> + *Long Name*: <%= ext.long_name %> + - *Version Requirement*: <%= ext_req.requirement_specs.map(&:to_s).join(", ") %> + - <% if portfolios.size > 1 -%> .<%= ext.name %> Extension Presence |=== diff --git a/backends/portfolio/templates/inst_appendix.adoc.erb b/backends/portfolio/templates/inst_appendix.adoc.erb index 47c721d27..a11eb46cb 100644 --- a/backends/portfolio/templates/inst_appendix.adoc.erb +++ b/backends/portfolio/templates/inst_appendix.adoc.erb @@ -128,7 +128,7 @@ This instruction may result in the following synchronous exceptions: <% if defined?(gen_ctp_content) && gen_ctp_content -%> <% unless inst.cert_coverage_points.empty? -%> -==== Certification Coverage Points +==== Coverage Points [%autowidth] |=== @@ -146,25 +146,24 @@ a| <% cp.cert_links.each do |link| -%> <% end # unless no coverage points -%> <% unless inst.cert_test_procedures.empty? -%> -==== Certification Test Procedures - -[%autowidth] -|=== -| Name | Description | Coverage Points | Steps +==== Test Procedures <% inst.cert_test_procedures.each do |tp| -%> -| <%= tp.name %> -| <%= tp.description %> -a| <% tp.cert_coverage_points.each do |cp| -%> +*Name*: <%= tp.name %> + +*Description*: <%= tp.description %> + + +.*Coverage Points*: +<% tp.cert_coverage_points.each do |cp| -%> * <%= cp.name %> <% end # each cp -%> -a| <% tp.cert_steps.each do |step| -%> + +.*Steps*: +<% tp.cert_steps.each do |step| -%> . <%= step.name %> * <%= step.description %> -<%= step.note.nil? ? "" : "* " + step.note %> +<%= step.note.nil? ? "" : "[NOTE]\n" + step.note %> <% end # each step -%> <% end # each test plan -%> -|=== <% end # unless no test plans -%> From 4c2c05bb4ebb6ee66f91c9d8ce2b33daf5a3f4b2 Mon Sep 17 00:00:00 2001 From: James Ball Date: Thu, 23 Jan 2025 06:11:58 +0000 Subject: [PATCH 47/61] Fixed regress bug due to incorrect constructor call to ExtensionVersion. --- lib/arch_obj_models/database_obj.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/arch_obj_models/database_obj.rb b/lib/arch_obj_models/database_obj.rb index 642268001..782adadf6 100644 --- a/lib/arch_obj_models/database_obj.rb +++ b/lib/arch_obj_models/database_obj.rb @@ -504,23 +504,23 @@ def flat_op def flat_versions case @hsh when String - [ExtensionRequirement.new(@hsh, arch: @arch)] + [ExtensionRequirement.new(@hsh, @arch)] when Hash if @hsh.key?("name") if @hsh.key?("version").nil? - [ExtensionRequirement.new(@hsh["name"], arch: @arch)] + [ExtensionRequirement.new(@hsh["name"], @arch)] else - [ExtensionRequirement.new(@hsh["name"], @hsh["version"], arch: @arch)] + [ExtensionRequirement.new(@hsh["name"], @hsh["version"], @arch)] end else @hsh[@hsh.keys.first].map do |r| if r.is_a?(String) - ExtensionRequirement.new(r, arch: @arch) + ExtensionRequirement.new(r, @arch) else if r.key?("version").nil? - ExtensionRequirement.new(r["name"], arch: @arch) + ExtensionRequirement.new(r["name"], @arch) else - ExtensionRequirement.new(r["name"], r["version"], arch: @arch) + ExtensionRequirement.new(r["name"], r["version"], @arch) end end end From 9f1a0bf4d4b3663d1df98cefcc9d70180bc9f2b2 Mon Sep 17 00:00:00 2001 From: James Ball Date: Thu, 23 Jan 2025 07:09:29 +0000 Subject: [PATCH 48/61] Add CTP info to extensions, CSRs, and CSR fields --- arch/csr/misa.yaml | 40 +++++++++++++++++ arch/ext/Xmock.yaml | 25 +++++++++++ .../templates/coverage_points.adoc.erb | 17 ++++++++ .../portfolio/templates/csr_appendix.adoc.erb | 14 +++++- .../portfolio/templates/ext_appendix.adoc.erb | 6 +++ .../templates/inst_appendix.adoc.erb | 43 +------------------ .../templates/test_procedures.adoc.erb | 21 +++++++++ backends/proc_ctp/tasks.rake | 2 + 8 files changed, 125 insertions(+), 43 deletions(-) create mode 100644 backends/portfolio/templates/coverage_points.adoc.erb create mode 100644 backends/portfolio/templates/test_procedures.adoc.erb diff --git a/arch/csr/misa.yaml b/arch/csr/misa.yaml index 5b3674d80..bc78ca454 100644 --- a/arch/csr/misa.yaml +++ b/arch/csr/misa.yaml @@ -137,6 +137,29 @@ fields: return (implemented?(ExtensionName::M) && MUTABLE_MISA_M) ? CsrFieldType::RW : CsrFieldType::RO; definedBy: M reset_value: 1 + cert-coverage-points: + - name: Disabling `misa.M` bit + description: What happens when you turn off `misa.M` + links: + - manual:csr:misa:disabling-extension + cert-test-procedures: + - name: Testing M + description: Execute with M on/off + coverage-points: + - Disabling `misa.M` bit + steps: + - name: on + description: Turn on `misa.M` + - name: execute + description: Execute every in-scope multiply extension instruction + - name: check + description: Check that every multiply extension instruction works as normal + - name: off + description: Turn off `misa.M` + - name: execute + description: Execute every in-scope multiply extension instruction + - name: check + description: Check that every multiply extension instruction throws illegal instruction exception S: location: 19 description: | @@ -184,3 +207,20 @@ sw_read(): | (CSR[misa].C << 2) | (CSR[misa].B << 1) | CSR[misa].A); +cert-coverage-points: + - name: Disabling `misa` bits + description: What happens when you turn off bits + links: + - manual:csr:misa:disabling-extension +cert-test-procedures: + - name: Testing bits + description: Turn on/off each bit and see what happens + coverage-points: + - Disabling `misa` bits + steps: + - name: setup + description: Turn on all bits + - name: loop + description: Turn off each present bit invidually and try affected behaviors + - name: check + description: fail unless turning off bit disables extension as expected diff --git a/arch/ext/Xmock.yaml b/arch/ext/Xmock.yaml index db1c0e0b7..099ffec0f 100644 --- a/arch/ext/Xmock.yaml +++ b/arch/ext/Xmock.yaml @@ -154,3 +154,28 @@ params: type: boolean maxItems: 8 minItems: 8 +cert-coverage-points: + - name: Mock coverage point 1 + description: Let's have fun with the `Xmock` extension + links: + - manual:inst:mul:encoding + - udb:doc:inst:mock + - name: Mock coverage point 2 + description: And some more fun! + links: + - manual:csr:misa:disabling-extension +cert-test-procedures: + - name: My first procedure + description: Verify that when it rains in Spain, it rains mainly on the plains! + coverage-points: + - Mock coverage point 2 + - Mock coverage point 1 + steps: + - name: wait for rain + description: First we need some rain + note: This is getting silly. Very unprofessional. + - name: measure rainfall + description: Get a bunch of buckets around Spain + note: Yup, pretty silly + - name: compare rainful + description: fail unless more rain on plains than other regions diff --git a/backends/portfolio/templates/coverage_points.adoc.erb b/backends/portfolio/templates/coverage_points.adoc.erb new file mode 100644 index 000000000..e24898efb --- /dev/null +++ b/backends/portfolio/templates/coverage_points.adoc.erb @@ -0,0 +1,17 @@ +<% unless db_obj.cert_coverage_points.empty? -%> +==== Coverage Points + +[%autowidth] +|=== +| Name | Description | Links + +<% db_obj.cert_coverage_points.each do |cp| -%> +| <%= cp.name %> +| <%= cp.description %> +a| <% cp.cert_links.each do |link| -%> +* <%= link.to_adoc %> +<% end # each link -%> +<% end # each coverage point -%> +|=== + +<% end # unless no coverage points -%> diff --git a/backends/portfolio/templates/csr_appendix.adoc.erb b/backends/portfolio/templates/csr_appendix.adoc.erb index 82fb02767..cdc6e4bef 100644 --- a/backends/portfolio/templates/csr_appendix.adoc.erb +++ b/backends/portfolio/templates/csr_appendix.adoc.erb @@ -130,10 +130,15 @@ Type:: Reset value:: <%= field.reset_value_pretty(design) %> +<% if defined?(gen_ctp_content) && gen_ctp_content -%> +<%= portfolio_design.include_erb("coverage_points.adoc.erb", { "db_obj" => field }) %> +<%= portfolio_design.include_erb("test_procedures.adoc.erb", { "db_obj" => field }) %> +<% end # if gen_ctp_content -%> + **** -<% end -%> -<% end -%> +<% end # Each field -%> +<% end # if no fields -%> <% if csr.implemented_fields(design).map(&:has_custom_sw_write?).any? -%> ==== Software write @@ -166,4 +171,9 @@ This CSR may return a value that is different from what is stored in hardware. ---- <% end -%> +<% if defined?(gen_ctp_content) && gen_ctp_content -%> +<%= portfolio_design.include_erb("coverage_points.adoc.erb", { "db_obj" => csr }) %> +<%= portfolio_design.include_erb("test_procedures.adoc.erb", { "db_obj" => csr }) %> +<% end # if gen_ctp_content -%> + <% end # do in_scope_csrs -%> diff --git a/backends/portfolio/templates/ext_appendix.adoc.erb b/backends/portfolio/templates/ext_appendix.adoc.erb index 08a0f7ff7..ddd05106c 100644 --- a/backends/portfolio/templates/ext_appendix.adoc.erb +++ b/backends/portfolio/templates/ext_appendix.adoc.erb @@ -121,4 +121,10 @@ This extension has the following parameters (AKA implementation options): -- <% end # do param -%> <% end # unless table -%> + +<% if defined?(gen_ctp_content) && gen_ctp_content -%> +<%= portfolio_design.include_erb("coverage_points.adoc.erb", { "db_obj" => ext }) %> +<%= portfolio_design.include_erb("test_procedures.adoc.erb", { "db_obj" => ext }) %> +<% end # if gen_ctp_content -%> + <% end # do ext_req -%> diff --git a/backends/portfolio/templates/inst_appendix.adoc.erb b/backends/portfolio/templates/inst_appendix.adoc.erb index a11eb46cb..751c2b129 100644 --- a/backends/portfolio/templates/inst_appendix.adoc.erb +++ b/backends/portfolio/templates/inst_appendix.adoc.erb @@ -126,47 +126,8 @@ This instruction may result in the following synchronous exceptions: <% end -%> <% if defined?(gen_ctp_content) && gen_ctp_content -%> - -<% unless inst.cert_coverage_points.empty? -%> -==== Coverage Points - -[%autowidth] -|=== -| Name | Description | Links - -<% inst.cert_coverage_points.each do |cp| -%> -| <%= cp.name %> -| <%= cp.description %> -a| <% cp.cert_links.each do |link| -%> -* <%= link.to_adoc %> -<% end # each link -%> -<% end # each coverage point -%> -|=== - -<% end # unless no coverage points -%> - -<% unless inst.cert_test_procedures.empty? -%> -==== Test Procedures - -<% inst.cert_test_procedures.each do |tp| -%> -*Name*: <%= tp.name %> + -*Description*: <%= tp.description %> + - -.*Coverage Points*: -<% tp.cert_coverage_points.each do |cp| -%> -* <%= cp.name %> -<% end # each cp -%> - -.*Steps*: -<% tp.cert_steps.each do |step| -%> -. <%= step.name %> -* <%= step.description %> -<%= step.note.nil? ? "" : "[NOTE]\n" + step.note %> -<% end # each step -%> -<% end # each test plan -%> - -<% end # unless no test plans -%> - +<%= portfolio_design.include_erb("coverage_points.adoc.erb", { "db_obj" => inst }) %> +<%= portfolio_design.include_erb("test_procedures.adoc.erb", { "db_obj" => inst }) %> <% end # if gen_ctp_content -%> <% end # each in_scope instruction -%> diff --git a/backends/portfolio/templates/test_procedures.adoc.erb b/backends/portfolio/templates/test_procedures.adoc.erb new file mode 100644 index 000000000..7d9b68523 --- /dev/null +++ b/backends/portfolio/templates/test_procedures.adoc.erb @@ -0,0 +1,21 @@ +<% unless db_obj.cert_test_procedures.empty? -%> +==== Test Procedures + +<% db_obj.cert_test_procedures.each do |tp| -%> +*Name*: <%= tp.name %> + +*Description*: <%= tp.description %> + + +.*Coverage Points*: +<% tp.cert_coverage_points.each do |cp| -%> +* <%= cp.name %> +<% end # each cp -%> + +.*Steps*: +<% tp.cert_steps.each do |step| -%> +. <%= step.name %> +* <%= step.description %> +<%= step.note.nil? ? "" : "[NOTE]\n" + step.note %> +<% end # each step -%> +<% end # each test plan -%> + +<% end # unless no test plans -%> diff --git a/backends/proc_ctp/tasks.rake b/backends/proc_ctp/tasks.rake index 5d0f0245f..c053f1a23 100644 --- a/backends/proc_ctp/tasks.rake +++ b/backends/proc_ctp/tasks.rake @@ -27,6 +27,8 @@ Dir.glob("#{$root}/arch/proc_cert_model/*.yaml") do |f| "#{$root}/backends/portfolio/templates/inst_appendix.adoc.erb", "#{$root}/backends/portfolio/templates/csr_appendix.adoc.erb", "#{$root}/backends/portfolio/templates/beginning.adoc.erb", + "#{$root}/backends/portfolio/templates/coverage_points.adoc.erb", + "#{$root}/backends/portfolio/templates/test_procedures.adoc.erb", "#{$root}/backends/proc_cert/templates/typographic.adoc.erb", "#{$root}/backends/proc_cert/templates/rev_history.adoc.erb", "#{PROC_CTP_DOC_DIR}/templates/proc_ctp.adoc.erb" From d9a77501c849cb9f1221b0f23693968e255e341c Mon Sep 17 00:00:00 2001 From: James Ball Date: Sat, 25 Jan 2025 02:06:29 +0000 Subject: [PATCH 49/61] Fix array in schema --- schemas/schema_defs.json | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/schemas/schema_defs.json b/schemas/schema_defs.json index 251a03c2b..d69c7ff0c 100644 --- a/schemas/schema_defs.json +++ b/schemas/schema_defs.json @@ -271,14 +271,20 @@ "cert-coverage-points": { "description": "Architecturally visible behaviors requiring validation by certification tests", "type": "array", - "required": ["name", "link", "description"], + "required": ["id", "name", "links", "description"], "properties": { + "id": { + "type": "string" + }, "name": { "type": "string" }, - "link": { - "type": "string", - "description": "Cross reference to UDB documentation, ISA manual, or IDL code" + "links": { + "description": "Cross reference to UDB documentation, ISA manual, or IDL code", + "type": "array", + "items": { + "type": "string" + } }, "description": { "type": "string" @@ -289,8 +295,11 @@ "cert-test-procedures": { "description": "Procedure test must follow to test certification coverage points", "type": "array", - "required": ["name", "description", "coverage-points"], + "required": ["id", "name", "description", "coverage-points"], "properties": { + "id": { + "type": "string" + }, "name": { "type": "string" }, @@ -299,7 +308,7 @@ }, "coverage-points": { "type": "array", - "description": "List of certification coverage points to be validated", + "description": "List of certification coverage point IDs to be validated", "items": { "type": "string" } From fbfb4b2ac698f677e3fe599f38bd16cc9b460ce5 Mon Sep 17 00:00:00 2001 From: James Ball Date: Sat, 25 Jan 2025 02:10:27 +0000 Subject: [PATCH 50/61] Improve formating of CTP and add mandatory ID --- arch/csr/misa.yaml | 24 +++--- arch/ext/Xmock.yaml | 20 +++-- arch/inst/M/mul.yaml | 32 ++++---- arch/inst/mock.yaml | 19 +++-- backends/portfolio/tasks.rake | 15 +++- .../templates/coverage_points.adoc.erb | 7 +- .../portfolio/templates/csr_appendix.adoc.erb | 23 ++++-- .../templates/test_procedures.adoc.erb | 25 +++++-- backends/proc_ctp/templates/proc_ctp.adoc.erb | 39 +++++++--- lib/arch_obj_models/database_obj.rb | 73 +++++++++++++------ 10 files changed, 177 insertions(+), 100 deletions(-) diff --git a/arch/csr/misa.yaml b/arch/csr/misa.yaml index bc78ca454..e3dd0b676 100644 --- a/arch/csr/misa.yaml +++ b/arch/csr/misa.yaml @@ -138,15 +138,15 @@ fields: definedBy: M reset_value: 1 cert-coverage-points: - - name: Disabling `misa.M` bit + - id: csr.misa.M.disabled + name: Disabling `misa.M` bit description: What happens when you turn off `misa.M` - links: - - manual:csr:misa:disabling-extension + # XXX links: [manual:csr:misa:disabling-extension] cert-test-procedures: - - name: Testing M + - id: csr.misa.M.muldiv_with_M_on&off + name: Testing M description: Execute with M on/off - coverage-points: - - Disabling `misa.M` bit + coverage-points: [csr.misa.M.disabled] steps: - name: on description: Turn on `misa.M` @@ -208,15 +208,15 @@ sw_read(): | (CSR[misa].B << 1) | CSR[misa].A); cert-coverage-points: - - name: Disabling `misa` bits + - id: csr.misa.disabling_bits + name: Disabling `misa` bits description: What happens when you turn off bits - links: - - manual:csr:misa:disabling-extension + # XXX links: [manual:csr:misa:disabling-extension] cert-test-procedures: - - name: Testing bits + - id: csr.misa.off&on + name: Testing bits description: Turn on/off each bit and see what happens - coverage-points: - - Disabling `misa` bits + coverage-points: [csr.misa.disabling_bits] steps: - name: setup description: Turn on all bits diff --git a/arch/ext/Xmock.yaml b/arch/ext/Xmock.yaml index 099ffec0f..19bf11db9 100644 --- a/arch/ext/Xmock.yaml +++ b/arch/ext/Xmock.yaml @@ -155,21 +155,19 @@ params: maxItems: 8 minItems: 8 cert-coverage-points: - - name: Mock coverage point 1 + - id: ext.Xmock.cov1 + name: Mock coverage point 1 description: Let's have fun with the `Xmock` extension - links: - - manual:inst:mul:encoding - - udb:doc:inst:mock - - name: Mock coverage point 2 + # XXX links: [manual:inst:mul:encoding, udb:doc:inst:mock] + - id: ext.Xmock.cov2 + name: Mock coverage point 2 description: And some more fun! - links: - - manual:csr:misa:disabling-extension + # XXX links: [manual:csr:misa:disabling-extension] cert-test-procedures: - - name: My first procedure + - id: ext.Xmock.my_first + name: My first procedure description: Verify that when it rains in Spain, it rains mainly on the plains! - coverage-points: - - Mock coverage point 2 - - Mock coverage point 1 + coverage-points: [ext.Xmock.cov1, ext.Xmock.cov2] steps: - name: wait for rain description: First we need some rain diff --git a/arch/inst/M/mul.yaml b/arch/inst/M/mul.yaml index b982b8d74..fa27f42a2 100644 --- a/arch/inst/M/mul.yaml +++ b/arch/inst/M/mul.yaml @@ -71,33 +71,31 @@ sail(): | } cert-coverage-points: - - name: Encoding - description: | - Encoding of `mul` instruction - links: - - manual:inst:mul:encoding - - name: Basic operation - description: | - Basic operation of `mul` instruction - links: - - manual:inst:mul:operation - - name: Illegal instruction exception when misa.M is 0 + - id: inst.mul.encoding + name: Encoding + description: Encoding of `mul` instruction + # XXX links: [manual:inst:mul:encoding] + - id: inst.mul.basic_op + name: Basic operation + description: Basic operation of `mul` instruction + # XXX links: [manual:inst:mul:operation] + - id: inst.mul.ill_exc_misa_M_disabled + name: Illegal instruction exception when misa.M is 0 description: | An illegal instruction exception is raised when the instruction is executed and `misa.M` is 0. - links: - - manual:csr:misa:disabling-extension + # XXX links: [manual:csr:misa:disabling-extension] cert-test-procedures: - - name: Encoding + - id: inst.mul.encoding + name: Encoding description: Verify the encoding of the `mul` instruction - coverage-points: - - Encoding + coverage-points: [inst.mul.encoding] steps: - name: setup description: Load a variety of known values into rs1 & rs2 with a variety of rs1/rs2/rd values. - name: execution - description: Execute the `mock` instruction + description: Execute the `mul` instruction - name: validation description: Check each result in rd - name: teardown diff --git a/arch/inst/mock.yaml b/arch/inst/mock.yaml index ea6c1acb2..32282a591 100644 --- a/arch/inst/mock.yaml +++ b/arch/inst/mock.yaml @@ -62,13 +62,12 @@ sail(): | } cert-coverage-points: - - name: Encoding and basic operation - description: | - Encoding and basic operation for `mock` instruction - links: - - manual:inst:mul:encoding - - udb:doc:inst:mock - - name: Illegal instruction exception when misa.M is 0 + - id: inst.mock.encoding&basic_op + name: Encoding and basic operation + description: Encoding and basic operation for `mock` instruction + # XXX links: [manual:inst:mul:encoding, udb:doc:inst:mock] + - id: inst.mock.ill_exc_misa_M_disabled + name: Illegal instruction exception when misa.M is 0 description: | An illegal instruction exception is raised when the instruction is executed and `misa.M` is 0. @@ -77,10 +76,10 @@ cert-coverage-points: # - idl:code:inst:mock:illegal-inst-exc-misa-disabled cert-test-procedures: - - name: Encoding + - id: inst.mock.enc_and_basic + name: Encoding description: Verify the encoding and basic operation of the `mock` instruction - coverage-points: - - Encoding and basic operation + coverage-points: [inst.mock.encoding&basic_op] steps: - name: setup description: Load a variety of known values into rs1 & rs2 with a variety of rs1/rs2/rd values. diff --git a/backends/portfolio/tasks.rake b/backends/portfolio/tasks.rake index ebd0c9012..05a7bdecd 100644 --- a/backends/portfolio/tasks.rake +++ b/backends/portfolio/tasks.rake @@ -64,11 +64,11 @@ def pf_adoc2pdf(adoc_file, target_pname) # Write out command used to convert adoc to PDF to allow running this # manually during development. - run_pname = File.dirname(adoc_file) + "/run.sh" + run_pname = File.dirname(adoc_file) + "/adoc2pdf.sh" sh "rm -f #{run_pname}" sh "echo '#!/bin/bash' >#{run_pname}" sh "echo >>#{run_pname}" - sh "echo #{cmd} >>#{run_pname}" + sh "echo bundle exec #{cmd} >>#{run_pname}" sh "chmod +x #{run_pname}" # Now run the actual command. @@ -96,6 +96,17 @@ def pf_adoc2html(adoc_file, target_pname) ].join(" ") puts "UPDATE: bundle exec #{cmd}" + + # Write out command used to convert adoc to HTML to allow running this + # manually during development. + run_pname = File.dirname(adoc_file) + "/adoc2html.sh" + sh "rm -f #{run_pname}" + sh "echo '#!/bin/bash' >#{run_pname}" + sh "echo >>#{run_pname}" + sh "echo bundle exec #{cmd} >>#{run_pname}" + sh "chmod +x #{run_pname}" + + # Now run the actual command. sh cmd puts "UPDATE: Generated HTML in #{target_pname}" diff --git a/backends/portfolio/templates/coverage_points.adoc.erb b/backends/portfolio/templates/coverage_points.adoc.erb index e24898efb..d91c6904f 100644 --- a/backends/portfolio/templates/coverage_points.adoc.erb +++ b/backends/portfolio/templates/coverage_points.adoc.erb @@ -1,11 +1,16 @@ <% unless db_obj.cert_coverage_points.empty? -%> +<% if defined?(use_description_list) && use_description_list -%> +Coverage Points: :: +<% else -%> ==== Coverage Points +<% end -%> [%autowidth] |=== -| Name | Description | Links +| ID | Name | Description | Links <% db_obj.cert_coverage_points.each do |cp| -%> +| <%= cp.id %> | <%= cp.name %> | <%= cp.description %> a| <% cp.cert_links.each do |link| -%> diff --git a/backends/portfolio/templates/csr_appendix.adoc.erb b/backends/portfolio/templates/csr_appendix.adoc.erb index cdc6e4bef..3accf7160 100644 --- a/backends/portfolio/templates/csr_appendix.adoc.erb +++ b/backends/portfolio/templates/csr_appendix.adoc.erb @@ -117,22 +117,33 @@ This CSR has no fields. However, it must still exist (not cause an `Illegal Inst IMPORTANT: <%= field.name %> is only defined in <%= field.base32_only? ? "RV32" : "RV64" %> (`<%= field.base32_only? ? field.location_cond32 : field.location_cond64 %>`) <% end -%> +// These four asterisks are called a "delimited sidebar block" +// (see https://docs.asciidoctor.org/asciidoc/latest/blocks/sidebars/) +// and cause the text until the next four asterisks to have a thin border around it. +// You can also use the [sidebar] attribute on a block. +// +// One limitation of sidebars is the normal section heading syntax +// (e.g., == for a level 1 heading) won't work. Instead, you have to +// do what's described in https://github.com/asciidoctor/asciidoctor/issues/1709 +// but that would be a level 5 heading in this CSR field case which is pretty ugly. + +.<%= csr.name + "." + field.name %> **** -Location:: +Location: :: <%= field.location_pretty(design) %> -Description:: +Description: :: <%= field.description.gsub("\n", " +\n") %> -Type:: +Type: :: <%= field.type_pretty(design.symtab) %> -Reset value:: +Reset value: :: <%= field.reset_value_pretty(design) %> <% if defined?(gen_ctp_content) && gen_ctp_content -%> -<%= portfolio_design.include_erb("coverage_points.adoc.erb", { "db_obj" => field }) %> -<%= portfolio_design.include_erb("test_procedures.adoc.erb", { "db_obj" => field }) %> +<%= portfolio_design.include_erb("coverage_points.adoc.erb", { "db_obj" => field, "use_description_list" => true }) %> +<%= portfolio_design.include_erb("test_procedures.adoc.erb", { "db_obj" => field, "use_description_list" => true }) %> <% end # if gen_ctp_content -%> **** diff --git a/backends/portfolio/templates/test_procedures.adoc.erb b/backends/portfolio/templates/test_procedures.adoc.erb index 7d9b68523..6c9c79950 100644 --- a/backends/portfolio/templates/test_procedures.adoc.erb +++ b/backends/portfolio/templates/test_procedures.adoc.erb @@ -1,21 +1,30 @@ <% unless db_obj.cert_test_procedures.empty? -%> +<% if defined?(use_description_list) && use_description_list -%> +Test Procedures: :: +<% else -%> ==== Test Procedures +<% end -%> <% db_obj.cert_test_procedures.each do |tp| -%> -*Name*: <%= tp.name %> + -*Description*: <%= tp.description %> + +[%autowidth] +.*ID <%= tp.id %>* Test Procedure +|=== -.*Coverage Points*: +| *Name* | <%= tp.name %> +| *Description* | <%= tp.description %> +| *Coverage Points* +a| <% tp.cert_coverage_points.each do |cp| -%> -* <%= cp.name %> +* <%= cp.id %> <% end # each cp -%> - -.*Steps*: +| *Steps* +a| <% tp.cert_steps.each do |step| -%> . <%= step.name %> -* <%= step.description %> +.. <%= step.description %> <%= step.note.nil? ? "" : "[NOTE]\n" + step.note %> <% end # each step -%> -<% end # each test plan -%> +<% end # each test procedure -%> +|=== <% end # unless no test plans -%> diff --git a/backends/proc_ctp/templates/proc_ctp.adoc.erb b/backends/proc_ctp/templates/proc_ctp.adoc.erb index b384ff726..551efb94a 100644 --- a/backends/proc_ctp/templates/proc_ctp.adoc.erb +++ b/backends/proc_ctp/templates/proc_ctp.adoc.erb @@ -23,22 +23,37 @@ <%= proc_cert_class.introduction %> -Links to ISA manual anchors: - -* <> -* <> -* <> -* <> - === What's a CTP? -Certification Test Plans (CTPs) list certification coverage points and how they will be tested. +Certification Test Plans (CTPs) list certification coverage points and how they will be tested via +certification test procedures (step by step descriptions of tests). CTPs are developed by the RVI CSC (Certification Steering Committee) organization in collaboration -with the RVI TSC (Technical Steering Committee) organization who creates RISC-V standards. +with the RVI TSC (Technical Steering Committee) organization who creates RISC-V standards including +the RISC-V Unprivileged and Privileged ISA manuals. + +Each certificate has a corresponding CRD and CTP: + +* The CRD defines the certification requirements an implementation must meet to obtain certification. +* The CTP defines the certification coverage points and certification test procedures followed by the certification + tests that an implementation must pass to obtain certification. + +A CTP is structured into 3 volumes as follows: + +* Volume A is the core CTP documentation including an appendix each for extension, instruction, and CSR + that could be present in a certificate. +* Volume B is the RISC-V Unprivileged ISA manual (uses release specified by the certificate) +* Volume C is the RISC-V Privileged ISA manual (uses release specified by the certificate) + +The certification coverage points reference text in any or all of the following: + +* RISC-V ISA manuals in volumes B and C +* Community-generated documentation located in volume A (https://github.com/riscv-software-src/riscv-unified-db) +* IDL (ISA Description Language) executable psuedo-code descriptions located in volume A -Each certificated offered has a corresponding CRD and CTP. The CRD defines the certification requirements -that an implementation must meet and what behaviors are covered by the certificate. The CTP uses the CRD -to determine the required coverage points. +The RISC-V ISA manuals in Volumes B and C are the preferred reference for certification coverage points since these manuals +represent the ratified RISC-V standards from the TSC. +However, if the information in the ISA manuals isn't sufficiently clear or complete for certification purposes +the content in Volume A is used to fill that gap. === Naming Scheme diff --git a/lib/arch_obj_models/database_obj.rb b/lib/arch_obj_models/database_obj.rb index 782adadf6..eb0f9563e 100644 --- a/lib/arch_obj_models/database_obj.rb +++ b/lib/arch_obj_models/database_obj.rb @@ -58,21 +58,22 @@ def cert_coverage_points @cert_coverage_points end - # @return [Hash] Hash of all coverage points defined by database object + # @return [Hash] Hash with ID as key of all coverage points defined by database object def cert_coverage_point_hash return @cert_coverage_point_hash unless @cert_coverage_point_hash.nil? @cert_coverage_point_hash = {} cert_coverage_points.each do |cp| - @cert_coverage_point_hash[cp.name] = cp + @cert_coverage_point_hash[cp.id] = cp end @cert_coverage_point_hash end + # @param id [String] Unique ID for the coverage point # @return [CertCoveragePoint] - # @return [nil] if there is no certification coverage pointed named +name+ - def cert_coverage_point(name) - cert_coverage_point_hash[name] + # @return [nil] if there is no certification coverage pointed with ID of +id+ + def cert_coverage_point(id) + cert_coverage_point_hash[id] end # @return [Array] @@ -92,15 +93,16 @@ def cert_test_procedure_hash @cert_test_procedure_hash = {} cert_test_procedures.each do |tp| - @cert_test_procedure_hash[tp.name] = tp + @cert_test_procedure_hash[tp.id] = tp end @cert_test_procedure_hash end + # @param id [String] Unique ID for test procedure # @return [CertTestProcedure] - # @return [nil] if there is no certification test plan named +name+ - def cert_test_procedure(name) - cert_test_procedure_hash[name] + # @return [nil] if there is no certification test procedure with ID +id+ + def cert_test_procedure(id) + cert_test_procedure_hash[id] end # Exception raised when there is a problem with a schema file @@ -756,6 +758,10 @@ def initialize(data, db_obj) @data = data @db_obj = db_obj + + raise ArgumentError, "Missing certification coverage point name for #{db_obj.name} of kind #{db_obj.kind}" if name.nil? + raise ArgumentError, "Missing certification coverage point description for #{db_obj.name} of kind #{db_obj.kind}" if description.nil? + raise ArgumentError, "Missing certification coverage point ID for #{db_obj.name} of kind #{db_obj.kind}" if id.nil? end # @return [String] Name of the coverage point @@ -764,28 +770,39 @@ def name = @data["name"] # @return [String] Description of coverage point (could be multiple lines) def description = @data["description"] + # @return [String] Unique ID of the coverage point + def id = @data["id"] + # @return [Array] List of certification point links (cross references) def cert_links return @cert_links unless @cert_links.nil? @cert_links = [] @data["links"]&.each do |link_data| - @cert_links << CertLink.new(link_data) + @cert_links << CertLink.new(link_data, @db_obj) end + + raise "Missing links for certification coverage point ID '#{id}' of kind #{@db_obj.kind}" if @cert_links.empty? + @cert_links end end class CertLink # @param data [String] The cross reference link provided in the YAML - def initialize(data) + def initialize(data, db_obj) raise ArgumentError, "Need String but was passed a #{data.class}" unless data.is_a?(String) - @data = data + @id = data + + raise ArgumentError, "Missing link to certfication coverage point ID for #{db_obj.name} of kind #{db_obj.kind}" if id.nil? end + # @return [String] Unique ID of the linked to coverage point + def id = @id + # @return [String] Asciidoc to create desired link. def to_adoc - "<<#{@data},#{@data}>>" + "<<#{@id},#{@id}>>" end end @@ -798,44 +815,57 @@ def initialize(data, db_obj) @data = data @db_obj = db_obj + + raise ArgumentError, "Missing certification test procedure name for #{db_obj.name} of kind #{db_obj.kind}" if name.nil? + raise ArgumentError, "Missing certification test procedure description for #{db_obj.name} of kind #{db_obj.kind}" if description.nil? + raise ArgumentError, "Missing certification test procedure ID for #{db_obj.name} of kind #{db_obj.kind}" if id.nil? end - # @return [String] Name of the test plan + # @return [String] Name of the test procedure def name = @data["name"] - # @return [String] Description of test plan (could be multiple lines) + # @return [String] Description of test procedure (could be multiple lines) def description = @data["description"] + # @return [String] Unique ID of the test procedure + def id = @data["id"] + # @return [Array] def cert_coverage_points return @cert_coverage_points unless @cert_coverage_points.nil? @cert_coverage_points = [] - @data["coverage-points"]&.each do |name| - cp = @db_obj.cert_coverage_point(name) - raise ArgumentError, "CertTestProcedure named '#{name}' for '#{@db_obj.name}' of kind #{@db_obj.kind} can't be found" if cp.nil? + @data["coverage-points"]&.each do |id| + cp = @db_obj.cert_coverage_point(id) + raise ArgumentError, "Can't find certification test procedure with ID '#{id}' for '#{@db_obj.name}' of kind #{@db_obj.kind}" if cp.nil? @cert_coverage_points << cp end @cert_coverage_points end - # @return [Array] List of certification test plan steps + # @return [Array] List of certification test procedure steps def cert_steps return @cert_steps unless @cert_steps.nil? @cert_steps = [] @data["steps"]&.each do |step_data| - @cert_steps << CertStep.new(step_data) + @cert_steps << CertStep.new(step_data, @db_obj) end + + raise "No steps for certification procedure ID '#{id}' of kind #{@db_obj.kind}" if @cert_steps.empty? + @cert_steps end end class CertStep # @param data [Hash] The step information from the YAML - def initialize(data) + def initialize(data, db_obj) raise ArgumentError, "Need Hash but was passed a #{data.class}" unless data.is_a?(Hash) @data = data + + raise ArgumentError, "Missing certification step name for #{db_obj.name} of kind #{db_obj.kind}" if name.nil? + raise ArgumentError, "Missing certification step description for #{db_obj.name} of kind #{db_obj.kind}" if description.nil? end # @return [String] Name of the step @@ -845,5 +875,6 @@ def name = @data["name"] def description = @data["description"] # @return [String] Optional note (can be nil) + # @return [nil] Optional def note = @data["note"] end From 0f922145205227afb5f00b0c523f7ee59def974b Mon Sep 17 00:00:00 2001 From: James Ball Date: Fri, 24 Jan 2025 18:46:28 -0800 Subject: [PATCH 51/61] Remove comments to work around commit yaml checker problem. --- arch/csr/misa.yaml | 6 ++++-- arch/ext/Xmock.yaml | 7 +++++-- arch/inst/M/mul.yaml | 9 ++++++--- arch/inst/mock.yaml | 4 +++- 4 files changed, 18 insertions(+), 8 deletions(-) diff --git a/arch/csr/misa.yaml b/arch/csr/misa.yaml index e3dd0b676..248ea5fe3 100644 --- a/arch/csr/misa.yaml +++ b/arch/csr/misa.yaml @@ -141,7 +141,8 @@ fields: - id: csr.misa.M.disabled name: Disabling `misa.M` bit description: What happens when you turn off `misa.M` - # XXX links: [manual:csr:misa:disabling-extension] + links: + - manual:csr:misa:disabling-extension cert-test-procedures: - id: csr.misa.M.muldiv_with_M_on&off name: Testing M @@ -211,7 +212,8 @@ cert-coverage-points: - id: csr.misa.disabling_bits name: Disabling `misa` bits description: What happens when you turn off bits - # XXX links: [manual:csr:misa:disabling-extension] + links: + - manual:csr:misa:disabling-extension cert-test-procedures: - id: csr.misa.off&on name: Testing bits diff --git a/arch/ext/Xmock.yaml b/arch/ext/Xmock.yaml index 19bf11db9..f6c3a8772 100644 --- a/arch/ext/Xmock.yaml +++ b/arch/ext/Xmock.yaml @@ -158,11 +158,14 @@ cert-coverage-points: - id: ext.Xmock.cov1 name: Mock coverage point 1 description: Let's have fun with the `Xmock` extension - # XXX links: [manual:inst:mul:encoding, udb:doc:inst:mock] + links: + - manual:inst:mul:encoding + - udb:doc:inst:mock - id: ext.Xmock.cov2 name: Mock coverage point 2 description: And some more fun! - # XXX links: [manual:csr:misa:disabling-extension] + links: + - manual:csr:misa:disabling-extension cert-test-procedures: - id: ext.Xmock.my_first name: My first procedure diff --git a/arch/inst/M/mul.yaml b/arch/inst/M/mul.yaml index fa27f42a2..d198d8acd 100644 --- a/arch/inst/M/mul.yaml +++ b/arch/inst/M/mul.yaml @@ -74,17 +74,20 @@ cert-coverage-points: - id: inst.mul.encoding name: Encoding description: Encoding of `mul` instruction - # XXX links: [manual:inst:mul:encoding] + links: + - manual:inst:mul:encoding - id: inst.mul.basic_op name: Basic operation description: Basic operation of `mul` instruction - # XXX links: [manual:inst:mul:operation] + links: + - manual:inst:mul:operation - id: inst.mul.ill_exc_misa_M_disabled name: Illegal instruction exception when misa.M is 0 description: | An illegal instruction exception is raised when the instruction is executed and `misa.M` is 0. - # XXX links: [manual:csr:misa:disabling-extension] + links: + - manual:csr:misa:disabling-extension cert-test-procedures: - id: inst.mul.encoding diff --git a/arch/inst/mock.yaml b/arch/inst/mock.yaml index 32282a591..a2144edc8 100644 --- a/arch/inst/mock.yaml +++ b/arch/inst/mock.yaml @@ -65,7 +65,9 @@ cert-coverage-points: - id: inst.mock.encoding&basic_op name: Encoding and basic operation description: Encoding and basic operation for `mock` instruction - # XXX links: [manual:inst:mul:encoding, udb:doc:inst:mock] + links: + - manual:inst:mul:encoding + - udb:doc:inst:mock - id: inst.mock.ill_exc_misa_M_disabled name: Illegal instruction exception when misa.M is 0 description: | From 66d9f282905e133366fdc979c4533e3dfa84786e Mon Sep 17 00:00:00 2001 From: James Ball Date: Sat, 25 Jan 2025 19:26:36 +0000 Subject: [PATCH 52/61] Create proper adoc for CTP book part 1. Part 2 & 3 (ISA manuals) are giving an invalid part error. --- .../portfolio/templates/beginning.adoc.erb | 3 +- backends/proc_ctp/templates/proc_ctp.adoc.erb | 40 ++++++++++++------- 2 files changed, 27 insertions(+), 16 deletions(-) diff --git a/backends/portfolio/templates/beginning.adoc.erb b/backends/portfolio/templates/beginning.adoc.erb index a99fb664b..62df4537b 100644 --- a/backends/portfolio/templates/beginning.adoc.erb +++ b/backends/portfolio/templates/beginning.adoc.erb @@ -4,7 +4,6 @@ :revmark: "TODO: revmark" :company: RISC-V :url-riscv: https://riscv.org -:doctype: book :preface-title: Licensing and Acknowledgements :colophon: :appendix-caption: Appendix @@ -29,7 +28,7 @@ :appendix-refsig: Appendix :sectnums: :toc: left -:toclevels: 5 +:toclevels: 2 // Determined that uncommenting this causes cross-references to IDL functions // from instruction IDL code to not link. The IDL code uses this // block tag to get "source" formatting: diff --git a/backends/proc_ctp/templates/proc_ctp.adoc.erb b/backends/proc_ctp/templates/proc_ctp.adoc.erb index 551efb94a..bfb409c7e 100644 --- a/backends/proc_ctp/templates/proc_ctp.adoc.erb +++ b/backends/proc_ctp/templates/proc_ctp.adoc.erb @@ -1,6 +1,22 @@ <%= portfolio_design.include_erb("beginning.adoc.erb") %> -= <%= proc_cert_model.name %> Processor Certification Test Plan +// Book title. Need "":doctype: book" immediately after book title (no blank lines allowed). += <%= proc_cert_model.name %> Processor CTP (Certification Test Plan) +:doctype: book + +[preface] += CTP Book Structure + +A CTP book is composed of 3 book parts as follows: + +* <> is the core CTP documentation including an appendix each for extension, instruction, and CSR + that could be present in a certificate. +* <> is the RISC-V Unprivileged ISA manual, volume I (uses release specified by the certificate) +* <> is the RISC-V Privileged ISA manual, volume II (uses release specified by the certificate) + +// Part 1 title +[#udb:doc] += <%= proc_cert_model.name %> Processor Certification Test Plan: Part 1 [Preface] == CTP Revision History @@ -37,23 +53,16 @@ Each certificate has a corresponding CRD and CTP: * The CTP defines the certification coverage points and certification test procedures followed by the certification tests that an implementation must pass to obtain certification. -A CTP is structured into 3 volumes as follows: - -* Volume A is the core CTP documentation including an appendix each for extension, instruction, and CSR - that could be present in a certificate. -* Volume B is the RISC-V Unprivileged ISA manual (uses release specified by the certificate) -* Volume C is the RISC-V Privileged ISA manual (uses release specified by the certificate) - The certification coverage points reference text in any or all of the following: -* RISC-V ISA manuals in volumes B and C -* Community-generated documentation located in volume A (https://github.com/riscv-software-src/riscv-unified-db) -* IDL (ISA Description Language) executable psuedo-code descriptions located in volume A +* RISC-V ISA manuals in parts 2 and 3 +* Community-generated documentation located in part 1 (https://github.com/riscv-software-src/riscv-unified-db) +* IDL (ISA Description Language) executable psuedo-code descriptions located in part 1 -The RISC-V ISA manuals in Volumes B and C are the preferred reference for certification coverage points since these manuals -represent the ratified RISC-V standards from the TSC. +The RISC-V ISA manuals are the preferred reference for certification coverage points since these manuals +represent the ratified RISC-V standards. However, if the information in the ISA manuals isn't sufficiently clear or complete for certification purposes -the content in Volume A is used to fill that gap. +the content in Part 1 is used. === Naming Scheme @@ -117,3 +126,6 @@ include::ext/riscv-isa-manual/src/riscv-unprivileged.adoc[] // Reset chapter numbering :!chapter-number: include::ext/riscv-isa-manual/src/riscv-privileged.adoc[] + +// Make sure this didn't get overwritten by the included volumes (AKA book parts). +// :toclevels: 2 From 7c5b2ec5c996736cdcebe19a922a15d1a8b882c4 Mon Sep 17 00:00:00 2001 From: James Ball Date: Sat, 25 Jan 2025 22:06:44 +0000 Subject: [PATCH 53/61] New version of isa manual (uses level 2 instead of level 1 headings for colophons and now the isa manuals are proper adoc "parts" in the overall "book"). --- ext/csc-riscv-isa-manual | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/csc-riscv-isa-manual b/ext/csc-riscv-isa-manual index 97992e293..cdb14d641 160000 --- a/ext/csc-riscv-isa-manual +++ b/ext/csc-riscv-isa-manual @@ -1 +1 @@ -Subproject commit 97992e29382102911efaf26c5fb3f7101320e6d8 +Subproject commit cdb14d64167d7f1634d3d91045db0c13a7924987 From d4b3148428e8f112c69a10da35bd583ee85a3267 Mon Sep 17 00:00:00 2001 From: James Ball Date: Sun, 26 Jan 2025 03:02:59 +0000 Subject: [PATCH 54/61] Improved display of coverage points and test procedures. --- .../portfolio/templates/beginning.adoc.erb | 2 +- .../templates/coverage_points.adoc.erb | 5 +- .../portfolio/templates/csr_appendix.adoc.erb | 3 +- .../portfolio/templates/ext_appendix.adoc.erb | 2 +- .../templates/test_procedures.adoc.erb | 5 +- backends/proc_ctp/templates/proc_ctp.adoc.erb | 103 ++++++++++++++---- lib/arch_obj_models/database_obj.rb | 2 +- 7 files changed, 89 insertions(+), 33 deletions(-) diff --git a/backends/portfolio/templates/beginning.adoc.erb b/backends/portfolio/templates/beginning.adoc.erb index 62df4537b..460319544 100644 --- a/backends/portfolio/templates/beginning.adoc.erb +++ b/backends/portfolio/templates/beginning.adoc.erb @@ -28,7 +28,7 @@ :appendix-refsig: Appendix :sectnums: :toc: left -:toclevels: 2 +:toclevels: 5 // Determined that uncommenting this causes cross-references to IDL functions // from instruction IDL code to not link. The IDL code uses this // block tag to get "source" formatting: diff --git a/backends/portfolio/templates/coverage_points.adoc.erb b/backends/portfolio/templates/coverage_points.adoc.erb index d91c6904f..cbea585d1 100644 --- a/backends/portfolio/templates/coverage_points.adoc.erb +++ b/backends/portfolio/templates/coverage_points.adoc.erb @@ -1,8 +1,9 @@ <% unless db_obj.cert_coverage_points.empty? -%> +<% nice_name = db_obj.is_a?(CsrField) ? "#{db_obj.parent.name}.#{db_obj.name}" : db_obj.name -%> <% if defined?(use_description_list) && use_description_list -%> -Coverage Points: :: +Coverage Points for `<%= nice_name %>`: :: <% else -%> -==== Coverage Points +==== Coverage Points for `<%= nice_name %>` <% end -%> [%autowidth] diff --git a/backends/portfolio/templates/csr_appendix.adoc.erb b/backends/portfolio/templates/csr_appendix.adoc.erb index 3accf7160..0601dddc0 100644 --- a/backends/portfolio/templates/csr_appendix.adoc.erb +++ b/backends/portfolio/templates/csr_appendix.adoc.erb @@ -111,7 +111,7 @@ This CSR has no fields. However, it must still exist (not cause an `Illegal Inst <% csr.implemented_fields(design).each do |field| -%> <%= anchor_for_udb_doc_csr_field(csr.name, field.name) %> -===== `<%= field.name %>` Field +===== `<%= csr.name %>.<%= field.name %>` Field <% if !field.defined_in_all_bases? -%> IMPORTANT: <%= field.name %> is only defined in <%= field.base32_only? ? "RV32" : "RV64" %> (`<%= field.base32_only? ? field.location_cond32 : field.location_cond64 %>`) @@ -127,7 +127,6 @@ IMPORTANT: <%= field.name %> is only defined in <%= field.base32_only? ? "RV32" // do what's described in https://github.com/asciidoctor/asciidoctor/issues/1709 // but that would be a level 5 heading in this CSR field case which is pretty ugly. -.<%= csr.name + "." + field.name %> **** Location: :: <%= field.location_pretty(design) %> diff --git a/backends/portfolio/templates/ext_appendix.adoc.erb b/backends/portfolio/templates/ext_appendix.adoc.erb index ddd05106c..9a3dd5658 100644 --- a/backends/portfolio/templates/ext_appendix.adoc.erb +++ b/backends/portfolio/templates/ext_appendix.adoc.erb @@ -28,7 +28,7 @@ Ratification date::: <%= v.ratification_date %> <% end # if %> - <% if v.changes.empty? -%> + <% unless v.changes.empty? -%> Changes::: <% v.changes.each do |c| -%> diff --git a/backends/portfolio/templates/test_procedures.adoc.erb b/backends/portfolio/templates/test_procedures.adoc.erb index 6c9c79950..7c3273be8 100644 --- a/backends/portfolio/templates/test_procedures.adoc.erb +++ b/backends/portfolio/templates/test_procedures.adoc.erb @@ -1,8 +1,9 @@ <% unless db_obj.cert_test_procedures.empty? -%> +<% nice_name = db_obj.is_a?(CsrField) ? "#{db_obj.parent.name}.#{db_obj.name}" : db_obj.name -%> <% if defined?(use_description_list) && use_description_list -%> -Test Procedures: :: +Test Procedures for `<%= nice_name %>`: :: <% else -%> -==== Test Procedures +==== Test Procedures for `<%= nice_name %>` <% end -%> <% db_obj.cert_test_procedures.each do |tp| -%> diff --git a/backends/proc_ctp/templates/proc_ctp.adoc.erb b/backends/proc_ctp/templates/proc_ctp.adoc.erb index bfb409c7e..853c82f1e 100644 --- a/backends/proc_ctp/templates/proc_ctp.adoc.erb +++ b/backends/proc_ctp/templates/proc_ctp.adoc.erb @@ -82,32 +82,90 @@ The specific rules for updating the version number for a CTP are as follows: <%= proc_cert_design.include_erb("proc_naming_scheme.adoc.erb") %> -== Instruction Summary +== Coverage Points -[%autowidth] -|=== -| Name | Long Name +This section contains a view of the coverage point information organized by kind +(i.e., extension, instruction, or CSR). +This document is generated by a database backend so all views of the information are consistent. -<% design.in_scope_instructions.each do |inst| -%> -| <%= link_to_udb_doc_inst(inst.name) %> -| <%= inst.long_name %> -<% end # do -%> -|=== +=== Extension Coverage Points -== CSR Summary +<% proc_cert_model.in_scope_extensions.each do |ext| -%> +<%= portfolio_design.include_erb("coverage_points.adoc.erb", { "db_obj" => ext }) %> +<% end -%> -[%autowidth] -|=== -| Name | Long Name | Address | Mode | Primary Extension +=== Instruction Coverage Points -<% design.in_scope_csrs.sort_by!(&:name).each do |csr| -%> -| <%= link_to_udb_doc_csr(csr.name) %> -| <%= csr.long_name %> -| <%= "0x#{csr.address.to_s(16)}" %> -| <%= csr.priv_mode %> -| <%= csr.primary_defined_by %> -<% end # do -%> -|=== +<% proc_cert_model.in_scope_instructions(design).each do |inst| -%> +<%= portfolio_design.include_erb("coverage_points.adoc.erb", { "db_obj" => inst }) %> +<% end -%> + +=== CSR Coverage Points + +<% proc_cert_model.in_scope_csrs(design).each do |csr| -%> +<%= portfolio_design.include_erb("coverage_points.adoc.erb", { "db_obj" => csr }) %> +<% csr.implemented_fields(design).each do |field| -%> +<%= portfolio_design.include_erb("coverage_points.adoc.erb", { "db_obj" => field }) %> +<% end -%> +<% end -%> + +== Test Procedures + +This section contains just view of the test procedure information organized by kind +(i.e., extension, instruction, or CSR). +This document is generated by a database backend so all views of the information are consistent. + +=== Extension Test Procedures + +<% proc_cert_model.in_scope_extensions.each do |ext| -%> +<%= portfolio_design.include_erb("test_procedures.adoc.erb", { "db_obj" => ext }) %> +<% end -%> + +=== Instruction Test Procedures + +<% proc_cert_model.in_scope_instructions(design).each do |inst| -%> +<%= portfolio_design.include_erb("test_procedures.adoc.erb", { "db_obj" => inst }) %> +<% end -%> + +=== CSR Test Procedures + +<% proc_cert_model.in_scope_csrs(design).each do |csr| -%> +<%= portfolio_design.include_erb("test_procedures.adoc.erb", { "db_obj" => csr }) %> +<% csr.implemented_fields(design).each do |field| -%> +<%= portfolio_design.include_erb("test_procedures.adoc.erb", { "db_obj" => field }) %> +<% end -%> +<% end -%> + +== Combined Coverage Points & Test Procedures + +This section contains a combined view of the coverage point and test procedure information organized +by kind (i.e., extension, instruction, or CSR). +This document is generated by a database backend so all views of the information are consistent. + +=== Extension Coverage Points & Test Procedures + +<% proc_cert_model.in_scope_extensions.each do |ext| -%> +<%= portfolio_design.include_erb("coverage_points.adoc.erb", { "db_obj" => ext }) %> +<%= portfolio_design.include_erb("test_procedures.adoc.erb", { "db_obj" => ext }) %> +<% end -%> + +=== Instruction Coverage Points & Test Procedures + +<% proc_cert_model.in_scope_instructions(design).each do |inst| -%> +<%= portfolio_design.include_erb("coverage_points.adoc.erb", { "db_obj" => inst }) %> +<%= portfolio_design.include_erb("test_procedures.adoc.erb", { "db_obj" => inst }) %> +<% end -%> + +=== CSR Coverage Points & Test Procedures + +<% proc_cert_model.in_scope_csrs(design).each do |csr| -%> +<%= portfolio_design.include_erb("coverage_points.adoc.erb", { "db_obj" => csr }) %> +<%= portfolio_design.include_erb("test_procedures.adoc.erb", { "db_obj" => csr }) %> +<% csr.implemented_fields(design).each do |field| -%> +<%= portfolio_design.include_erb("coverage_points.adoc.erb", { "db_obj" => field, "use_description_list" => true }) %> +<%= portfolio_design.include_erb("test_procedures.adoc.erb", { "db_obj" => field, "use_description_list" => true }) %> +<% end -%> +<% end -%> // Appendices <%= portfolio_design.include_erb("ext_appendix.adoc.erb", { "gen_ctp_content" => true }) %> @@ -126,6 +184,3 @@ include::ext/riscv-isa-manual/src/riscv-unprivileged.adoc[] // Reset chapter numbering :!chapter-number: include::ext/riscv-isa-manual/src/riscv-privileged.adoc[] - -// Make sure this didn't get overwritten by the included volumes (AKA book parts). -// :toclevels: 2 diff --git a/lib/arch_obj_models/database_obj.rb b/lib/arch_obj_models/database_obj.rb index eb0f9563e..36d0201bd 100644 --- a/lib/arch_obj_models/database_obj.rb +++ b/lib/arch_obj_models/database_obj.rb @@ -250,7 +250,7 @@ def __source def definedBy = @data["definedBy"] def inspect - self.class.name + self.class.name + "##{name}" end # make the underlying YAML description available with [] From 0fbbf50965968c3390099ce087624ea621170eb7 Mon Sep 17 00:00:00 2001 From: James Ball Date: Sun, 26 Jan 2025 07:02:06 +0000 Subject: [PATCH 55/61] Added Sail operation to instruction appendix. --- backends/portfolio/templates/inst_appendix.adoc.erb | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/backends/portfolio/templates/inst_appendix.adoc.erb b/backends/portfolio/templates/inst_appendix.adoc.erb index 751c2b129..513ac89bb 100644 --- a/backends/portfolio/templates/inst_appendix.adoc.erb +++ b/backends/portfolio/templates/inst_appendix.adoc.erb @@ -100,17 +100,24 @@ RV64:: ---- <% end -%> +<% if inst.key?("operation()") -%> ==== IDL Operation -<% xlens = inst.base.nil? ? [32, 64] : [inst.base] -%> - -<% if inst.key?("operation()") -%> [source,idl,subs="specialchars,macros"] ---- <%= inst.operation_ast(design.symtab).gen_adoc %> ---- <% end -%> +<% if inst.key?("sail()") -%> +==== Sail Operation + +[source,sail] +---- +<%= inst["sail()"] %> +---- +<% end -%> + ==== Exceptions <% exception_list = inst.reachable_exceptions_str(design.symtab) -%> From 6e40ed8a2b5fc9ee8ec3ecc60f21ed65e77b1cfc Mon Sep 17 00:00:00 2001 From: James Ball Date: Sun, 26 Jan 2025 16:11:59 +0000 Subject: [PATCH 56/61] Added links from test procedures to coverage points. --- .../templates/coverage_points.adoc.erb | 2 +- .../portfolio/templates/csr_appendix.adoc.erb | 8 ++--- .../portfolio/templates/ext_appendix.adoc.erb | 4 +-- .../templates/inst_appendix.adoc.erb | 4 +-- .../templates/test_procedures.adoc.erb | 2 +- backends/proc_ctp/templates/proc_ctp.adoc.erb | 32 +++++++++--------- lib/backend_helpers.rb | 33 ++++++++++++++++--- lib/test/test_backend_helpers.rb | 15 +++++++++ 8 files changed, 70 insertions(+), 30 deletions(-) diff --git a/backends/portfolio/templates/coverage_points.adoc.erb b/backends/portfolio/templates/coverage_points.adoc.erb index cbea585d1..56dd2f32f 100644 --- a/backends/portfolio/templates/coverage_points.adoc.erb +++ b/backends/portfolio/templates/coverage_points.adoc.erb @@ -11,7 +11,7 @@ Coverage Points for `<%= nice_name %>`: :: | ID | Name | Description | Links <% db_obj.cert_coverage_points.each do |cp| -%> -| <%= cp.id %> +| <%= defined?(org) ? anchor_for_udb_cert_cov_pt(org, cp.id) : "" %><%= cp.id %> | <%= cp.name %> | <%= cp.description %> a| <% cp.cert_links.each do |link| -%> diff --git a/backends/portfolio/templates/csr_appendix.adoc.erb b/backends/portfolio/templates/csr_appendix.adoc.erb index 0601dddc0..b61a41f82 100644 --- a/backends/portfolio/templates/csr_appendix.adoc.erb +++ b/backends/portfolio/templates/csr_appendix.adoc.erb @@ -141,8 +141,8 @@ Reset value: :: <%= field.reset_value_pretty(design) %> <% if defined?(gen_ctp_content) && gen_ctp_content -%> -<%= portfolio_design.include_erb("coverage_points.adoc.erb", { "db_obj" => field, "use_description_list" => true }) %> -<%= portfolio_design.include_erb("test_procedures.adoc.erb", { "db_obj" => field, "use_description_list" => true }) %> +<%= portfolio_design.include_erb("coverage_points.adoc.erb", { "db_obj" => field, "org" => "appendix", "use_description_list" => true }) %> +<%= portfolio_design.include_erb("test_procedures.adoc.erb", { "db_obj" => field, "org" => "appendix", "use_description_list" => true }) %> <% end # if gen_ctp_content -%> **** @@ -182,8 +182,8 @@ This CSR may return a value that is different from what is stored in hardware. <% end -%> <% if defined?(gen_ctp_content) && gen_ctp_content -%> -<%= portfolio_design.include_erb("coverage_points.adoc.erb", { "db_obj" => csr }) %> -<%= portfolio_design.include_erb("test_procedures.adoc.erb", { "db_obj" => csr }) %> +<%= portfolio_design.include_erb("coverage_points.adoc.erb", { "db_obj" => csr, "org" => "appendix" }) %> +<%= portfolio_design.include_erb("test_procedures.adoc.erb", { "db_obj" => csr, "org" => "appendix" }) %> <% end # if gen_ctp_content -%> <% end # do in_scope_csrs -%> diff --git a/backends/portfolio/templates/ext_appendix.adoc.erb b/backends/portfolio/templates/ext_appendix.adoc.erb index 9a3dd5658..721fea5f8 100644 --- a/backends/portfolio/templates/ext_appendix.adoc.erb +++ b/backends/portfolio/templates/ext_appendix.adoc.erb @@ -123,8 +123,8 @@ This extension has the following parameters (AKA implementation options): <% end # unless table -%> <% if defined?(gen_ctp_content) && gen_ctp_content -%> -<%= portfolio_design.include_erb("coverage_points.adoc.erb", { "db_obj" => ext }) %> -<%= portfolio_design.include_erb("test_procedures.adoc.erb", { "db_obj" => ext }) %> +<%= portfolio_design.include_erb("coverage_points.adoc.erb", { "db_obj" => ext, "org" => "appendix"}) %> +<%= portfolio_design.include_erb("test_procedures.adoc.erb", { "db_obj" => ext, "org" => "appendix"}) %> <% end # if gen_ctp_content -%> <% end # do ext_req -%> diff --git a/backends/portfolio/templates/inst_appendix.adoc.erb b/backends/portfolio/templates/inst_appendix.adoc.erb index 513ac89bb..0f6f809c8 100644 --- a/backends/portfolio/templates/inst_appendix.adoc.erb +++ b/backends/portfolio/templates/inst_appendix.adoc.erb @@ -133,8 +133,8 @@ This instruction may result in the following synchronous exceptions: <% end -%> <% if defined?(gen_ctp_content) && gen_ctp_content -%> -<%= portfolio_design.include_erb("coverage_points.adoc.erb", { "db_obj" => inst }) %> -<%= portfolio_design.include_erb("test_procedures.adoc.erb", { "db_obj" => inst }) %> +<%= portfolio_design.include_erb("coverage_points.adoc.erb", { "db_obj" => inst, "org" => "appendix" }) %> +<%= portfolio_design.include_erb("test_procedures.adoc.erb", { "db_obj" => inst, "org" => "appendix" }) %> <% end # if gen_ctp_content -%> <% end # each in_scope instruction -%> diff --git a/backends/portfolio/templates/test_procedures.adoc.erb b/backends/portfolio/templates/test_procedures.adoc.erb index 7c3273be8..ffc98207a 100644 --- a/backends/portfolio/templates/test_procedures.adoc.erb +++ b/backends/portfolio/templates/test_procedures.adoc.erb @@ -16,7 +16,7 @@ Test Procedures for `<%= nice_name %>`: :: | *Coverage Points* a| <% tp.cert_coverage_points.each do |cp| -%> -* <%= cp.id %> +* <%= defined?(org) ? link_to_udb_cert_cov_pt(org, cp.id) : cp.id %> <% end # each cp -%> | *Steps* a| diff --git a/backends/proc_ctp/templates/proc_ctp.adoc.erb b/backends/proc_ctp/templates/proc_ctp.adoc.erb index 853c82f1e..9710905ef 100644 --- a/backends/proc_ctp/templates/proc_ctp.adoc.erb +++ b/backends/proc_ctp/templates/proc_ctp.adoc.erb @@ -91,21 +91,21 @@ This document is generated by a database backend so all views of the information === Extension Coverage Points <% proc_cert_model.in_scope_extensions.each do |ext| -%> -<%= portfolio_design.include_erb("coverage_points.adoc.erb", { "db_obj" => ext }) %> +<%= portfolio_design.include_erb("coverage_points.adoc.erb", { "db_obj" => ext, "org" => "sep"}) %> <% end -%> === Instruction Coverage Points <% proc_cert_model.in_scope_instructions(design).each do |inst| -%> -<%= portfolio_design.include_erb("coverage_points.adoc.erb", { "db_obj" => inst }) %> +<%= portfolio_design.include_erb("coverage_points.adoc.erb", { "db_obj" => inst, "org" => "sep"}) %> <% end -%> === CSR Coverage Points <% proc_cert_model.in_scope_csrs(design).each do |csr| -%> -<%= portfolio_design.include_erb("coverage_points.adoc.erb", { "db_obj" => csr }) %> +<%= portfolio_design.include_erb("coverage_points.adoc.erb", { "db_obj" => csr, "org" => "sep" }) %> <% csr.implemented_fields(design).each do |field| -%> -<%= portfolio_design.include_erb("coverage_points.adoc.erb", { "db_obj" => field }) %> +<%= portfolio_design.include_erb("coverage_points.adoc.erb", { "db_obj" => field, "org" => "sep" }) %> <% end -%> <% end -%> @@ -118,21 +118,21 @@ This document is generated by a database backend so all views of the information === Extension Test Procedures <% proc_cert_model.in_scope_extensions.each do |ext| -%> -<%= portfolio_design.include_erb("test_procedures.adoc.erb", { "db_obj" => ext }) %> +<%= portfolio_design.include_erb("test_procedures.adoc.erb", { "db_obj" => ext, "org" => "sep" }) %> <% end -%> === Instruction Test Procedures <% proc_cert_model.in_scope_instructions(design).each do |inst| -%> -<%= portfolio_design.include_erb("test_procedures.adoc.erb", { "db_obj" => inst }) %> +<%= portfolio_design.include_erb("test_procedures.adoc.erb", { "db_obj" => inst, "org" => "sep" }) %> <% end -%> === CSR Test Procedures <% proc_cert_model.in_scope_csrs(design).each do |csr| -%> -<%= portfolio_design.include_erb("test_procedures.adoc.erb", { "db_obj" => csr }) %> +<%= portfolio_design.include_erb("test_procedures.adoc.erb", { "db_obj" => csr, "org" => "sep" }) %> <% csr.implemented_fields(design).each do |field| -%> -<%= portfolio_design.include_erb("test_procedures.adoc.erb", { "db_obj" => field }) %> +<%= portfolio_design.include_erb("test_procedures.adoc.erb", { "db_obj" => field, "org" => "sep" }) %> <% end -%> <% end -%> @@ -145,25 +145,25 @@ This document is generated by a database backend so all views of the information === Extension Coverage Points & Test Procedures <% proc_cert_model.in_scope_extensions.each do |ext| -%> -<%= portfolio_design.include_erb("coverage_points.adoc.erb", { "db_obj" => ext }) %> -<%= portfolio_design.include_erb("test_procedures.adoc.erb", { "db_obj" => ext }) %> +<%= portfolio_design.include_erb("coverage_points.adoc.erb", { "db_obj" => ext, "org" => "combo"}) %> +<%= portfolio_design.include_erb("test_procedures.adoc.erb", { "db_obj" => ext, "org" => "combo"}) %> <% end -%> === Instruction Coverage Points & Test Procedures <% proc_cert_model.in_scope_instructions(design).each do |inst| -%> -<%= portfolio_design.include_erb("coverage_points.adoc.erb", { "db_obj" => inst }) %> -<%= portfolio_design.include_erb("test_procedures.adoc.erb", { "db_obj" => inst }) %> +<%= portfolio_design.include_erb("coverage_points.adoc.erb", { "db_obj" => inst, "org" => "combo"}) %> +<%= portfolio_design.include_erb("test_procedures.adoc.erb", { "db_obj" => inst, "org" => "combo"}) %> <% end -%> === CSR Coverage Points & Test Procedures <% proc_cert_model.in_scope_csrs(design).each do |csr| -%> -<%= portfolio_design.include_erb("coverage_points.adoc.erb", { "db_obj" => csr }) %> -<%= portfolio_design.include_erb("test_procedures.adoc.erb", { "db_obj" => csr }) %> +<%= portfolio_design.include_erb("coverage_points.adoc.erb", { "db_obj" => csr, "org" => "combo"}) %> +<%= portfolio_design.include_erb("test_procedures.adoc.erb", { "db_obj" => csr, "org" => "combo"}) %> <% csr.implemented_fields(design).each do |field| -%> -<%= portfolio_design.include_erb("coverage_points.adoc.erb", { "db_obj" => field, "use_description_list" => true }) %> -<%= portfolio_design.include_erb("test_procedures.adoc.erb", { "db_obj" => field, "use_description_list" => true }) %> +<%= portfolio_design.include_erb("coverage_points.adoc.erb", { "db_obj" => field, "use_description_list" => true, "org" => "combo"}) %> +<%= portfolio_design.include_erb("test_procedures.adoc.erb", { "db_obj" => field, "use_description_list" => true, "org" => "combo"}) %> <% end -%> <% end -%> diff --git a/lib/backend_helpers.rb b/lib/backend_helpers.rb index 6b47f8ad9..0141747a4 100644 --- a/lib/backend_helpers.rb +++ b/lib/backend_helpers.rb @@ -10,9 +10,9 @@ class String # Should be called on all RISC-V extension, instruction, CSR, and CSR field names. # Parameters never have periods in their names so they don't need to be sanitized. # - # @param name [String] Some RISC-V name which might have periods in it - # @return [String] New String with periods replaced with underscores - def sanitize = String.new(self).gsub(".", "_") + # @param name [String] Some RISC-V name which might have periods in it or ampersand + # @return [String] New String with periods replaced with underscores and ampersands replaced with "_and_" + def sanitize = String.new(self).gsub(".", "_").gsub("&", "_and_") end # This module is included in the Design class so its methods are available to be called directly @@ -94,6 +94,14 @@ def link_into_idl_inst_code(inst_name, loc_name) end # TODO: Add csr and csr_field support + # @return [String] A hyperlink to a UDB certification coverage point (separate chapters for cov pts and test procs) + # @param org [String] Organization of coverage points and test procedures (sep=separate chapters, combo=combined chapters, appendix=appendix) + # @param id [String] ID of the coverage point + def link_to_udb_cert_cov_pt(org, id) + raise ArgumentError, "Unknown org value of '#{org}' for ID '#{id}'" unless org == "sep" || org == "combo" || org == "appendix" + "%%UDB_CERT_COV_PT_LINK%#{org};#{id.sanitize};#{id}%%" + end + ########### # ANCHORS # ########### @@ -137,7 +145,7 @@ def anchor_for_udb_doc_idl_func(name) "[#udb:doc:func:#{name.sanitize}]" end - # @return [String] A anchor inside IDL instruction code + # @return [String] An anchor inside IDL instruction code # @param func_name [String] Name of the instruction # @param func_name [String] Name of the location within the instruction code def anchor_inside_idl_inst_code(inst_name, loc_name) @@ -145,6 +153,15 @@ def anchor_inside_idl_inst_code(inst_name, loc_name) end # TODO: Add csr and csr_field support + # @return [String] An anchor for a UDB certification coverage point (separate chapters for cov pts and test procs) + # @param org [String] Organization of coverage points and test procedures (sep=separate chapters, combo=combined chapters, appendix=appendix) + # @param id [String] ID of the coverage point + # Have to use [[anchor]] instead of [#anchor] since only the former works when in a table cell. + def anchor_for_udb_cert_cov_pt(org, id) + raise ArgumentError, "Unknown org value of '#{org}' for ID '#{id}'" unless org == "sep" || org == "combo" || org == "appendix" + "[[udb:cert:cov_pt:#{org}:#{id.sanitize}]]" + end + private #@ param s [String] def check_no_periods(s) @@ -208,6 +225,14 @@ def resolve_links(path_or_str) else raise "Unhandled link type of '#{type}' for '#{name}' with link_text '#{link_text}'" end + end.gsub(/%%UDB_CERT_COV_PT_LINK%([^;%]+)\s*;\s*([^;%]+)\s*;\s*([^%]+)%%/) do + org = Regexp.last_match[1] # sep or combo + id = Regexp.last_match[2] + link_text = Regexp.last_match[3] + + raise "Unhandled link org of '#{org}' for ID '#{id}' with link_text '#{link_text}'" unless org == "sep" || org == "combo" || org == "appendix" + + "<>" end end end diff --git a/lib/test/test_backend_helpers.rb b/lib/test/test_backend_helpers.rb index 68e31c92f..914e9f591 100644 --- a/lib/test/test_backend_helpers.rb +++ b/lib/test/test_backend_helpers.rb @@ -59,6 +59,14 @@ def test_idl_code assert_equal("%%IDL_CODE_LINK%inst;fo_o.ba_r;fo.o.ba.r%%", link_into_idl_inst_code("fo.o","ba.r")) assert_equal("[#idl:code:inst:fo_o:ba_r]", anchor_inside_idl_inst_code("fo.o","ba.r")) end + + def test_cert_cov_pt + assert_equal("%%UDB_CERT_COV_PT_LINK%sep;foo_and_bar;foo&bar%%", link_to_udb_cert_cov_pt("sep", "foo&bar")) + assert_equal("[[udb:cert:cov_pt:sep:foo]]", anchor_for_udb_cert_cov_pt("sep", "foo")) + assert_equal("%%UDB_CERT_COV_PT_LINK%combo;fo_o;fo.o%%", link_to_udb_cert_cov_pt("combo", "fo.o")) + assert_equal("[[udb:cert:cov_pt:combo:fo_o]]", anchor_for_udb_cert_cov_pt("combo", "fo.o")) + assert_raises(ArgumentError) { link_to_udb_cert_cov_pt("bad-org-value","abc") } + end end class TestAsciidocUtils < Minitest::Test @@ -96,6 +104,13 @@ def test_resolve_links_idl_code assert_equal("<>", AsciidocUtils.resolve_links("%%IDL_CODE_LINK%inst;foo.bar;zort%%")) assert_equal("<>", AsciidocUtils.resolve_links(link_into_idl_inst_code("foo","bar"))) end + + def test_resolve_links_cert_cov_pt + assert_equal("<>", AsciidocUtils.resolve_links("%%UDB_CERT_COV_PT_LINK%sep;foo;bar%%")) + assert_equal("<>", AsciidocUtils.resolve_links(link_to_udb_cert_cov_pt("sep", "foo"))) + assert_equal("<>", AsciidocUtils.resolve_links("%%UDB_CERT_COV_PT_LINK%combo;foo;bar%%")) + assert_equal("<>", AsciidocUtils.resolve_links(link_to_udb_cert_cov_pt("combo", "foo"))) + end end class TestAntoraUtils < Minitest::Test From d6edb6e9a759f203c2469fe0883f5f12cb096f57 Mon Sep 17 00:00:00 2001 From: James Ball Date: Sun, 26 Jan 2025 23:32:11 +0000 Subject: [PATCH 57/61] Fix bug in display of all extensions that define a parameter in a CRD. --- backends/manual/templates/param_list.adoc.erb | 2 +- .../templates/coverage_points.adoc.erb | 2 +- backends/proc_crd/templates/proc_crd.adoc.erb | 4 +- lib/arch_obj_models/parameter.rb | 43 +++++++++---------- lib/backend_helpers.rb | 5 ++- lib/test/test_backend_helpers.rb | 10 ++--- 6 files changed, 33 insertions(+), 33 deletions(-) diff --git a/backends/manual/templates/param_list.adoc.erb b/backends/manual/templates/param_list.adoc.erb index d443ec46c..0cad6885c 100644 --- a/backends/manual/templates/param_list.adoc.erb +++ b/backends/manual/templates/param_list.adoc.erb @@ -12,7 +12,7 @@ The following <%= params.size %> parameters are defined in this manual: <%- params.each do |param| -%> | <%= param.name %> | <%= param.schema.to_pretty_s %> -| <%= param.exts.map { |ext| link_to_udb_doc_ext_param(ext.name, param.name) }.join(", ") %> +| <%= param.exts.map { |ext| link_to_udb_doc_ext_param(ext.name, param.name, ext.name) }.join(", ") %> a| <%= param.desc %> <%- end -%> |=== diff --git a/backends/portfolio/templates/coverage_points.adoc.erb b/backends/portfolio/templates/coverage_points.adoc.erb index 56dd2f32f..362f2d776 100644 --- a/backends/portfolio/templates/coverage_points.adoc.erb +++ b/backends/portfolio/templates/coverage_points.adoc.erb @@ -8,7 +8,7 @@ Coverage Points for `<%= nice_name %>`: :: [%autowidth] |=== -| ID | Name | Description | Links +| ID | Name | Description | Documentation Links <% db_obj.cert_coverage_points.each do |cp| -%> | <%= defined?(org) ? anchor_for_udb_cert_cov_pt(org, cp.id) : "" %><%= cp.id %> diff --git a/backends/proc_crd/templates/proc_crd.adoc.erb b/backends/proc_crd/templates/proc_crd.adoc.erb index bc5fb34fb..a25b939ef 100644 --- a/backends/proc_crd/templates/proc_crd.adoc.erb +++ b/backends/proc_crd/templates/proc_crd.adoc.erb @@ -235,7 +235,7 @@ None | <%= param.name_potentially_with_link(in_scope_exts) %> | <%= param.schema_type %> | <%= in_scope_param.allowed_values %> -| <%= in_scope_exts.each { |ext| param.name_with_link(ext) } %> +| <%= in_scope_exts.map { |ext| link_to_udb_doc_ext_param(ext.name, param.name, ext.name) }.join(", ") %> a| <%= in_scope_param.note %> <% end # do -%> |=== @@ -258,7 +258,7 @@ None <% exts = portfolio_design.all_in_scope_exts_without_param(param) -%> | <%= param.name_potentially_with_link(exts) %> | <%= param.schema_type %> -| <%= exts.each { |ext| param.name_with_link(ext) } %> +| <%= exts.map { |ext| link_to_udb_doc_ext_param(ext.name, param.name, ext.name) }.join(", ") %> <% end # do -%> |=== diff --git a/lib/arch_obj_models/parameter.rb b/lib/arch_obj_models/parameter.rb index 19fef8bb6..15eed6285 100644 --- a/lib/arch_obj_models/parameter.rb +++ b/lib/arch_obj_models/parameter.rb @@ -40,33 +40,39 @@ def schema_type # @param name [String] # @param data [Hash] List of all in-scope extensions that define this parameter. - # @return [String] Text that includes the parameter name and a link to the parameter definition + # @return [String] Text to create a link to the parameter definition with the link text the parameter name. # if only one extension defines the parameter, otherwise just the parameter name. def name_potentially_with_link(in_scope_exts) - raise ArgumentError, "Expecting Array" unless in_scope_exts.is_a?(Array) + raise ArgumentError, "Expecting Array but got #{in_scope_exts.class}" unless in_scope_exts.is_a?(Array) raise ArgumentError, "Expecting Array[Extension]" unless in_scope_exts[0].is_a?(Extension) if in_scope_exts.size == 1 - link_to_udb_doc_ext_param(in_scope_exts[0].name, name) + link_to_udb_doc_ext_param(in_scope_exts[0].name, name, name) else name end diff --git a/lib/backend_helpers.rb b/lib/backend_helpers.rb index 0141747a4..20fd349a3 100644 --- a/lib/backend_helpers.rb +++ b/lib/backend_helpers.rb @@ -56,9 +56,10 @@ def link_to_udb_doc_ext(ext_name) # @return [String] A hyperlink to UDB parameter documentation # @param ext_name [String] Name of the extension # @param param_name [String] Name of the parameter - def link_to_udb_doc_ext_param(ext_name, param_name) + # @param link_text [String] What to put in the link text (don't assume param_name) + def link_to_udb_doc_ext_param(ext_name, param_name, link_text) check_no_periods(param_name) - "%%UDB_DOC_LINK%ext_param;#{ext_name.sanitize}.#{param_name};#{param_name}%%" + "%%UDB_DOC_LINK%ext_param;#{ext_name.sanitize}.#{param_name};#{link_text}%%" end # @return [String] A hyperlink to UDB instruction documentation diff --git a/lib/test/test_backend_helpers.rb b/lib/test/test_backend_helpers.rb index 914e9f591..28ff5876b 100644 --- a/lib/test/test_backend_helpers.rb +++ b/lib/test/test_backend_helpers.rb @@ -17,11 +17,11 @@ def test_ext end def test_ext_param - assert_equal("%%UDB_DOC_LINK%ext_param;foo.bar;bar%%", link_to_udb_doc_ext_param("foo","bar")) + assert_equal("%%UDB_DOC_LINK%ext_param;foo.bar;zort%%", link_to_udb_doc_ext_param("foo","bar","zort")) assert_equal("[#udb:doc:ext_param:foo:bar]", anchor_for_udb_doc_ext_param("foo","bar")) - assert_equal("%%UDB_DOC_LINK%ext_param;fo_o.bar;bar%%", link_to_udb_doc_ext_param("fo.o","bar")) + assert_equal("%%UDB_DOC_LINK%ext_param;fo_o.bar;fluffy%%", link_to_udb_doc_ext_param("fo.o","bar","fluffy")) assert_equal("[#udb:doc:ext_param:fo_o:bar]", anchor_for_udb_doc_ext_param("fo.o","bar")) - assert_raises(ArgumentError) { link_to_udb_doc_ext_param("foo","ba.r") } + assert_raises(ArgumentError) { link_to_udb_doc_ext_param("foo","ba.r","fluffy") } assert_raises(ArgumentError) { anchor_for_udb_doc_ext_param("foo","ba.r") } end @@ -77,7 +77,7 @@ def test_resolve_links_ext def test_resolve_links_ext_param assert_equal("<>", AsciidocUtils.resolve_links("%%UDB_DOC_LINK%ext_param;foo.bar;zort%%")) - assert_equal("<>", AsciidocUtils.resolve_links(link_to_udb_doc_ext_param("foo","bar"))) + assert_equal("<>", AsciidocUtils.resolve_links(link_to_udb_doc_ext_param("foo","bar","bob"))) end def test_resolve_links_inst @@ -121,7 +121,7 @@ def test_resolve_links_ext def test_resolve_links_ext_param assert_equal("xref:exts:foo.adoc#udb:doc:ext_param:foo:bar[[zort\\]]", AntoraUtils.resolve_links("%%UDB_DOC_LINK%ext_param;foo.bar;[zort]%%")) - assert_equal("xref:exts:foo.adoc#udb:doc:ext_param:foo:bar[bar]", AntoraUtils.resolve_links(link_to_udb_doc_ext_param("foo","bar"))) + assert_equal("xref:exts:foo.adoc#udb:doc:ext_param:foo:bar[bob]", AntoraUtils.resolve_links(link_to_udb_doc_ext_param("foo","bar","bob"))) end def test_resolve_links_inst From f6fb42a1d86507dcdaf80326aae6c12ceac503b7 Mon Sep 17 00:00:00 2001 From: James Ball Date: Mon, 27 Jan 2025 02:35:38 +0000 Subject: [PATCH 58/61] Renaming CertLinks to DocLinks since parameters will also use them. --- arch/csr/misa.yaml | 4 +-- arch/ext/Xmock.yaml | 4 +-- arch/inst/M/mul.yaml | 6 ++-- arch/inst/mock.yaml | 4 +-- .../templates/coverage_points.adoc.erb | 2 +- lib/arch_obj_models/database_obj.rb | 31 ++++++++++++------- schemas/schema_defs.json | 4 +-- 7 files changed, 32 insertions(+), 23 deletions(-) diff --git a/arch/csr/misa.yaml b/arch/csr/misa.yaml index 248ea5fe3..6c747ec0a 100644 --- a/arch/csr/misa.yaml +++ b/arch/csr/misa.yaml @@ -141,7 +141,7 @@ fields: - id: csr.misa.M.disabled name: Disabling `misa.M` bit description: What happens when you turn off `misa.M` - links: + doc_links: - manual:csr:misa:disabling-extension cert-test-procedures: - id: csr.misa.M.muldiv_with_M_on&off @@ -212,7 +212,7 @@ cert-coverage-points: - id: csr.misa.disabling_bits name: Disabling `misa` bits description: What happens when you turn off bits - links: + doc_links: - manual:csr:misa:disabling-extension cert-test-procedures: - id: csr.misa.off&on diff --git a/arch/ext/Xmock.yaml b/arch/ext/Xmock.yaml index f6c3a8772..949183a53 100644 --- a/arch/ext/Xmock.yaml +++ b/arch/ext/Xmock.yaml @@ -158,13 +158,13 @@ cert-coverage-points: - id: ext.Xmock.cov1 name: Mock coverage point 1 description: Let's have fun with the `Xmock` extension - links: + doc_links: - manual:inst:mul:encoding - udb:doc:inst:mock - id: ext.Xmock.cov2 name: Mock coverage point 2 description: And some more fun! - links: + doc_links: - manual:csr:misa:disabling-extension cert-test-procedures: - id: ext.Xmock.my_first diff --git a/arch/inst/M/mul.yaml b/arch/inst/M/mul.yaml index d198d8acd..03d02dbd9 100644 --- a/arch/inst/M/mul.yaml +++ b/arch/inst/M/mul.yaml @@ -74,19 +74,19 @@ cert-coverage-points: - id: inst.mul.encoding name: Encoding description: Encoding of `mul` instruction - links: + doc_links: - manual:inst:mul:encoding - id: inst.mul.basic_op name: Basic operation description: Basic operation of `mul` instruction - links: + doc_links: - manual:inst:mul:operation - id: inst.mul.ill_exc_misa_M_disabled name: Illegal instruction exception when misa.M is 0 description: | An illegal instruction exception is raised when the instruction is executed and `misa.M` is 0. - links: + doc_links: - manual:csr:misa:disabling-extension cert-test-procedures: diff --git a/arch/inst/mock.yaml b/arch/inst/mock.yaml index a2144edc8..1ee647359 100644 --- a/arch/inst/mock.yaml +++ b/arch/inst/mock.yaml @@ -65,7 +65,7 @@ cert-coverage-points: - id: inst.mock.encoding&basic_op name: Encoding and basic operation description: Encoding and basic operation for `mock` instruction - links: + doc_links: - manual:inst:mul:encoding - udb:doc:inst:mock - id: inst.mock.ill_exc_misa_M_disabled @@ -73,7 +73,7 @@ cert-coverage-points: description: | An illegal instruction exception is raised when the instruction is executed and `misa.M` is 0. - links: + doc_links: - manual:csr:misa:disabling-extension # - idl:code:inst:mock:illegal-inst-exc-misa-disabled diff --git a/backends/portfolio/templates/coverage_points.adoc.erb b/backends/portfolio/templates/coverage_points.adoc.erb index 362f2d776..3906347cb 100644 --- a/backends/portfolio/templates/coverage_points.adoc.erb +++ b/backends/portfolio/templates/coverage_points.adoc.erb @@ -14,7 +14,7 @@ Coverage Points for `<%= nice_name %>`: :: | <%= defined?(org) ? anchor_for_udb_cert_cov_pt(org, cp.id) : "" %><%= cp.id %> | <%= cp.name %> | <%= cp.description %> -a| <% cp.cert_links.each do |link| -%> +a| <% cp.doc_links.each do |link| -%> * <%= link.to_adoc %> <% end # each link -%> <% end # each coverage point -%> diff --git a/lib/arch_obj_models/database_obj.rb b/lib/arch_obj_models/database_obj.rb index 36d0201bd..8e1312d36 100644 --- a/lib/arch_obj_models/database_obj.rb +++ b/lib/arch_obj_models/database_obj.rb @@ -773,28 +773,37 @@ def description = @data["description"] # @return [String] Unique ID of the coverage point def id = @data["id"] - # @return [Array] List of certification point links (cross references) - def cert_links - return @cert_links unless @cert_links.nil? + # @return [Array] List of certification point documentation links + def doc_links + return @doc_links unless @doc_links.nil? - @cert_links = [] - @data["links"]&.each do |link_data| - @cert_links << CertLink.new(link_data, @db_obj) + @doc_links = [] + @data["doc_links"]&.each do |link_data| + @doc_links << DocLink.new(link_data, @db_obj) end - raise "Missing links for certification coverage point ID '#{id}' of kind #{@db_obj.kind}" if @cert_links.empty? + raise "Missing doc_links for certification coverage point ID '#{id}' of kind #{@db_obj.kind}" if @doc_links.empty? - @cert_links + @doc_links end end -class CertLink - # @param data [String] The cross reference link provided in the YAML +# Used to create links into RISC-V documentation with the following formats: +# ISA manuals manual:ext:: +# manual:inst:: +# manual:csr:: +# manual:csr:: +# non-ISA system component standards, UDB generated documentation, +# and regions of Sail/IDL pseudo-code. +# +# +class DocLink + # @param data [String] The documentation link provided in the YAML def initialize(data, db_obj) raise ArgumentError, "Need String but was passed a #{data.class}" unless data.is_a?(String) @id = data - raise ArgumentError, "Missing link to certfication coverage point ID for #{db_obj.name} of kind #{db_obj.kind}" if id.nil? + raise ArgumentError, "Missing documentation link for #{db_obj.name} of kind #{db_obj.kind}" if @id.nil? end # @return [String] Unique ID of the linked to coverage point diff --git a/schemas/schema_defs.json b/schemas/schema_defs.json index d69c7ff0c..df5fdf105 100644 --- a/schemas/schema_defs.json +++ b/schemas/schema_defs.json @@ -279,8 +279,8 @@ "name": { "type": "string" }, - "links": { - "description": "Cross reference to UDB documentation, ISA manual, or IDL code", + "doc_links": { + "description": "Link to UDB documentation, ISA manual, Sail code, or IDL code", "type": "array", "items": { "type": "string" From c8da061502feb213f3d84572603d62dbb9135898 Mon Sep 17 00:00:00 2001 From: James Ball Date: Sun, 26 Jan 2025 20:29:22 -0800 Subject: [PATCH 59/61] Renaming of CTP things --- arch/csr/misa.yaml | 14 ++-- arch/ext/Xmock.yaml | 6 +- arch/inst/M/mul.yaml | 6 +- arch/inst/mock.yaml | 6 +- .../templates/coverage_points.adoc.erb | 2 +- .../templates/test_procedures.adoc.erb | 2 +- lib/arch_obj_models/database_obj.rb | 56 ++++++++----- lib/backend_helpers.rb | 78 +++++++++---------- lib/test/test_backend_helpers.rb | 43 +++++----- schemas/csr_schema.json | 16 ++-- schemas/ext_schema.json | 8 +- schemas/inst_schema.json | 8 +- schemas/profile_schema.json | 8 +- schemas/schema_defs.json | 8 +- 14 files changed, 139 insertions(+), 122 deletions(-) diff --git a/arch/csr/misa.yaml b/arch/csr/misa.yaml index 6c747ec0a..c0ca0267d 100644 --- a/arch/csr/misa.yaml +++ b/arch/csr/misa.yaml @@ -137,17 +137,17 @@ fields: return (implemented?(ExtensionName::M) && MUTABLE_MISA_M) ? CsrFieldType::RW : CsrFieldType::RO; definedBy: M reset_value: 1 - cert-coverage-points: - - id: csr.misa.M.disabled + cert_coverage_points: + - id: csr_field.misa.M.disabled name: Disabling `misa.M` bit description: What happens when you turn off `misa.M` doc_links: - manual:csr:misa:disabling-extension - cert-test-procedures: + cert_test_procedures: - id: csr.misa.M.muldiv_with_M_on&off name: Testing M description: Execute with M on/off - coverage-points: [csr.misa.M.disabled] + coverage_points: [csr_field.misa.M.disabled] steps: - name: on description: Turn on `misa.M` @@ -208,17 +208,17 @@ sw_read(): | (CSR[misa].C << 2) | (CSR[misa].B << 1) | CSR[misa].A); -cert-coverage-points: +cert_coverage_points: - id: csr.misa.disabling_bits name: Disabling `misa` bits description: What happens when you turn off bits doc_links: - manual:csr:misa:disabling-extension -cert-test-procedures: +cert_test_procedures: - id: csr.misa.off&on name: Testing bits description: Turn on/off each bit and see what happens - coverage-points: [csr.misa.disabling_bits] + coverage_points: [csr.misa.disabling_bits] steps: - name: setup description: Turn on all bits diff --git a/arch/ext/Xmock.yaml b/arch/ext/Xmock.yaml index 949183a53..4b24014e2 100644 --- a/arch/ext/Xmock.yaml +++ b/arch/ext/Xmock.yaml @@ -154,7 +154,7 @@ params: type: boolean maxItems: 8 minItems: 8 -cert-coverage-points: +cert_coverage_points: - id: ext.Xmock.cov1 name: Mock coverage point 1 description: Let's have fun with the `Xmock` extension @@ -166,11 +166,11 @@ cert-coverage-points: description: And some more fun! doc_links: - manual:csr:misa:disabling-extension -cert-test-procedures: +cert_test_procedures: - id: ext.Xmock.my_first name: My first procedure description: Verify that when it rains in Spain, it rains mainly on the plains! - coverage-points: [ext.Xmock.cov1, ext.Xmock.cov2] + coverage_points: [ext.Xmock.cov1, ext.Xmock.cov2] steps: - name: wait for rain description: First we need some rain diff --git a/arch/inst/M/mul.yaml b/arch/inst/M/mul.yaml index 03d02dbd9..18ec9eb8d 100644 --- a/arch/inst/M/mul.yaml +++ b/arch/inst/M/mul.yaml @@ -70,7 +70,7 @@ sail(): | } } -cert-coverage-points: +cert_coverage_points: - id: inst.mul.encoding name: Encoding description: Encoding of `mul` instruction @@ -89,11 +89,11 @@ cert-coverage-points: doc_links: - manual:csr:misa:disabling-extension -cert-test-procedures: +cert_test_procedures: - id: inst.mul.encoding name: Encoding description: Verify the encoding of the `mul` instruction - coverage-points: [inst.mul.encoding] + coverage_points: [inst.mul.encoding] steps: - name: setup description: Load a variety of known values into rs1 & rs2 with a variety of rs1/rs2/rd values. diff --git a/arch/inst/mock.yaml b/arch/inst/mock.yaml index 1ee647359..2ab811621 100644 --- a/arch/inst/mock.yaml +++ b/arch/inst/mock.yaml @@ -61,7 +61,7 @@ sail(): | } } -cert-coverage-points: +cert_coverage_points: - id: inst.mock.encoding&basic_op name: Encoding and basic operation description: Encoding and basic operation for `mock` instruction @@ -77,11 +77,11 @@ cert-coverage-points: - manual:csr:misa:disabling-extension # - idl:code:inst:mock:illegal-inst-exc-misa-disabled -cert-test-procedures: +cert_test_procedures: - id: inst.mock.enc_and_basic name: Encoding description: Verify the encoding and basic operation of the `mock` instruction - coverage-points: [inst.mock.encoding&basic_op] + coverage_points: [inst.mock.encoding&basic_op] steps: - name: setup description: Load a variety of known values into rs1 & rs2 with a variety of rs1/rs2/rd values. diff --git a/backends/portfolio/templates/coverage_points.adoc.erb b/backends/portfolio/templates/coverage_points.adoc.erb index 3906347cb..703354952 100644 --- a/backends/portfolio/templates/coverage_points.adoc.erb +++ b/backends/portfolio/templates/coverage_points.adoc.erb @@ -11,7 +11,7 @@ Coverage Points for `<%= nice_name %>`: :: | ID | Name | Description | Documentation Links <% db_obj.cert_coverage_points.each do |cp| -%> -| <%= defined?(org) ? anchor_for_udb_cert_cov_pt(org, cp.id) : "" %><%= cp.id %> +| <%= defined?(org) ? anchor_for_udb_doc_cov_pt(org, cp.id) : "" %><%= cp.id %> | <%= cp.name %> | <%= cp.description %> a| <% cp.doc_links.each do |link| -%> diff --git a/backends/portfolio/templates/test_procedures.adoc.erb b/backends/portfolio/templates/test_procedures.adoc.erb index ffc98207a..7c8ff955f 100644 --- a/backends/portfolio/templates/test_procedures.adoc.erb +++ b/backends/portfolio/templates/test_procedures.adoc.erb @@ -16,7 +16,7 @@ Test Procedures for `<%= nice_name %>`: :: | *Coverage Points* a| <% tp.cert_coverage_points.each do |cp| -%> -* <%= defined?(org) ? link_to_udb_cert_cov_pt(org, cp.id) : cp.id %> +* <%= defined?(org) ? link_to_udb_doc_cov_pt(org, cp.id) : cp.id %> <% end # each cp -%> | *Steps* a| diff --git a/lib/arch_obj_models/database_obj.rb b/lib/arch_obj_models/database_obj.rb index 8e1312d36..5513b4722 100644 --- a/lib/arch_obj_models/database_obj.rb +++ b/lib/arch_obj_models/database_obj.rb @@ -52,7 +52,7 @@ def cert_coverage_points return @cert_coverage_points unless @cert_coverage_points.nil? @cert_coverage_points = [] - @data["cert-coverage-points"]&.each do |cert_data| + @data["cert_coverage_points"]&.each do |cert_data| @cert_coverage_points << CertCoveragePoint.new(cert_data, self) end @cert_coverage_points @@ -81,7 +81,7 @@ def cert_test_procedures return @cert_test_procedures unless @cert_test_procedures.nil? @cert_test_procedures = [] - @data["cert-test-procedures"]&.each do |cert_data| + @data["cert_test_procedures"]&.each do |cert_data| @cert_test_procedures << CertTestProcedure.new(cert_data, self) end @cert_test_procedures @@ -778,8 +778,8 @@ def doc_links return @doc_links unless @doc_links.nil? @doc_links = [] - @data["doc_links"]&.each do |link_data| - @doc_links << DocLink.new(link_data, @db_obj) + @data["doc_links"]&.each do |dst| + @doc_links << DocLink.new(dst, @db_obj) end raise "Missing doc_links for certification coverage point ID '#{id}' of kind #{@db_obj.kind}" if @doc_links.empty? @@ -788,30 +788,46 @@ def doc_links end end -# Used to create links into RISC-V documentation with the following formats: -# ISA manuals manual:ext:: -# manual:inst:: -# manual:csr:: -# manual:csr:: -# non-ISA system component standards, UDB generated documentation, -# and regions of Sail/IDL pseudo-code. -# +# Creates links into RISC-V documentation with the following formats for the destination link: # +# Documenation Format +# ============ =============================================================== +# ISA manuals manual:ext:: +# manual:inst:: +# manual:csr:: +# manual:csr_field::: +# manual:param::: +# where is the location within the ISA manual documentation +# UDB doc udb:doc:ext: +# udb:doc:ext_param:: +# udb:doc:inst: +# udb:doc:csr: +# udb:doc:csr_field:: +# udb:doc:func: (Documentation of common/built-in IDL functions) +# udb:doc:cov_pt:: +# where is: +# sep for UDB documentation that "separates" coverage points from test plans +# combo for UDB documentation that "combines" coverage points with test plans +# appendix for UDB documentation that has coverage points and test plans in appendices +# where is the ID of the coverage point +# IDL code idl:code:inst:: +# TODO for CSR and CSR Fields class DocLink - # @param data [String] The documentation link provided in the YAML - def initialize(data, db_obj) - raise ArgumentError, "Need String but was passed a #{data.class}" unless data.is_a?(String) - @id = data + # @param dst_link [String] The documentation link provided in the YAML + # @param db_obj [String] Database object + def initialize(dst_link, db_obj) + raise ArgumentError, "Need String but was passed a #{data.class}" unless dst_link.is_a?(String) + @dst_link = dst_link - raise ArgumentError, "Missing documentation link for #{db_obj.name} of kind #{db_obj.kind}" if @id.nil? + raise ArgumentError, "Missing documentation link for #{db_obj.name} of kind #{db_obj.kind}" if @dst_link.nil? end # @return [String] Unique ID of the linked to coverage point - def id = @id + def dst_link = @dst_link # @return [String] Asciidoc to create desired link. def to_adoc - "<<#{@id},#{@id}>>" + "<<#{@dst_link},#{@dst_link}>>" end end @@ -844,7 +860,7 @@ def cert_coverage_points return @cert_coverage_points unless @cert_coverage_points.nil? @cert_coverage_points = [] - @data["coverage-points"]&.each do |id| + @data["coverage_points"]&.each do |id| cp = @db_obj.cert_coverage_point(id) raise ArgumentError, "Can't find certification test procedure with ID '#{id}' for '#{@db_obj.name}' of kind #{@db_obj.kind}" if cp.nil? @cert_coverage_points << cp diff --git a/lib/backend_helpers.rb b/lib/backend_helpers.rb index 20fd349a3..e2cf35b51 100644 --- a/lib/backend_helpers.rb +++ b/lib/backend_helpers.rb @@ -87,21 +87,21 @@ def link_to_udb_doc_idl_func(func_name) "%%UDB_DOC_LINK%func;#{func_name.sanitize};#{func_name}%%" end - # @return [String] A hyperlink into IDL instruction code - # @param func_name [String] Name of the instruction - # @param func_name [String] Name of the location within the instruction code - def link_into_idl_inst_code(inst_name, loc_name) - "%%IDL_CODE_LINK%inst;#{inst_name.sanitize}.#{loc_name.sanitize};#{inst_name}.#{loc_name}%%" - end - # TODO: Add csr and csr_field support - # @return [String] A hyperlink to a UDB certification coverage point (separate chapters for cov pts and test procs) # @param org [String] Organization of coverage points and test procedures (sep=separate chapters, combo=combined chapters, appendix=appendix) # @param id [String] ID of the coverage point - def link_to_udb_cert_cov_pt(org, id) + def link_to_udb_doc_cov_pt(org, id) raise ArgumentError, "Unknown org value of '#{org}' for ID '#{id}'" unless org == "sep" || org == "combo" || org == "appendix" - "%%UDB_CERT_COV_PT_LINK%#{org};#{id.sanitize};#{id}%%" + "%%UDB_DOC_COV_PT_LINK%#{org};#{id.sanitize};#{id}%%" + end + + # @return [String] A hyperlink into IDL instruction code + # @param func_name [String] Name of the instruction + # @param id [String] ID within the instruction code + def link_into_idl_inst_code(inst_name, id) + "%%IDL_CODE_LINK%inst;#{inst_name.sanitize}.#{id.sanitize};#{inst_name}.#{id}%%" end + # TODO: Add csr and csr_field support ########### # ANCHORS # @@ -146,23 +146,23 @@ def anchor_for_udb_doc_idl_func(name) "[#udb:doc:func:#{name.sanitize}]" end - # @return [String] An anchor inside IDL instruction code - # @param func_name [String] Name of the instruction - # @param func_name [String] Name of the location within the instruction code - def anchor_inside_idl_inst_code(inst_name, loc_name) - "[#idl:code:inst:#{inst_name.sanitize}:#{loc_name.sanitize}]" - end - # TODO: Add csr and csr_field support - - # @return [String] An anchor for a UDB certification coverage point (separate chapters for cov pts and test procs) - # @param org [String] Organization of coverage points and test procedures (sep=separate chapters, combo=combined chapters, appendix=appendix) + # @return [String] An anchor for a UDB coverage point documentation + # @param org [String] Document organization of coverage points and test procedures (sep=separate chapters, combo=combined chapters, appendix=appendix) # @param id [String] ID of the coverage point # Have to use [[anchor]] instead of [#anchor] since only the former works when in a table cell. - def anchor_for_udb_cert_cov_pt(org, id) + def anchor_for_udb_doc_cov_pt(org, id) raise ArgumentError, "Unknown org value of '#{org}' for ID '#{id}'" unless org == "sep" || org == "combo" || org == "appendix" - "[[udb:cert:cov_pt:#{org}:#{id.sanitize}]]" + "[[udb:doc:cov_pt:#{org}:#{id.sanitize}]]" end + # @return [String] An anchor inside IDL instruction code + # @param func_name [String] Name of the instruction + # @param id [String] ID within the instruction code + def anchor_inside_idl_inst_code(inst_name, id) + "[#idl:code:inst:#{inst_name.sanitize}:#{id.sanitize}]" + end + # TODO: Add csr and csr_field support + private #@ param s [String] def check_no_periods(s) @@ -213,6 +213,14 @@ def resolve_links(path_or_str) else raise "Unhandled link type of '#{type}' for '#{name}' with link_text '#{link_text}'" end + end.gsub(/%%UDB_DOC_COV_PT_LINK%([^;%]+)\s*;\s*([^;%]+)\s*;\s*([^%]+)%%/) do + org = Regexp.last_match[1] # "sep", "combo", or "appendix" + id = Regexp.last_match[2] + link_text = Regexp.last_match[3] + + raise "Unhandled link org of '#{org}' for ID '#{id}' with link_text '#{link_text}'" unless org == "sep" || org == "combo" || org == "appendix" + + "<>" end.gsub(/%%IDL_CODE_LINK%([^;%]+)\s*;\s*([^;%]+)\s*;\s*([^%]+)%%/) do type = Regexp.last_match[1] name = Regexp.last_match[2] @@ -220,20 +228,12 @@ def resolve_links(path_or_str) case type when "inst" - inst_name, loc_name = name.split('.') - "<>" + inst_name, id = name.split('.') + "<>" # TODO: Add csr and csr_field support else raise "Unhandled link type of '#{type}' for '#{name}' with link_text '#{link_text}'" end - end.gsub(/%%UDB_CERT_COV_PT_LINK%([^;%]+)\s*;\s*([^;%]+)\s*;\s*([^%]+)%%/) do - org = Regexp.last_match[1] # sep or combo - id = Regexp.last_match[2] - link_text = Regexp.last_match[3] - - raise "Unhandled link org of '#{org}' for ID '#{id}' with link_text '#{link_text}'" unless org == "sep" || org == "combo" || org == "appendix" - - "<>" end end end @@ -276,17 +276,17 @@ def resolve_links(path_or_str) case type when "ext" - "xref:exts:#{name}.adoc#udb:doc:ext:#{name}[#{link_text.gsub(']', '\]')}]" + "xref:exts:#{name}.adoc#udb:doc:ext:#{name}[#{link_text}]" when "ext_param" ext_name, param_name = name.split('.') - "xref:exts:#{ext_name}.adoc#udb:doc:ext_param:#{ext_name}:#{param_name}[#{link_text.gsub(']', '\]')}]" + "xref:exts:#{ext_name}.adoc#udb:doc:ext_param:#{ext_name}:#{param_name}[#{link_text}]" when "inst" - "xref:insts:#{name}.adoc#udb:doc:inst:#{name}[#{link_text.gsub(']', '\]')}]" + "xref:insts:#{name}.adoc#udb:doc:inst:#{name}[#{link_text}]" when "csr" - "xref:csrs:#{name}.adoc#udb:doc:csr:#{name}[#{link_text.gsub(']', '\]')}]" + "xref:csrs:#{name}.adoc#udb:doc:csr:#{name}[#{link_text}]" when "csr_field" csr_name, field_name = name.split('.') - "xref:csrs:#{csr_name}.adoc#udb:doc:csr_field:#{csr_name}:#{field_name}[#{link_text.gsub(']', '\]')}]" + "xref:csrs:#{csr_name}.adoc#udb:doc:csr_field:#{csr_name}:#{field_name}[#{link_text}]" when "func" # All functions are in the same file called "funcs.adoc". "xref:funcs:funcs.adoc#udb:doc:func:#{name}[#{link_text.gsub(']', '\]')}]" @@ -300,8 +300,8 @@ def resolve_links(path_or_str) case type when "inst" - inst_name, loc_name = name.split('.') - "xref:insts:#{inst_name}.adoc#idl:code:inst:#{inst_name}:#{loc_name}[#{link_text.gsub(']', '\]')}]" + inst_name, id = name.split('.') + "xref:insts:#{inst_name}.adoc#idl:code:inst:#{inst_name}:#{id}[#{link_text}]" # TODO: Add csr and csr_field support else raise "Unhandled link type of '#{type}' for '#{name}' with link_text '#{link_text}'" diff --git a/lib/test/test_backend_helpers.rb b/lib/test/test_backend_helpers.rb index 28ff5876b..0d63840f2 100644 --- a/lib/test/test_backend_helpers.rb +++ b/lib/test/test_backend_helpers.rb @@ -46,6 +46,14 @@ def test_csr_field assert_equal("[#udb:doc:csr_field:fo_o:ba_r]", anchor_for_udb_doc_csr_field("fo.o","ba.r")) end + def test_cov_pt + assert_equal("%%UDB_DOC_COV_PT_LINK%sep;foo_and_bar;foo&bar%%", link_to_udb_doc_cov_pt("sep", "foo&bar")) + assert_equal("[[udb:doc:cov_pt:sep:foo]]", anchor_for_udb_doc_cov_pt("sep", "foo")) + assert_equal("%%UDB_DOC_COV_PT_LINK%combo;fo_o;fo.o%%", link_to_udb_doc_cov_pt("combo", "fo.o")) + assert_equal("[[udb:doc:cov_pt:combo:fo_o]]", anchor_for_udb_doc_cov_pt("combo", "fo.o")) + assert_raises(ArgumentError) { link_to_udb_doc_cov_pt("bad-org-value","abc") } + end + def test_idl_func assert_equal("%%UDB_DOC_LINK%func;foo;foo%%", link_to_udb_doc_idl_func("foo")) assert_equal("[#udb:doc:func:foo]", anchor_for_udb_doc_idl_func("foo")) @@ -60,13 +68,6 @@ def test_idl_code assert_equal("[#idl:code:inst:fo_o:ba_r]", anchor_inside_idl_inst_code("fo.o","ba.r")) end - def test_cert_cov_pt - assert_equal("%%UDB_CERT_COV_PT_LINK%sep;foo_and_bar;foo&bar%%", link_to_udb_cert_cov_pt("sep", "foo&bar")) - assert_equal("[[udb:cert:cov_pt:sep:foo]]", anchor_for_udb_cert_cov_pt("sep", "foo")) - assert_equal("%%UDB_CERT_COV_PT_LINK%combo;fo_o;fo.o%%", link_to_udb_cert_cov_pt("combo", "fo.o")) - assert_equal("[[udb:cert:cov_pt:combo:fo_o]]", anchor_for_udb_cert_cov_pt("combo", "fo.o")) - assert_raises(ArgumentError) { link_to_udb_cert_cov_pt("bad-org-value","abc") } - end end class TestAsciidocUtils < Minitest::Test @@ -100,52 +101,52 @@ def test_resolve_links_func assert_equal("<>", AsciidocUtils.resolve_links(link_to_udb_doc_idl_func("foo"))) end + def test_resolve_links_cov_pt + assert_equal("<>", AsciidocUtils.resolve_links("%%UDB_DOC_COV_PT_LINK%sep;foo;bar%%")) + assert_equal("<>", AsciidocUtils.resolve_links(link_to_udb_doc_cov_pt("sep", "foo"))) + assert_equal("<>", AsciidocUtils.resolve_links("%%UDB_DOC_COV_PT_LINK%combo;foo;bar%%")) + assert_equal("<>", AsciidocUtils.resolve_links(link_to_udb_doc_cov_pt("combo", "foo"))) + end + def test_resolve_links_idl_code assert_equal("<>", AsciidocUtils.resolve_links("%%IDL_CODE_LINK%inst;foo.bar;zort%%")) assert_equal("<>", AsciidocUtils.resolve_links(link_into_idl_inst_code("foo","bar"))) end - - def test_resolve_links_cert_cov_pt - assert_equal("<>", AsciidocUtils.resolve_links("%%UDB_CERT_COV_PT_LINK%sep;foo;bar%%")) - assert_equal("<>", AsciidocUtils.resolve_links(link_to_udb_cert_cov_pt("sep", "foo"))) - assert_equal("<>", AsciidocUtils.resolve_links("%%UDB_CERT_COV_PT_LINK%combo;foo;bar%%")) - assert_equal("<>", AsciidocUtils.resolve_links(link_to_udb_cert_cov_pt("combo", "foo"))) - end end class TestAntoraUtils < Minitest::Test def test_resolve_links_ext - assert_equal("xref:exts:foo.adoc#udb:doc:ext:foo[bar\\]]", AntoraUtils.resolve_links("%%UDB_DOC_LINK%ext;foo;bar]%%")) + assert_equal("xref:exts:foo.adoc#udb:doc:ext:foo[bar]", AntoraUtils.resolve_links("%%UDB_DOC_LINK%ext;foo;bar%%")) assert_equal("xref:exts:foo.adoc#udb:doc:ext:foo[foo]", AntoraUtils.resolve_links(link_to_udb_doc_ext("foo"))) end def test_resolve_links_ext_param - assert_equal("xref:exts:foo.adoc#udb:doc:ext_param:foo:bar[[zort\\]]", AntoraUtils.resolve_links("%%UDB_DOC_LINK%ext_param;foo.bar;[zort]%%")) + assert_equal("xref:exts:foo.adoc#udb:doc:ext_param:foo:bar[[zort]]", AntoraUtils.resolve_links("%%UDB_DOC_LINK%ext_param;foo.bar;[zort]%%")) assert_equal("xref:exts:foo.adoc#udb:doc:ext_param:foo:bar[bob]", AntoraUtils.resolve_links(link_to_udb_doc_ext_param("foo","bar","bob"))) end def test_resolve_links_inst - assert_equal("xref:insts:foo.adoc#udb:doc:inst:foo[[[bar\\]\\]]", AntoraUtils.resolve_links("%%UDB_DOC_LINK%inst;foo;[[bar]]%%")) + assert_equal("xref:insts:foo.adoc#udb:doc:inst:foo[bar]", AntoraUtils.resolve_links("%%UDB_DOC_LINK%inst;foo;bar%%")) assert_equal("xref:insts:foo.adoc#udb:doc:inst:foo[foo]", AntoraUtils.resolve_links(link_to_udb_doc_inst("foo"))) end def test_resolve_links_csr - assert_equal("xref:csrs:foo.adoc#udb:doc:csr:foo[\\]bar]", AntoraUtils.resolve_links("%%UDB_DOC_LINK%csr;foo;]bar%%")) + assert_equal("xref:csrs:foo.adoc#udb:doc:csr:foo[bar]", AntoraUtils.resolve_links("%%UDB_DOC_LINK%csr;foo;bar%%")) assert_equal("xref:csrs:foo.adoc#udb:doc:csr:foo[foo]", AntoraUtils.resolve_links(link_to_udb_doc_csr("foo"))) end def test_resolve_links_csr_field - assert_equal("xref:csrs:foo.adoc#udb:doc:csr_field:foo:bar[zort\\]]", AntoraUtils.resolve_links("%%UDB_DOC_LINK%csr_field;foo.bar;zort]%%")) + assert_equal("xref:csrs:foo.adoc#udb:doc:csr_field:foo:bar[zort]", AntoraUtils.resolve_links("%%UDB_DOC_LINK%csr_field;foo.bar;zort%%")) assert_equal("xref:csrs:foo.adoc#udb:doc:csr_field:foo:bar[foo.bar]", AntoraUtils.resolve_links(link_to_udb_doc_csr_field("foo","bar"))) end def test_resolve_links_func - assert_equal("xref:funcs:funcs.adoc#udb:doc:func:foo[\\]bar]", AntoraUtils.resolve_links("%%UDB_DOC_LINK%func;foo;]bar%%")) + assert_equal("xref:funcs:funcs.adoc#udb:doc:func:foo[bar]", AntoraUtils.resolve_links("%%UDB_DOC_LINK%func;foo;bar%%")) assert_equal("xref:funcs:funcs.adoc#udb:doc:func:foo[foo]", AntoraUtils.resolve_links(link_to_udb_doc_idl_func("foo"))) end def test_resolve_links_idl_code - assert_equal("xref:insts:foo.adoc#idl:code:inst:foo:bar[zort\\]]", AntoraUtils.resolve_links("%%IDL_CODE_LINK%inst;foo.bar;zort]%%")) + assert_equal("xref:insts:foo.adoc#idl:code:inst:foo:bar[zort]", AntoraUtils.resolve_links("%%IDL_CODE_LINK%inst;foo.bar;zort%%")) assert_equal("xref:insts:foo.adoc#idl:code:inst:foo:bar[foo.bar]", AntoraUtils.resolve_links(link_into_idl_inst_code("foo","bar"))) end end diff --git a/schemas/csr_schema.json b/schemas/csr_schema.json index 648ad0bc1..bbceb6b6f 100644 --- a/schemas/csr_schema.json +++ b/schemas/csr_schema.json @@ -106,11 +106,11 @@ ], "description": "Extension(s) that affect the definition of the field beyond the extension (or base) the field is originally defined in" }, - "cert-coverage-points": { - "$ref": "schema_defs.json#/$defs/cert-coverage-points" + "cert_coverage_points": { + "$ref": "schema_defs.json#/$defs/cert_coverage_points" }, - "cert-test-procedures": { - "$ref": "schema_defs.json#/$defs/cert-test-procedures" + "cert_test_procedures": { + "$ref": "schema_defs.json#/$defs/cert_test_procedures" } }, @@ -276,11 +276,11 @@ "description": "Path to the source file this definition came from; used by downstream tooling -- not expected to be in handwritten files", "type": "string" }, - "cert-coverage-points": { - "$ref": "schema_defs.json#/$defs/cert-coverage-points" + "cert_coverage_points": { + "$ref": "schema_defs.json#/$defs/cert_coverage_points" }, - "cert-test-procedures": { - "$ref": "schema_defs.json#/$defs/cert-test-procedures" + "cert_test_procedures": { + "$ref": "schema_defs.json#/$defs/cert_test_procedures" } }, "additionalProperties": false, diff --git a/schemas/ext_schema.json b/schemas/ext_schema.json index 1a3709324..6d7a2131a 100644 --- a/schemas/ext_schema.json +++ b/schemas/ext_schema.json @@ -291,11 +291,11 @@ "type": "string", "description": "Source file where this extension was defined" }, - "cert-coverage-points": { - "$ref": "schema_defs.json#/$defs/cert-coverage-points" + "cert_coverage_points": { + "$ref": "schema_defs.json#/$defs/cert_coverage_points" }, - "cert-test-procedures": { - "$ref": "schema_defs.json#/$defs/cert-test-procedures" + "cert_test_procedures": { + "$ref": "schema_defs.json#/$defs/cert_test_procedures" } }, "additionalProperties": false diff --git a/schemas/inst_schema.json b/schemas/inst_schema.json index 60bfa42b0..fe2c655f0 100644 --- a/schemas/inst_schema.json +++ b/schemas/inst_schema.json @@ -257,11 +257,11 @@ "type": "string", "description": "Functional description of the instruction using Sail" }, - "cert-coverage-points": { - "$ref": "schema_defs.json#/$defs/cert-coverage-points" + "cert_coverage_points": { + "$ref": "schema_defs.json#/$defs/cert_coverage_points" }, - "cert-test-procedures": { - "$ref": "schema_defs.json#/$defs/cert-test-procedures" + "cert_test_procedures": { + "$ref": "schema_defs.json#/$defs/cert_test_procedures" }, "assembly": { "type": "string", diff --git a/schemas/profile_schema.json b/schemas/profile_schema.json index 129653bb4..e0f36b436 100644 --- a/schemas/profile_schema.json +++ b/schemas/profile_schema.json @@ -20,11 +20,11 @@ "type": "integer", "description": "32 for RV32I or 64 for RV64I" }, - "cert-coverage-points": { - "$ref": "schema_defs.json#/$defs/cert-coverage-points" + "cert_coverage_points": { + "$ref": "schema_defs.json#/$defs/cert_coverage_points" }, - "cert-test-procedures": { - "$ref": "schema_defs.json#/$defs/cert-test-procedures" + "cert_test_procedures": { + "$ref": "schema_defs.json#/$defs/cert_test_procedures" } } } diff --git a/schemas/schema_defs.json b/schemas/schema_defs.json index df5fdf105..db86c92e7 100644 --- a/schemas/schema_defs.json +++ b/schemas/schema_defs.json @@ -268,7 +268,7 @@ } } }, - "cert-coverage-points": { + "cert_coverage_points": { "description": "Architecturally visible behaviors requiring validation by certification tests", "type": "array", "required": ["id", "name", "links", "description"], @@ -292,10 +292,10 @@ }, "additionalProperties": false }, - "cert-test-procedures": { + "cert_test_procedures": { "description": "Procedure test must follow to test certification coverage points", "type": "array", - "required": ["id", "name", "description", "coverage-points"], + "required": ["id", "name", "description", "coverage_points"], "properties": { "id": { "type": "string" @@ -306,7 +306,7 @@ "description": { "type": "string" }, - "coverage-points": { + "coverage_points": { "type": "array", "description": "List of certification coverage point IDs to be validated", "items": { From 8cd606e8b03f2cc8b79dca7ab5baa066236a7c7f Mon Sep 17 00:00:00 2001 From: James Ball Date: Fri, 31 Jan 2025 13:26:44 -0800 Subject: [PATCH 60/61] More link doc --- lib/arch_obj_models/database_obj.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/arch_obj_models/database_obj.rb b/lib/arch_obj_models/database_obj.rb index 5513b4722..31721cb36 100644 --- a/lib/arch_obj_models/database_obj.rb +++ b/lib/arch_obj_models/database_obj.rb @@ -794,6 +794,8 @@ def doc_links # ============ =============================================================== # ISA manuals manual:ext:: # manual:inst:: +# manual:insts:[-]+: +# manual:inst_group:: # manual:csr:: # manual:csr_field::: # manual:param::: From e14bed4dc40d046e0b5c14bbcb2c8d9cc463ae4d Mon Sep 17 00:00:00 2001 From: James Ball Date: Sat, 1 Feb 2025 20:25:06 +0000 Subject: [PATCH 61/61] Added priv modes and related specs to CTP by moving it from CRD into templates they can share. --- .../proc_cert/templates/priv_modes.adoc.erb | 12 ++++++++ .../templates/related_specs.adoc.erb | 12 ++++++++ backends/proc_crd/tasks.rake | 3 ++ backends/proc_crd/templates/proc_crd.adoc.erb | 28 ++----------------- backends/proc_ctp/tasks.rake | 2 ++ backends/proc_ctp/templates/proc_ctp.adoc.erb | 3 ++ 6 files changed, 34 insertions(+), 26 deletions(-) create mode 100644 backends/proc_cert/templates/priv_modes.adoc.erb create mode 100644 backends/proc_cert/templates/related_specs.adoc.erb diff --git a/backends/proc_cert/templates/priv_modes.adoc.erb b/backends/proc_cert/templates/priv_modes.adoc.erb new file mode 100644 index 000000000..58d8c3eb8 --- /dev/null +++ b/backends/proc_cert/templates/priv_modes.adoc.erb @@ -0,0 +1,12 @@ +=== Privileged Modes + +|=== +| M | S | U | VS | VU + +| <% 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 -%> + +|=== diff --git a/backends/proc_cert/templates/related_specs.adoc.erb b/backends/proc_cert/templates/related_specs.adoc.erb new file mode 100644 index 000000000..2d2a12979 --- /dev/null +++ b/backends/proc_cert/templates/related_specs.adoc.erb @@ -0,0 +1,12 @@ +=== Related Specifications + +[cols="2,2,3,3,3"] +|=== +| Certificate Model | TSC Profile | Unpriv ISA Manual | Priv ISA Manual | Debug Manual + +| <%= 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 %> +|=== diff --git a/backends/proc_crd/tasks.rake b/backends/proc_crd/tasks.rake index d102a514a..750cad6ba 100644 --- a/backends/proc_crd/tasks.rake +++ b/backends/proc_crd/tasks.rake @@ -26,6 +26,9 @@ Dir.glob("#{$root}/arch/proc_cert_model/*.yaml") do |f| "#{$root}/backends/portfolio/templates/beginning.adoc.erb", "#{$root}/backends/proc_cert/templates/typographic.adoc.erb", "#{$root}/backends/proc_cert/templates/rev_history.adoc.erb", + "#{$root}/backends/proc_cert/templates/related_specs.adoc.erb", + "#{$root}/backends/proc_cert/templates/priv_modes.adoc.erb", + "#{$root}/backends/proc_cert/templates/rev_history.adoc.erb", "#{PROC_CRD_DOC_DIR}/templates/proc_crd.adoc.erb" ] do |t| proc_cert_create_adoc("#{PROC_CRD_DOC_DIR}/templates/proc_crd.adoc.erb", t.name, model_name) diff --git a/backends/proc_crd/templates/proc_crd.adoc.erb b/backends/proc_crd/templates/proc_crd.adoc.erb index a25b939ef..5a591f20a 100644 --- a/backends/proc_crd/templates/proc_crd.adoc.erb +++ b/backends/proc_crd/templates/proc_crd.adoc.erb @@ -137,32 +137,8 @@ It is OUT-OF-SCOPE for certification tests to write all possible values of WPRI (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 - -[cols="2,2,3,3,3"] -|=== -| Certificate Model | TSC Profile | Unpriv ISA Manual | Priv ISA Manual | Debug Manual - -| <%= 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 - -|=== -| M | S | U | VS | VU - -| <% 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 -%> - -|=== - +<%= proc_cert_design.include_erb("related_specs.adoc.erb") %> +<%= proc_cert_design.include_erb("priv_modes.adoc.erb") %> <<< == Extensions diff --git a/backends/proc_ctp/tasks.rake b/backends/proc_ctp/tasks.rake index c053f1a23..b33460c7f 100644 --- a/backends/proc_ctp/tasks.rake +++ b/backends/proc_ctp/tasks.rake @@ -31,6 +31,8 @@ Dir.glob("#{$root}/arch/proc_cert_model/*.yaml") do |f| "#{$root}/backends/portfolio/templates/test_procedures.adoc.erb", "#{$root}/backends/proc_cert/templates/typographic.adoc.erb", "#{$root}/backends/proc_cert/templates/rev_history.adoc.erb", + "#{$root}/backends/proc_cert/templates/related_specs.adoc.erb", + "#{$root}/backends/proc_cert/templates/priv_modes.adoc.erb", "#{PROC_CTP_DOC_DIR}/templates/proc_ctp.adoc.erb" ] do |t| # Ensure that the required submodule repositories are up-to-date. diff --git a/backends/proc_ctp/templates/proc_ctp.adoc.erb b/backends/proc_ctp/templates/proc_ctp.adoc.erb index 9710905ef..90f8b2793 100644 --- a/backends/proc_ctp/templates/proc_ctp.adoc.erb +++ b/backends/proc_ctp/templates/proc_ctp.adoc.erb @@ -82,6 +82,9 @@ The specific rules for updating the version number for a CTP are as follows: <%= proc_cert_design.include_erb("proc_naming_scheme.adoc.erb") %> +<%= proc_cert_design.include_erb("related_specs.adoc.erb") %> +<%= proc_cert_design.include_erb("priv_modes.adoc.erb") %> + == Coverage Points This section contains a view of the coverage point information organized by kind