diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000000..e655c54104 --- /dev/null +++ b/.clang-format @@ -0,0 +1,15 @@ +--- +BasedOnStyle: Google +IndentWidth: 2 +Language: Cpp +# AlignConsecutiveAssignments: true +# AlignConsecutiveDeclarations: true +# AlignEscapedNewlines: Right +# AlignOperands: true +# AlignTrailingComments: true +# AllowShortBlocksOnASingleLine: false +# AllowShortCaseLabelsOnASingleLine: true +# AlwaysBreakTemplateDeclarations: Yes + +FixNamespaceComments: true +NamespaceIndentation: All diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 4a5d9f57a8..f708c60c03 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -6,19 +6,26 @@ WORKDIR /workspace RUN export DEBIAN_FRONTEND=noninteractive RUN apt-get update -RUN apt-get install -y --no-install-recommends git gh -RUN apt-get install -y --no-install-recommends less -RUN apt-get install -y --no-install-recommends python3 -RUN apt-get install -y --no-install-recommends python3.12-venv -RUN apt-get install -y --no-install-recommends python3-pip -RUN apt-get install -y --no-install-recommends build-essential -RUN apt-get install -y --no-install-recommends ruby -RUN apt-get install -y --no-install-recommends ruby-dev -RUN apt-get install -y --no-install-recommends bundler -RUN apt-get install -y --no-install-recommends nodejs -RUN apt-get install -y --no-install-recommends npm -RUN apt-get install -y --no-install-recommends ditaa -RUN apt-get install -y --no-install-recommends libyaml-dev +RUN apt-get install -y --no-install-recommends git \ + gh \ + less \ + python3 \ + python3.12-venv \ + python3-pip \ + build-essential \ + ruby \ + ruby-dev \ + bundler \ + nodejs \ + npm \ + ditaa \ + libyaml-dev \ + cmake \ + g++ \ + clang-format \ + clang-tidy \ + libelf-dev \ + gcc-riscv64-unknown-elf RUN apt-get clean autoclean RUN apt-get autoremove -y RUN rm -rf /var/lib/{apt,dpkg,cache,log}/* diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index e3fd23d188..819513e07d 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -31,7 +31,7 @@ "zhwu95.riscv", "mathematic.vscode-pdf", "CraigMaslowski.erb", - "HowerLimited.idl-vscode" + "HowerLimited.udb-extension-pack-vscode" ] } }, diff --git a/.github/workflows/regress.yml b/.github/workflows/regress.yml index dd1a5de35d..1bd50a4096 100644 --- a/.github/workflows/regress.yml +++ b/.github/workflows/regress.yml @@ -103,7 +103,7 @@ jobs: name: Build container run: ./bin/build_container - name: Generate HTML ISA manual - run: ./do gen:html[generic_rv64] + run: ./do gen:html[example_rv64_with_overlay] regress-gen-ext-pdf: runs-on: ubuntu-latest env: diff --git a/.gitignore b/.gitignore index a9bdc6be41..8f6230a0b8 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,8 @@ .stamps .venv .asciidoctor +.vscode/* +!.vscode/launch.json .container-type diag-ditaa-* arch/manual/isa/**/riscv-isa-manual diff --git a/.gitmodules b/.gitmodules index 7b74de08dc..dca08ddbfe 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/riscv-tests"] + path = ext/riscv-tests + url = https://github.com/riscv-software-src/riscv-tests.git diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 49580e004c..00d1ce8e7b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -58,6 +58,12 @@ repos: # files: ^arch/manual/.*\.(yaml|yml)$ # args: ["--schemafile", "schemas/manual_version_schema.json"] + - repo: https://github.com/pre-commit/mirrors-clang-format + rev: "v18.1.3" + hooks: + - id: clang-format + types_or: [c++, c] + files: \.(hpp|cpp)$ - repo: https://github.com/psf/black-pre-commit-mirror rev: 25.1.0 hooks: diff --git a/.vscode/launch.json b/.vscode/launch.json index 09d8dae10e..ccaf87153d 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -27,6 +27,46 @@ "script": "gen:profile[RVA20]", "args": [], "askParameters": false + }, + { + "type": "cppdbg", + "name": "Run MC100-32 iss", + "request": "launch", + "program": "${workspaceFolder}/gen/cpp_hart_gen/MC100-32_Debug/build/iss", + "setupCommands": [ + { + "text": "set output-radix 16", + "description": "Display hex by default" + } + ], + "cwd": "${workspaceFolder}", + "args": [ + "-m", "MC100-32", "-c", + "${workspaceFolder}/cfgs/mc100-32-full-example.yaml", + "ext/riscv-tests/isa/rv32mi-p-mcsr" + ], + "linux": { + "MIMode": "gdb", + "miDebuggerPath": "${workspaceFolder}/bin/gdb" + } + }, + { + "type": "cppdbg", + "name": "Run MC100-32 coremark", + "request": "launch", + "program": "${workspaceFolder}/gen/cpp_hart_gen/MC100-32_Debug/build/iss", + "setupCommands": [ + { + "text": "set output-radix 16", + "description": "Display hex by default" + } + ], + "cwd": "${workspaceFolder}", + "args": ["-m", "MC100-32", "-c", "${workspaceFolder}/cfgs/mc100-32-full-example.yaml", "ext/riscv-coremark/coremark/coremark.bare.riscv"], + "linux": { + "MIMode": "gdb", + "miDebuggerPath": "${workspaceFolder}/bin/gdb" + } } ] } diff --git a/Gemfile b/Gemfile index 6c343eaf6e..199ed5b982 100644 --- a/Gemfile +++ b/Gemfile @@ -9,8 +9,9 @@ gem "asciidoctor-diagram", "~> 2.2" gem "asciidoctor-pdf" gem "base64" gem "bigdecimal" +gem "concurrent-ruby", require: "concurrent" +gem "concurrent-ruby-ext" gem "json_schemer", "~> 1.0" -gem "minitest" gem "pygments.rb" gem "rake", "~> 13.0" gem "rouge" @@ -21,7 +22,9 @@ gem "webrick" gem "yard" group :development do + gem "awesome_print" gem "debug" + gem "minitest" gem "rdbg" gem "rubocop-minitest" gem "ruby-prof" diff --git a/Gemfile.lock b/Gemfile.lock index 265db213ef..2a334c1ee3 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -9,8 +9,8 @@ GIT GEM remote: https://rubygems.org/ specs: - Ascii85 (1.1.1) - activesupport (8.0.0) + Ascii85 (2.0.1) + activesupport (8.0.2) base64 benchmark (>= 0.3) bigdecimal @@ -27,16 +27,16 @@ GEM public_suffix (>= 2.0.2, < 7.0) afm (0.2.2) asciidoctor (2.0.23) - asciidoctor-diagram (2.3.0) + asciidoctor-diagram (2.3.2) asciidoctor (>= 1.5.7, < 3.x) asciidoctor-diagram-ditaamini (~> 1.0) asciidoctor-diagram-plantuml (~> 1.2021) rexml asciidoctor-diagram-batik (1.17) asciidoctor-diagram-ditaamini (1.0.3) - asciidoctor-diagram-plantuml (1.2024.5) + asciidoctor-diagram-plantuml (1.2025.2) asciidoctor-diagram-batik (~> 1.17) - asciidoctor-pdf (2.3.14) + asciidoctor-pdf (2.3.19) asciidoctor (~> 2.0) concurrent-ruby (~> 1.1) matrix (~> 0.4) @@ -46,59 +46,69 @@ GEM prawn-table (~> 0.2.0) prawn-templates (~> 0.1.0) treetop (~> 1.6.0) - ast (2.4.2) + ttfunk (~> 1.7.0) + ast (2.4.3) + awesome_print (1.9.2) backport (1.2.0) base64 (0.2.0) - benchmark (0.3.0) - bigdecimal (3.1.8) - concurrent-ruby (1.3.3) - connection_pool (2.4.1) - css_parser (1.17.1) + benchmark (0.4.0) + bigdecimal (3.1.9) + concurrent-ruby (1.3.5) + concurrent-ruby-ext (1.3.5) + concurrent-ruby (= 1.3.5) + connection_pool (2.5.0) + css_parser (1.21.1) addressable - debug (1.9.2) + date (3.4.1) + debug (1.10.0) irb (~> 1.10) reline (>= 0.3.8) - diff-lcs (1.5.1) + diff-lcs (1.6.0) drb (2.2.1) - e2mmap (0.1.0) hana (1.3.7) hashery (2.1.2) - i18n (1.14.6) + i18n (1.14.7) concurrent-ruby (~> 1.0) - io-console (0.7.2) - irb (1.14.1) + io-console (0.8.0) + irb (1.15.1) + pp (>= 0.6.0) rdoc (>= 4.0.0) reline (>= 0.4.2) jaro_winkler (1.6.0) - json (2.7.2) + json (2.10.2) json_schemer (1.0.3) hana (~> 1.3) regexp_parser (~> 2.0) simpleidn (~> 0.2) - kramdown (2.4.0) - rexml + kramdown (2.5.1) + rexml (>= 3.3.9) kramdown-parser-gfm (1.1.0) kramdown (~> 2.0) - language_server-protocol (3.17.0.3) - logger (1.6.2) + language_server-protocol (3.17.0.4) + lint_roller (1.1.0) + logger (1.6.6) matrix (0.4.2) - minitest (5.24.1) - nokogiri (1.16.5-aarch64-linux) + minitest (5.25.5) + nokogiri (1.18.6-aarch64-linux-gnu) racc (~> 1.4) - nokogiri (1.16.5-x86_64-linux) + nokogiri (1.18.6-x86_64-linux-gnu) racc (~> 1.4) - parallel (1.24.0) - parser (3.3.2.0) + observer (0.1.2) + ostruct (0.6.1) + parallel (1.26.3) + parser (3.3.7.2) ast (~> 2.4.1) racc pdf-core (0.9.0) - pdf-reader (2.12.0) - Ascii85 (~> 1.0) + pdf-reader (2.14.1) + Ascii85 (>= 1.0, < 3.0, != 2.0.0) afm (~> 0.2.1) hashery (~> 2.0) ruby-rc4 ttfunk polyglot (0.3.5) + pp (0.6.2) + prettyprint prawn (2.4.0) pdf-core (~> 0.9.0) ttfunk (~> 1.7) @@ -114,76 +124,85 @@ GEM prawn-templates (0.1.2) pdf-reader (~> 2.0) prawn (~> 2.2) - psych (5.2.0) + prettyprint (0.2.0) + psych (5.2.3) + date stringio - public_suffix (6.0.0) + public_suffix (6.0.1) pygments.rb (3.0.0) - racc (1.8.0) + racc (1.8.1) rainbow (3.1.1) rake (13.2.1) - rbs (2.8.4) + rbs (3.9.1) + logger rdbg (0.1.0) debug (>= 1.2.2) - rdoc (6.8.1) + rdoc (6.13.0) psych (>= 4.0.0) - regexp_parser (2.9.2) - reline (0.5.11) + regexp_parser (2.10.0) + reline (0.6.0) io-console (~> 0.5) - reverse_markdown (2.1.1) + reverse_markdown (3.0.0) nokogiri - rexml (3.2.8) - strscan (>= 3.0.9) - rouge (4.3.0) - rubocop (1.64.1) + rexml (3.4.1) + rouge (4.5.1) + rubocop (1.74.0) json (~> 2.3) - language_server-protocol (>= 3.17.0) + language_server-protocol (~> 3.17.0.2) + lint_roller (~> 1.1.0) parallel (~> 1.10) parser (>= 3.3.0.2) rainbow (>= 2.2.2, < 4.0) - regexp_parser (>= 1.8, < 3.0) - rexml (>= 3.2.5, < 4.0) - rubocop-ast (>= 1.31.1, < 2.0) + regexp_parser (>= 2.9.3, < 3.0) + rubocop-ast (>= 1.38.0, < 2.0) ruby-progressbar (~> 1.7) - unicode-display_width (>= 2.4.0, < 3.0) - rubocop-ast (1.31.3) - parser (>= 3.3.1.0) - rubocop-minitest (0.35.1) - rubocop (>= 1.61, < 2.0) - rubocop-ast (>= 1.31.1, < 2.0) + unicode-display_width (>= 2.4.0, < 4.0) + rubocop-ast (1.41.0) + parser (>= 3.3.7.2) + rubocop-minitest (0.37.1) + lint_roller (~> 1.1) + rubocop (>= 1.72.1, < 2.0) + rubocop-ast (>= 1.38.0, < 2.0) ruby-prof (1.7.1) ruby-progressbar (1.13.0) ruby-rc4 (0.1.5) - securerandom (0.4.0) + securerandom (0.4.1) simpleidn (0.2.3) - solargraph (0.50.0) + solargraph (0.52.0) backport (~> 1.2) benchmark bundler (~> 2.0) diff-lcs (~> 1.4) - e2mmap - jaro_winkler (~> 1.5) + jaro_winkler (~> 1.6) kramdown (~> 2.3) kramdown-parser-gfm (~> 1.1) + logger (~> 1.6) + observer (~> 0.1) + ostruct (~> 0.6) parser (~> 3.0) - rbs (~> 2.0) - reverse_markdown (~> 2.0) + rbs (~> 3.0) + reverse_markdown (>= 2.0, < 4) rubocop (~> 1.38) thor (~> 1.0) tilt (~> 2.0) yard (~> 0.9, >= 0.9.24) - stringio (3.1.2) - strscan (3.1.0) - thor (1.3.1) - tilt (2.3.0) + yard-solargraph (~> 0.1) + stringio (3.1.5) + thor (1.3.2) + tilt (2.6.0) treetop (1.6.12) polyglot (~> 0.3) ttfunk (1.7.0) tzinfo (2.0.6) concurrent-ruby (~> 1.0) - unicode-display_width (2.5.0) - uri (1.0.2) - webrick (1.8.1) - yard (0.9.36) + unicode-display_width (3.1.4) + unicode-emoji (~> 4.0, >= 4.0.4) + unicode-emoji (4.0.4) + uri (1.0.3) + webrick (1.9.1) + yard (0.9.37) + yard-solargraph (0.1.0) + yard (~> 0.9) PLATFORMS aarch64-linux-gnu @@ -193,8 +212,11 @@ DEPENDENCIES activesupport asciidoctor-diagram (~> 2.2) asciidoctor-pdf + awesome_print base64 bigdecimal + concurrent-ruby + concurrent-ruby-ext debug json_schemer (~> 1.0) minitest diff --git a/README.adoc b/README.adoc index 30f7d8668d..e4a8267bf4 100644 --- a/README.adoc +++ b/README.adoc @@ -22,7 +22,7 @@ This repository contains: === Working examples: - * Generate https://riscv-software-src.github.io/riscv-unified-db/manual/html/index.html[configuration-specific documentation] tailored to the set of implemented extensions and unnamed implementation options (_e.g._, `./do gen:html[generic_rv64]`). + * Generate https://riscv-software-src.github.io/riscv-unified-db/manual/html/index.html[configuration-specific documentation] tailored to the set of implemented extensions and unnamed implementation options (_e.g._, `./do gen:html[example_rv64_with_overlay]`). ** Only implemented extensions/instructions/CSRs are included ** Unreachable/unimplemented parts of the formal specification are pruned away ** A dedicated documentation page for every implemented instruction, including its encoding, pruned execution behavior, and what types of exceptions it may cause. @@ -33,7 +33,7 @@ This repository contains: ** Per-instruction documentation ** Per-CSR documentation ** Formal specification - * Generate a single YAML file containing *everything* knowable about a configuration (_e.g._, `./do gen:cfg_arch[generic_rv64]`). + * Generate a single YAML file containing *everything* knowable about a configuration (_e.g._, `./do gen:cfg_arch[example_rv64_with_overlay]`). === Possibilities: @@ -103,8 +103,8 @@ Quick start: # generate all versions of ISA manual, as an Antora static website ./do gen:html_manual MANUAL_NAME=isa VERSIONS=all -# generate an implementation-specific spec for the 'generic_rv64' config -./do gen:arch[generic_rv64] +# generate an implementation-specific spec for the 'example_rv64_with_overlay' config +./do gen:arch[example_rv64_with_overlay] ---- == More info diff --git a/Rakefile b/Rakefile index e335a7285f..0d930c346c 100755 --- a/Rakefile +++ b/Rakefile @@ -1,5 +1,9 @@ # frozen_string_literal: true +$jobs = ENV["JOBS"].nil? ? 1 : ENV["JOBS"].to_i +Rake.application.options.thread_pool_size = $jobs +puts "Running with #{Rake.application.options.thread_pool_size} job(s)" + require "etc" $root = Pathname.new(__FILE__).dirname.realpath @@ -19,17 +23,64 @@ end directory "#{$root}/.stamps" -def cfg_arch_for(config_name) - Rake::Task["#{$root}/.stamps/resolve-#{config_name}.stamp"].invoke +def cfg_arch_for(config) + raise ArgumentError, "excpecting String or Pathname" unless config.is_a?(String) || config.is_a?(Pathname) + config = config.to_s + + $cfg_archs ||= {} + return $cfg_archs[config] unless $cfg_archs[config].nil? + + # does the gen cfg already exist? + if File.exist?("#{$root}/gen/cfgs/#{config}.yaml") + config_yaml = YAML.load_file("#{$root}/gen/cfgs/#{config}.yaml") + if File.mtime("#{$root}/gen/cfgs/#{config}.yaml") < File.mtime(config_yaml["$source"]) + cfg_arch = + ConfiguredArchitecture.new( + config, + $root / "gen" / "resolved_arch" / config + ) + $cfg_archs[config] = cfg_arch + return cfg_arch + end + end + + config_path = + if File.exist?("#{$root}/cfgs/#{config}.yaml") + "#{$root}/cfgs/#{config}.yaml" + elsif File.exist? config + File.realpath(config) + else + raise ArgumentError, "Can't find config #{config}" + end - @cfg_archs ||= {} - return @cfg_archs[config_name] if @cfg_archs.key?(config_name) + config_yaml = YAML.load_file(config_path) + config_name = config_yaml["name"] + + overlay_dir = + if config_yaml["arch_overlay"].nil? + "/does/not/exist" + elsif File.exist?("#{$root}/arch_overlay/#{config_yaml['arch_overlay']}") + "#{$root}/arch_overlay/#{config_yaml['arch_overlay']}" + elsif File.directory?(config_yaml["arch_overlay"]) + File.realpath(config_yaml["arch_overlay"]) + else + raise ArgumentError, "Can't find arch_overlay #{config_yaml['arch_overlay']}" + end + + config_yaml["arch_overlay"] = overlay_dir + config_yaml["$source"] = config_path + + # write the config with arch_overlay expanded + unless File.exist?("#{$root}/gen/cfgs/#{config_name}.yaml") && (File.mtime("#{$root}/gen/cfgs/#{config_name}.yaml") < File.mtime(config_path)) + FileUtils.mkdir_p "#{$root}/gen/cfgs" + File.write "#{$root}/gen/cfgs/#{config_name}.yaml", YAML.dump(config_yaml) + end + Rake::Task["#{$root}/.stamps/resolve-#{config_name}.stamp"].invoke - @cfg_archs[config_name] = + $cfg_archs[config_name] = ConfiguredArchitecture.new( config_name, - $root / "gen" / "resolved_arch" / config_name, - overlay_path: $root / "cfgs" / config_name / "arch_overlay" + $root / "gen" / "resolved_arch" / config_name ) end @@ -55,15 +106,23 @@ end # rule to generate standard for any configurations with an overlay rule %r{#{$root}/.stamps/resolve-.+\.stamp} => proc { |tname| cfg_name = File.basename(tname, ".stamp").sub("resolve-", "") + raise "Missing gen/cfgs/#{tname}" unless File.exist?("#{$root}/cfgs/#{cfg_name}.yaml") + + cfg_path = "#{$root}/cfgs/#{cfg_name}.yaml" + cfg = Config.create(cfg_path) arch_files = Dir.glob("#{$root}/arch/**/*.yaml") - overlay_files = Dir.glob("#{$root}/cfgs/#{cfg_name}/arch_overlay/**/*.yaml") + overlay_files = cfg.overlay? ? Dir.glob("#{cfg.arch_overlay_abs}/**/*.yaml") : [] [ "#{$root}/.stamps", "#{$root}/lib/yaml_resolver.py" ] + arch_files + overlay_files } do |t| cfg_name = File.basename(t.name, ".stamp").sub("resolve-", "") - sh "#{$root}/.home/.venv/bin/python3 lib/yaml_resolver.py merge arch cfgs/#{cfg_name}/arch_overlay gen/arch/#{cfg_name}" + cfg_path = "#{$root}/cfgs/#{cfg_name}.yaml" + cfg = Config.create(cfg_path) + + overlay_dir = cfg.overlay? ? cfg.arch_overlay_abs : "/does/not/exist" + sh "#{$root}/.home/.venv/bin/python3 lib/yaml_resolver.py merge arch #{overlay_dir} gen/arch/#{cfg_name}" sh "#{$root}/.home/.venv/bin/python3 lib/yaml_resolver.py resolve gen/arch/#{cfg_name} gen/resolved_arch/#{cfg_name}" FileUtils.touch t.name @@ -117,19 +176,68 @@ end namespace :test do - task :insts do - puts "Checking instruction encodings..." - inst_paths = Dir.glob("#{$root}/arch/inst/**/*.yaml").map { |f| Pathname.new(f) } - inst_paths.each do |inst_path| - Validator.instance.validate_instruction(inst_path) + desc "Check that instruction encodings in the DB are consistent and do not conflict" + task :inst_encodings do + print "Checking for conflicts in instruction encodings.." + + cfg_arch = cfg_arch_for("_") + insts = cfg_arch.instructions + failed = false + insts.each_with_index do |inst, idx| + [32, 64].each do |xlen| + next unless inst.defined_in_base?(xlen) + + (idx...insts.size).each do |other_idx| + other_inst = insts[other_idx] + next unless other_inst.defined_in_base?(xlen) + next if other_inst == inst + + if inst.bad_encoding_conflict?(xlen, other_inst) + warn "In RV#{xlen}: #{inst.name} (#{inst.encoding(xlen).format}) conflicts with #{other_inst.name} (#{other_inst.encoding(xlen).format})" + failed = true + end + end + end end - puts "All instruction encodings pass basic sanity tests" + raise "Encoding test failed" if failed + + puts "done" end + + desc "Check that CSR definitions in the DB are consistent and do not conflict" + task :csrs do + print "Checking for conflicts in CSRs.." + + cfg_arch = cfg_arch_for("_") + csrs = cfg_arch.csrs + failed = false + csrs.each_with_index do |csr, idx| + [32, 64].each do |xlen| + next unless csr.defined_in_base?(xlen) + + (idx...csrs.size).each do |other_idx| + other_csr = csrs[other_idx] + next unless other_csr.defined_in_base?(xlen) + next if other_csr == csr + + if csr.address == other_csr.address && !csr.address.nil? + warn "CSRs #{csr.name} and #{other_csr.name} have conflicting addresses (#{csr.address})" + failed = true + end + end + end + end + raise "CSR test failed" if failed + + puts "done" + end + task schema: "#{$root}/.stamps/resolve-_.stamp" do puts "Checking arch files against schema.." Architecture.new("#{$root}/gen/resolved_arch/_").validate(show_progress: true) puts "All files validate against their schema" end + task idl: ["#{$root}/.stamps/resolve-rv32.stamp", "#{$root}/.stamps/resolve-rv64.stamp"] do print "Parsing IDL code for RV32..." cfg_arch32 = cfg_arch_for("rv32") @@ -303,6 +411,7 @@ namespace :test do Rake::Task["test:lib"].invoke Rake::Task["test:schema"].invoke Rake::Task["test:idl"].invoke + Rake::Task["test:inst_encodings"].invoke end desc <<~DESC @@ -321,7 +430,7 @@ namespace :test do ENV["VERSION"] = "latest" Rake::Task["gen:ext_pdf"].invoke - Rake::Task["gen:html"].invoke("generic_rv64") + Rake::Task["gen:html"].invoke("example_rv64_with_overlay") Rake::Task["#{$root}/gen/certificate_doc/pdf/MockCertificateModel.pdf"].invoke Rake::Task["#{$root}/gen/profile_doc/pdf/MockProfileRelease.pdf"].invoke diff --git a/arch/csr/I/pmpcfg0.yaml b/arch/csr/I/pmpcfg0.yaml index 0051ff041d..2630c3ba38 100644 --- a/arch/csr/I/pmpcfg0.yaml +++ b/arch/csr/I/pmpcfg0.yaml @@ -373,7 +373,7 @@ fields: return 0; } sw_write(csr_value): | - if ((CSR[pmpcfg0].pmp5cfg & 0x80) == 0) { + if ((xlen() == 64) && (CSR[pmpcfg0].pmp5cfg & 0x80) == 0) { # entry is not locked if (!(((csr_value.pmp5cfg & 0x1) == 0) && ((csr_value.pmp5cfg & 0x2) == 0x2))) { # not R = 0, W =1, which is reserved @@ -436,7 +436,7 @@ fields: return 0; } sw_write(csr_value): | - if ((CSR[pmpcfg0].pmp6cfg & 0x80) == 0) { + if ((xlen() == 64) && (CSR[pmpcfg0].pmp6cfg & 0x80) == 0) { # entry is not locked if (!(((csr_value.pmp6cfg & 0x1) == 0) && ((csr_value.pmp6cfg & 0x2) == 0x2))) { # not R = 0, W =1, which is reserved @@ -499,7 +499,7 @@ fields: return 0; } sw_write(csr_value): | - if ((CSR[pmpcfg0].pmp7cfg & 0x80) == 0) { + if ((xlen() == 64) && (CSR[pmpcfg0].pmp7cfg & 0x80) == 0) { # entry is not locked if (!(((csr_value.pmp7cfg & 0x1) == 0) && ((csr_value.pmp7cfg & 0x2) == 0x2))) { # not R = 0, W =1, which is reserved diff --git a/arch/csr/I/pmpcfg10.yaml b/arch/csr/I/pmpcfg10.yaml index 74d8950b36..b0a9f613ff 100644 --- a/arch/csr/I/pmpcfg10.yaml +++ b/arch/csr/I/pmpcfg10.yaml @@ -373,7 +373,7 @@ fields: return 0; } sw_write(csr_value): | - if ((CSR[pmpcfg10].pmp45cfg & 0x80) == 0) { + if ((xlen() == 64) && (CSR[pmpcfg10].pmp45cfg & 0x80) == 0) { # entry is not locked if (!(((csr_value.pmp45cfg & 0x1) == 0) && ((csr_value.pmp45cfg & 0x2) == 0x2))) { # not R = 0, W =1, which is reserved @@ -436,7 +436,7 @@ fields: return 0; } sw_write(csr_value): | - if ((CSR[pmpcfg10].pmp46cfg & 0x80) == 0) { + if ((xlen() == 64) && (CSR[pmpcfg10].pmp46cfg & 0x80) == 0) { # entry is not locked if (!(((csr_value.pmp46cfg & 0x1) == 0) && ((csr_value.pmp46cfg & 0x2) == 0x2))) { # not R = 0, W =1, which is reserved @@ -499,7 +499,7 @@ fields: return 0; } sw_write(csr_value): | - if ((CSR[pmpcfg10].pmp47cfg & 0x80) == 0) { + if ((xlen() == 64) && (CSR[pmpcfg10].pmp47cfg & 0x80) == 0) { # entry is not locked if (!(((csr_value.pmp47cfg & 0x1) == 0) && ((csr_value.pmp47cfg & 0x2) == 0x2))) { # not R = 0, W =1, which is reserved diff --git a/arch/csr/I/pmpcfg12.yaml b/arch/csr/I/pmpcfg12.yaml index a92d192f44..7943b623ab 100644 --- a/arch/csr/I/pmpcfg12.yaml +++ b/arch/csr/I/pmpcfg12.yaml @@ -373,7 +373,7 @@ fields: return 0; } sw_write(csr_value): | - if ((CSR[pmpcfg12].pmp53cfg & 0x80) == 0) { + if ((xlen() == 64) && (CSR[pmpcfg12].pmp53cfg & 0x80) == 0) { # entry is not locked if (!(((csr_value.pmp53cfg & 0x1) == 0) && ((csr_value.pmp53cfg & 0x2) == 0x2))) { # not R = 0, W =1, which is reserved @@ -436,7 +436,7 @@ fields: return 0; } sw_write(csr_value): | - if ((CSR[pmpcfg12].pmp54cfg & 0x80) == 0) { + if ((xlen() == 64) && (CSR[pmpcfg12].pmp54cfg & 0x80) == 0) { # entry is not locked if (!(((csr_value.pmp54cfg & 0x1) == 0) && ((csr_value.pmp54cfg & 0x2) == 0x2))) { # not R = 0, W =1, which is reserved @@ -499,7 +499,7 @@ fields: return 0; } sw_write(csr_value): | - if ((CSR[pmpcfg12].pmp55cfg & 0x80) == 0) { + if ((xlen() == 64) && (CSR[pmpcfg12].pmp55cfg & 0x80) == 0) { # entry is not locked if (!(((csr_value.pmp55cfg & 0x1) == 0) && ((csr_value.pmp55cfg & 0x2) == 0x2))) { # not R = 0, W =1, which is reserved diff --git a/arch/csr/I/pmpcfg14.yaml b/arch/csr/I/pmpcfg14.yaml index 3b1fe36291..062513daa8 100644 --- a/arch/csr/I/pmpcfg14.yaml +++ b/arch/csr/I/pmpcfg14.yaml @@ -373,7 +373,7 @@ fields: return 0; } sw_write(csr_value): | - if ((CSR[pmpcfg14].pmp61cfg & 0x80) == 0) { + if ((xlen() == 64) && (CSR[pmpcfg14].pmp61cfg & 0x80) == 0) { # entry is not locked if (!(((csr_value.pmp61cfg & 0x1) == 0) && ((csr_value.pmp61cfg & 0x2) == 0x2))) { # not R = 0, W =1, which is reserved @@ -436,7 +436,7 @@ fields: return 0; } sw_write(csr_value): | - if ((CSR[pmpcfg14].pmp62cfg & 0x80) == 0) { + if ((xlen() == 64) && (CSR[pmpcfg14].pmp62cfg & 0x80) == 0) { # entry is not locked if (!(((csr_value.pmp62cfg & 0x1) == 0) && ((csr_value.pmp62cfg & 0x2) == 0x2))) { # not R = 0, W =1, which is reserved @@ -499,7 +499,7 @@ fields: return 0; } sw_write(csr_value): | - if ((CSR[pmpcfg14].pmp63cfg & 0x80) == 0) { + if ((xlen() == 64) && (CSR[pmpcfg14].pmp63cfg & 0x80) == 0) { # entry is not locked if (!(((csr_value.pmp63cfg & 0x1) == 0) && ((csr_value.pmp63cfg & 0x2) == 0x2))) { # not R = 0, W =1, which is reserved diff --git a/arch/csr/I/pmpcfg2.yaml b/arch/csr/I/pmpcfg2.yaml index c852b7514a..771dceccbe 100644 --- a/arch/csr/I/pmpcfg2.yaml +++ b/arch/csr/I/pmpcfg2.yaml @@ -373,7 +373,7 @@ fields: return 0; } sw_write(csr_value): | - if ((CSR[pmpcfg2].pmp13cfg & 0x80) == 0) { + if ((xlen() == 64) && (CSR[pmpcfg2].pmp13cfg & 0x80) == 0) { # entry is not locked if (!(((csr_value.pmp13cfg & 0x1) == 0) && ((csr_value.pmp13cfg & 0x2) == 0x2))) { # not R = 0, W =1, which is reserved @@ -436,7 +436,7 @@ fields: return 0; } sw_write(csr_value): | - if ((CSR[pmpcfg2].pmp14cfg & 0x80) == 0) { + if ((xlen() == 64) && (CSR[pmpcfg2].pmp14cfg & 0x80) == 0) { # entry is not locked if (!(((csr_value.pmp14cfg & 0x1) == 0) && ((csr_value.pmp14cfg & 0x2) == 0x2))) { # not R = 0, W =1, which is reserved @@ -499,7 +499,7 @@ fields: return 0; } sw_write(csr_value): | - if ((CSR[pmpcfg2].pmp15cfg & 0x80) == 0) { + if ((xlen() == 64) && (CSR[pmpcfg2].pmp15cfg & 0x80) == 0) { # entry is not locked if (!(((csr_value.pmp15cfg & 0x1) == 0) && ((csr_value.pmp15cfg & 0x2) == 0x2))) { # not R = 0, W =1, which is reserved diff --git a/arch/csr/I/pmpcfg4.yaml b/arch/csr/I/pmpcfg4.yaml index 48237d9172..7079c73d6d 100644 --- a/arch/csr/I/pmpcfg4.yaml +++ b/arch/csr/I/pmpcfg4.yaml @@ -373,7 +373,7 @@ fields: return 0; } sw_write(csr_value): | - if ((CSR[pmpcfg4].pmp21cfg & 0x80) == 0) { + if ((xlen() == 64) && (CSR[pmpcfg4].pmp21cfg & 0x80) == 0) { # entry is not locked if (!(((csr_value.pmp21cfg & 0x1) == 0) && ((csr_value.pmp21cfg & 0x2) == 0x2))) { # not R = 0, W =1, which is reserved @@ -436,7 +436,7 @@ fields: return 0; } sw_write(csr_value): | - if ((CSR[pmpcfg4].pmp22cfg & 0x80) == 0) { + if ((xlen() == 64) && (CSR[pmpcfg4].pmp22cfg & 0x80) == 0) { # entry is not locked if (!(((csr_value.pmp22cfg & 0x1) == 0) && ((csr_value.pmp22cfg & 0x2) == 0x2))) { # not R = 0, W =1, which is reserved @@ -499,7 +499,7 @@ fields: return 0; } sw_write(csr_value): | - if ((CSR[pmpcfg4].pmp23cfg & 0x80) == 0) { + if ((xlen() == 64) && (CSR[pmpcfg4].pmp23cfg & 0x80) == 0) { # entry is not locked if (!(((csr_value.pmp23cfg & 0x1) == 0) && ((csr_value.pmp23cfg & 0x2) == 0x2))) { # not R = 0, W =1, which is reserved diff --git a/arch/csr/I/pmpcfg6.yaml b/arch/csr/I/pmpcfg6.yaml index 59c505bed3..74690b12cf 100644 --- a/arch/csr/I/pmpcfg6.yaml +++ b/arch/csr/I/pmpcfg6.yaml @@ -373,7 +373,7 @@ fields: return 0; } sw_write(csr_value): | - if ((CSR[pmpcfg6].pmp29cfg & 0x80) == 0) { + if ((xlen() == 64) && (CSR[pmpcfg6].pmp29cfg & 0x80) == 0) { # entry is not locked if (!(((csr_value.pmp29cfg & 0x1) == 0) && ((csr_value.pmp29cfg & 0x2) == 0x2))) { # not R = 0, W =1, which is reserved @@ -436,7 +436,7 @@ fields: return 0; } sw_write(csr_value): | - if ((CSR[pmpcfg6].pmp30cfg & 0x80) == 0) { + if ((xlen() == 64) && (CSR[pmpcfg6].pmp30cfg & 0x80) == 0) { # entry is not locked if (!(((csr_value.pmp30cfg & 0x1) == 0) && ((csr_value.pmp30cfg & 0x2) == 0x2))) { # not R = 0, W =1, which is reserved @@ -499,7 +499,7 @@ fields: return 0; } sw_write(csr_value): | - if ((CSR[pmpcfg6].pmp31cfg & 0x80) == 0) { + if ((xlen() == 64) && (CSR[pmpcfg6].pmp31cfg & 0x80) == 0) { # entry is not locked if (!(((csr_value.pmp31cfg & 0x1) == 0) && ((csr_value.pmp31cfg & 0x2) == 0x2))) { # not R = 0, W =1, which is reserved diff --git a/arch/csr/I/pmpcfg8.yaml b/arch/csr/I/pmpcfg8.yaml index 683ee1e44f..79b006f5e9 100644 --- a/arch/csr/I/pmpcfg8.yaml +++ b/arch/csr/I/pmpcfg8.yaml @@ -373,7 +373,7 @@ fields: return 0; } sw_write(csr_value): | - if ((CSR[pmpcfg8].pmp37cfg & 0x80) == 0) { + if ((xlen() == 64) && (CSR[pmpcfg8].pmp37cfg & 0x80) == 0) { # entry is not locked if (!(((csr_value.pmp37cfg & 0x1) == 0) && ((csr_value.pmp37cfg & 0x2) == 0x2))) { # not R = 0, W =1, which is reserved @@ -436,7 +436,7 @@ fields: return 0; } sw_write(csr_value): | - if ((CSR[pmpcfg8].pmp38cfg & 0x80) == 0) { + if ((xlen() == 64) && (CSR[pmpcfg8].pmp38cfg & 0x80) == 0) { # entry is not locked if (!(((csr_value.pmp38cfg & 0x1) == 0) && ((csr_value.pmp38cfg & 0x2) == 0x2))) { # not R = 0, W =1, which is reserved @@ -499,7 +499,7 @@ fields: return 0; } sw_write(csr_value): | - if ((CSR[pmpcfg8].pmp39cfg & 0x80) == 0) { + if ((xlen() == 64) && (CSR[pmpcfg8].pmp39cfg & 0x80) == 0) { # entry is not locked if (!(((csr_value.pmp39cfg & 0x1) == 0) && ((csr_value.pmp39cfg & 0x2) == 0x2))) { # not R = 0, W =1, which is reserved diff --git a/arch/csr/I/pmpcfgN.layout b/arch/csr/I/pmpcfgN.layout index b9d07665c4..d64fcf95db 100644 --- a/arch/csr/I/pmpcfgN.layout +++ b/arch/csr/I/pmpcfgN.layout @@ -68,7 +68,7 @@ fields: return 0; } sw_write(csr_value): | - if ((CSR[<%= "pmpcfg#{pmpcfg_num}" %>].pmp<%= pmpcfg_num*4 + i %>cfg & 0x80) == 0) { + if (<%- if i > 4 -%>(xlen() == 64) && <%- end -%>(CSR[<%= "pmpcfg#{pmpcfg_num}" %>].pmp<%= pmpcfg_num*4 + i %>cfg & 0x80) == 0) { # entry is not locked if (!(((csr_value.pmp<%= pmpcfg_num*4 + i %>cfg & 0x1) == 0) && ((csr_value.pmp<%= pmpcfg_num*4 + i %>cfg & 0x2) == 0x2))) { # not R = 0, W =1, which is reserved diff --git a/arch/csr/Zihpm/hpmcounter10.yaml b/arch/csr/Zihpm/hpmcounter10.yaml index 509504b052..40b6fbdbcc 100644 --- a/arch/csr/Zihpm/hpmcounter10.yaml +++ b/arch/csr/Zihpm/hpmcounter10.yaml @@ -86,7 +86,7 @@ sw_read(): | } } else if (mode() == PrivilegeMode::VS) { # access in VS mode - if (CSR[hcounteren].HPM10 == 1'b0 && CSR[mcounteren] == 1'b1) { + if (CSR[hcounteren].HPM10 == 1'b0 && CSR[mcounteren].HPM10 == 1'b1) { raise(ExceptionCode::VirtualInstruction, mode(), $encoding); } else if (CSR[mcounteren].HPM10 == 1'b0) { raise(ExceptionCode::IllegalInstruction, mode(), $encoding); diff --git a/arch/csr/Zihpm/hpmcounter10h.yaml b/arch/csr/Zihpm/hpmcounter10h.yaml index 86c92aae09..b903ef0217 100644 --- a/arch/csr/Zihpm/hpmcounter10h.yaml +++ b/arch/csr/Zihpm/hpmcounter10h.yaml @@ -56,7 +56,7 @@ sw_read(): | } } else if (mode() == PrivilegeMode::VS) { # access in VS mode - if (CSR[hcounteren].HPM10 == 1'b0 && CSR[mcounteren] == 1'b1) { + if (CSR[hcounteren].HPM10 == 1'b0 && CSR[mcounteren].HPM10 == 1'b1) { raise(ExceptionCode::VirtualInstruction, mode(), $encoding); } else if (CSR[mcounteren].HPM10 == 1'b0) { raise(ExceptionCode::IllegalInstruction, mode(), $encoding); diff --git a/arch/csr/Zihpm/hpmcounter11.yaml b/arch/csr/Zihpm/hpmcounter11.yaml index 81e45feb52..1217cc8161 100644 --- a/arch/csr/Zihpm/hpmcounter11.yaml +++ b/arch/csr/Zihpm/hpmcounter11.yaml @@ -86,7 +86,7 @@ sw_read(): | } } else if (mode() == PrivilegeMode::VS) { # access in VS mode - if (CSR[hcounteren].HPM11 == 1'b0 && CSR[mcounteren] == 1'b1) { + if (CSR[hcounteren].HPM11 == 1'b0 && CSR[mcounteren].HPM11 == 1'b1) { raise(ExceptionCode::VirtualInstruction, mode(), $encoding); } else if (CSR[mcounteren].HPM11 == 1'b0) { raise(ExceptionCode::IllegalInstruction, mode(), $encoding); diff --git a/arch/csr/Zihpm/hpmcounter11h.yaml b/arch/csr/Zihpm/hpmcounter11h.yaml index 2d0954a5e2..24c3187660 100644 --- a/arch/csr/Zihpm/hpmcounter11h.yaml +++ b/arch/csr/Zihpm/hpmcounter11h.yaml @@ -56,7 +56,7 @@ sw_read(): | } } else if (mode() == PrivilegeMode::VS) { # access in VS mode - if (CSR[hcounteren].HPM11 == 1'b0 && CSR[mcounteren] == 1'b1) { + if (CSR[hcounteren].HPM11 == 1'b0 && CSR[mcounteren].HPM11 == 1'b1) { raise(ExceptionCode::VirtualInstruction, mode(), $encoding); } else if (CSR[mcounteren].HPM11 == 1'b0) { raise(ExceptionCode::IllegalInstruction, mode(), $encoding); diff --git a/arch/csr/Zihpm/hpmcounter12.yaml b/arch/csr/Zihpm/hpmcounter12.yaml index 0960f7578e..f8f296a581 100644 --- a/arch/csr/Zihpm/hpmcounter12.yaml +++ b/arch/csr/Zihpm/hpmcounter12.yaml @@ -86,7 +86,7 @@ sw_read(): | } } else if (mode() == PrivilegeMode::VS) { # access in VS mode - if (CSR[hcounteren].HPM12 == 1'b0 && CSR[mcounteren] == 1'b1) { + if (CSR[hcounteren].HPM12 == 1'b0 && CSR[mcounteren].HPM12 == 1'b1) { raise(ExceptionCode::VirtualInstruction, mode(), $encoding); } else if (CSR[mcounteren].HPM12 == 1'b0) { raise(ExceptionCode::IllegalInstruction, mode(), $encoding); diff --git a/arch/csr/Zihpm/hpmcounter12h.yaml b/arch/csr/Zihpm/hpmcounter12h.yaml index 696b277901..c0c468c19d 100644 --- a/arch/csr/Zihpm/hpmcounter12h.yaml +++ b/arch/csr/Zihpm/hpmcounter12h.yaml @@ -56,7 +56,7 @@ sw_read(): | } } else if (mode() == PrivilegeMode::VS) { # access in VS mode - if (CSR[hcounteren].HPM12 == 1'b0 && CSR[mcounteren] == 1'b1) { + if (CSR[hcounteren].HPM12 == 1'b0 && CSR[mcounteren].HPM12 == 1'b1) { raise(ExceptionCode::VirtualInstruction, mode(), $encoding); } else if (CSR[mcounteren].HPM12 == 1'b0) { raise(ExceptionCode::IllegalInstruction, mode(), $encoding); diff --git a/arch/csr/Zihpm/hpmcounter13.yaml b/arch/csr/Zihpm/hpmcounter13.yaml index 24e9fc06fe..8713f1e88b 100644 --- a/arch/csr/Zihpm/hpmcounter13.yaml +++ b/arch/csr/Zihpm/hpmcounter13.yaml @@ -86,7 +86,7 @@ sw_read(): | } } else if (mode() == PrivilegeMode::VS) { # access in VS mode - if (CSR[hcounteren].HPM13 == 1'b0 && CSR[mcounteren] == 1'b1) { + if (CSR[hcounteren].HPM13 == 1'b0 && CSR[mcounteren].HPM13 == 1'b1) { raise(ExceptionCode::VirtualInstruction, mode(), $encoding); } else if (CSR[mcounteren].HPM13 == 1'b0) { raise(ExceptionCode::IllegalInstruction, mode(), $encoding); diff --git a/arch/csr/Zihpm/hpmcounter13h.yaml b/arch/csr/Zihpm/hpmcounter13h.yaml index 7eac1cb8be..cd055f9404 100644 --- a/arch/csr/Zihpm/hpmcounter13h.yaml +++ b/arch/csr/Zihpm/hpmcounter13h.yaml @@ -56,7 +56,7 @@ sw_read(): | } } else if (mode() == PrivilegeMode::VS) { # access in VS mode - if (CSR[hcounteren].HPM13 == 1'b0 && CSR[mcounteren] == 1'b1) { + if (CSR[hcounteren].HPM13 == 1'b0 && CSR[mcounteren].HPM13 == 1'b1) { raise(ExceptionCode::VirtualInstruction, mode(), $encoding); } else if (CSR[mcounteren].HPM13 == 1'b0) { raise(ExceptionCode::IllegalInstruction, mode(), $encoding); diff --git a/arch/csr/Zihpm/hpmcounter14.yaml b/arch/csr/Zihpm/hpmcounter14.yaml index 8708dd5a6b..47303cbb4e 100644 --- a/arch/csr/Zihpm/hpmcounter14.yaml +++ b/arch/csr/Zihpm/hpmcounter14.yaml @@ -86,7 +86,7 @@ sw_read(): | } } else if (mode() == PrivilegeMode::VS) { # access in VS mode - if (CSR[hcounteren].HPM14 == 1'b0 && CSR[mcounteren] == 1'b1) { + if (CSR[hcounteren].HPM14 == 1'b0 && CSR[mcounteren].HPM14 == 1'b1) { raise(ExceptionCode::VirtualInstruction, mode(), $encoding); } else if (CSR[mcounteren].HPM14 == 1'b0) { raise(ExceptionCode::IllegalInstruction, mode(), $encoding); diff --git a/arch/csr/Zihpm/hpmcounter14h.yaml b/arch/csr/Zihpm/hpmcounter14h.yaml index f34cb427a6..1a4290b354 100644 --- a/arch/csr/Zihpm/hpmcounter14h.yaml +++ b/arch/csr/Zihpm/hpmcounter14h.yaml @@ -56,7 +56,7 @@ sw_read(): | } } else if (mode() == PrivilegeMode::VS) { # access in VS mode - if (CSR[hcounteren].HPM14 == 1'b0 && CSR[mcounteren] == 1'b1) { + if (CSR[hcounteren].HPM14 == 1'b0 && CSR[mcounteren].HPM14 == 1'b1) { raise(ExceptionCode::VirtualInstruction, mode(), $encoding); } else if (CSR[mcounteren].HPM14 == 1'b0) { raise(ExceptionCode::IllegalInstruction, mode(), $encoding); diff --git a/arch/csr/Zihpm/hpmcounter15.yaml b/arch/csr/Zihpm/hpmcounter15.yaml index 007f40d8c7..a18732b248 100644 --- a/arch/csr/Zihpm/hpmcounter15.yaml +++ b/arch/csr/Zihpm/hpmcounter15.yaml @@ -86,7 +86,7 @@ sw_read(): | } } else if (mode() == PrivilegeMode::VS) { # access in VS mode - if (CSR[hcounteren].HPM15 == 1'b0 && CSR[mcounteren] == 1'b1) { + if (CSR[hcounteren].HPM15 == 1'b0 && CSR[mcounteren].HPM15 == 1'b1) { raise(ExceptionCode::VirtualInstruction, mode(), $encoding); } else if (CSR[mcounteren].HPM15 == 1'b0) { raise(ExceptionCode::IllegalInstruction, mode(), $encoding); diff --git a/arch/csr/Zihpm/hpmcounter15h.yaml b/arch/csr/Zihpm/hpmcounter15h.yaml index 3212ae429a..8f7a0fb94e 100644 --- a/arch/csr/Zihpm/hpmcounter15h.yaml +++ b/arch/csr/Zihpm/hpmcounter15h.yaml @@ -56,7 +56,7 @@ sw_read(): | } } else if (mode() == PrivilegeMode::VS) { # access in VS mode - if (CSR[hcounteren].HPM15 == 1'b0 && CSR[mcounteren] == 1'b1) { + if (CSR[hcounteren].HPM15 == 1'b0 && CSR[mcounteren].HPM15 == 1'b1) { raise(ExceptionCode::VirtualInstruction, mode(), $encoding); } else if (CSR[mcounteren].HPM15 == 1'b0) { raise(ExceptionCode::IllegalInstruction, mode(), $encoding); diff --git a/arch/csr/Zihpm/hpmcounter16.yaml b/arch/csr/Zihpm/hpmcounter16.yaml index 016420bf0f..61e454230c 100644 --- a/arch/csr/Zihpm/hpmcounter16.yaml +++ b/arch/csr/Zihpm/hpmcounter16.yaml @@ -86,7 +86,7 @@ sw_read(): | } } else if (mode() == PrivilegeMode::VS) { # access in VS mode - if (CSR[hcounteren].HPM16 == 1'b0 && CSR[mcounteren] == 1'b1) { + if (CSR[hcounteren].HPM16 == 1'b0 && CSR[mcounteren].HPM16 == 1'b1) { raise(ExceptionCode::VirtualInstruction, mode(), $encoding); } else if (CSR[mcounteren].HPM16 == 1'b0) { raise(ExceptionCode::IllegalInstruction, mode(), $encoding); diff --git a/arch/csr/Zihpm/hpmcounter16h.yaml b/arch/csr/Zihpm/hpmcounter16h.yaml index 9ffa840191..b3d05236b9 100644 --- a/arch/csr/Zihpm/hpmcounter16h.yaml +++ b/arch/csr/Zihpm/hpmcounter16h.yaml @@ -56,7 +56,7 @@ sw_read(): | } } else if (mode() == PrivilegeMode::VS) { # access in VS mode - if (CSR[hcounteren].HPM16 == 1'b0 && CSR[mcounteren] == 1'b1) { + if (CSR[hcounteren].HPM16 == 1'b0 && CSR[mcounteren].HPM16 == 1'b1) { raise(ExceptionCode::VirtualInstruction, mode(), $encoding); } else if (CSR[mcounteren].HPM16 == 1'b0) { raise(ExceptionCode::IllegalInstruction, mode(), $encoding); diff --git a/arch/csr/Zihpm/hpmcounter17.yaml b/arch/csr/Zihpm/hpmcounter17.yaml index 15f4889bdb..2d130ca5d2 100644 --- a/arch/csr/Zihpm/hpmcounter17.yaml +++ b/arch/csr/Zihpm/hpmcounter17.yaml @@ -86,7 +86,7 @@ sw_read(): | } } else if (mode() == PrivilegeMode::VS) { # access in VS mode - if (CSR[hcounteren].HPM17 == 1'b0 && CSR[mcounteren] == 1'b1) { + if (CSR[hcounteren].HPM17 == 1'b0 && CSR[mcounteren].HPM17 == 1'b1) { raise(ExceptionCode::VirtualInstruction, mode(), $encoding); } else if (CSR[mcounteren].HPM17 == 1'b0) { raise(ExceptionCode::IllegalInstruction, mode(), $encoding); diff --git a/arch/csr/Zihpm/hpmcounter17h.yaml b/arch/csr/Zihpm/hpmcounter17h.yaml index 801ef507f7..0cd5f864e3 100644 --- a/arch/csr/Zihpm/hpmcounter17h.yaml +++ b/arch/csr/Zihpm/hpmcounter17h.yaml @@ -56,7 +56,7 @@ sw_read(): | } } else if (mode() == PrivilegeMode::VS) { # access in VS mode - if (CSR[hcounteren].HPM17 == 1'b0 && CSR[mcounteren] == 1'b1) { + if (CSR[hcounteren].HPM17 == 1'b0 && CSR[mcounteren].HPM17 == 1'b1) { raise(ExceptionCode::VirtualInstruction, mode(), $encoding); } else if (CSR[mcounteren].HPM17 == 1'b0) { raise(ExceptionCode::IllegalInstruction, mode(), $encoding); diff --git a/arch/csr/Zihpm/hpmcounter18.yaml b/arch/csr/Zihpm/hpmcounter18.yaml index 1b378ac50a..b115107009 100644 --- a/arch/csr/Zihpm/hpmcounter18.yaml +++ b/arch/csr/Zihpm/hpmcounter18.yaml @@ -86,7 +86,7 @@ sw_read(): | } } else if (mode() == PrivilegeMode::VS) { # access in VS mode - if (CSR[hcounteren].HPM18 == 1'b0 && CSR[mcounteren] == 1'b1) { + if (CSR[hcounteren].HPM18 == 1'b0 && CSR[mcounteren].HPM18 == 1'b1) { raise(ExceptionCode::VirtualInstruction, mode(), $encoding); } else if (CSR[mcounteren].HPM18 == 1'b0) { raise(ExceptionCode::IllegalInstruction, mode(), $encoding); diff --git a/arch/csr/Zihpm/hpmcounter18h.yaml b/arch/csr/Zihpm/hpmcounter18h.yaml index cd38aad1ba..d45aa624b9 100644 --- a/arch/csr/Zihpm/hpmcounter18h.yaml +++ b/arch/csr/Zihpm/hpmcounter18h.yaml @@ -56,7 +56,7 @@ sw_read(): | } } else if (mode() == PrivilegeMode::VS) { # access in VS mode - if (CSR[hcounteren].HPM18 == 1'b0 && CSR[mcounteren] == 1'b1) { + if (CSR[hcounteren].HPM18 == 1'b0 && CSR[mcounteren].HPM18 == 1'b1) { raise(ExceptionCode::VirtualInstruction, mode(), $encoding); } else if (CSR[mcounteren].HPM18 == 1'b0) { raise(ExceptionCode::IllegalInstruction, mode(), $encoding); diff --git a/arch/csr/Zihpm/hpmcounter19.yaml b/arch/csr/Zihpm/hpmcounter19.yaml index f0a27a7a43..12a99ea82e 100644 --- a/arch/csr/Zihpm/hpmcounter19.yaml +++ b/arch/csr/Zihpm/hpmcounter19.yaml @@ -86,7 +86,7 @@ sw_read(): | } } else if (mode() == PrivilegeMode::VS) { # access in VS mode - if (CSR[hcounteren].HPM19 == 1'b0 && CSR[mcounteren] == 1'b1) { + if (CSR[hcounteren].HPM19 == 1'b0 && CSR[mcounteren].HPM19 == 1'b1) { raise(ExceptionCode::VirtualInstruction, mode(), $encoding); } else if (CSR[mcounteren].HPM19 == 1'b0) { raise(ExceptionCode::IllegalInstruction, mode(), $encoding); diff --git a/arch/csr/Zihpm/hpmcounter19h.yaml b/arch/csr/Zihpm/hpmcounter19h.yaml index b408c5a470..f5bff3f22d 100644 --- a/arch/csr/Zihpm/hpmcounter19h.yaml +++ b/arch/csr/Zihpm/hpmcounter19h.yaml @@ -56,7 +56,7 @@ sw_read(): | } } else if (mode() == PrivilegeMode::VS) { # access in VS mode - if (CSR[hcounteren].HPM19 == 1'b0 && CSR[mcounteren] == 1'b1) { + if (CSR[hcounteren].HPM19 == 1'b0 && CSR[mcounteren].HPM19 == 1'b1) { raise(ExceptionCode::VirtualInstruction, mode(), $encoding); } else if (CSR[mcounteren].HPM19 == 1'b0) { raise(ExceptionCode::IllegalInstruction, mode(), $encoding); diff --git a/arch/csr/Zihpm/hpmcounter20.yaml b/arch/csr/Zihpm/hpmcounter20.yaml index 60b930bb11..a8a406d7b1 100644 --- a/arch/csr/Zihpm/hpmcounter20.yaml +++ b/arch/csr/Zihpm/hpmcounter20.yaml @@ -86,7 +86,7 @@ sw_read(): | } } else if (mode() == PrivilegeMode::VS) { # access in VS mode - if (CSR[hcounteren].HPM20 == 1'b0 && CSR[mcounteren] == 1'b1) { + if (CSR[hcounteren].HPM20 == 1'b0 && CSR[mcounteren].HPM20 == 1'b1) { raise(ExceptionCode::VirtualInstruction, mode(), $encoding); } else if (CSR[mcounteren].HPM20 == 1'b0) { raise(ExceptionCode::IllegalInstruction, mode(), $encoding); diff --git a/arch/csr/Zihpm/hpmcounter20h.yaml b/arch/csr/Zihpm/hpmcounter20h.yaml index e1a78f6113..6734e2d79b 100644 --- a/arch/csr/Zihpm/hpmcounter20h.yaml +++ b/arch/csr/Zihpm/hpmcounter20h.yaml @@ -56,7 +56,7 @@ sw_read(): | } } else if (mode() == PrivilegeMode::VS) { # access in VS mode - if (CSR[hcounteren].HPM20 == 1'b0 && CSR[mcounteren] == 1'b1) { + if (CSR[hcounteren].HPM20 == 1'b0 && CSR[mcounteren].HPM20 == 1'b1) { raise(ExceptionCode::VirtualInstruction, mode(), $encoding); } else if (CSR[mcounteren].HPM20 == 1'b0) { raise(ExceptionCode::IllegalInstruction, mode(), $encoding); diff --git a/arch/csr/Zihpm/hpmcounter21.yaml b/arch/csr/Zihpm/hpmcounter21.yaml index 60ad4c6fcd..3ce7cb84a2 100644 --- a/arch/csr/Zihpm/hpmcounter21.yaml +++ b/arch/csr/Zihpm/hpmcounter21.yaml @@ -86,7 +86,7 @@ sw_read(): | } } else if (mode() == PrivilegeMode::VS) { # access in VS mode - if (CSR[hcounteren].HPM21 == 1'b0 && CSR[mcounteren] == 1'b1) { + if (CSR[hcounteren].HPM21 == 1'b0 && CSR[mcounteren].HPM21 == 1'b1) { raise(ExceptionCode::VirtualInstruction, mode(), $encoding); } else if (CSR[mcounteren].HPM21 == 1'b0) { raise(ExceptionCode::IllegalInstruction, mode(), $encoding); diff --git a/arch/csr/Zihpm/hpmcounter21h.yaml b/arch/csr/Zihpm/hpmcounter21h.yaml index 2158d10930..12012a7bca 100644 --- a/arch/csr/Zihpm/hpmcounter21h.yaml +++ b/arch/csr/Zihpm/hpmcounter21h.yaml @@ -56,7 +56,7 @@ sw_read(): | } } else if (mode() == PrivilegeMode::VS) { # access in VS mode - if (CSR[hcounteren].HPM21 == 1'b0 && CSR[mcounteren] == 1'b1) { + if (CSR[hcounteren].HPM21 == 1'b0 && CSR[mcounteren].HPM21 == 1'b1) { raise(ExceptionCode::VirtualInstruction, mode(), $encoding); } else if (CSR[mcounteren].HPM21 == 1'b0) { raise(ExceptionCode::IllegalInstruction, mode(), $encoding); diff --git a/arch/csr/Zihpm/hpmcounter22.yaml b/arch/csr/Zihpm/hpmcounter22.yaml index 2f5b264652..7943981fdd 100644 --- a/arch/csr/Zihpm/hpmcounter22.yaml +++ b/arch/csr/Zihpm/hpmcounter22.yaml @@ -86,7 +86,7 @@ sw_read(): | } } else if (mode() == PrivilegeMode::VS) { # access in VS mode - if (CSR[hcounteren].HPM22 == 1'b0 && CSR[mcounteren] == 1'b1) { + if (CSR[hcounteren].HPM22 == 1'b0 && CSR[mcounteren].HPM22 == 1'b1) { raise(ExceptionCode::VirtualInstruction, mode(), $encoding); } else if (CSR[mcounteren].HPM22 == 1'b0) { raise(ExceptionCode::IllegalInstruction, mode(), $encoding); diff --git a/arch/csr/Zihpm/hpmcounter22h.yaml b/arch/csr/Zihpm/hpmcounter22h.yaml index f8987c1b86..994240044e 100644 --- a/arch/csr/Zihpm/hpmcounter22h.yaml +++ b/arch/csr/Zihpm/hpmcounter22h.yaml @@ -56,7 +56,7 @@ sw_read(): | } } else if (mode() == PrivilegeMode::VS) { # access in VS mode - if (CSR[hcounteren].HPM22 == 1'b0 && CSR[mcounteren] == 1'b1) { + if (CSR[hcounteren].HPM22 == 1'b0 && CSR[mcounteren].HPM22 == 1'b1) { raise(ExceptionCode::VirtualInstruction, mode(), $encoding); } else if (CSR[mcounteren].HPM22 == 1'b0) { raise(ExceptionCode::IllegalInstruction, mode(), $encoding); diff --git a/arch/csr/Zihpm/hpmcounter23.yaml b/arch/csr/Zihpm/hpmcounter23.yaml index ec6b89bfa7..fc22bd72a5 100644 --- a/arch/csr/Zihpm/hpmcounter23.yaml +++ b/arch/csr/Zihpm/hpmcounter23.yaml @@ -86,7 +86,7 @@ sw_read(): | } } else if (mode() == PrivilegeMode::VS) { # access in VS mode - if (CSR[hcounteren].HPM23 == 1'b0 && CSR[mcounteren] == 1'b1) { + if (CSR[hcounteren].HPM23 == 1'b0 && CSR[mcounteren].HPM23 == 1'b1) { raise(ExceptionCode::VirtualInstruction, mode(), $encoding); } else if (CSR[mcounteren].HPM23 == 1'b0) { raise(ExceptionCode::IllegalInstruction, mode(), $encoding); diff --git a/arch/csr/Zihpm/hpmcounter23h.yaml b/arch/csr/Zihpm/hpmcounter23h.yaml index c9b572cd51..ecb16e6844 100644 --- a/arch/csr/Zihpm/hpmcounter23h.yaml +++ b/arch/csr/Zihpm/hpmcounter23h.yaml @@ -56,7 +56,7 @@ sw_read(): | } } else if (mode() == PrivilegeMode::VS) { # access in VS mode - if (CSR[hcounteren].HPM23 == 1'b0 && CSR[mcounteren] == 1'b1) { + if (CSR[hcounteren].HPM23 == 1'b0 && CSR[mcounteren].HPM23 == 1'b1) { raise(ExceptionCode::VirtualInstruction, mode(), $encoding); } else if (CSR[mcounteren].HPM23 == 1'b0) { raise(ExceptionCode::IllegalInstruction, mode(), $encoding); diff --git a/arch/csr/Zihpm/hpmcounter24.yaml b/arch/csr/Zihpm/hpmcounter24.yaml index aec19cb8d0..4f392aaa5e 100644 --- a/arch/csr/Zihpm/hpmcounter24.yaml +++ b/arch/csr/Zihpm/hpmcounter24.yaml @@ -86,7 +86,7 @@ sw_read(): | } } else if (mode() == PrivilegeMode::VS) { # access in VS mode - if (CSR[hcounteren].HPM24 == 1'b0 && CSR[mcounteren] == 1'b1) { + if (CSR[hcounteren].HPM24 == 1'b0 && CSR[mcounteren].HPM24 == 1'b1) { raise(ExceptionCode::VirtualInstruction, mode(), $encoding); } else if (CSR[mcounteren].HPM24 == 1'b0) { raise(ExceptionCode::IllegalInstruction, mode(), $encoding); diff --git a/arch/csr/Zihpm/hpmcounter24h.yaml b/arch/csr/Zihpm/hpmcounter24h.yaml index 6ccbffcf00..ffe667a456 100644 --- a/arch/csr/Zihpm/hpmcounter24h.yaml +++ b/arch/csr/Zihpm/hpmcounter24h.yaml @@ -56,7 +56,7 @@ sw_read(): | } } else if (mode() == PrivilegeMode::VS) { # access in VS mode - if (CSR[hcounteren].HPM24 == 1'b0 && CSR[mcounteren] == 1'b1) { + if (CSR[hcounteren].HPM24 == 1'b0 && CSR[mcounteren].HPM24 == 1'b1) { raise(ExceptionCode::VirtualInstruction, mode(), $encoding); } else if (CSR[mcounteren].HPM24 == 1'b0) { raise(ExceptionCode::IllegalInstruction, mode(), $encoding); diff --git a/arch/csr/Zihpm/hpmcounter25.yaml b/arch/csr/Zihpm/hpmcounter25.yaml index 577984436f..3bcedc674d 100644 --- a/arch/csr/Zihpm/hpmcounter25.yaml +++ b/arch/csr/Zihpm/hpmcounter25.yaml @@ -86,7 +86,7 @@ sw_read(): | } } else if (mode() == PrivilegeMode::VS) { # access in VS mode - if (CSR[hcounteren].HPM25 == 1'b0 && CSR[mcounteren] == 1'b1) { + if (CSR[hcounteren].HPM25 == 1'b0 && CSR[mcounteren].HPM25 == 1'b1) { raise(ExceptionCode::VirtualInstruction, mode(), $encoding); } else if (CSR[mcounteren].HPM25 == 1'b0) { raise(ExceptionCode::IllegalInstruction, mode(), $encoding); diff --git a/arch/csr/Zihpm/hpmcounter25h.yaml b/arch/csr/Zihpm/hpmcounter25h.yaml index 2662d4d51b..f4b3286a4f 100644 --- a/arch/csr/Zihpm/hpmcounter25h.yaml +++ b/arch/csr/Zihpm/hpmcounter25h.yaml @@ -56,7 +56,7 @@ sw_read(): | } } else if (mode() == PrivilegeMode::VS) { # access in VS mode - if (CSR[hcounteren].HPM25 == 1'b0 && CSR[mcounteren] == 1'b1) { + if (CSR[hcounteren].HPM25 == 1'b0 && CSR[mcounteren].HPM25 == 1'b1) { raise(ExceptionCode::VirtualInstruction, mode(), $encoding); } else if (CSR[mcounteren].HPM25 == 1'b0) { raise(ExceptionCode::IllegalInstruction, mode(), $encoding); diff --git a/arch/csr/Zihpm/hpmcounter26.yaml b/arch/csr/Zihpm/hpmcounter26.yaml index 20b5e5ccc5..1dee5e0c76 100644 --- a/arch/csr/Zihpm/hpmcounter26.yaml +++ b/arch/csr/Zihpm/hpmcounter26.yaml @@ -86,7 +86,7 @@ sw_read(): | } } else if (mode() == PrivilegeMode::VS) { # access in VS mode - if (CSR[hcounteren].HPM26 == 1'b0 && CSR[mcounteren] == 1'b1) { + if (CSR[hcounteren].HPM26 == 1'b0 && CSR[mcounteren].HPM26 == 1'b1) { raise(ExceptionCode::VirtualInstruction, mode(), $encoding); } else if (CSR[mcounteren].HPM26 == 1'b0) { raise(ExceptionCode::IllegalInstruction, mode(), $encoding); diff --git a/arch/csr/Zihpm/hpmcounter26h.yaml b/arch/csr/Zihpm/hpmcounter26h.yaml index e9c8b11c48..995483b4fe 100644 --- a/arch/csr/Zihpm/hpmcounter26h.yaml +++ b/arch/csr/Zihpm/hpmcounter26h.yaml @@ -56,7 +56,7 @@ sw_read(): | } } else if (mode() == PrivilegeMode::VS) { # access in VS mode - if (CSR[hcounteren].HPM26 == 1'b0 && CSR[mcounteren] == 1'b1) { + if (CSR[hcounteren].HPM26 == 1'b0 && CSR[mcounteren].HPM26 == 1'b1) { raise(ExceptionCode::VirtualInstruction, mode(), $encoding); } else if (CSR[mcounteren].HPM26 == 1'b0) { raise(ExceptionCode::IllegalInstruction, mode(), $encoding); diff --git a/arch/csr/Zihpm/hpmcounter27.yaml b/arch/csr/Zihpm/hpmcounter27.yaml index 52ba19b207..694b4ee993 100644 --- a/arch/csr/Zihpm/hpmcounter27.yaml +++ b/arch/csr/Zihpm/hpmcounter27.yaml @@ -86,7 +86,7 @@ sw_read(): | } } else if (mode() == PrivilegeMode::VS) { # access in VS mode - if (CSR[hcounteren].HPM27 == 1'b0 && CSR[mcounteren] == 1'b1) { + if (CSR[hcounteren].HPM27 == 1'b0 && CSR[mcounteren].HPM27 == 1'b1) { raise(ExceptionCode::VirtualInstruction, mode(), $encoding); } else if (CSR[mcounteren].HPM27 == 1'b0) { raise(ExceptionCode::IllegalInstruction, mode(), $encoding); diff --git a/arch/csr/Zihpm/hpmcounter27h.yaml b/arch/csr/Zihpm/hpmcounter27h.yaml index 5b18c3fc64..8e17ad1758 100644 --- a/arch/csr/Zihpm/hpmcounter27h.yaml +++ b/arch/csr/Zihpm/hpmcounter27h.yaml @@ -56,7 +56,7 @@ sw_read(): | } } else if (mode() == PrivilegeMode::VS) { # access in VS mode - if (CSR[hcounteren].HPM27 == 1'b0 && CSR[mcounteren] == 1'b1) { + if (CSR[hcounteren].HPM27 == 1'b0 && CSR[mcounteren].HPM27 == 1'b1) { raise(ExceptionCode::VirtualInstruction, mode(), $encoding); } else if (CSR[mcounteren].HPM27 == 1'b0) { raise(ExceptionCode::IllegalInstruction, mode(), $encoding); diff --git a/arch/csr/Zihpm/hpmcounter28.yaml b/arch/csr/Zihpm/hpmcounter28.yaml index 220586b963..be77b1867d 100644 --- a/arch/csr/Zihpm/hpmcounter28.yaml +++ b/arch/csr/Zihpm/hpmcounter28.yaml @@ -86,7 +86,7 @@ sw_read(): | } } else if (mode() == PrivilegeMode::VS) { # access in VS mode - if (CSR[hcounteren].HPM28 == 1'b0 && CSR[mcounteren] == 1'b1) { + if (CSR[hcounteren].HPM28 == 1'b0 && CSR[mcounteren].HPM28 == 1'b1) { raise(ExceptionCode::VirtualInstruction, mode(), $encoding); } else if (CSR[mcounteren].HPM28 == 1'b0) { raise(ExceptionCode::IllegalInstruction, mode(), $encoding); diff --git a/arch/csr/Zihpm/hpmcounter28h.yaml b/arch/csr/Zihpm/hpmcounter28h.yaml index 6aaa60f563..9a1e7714f3 100644 --- a/arch/csr/Zihpm/hpmcounter28h.yaml +++ b/arch/csr/Zihpm/hpmcounter28h.yaml @@ -56,7 +56,7 @@ sw_read(): | } } else if (mode() == PrivilegeMode::VS) { # access in VS mode - if (CSR[hcounteren].HPM28 == 1'b0 && CSR[mcounteren] == 1'b1) { + if (CSR[hcounteren].HPM28 == 1'b0 && CSR[mcounteren].HPM28 == 1'b1) { raise(ExceptionCode::VirtualInstruction, mode(), $encoding); } else if (CSR[mcounteren].HPM28 == 1'b0) { raise(ExceptionCode::IllegalInstruction, mode(), $encoding); diff --git a/arch/csr/Zihpm/hpmcounter29.yaml b/arch/csr/Zihpm/hpmcounter29.yaml index f63b3babc8..df27482266 100644 --- a/arch/csr/Zihpm/hpmcounter29.yaml +++ b/arch/csr/Zihpm/hpmcounter29.yaml @@ -86,7 +86,7 @@ sw_read(): | } } else if (mode() == PrivilegeMode::VS) { # access in VS mode - if (CSR[hcounteren].HPM29 == 1'b0 && CSR[mcounteren] == 1'b1) { + if (CSR[hcounteren].HPM29 == 1'b0 && CSR[mcounteren].HPM29 == 1'b1) { raise(ExceptionCode::VirtualInstruction, mode(), $encoding); } else if (CSR[mcounteren].HPM29 == 1'b0) { raise(ExceptionCode::IllegalInstruction, mode(), $encoding); diff --git a/arch/csr/Zihpm/hpmcounter29h.yaml b/arch/csr/Zihpm/hpmcounter29h.yaml index 65f583acc1..e3fd8e6a2f 100644 --- a/arch/csr/Zihpm/hpmcounter29h.yaml +++ b/arch/csr/Zihpm/hpmcounter29h.yaml @@ -56,7 +56,7 @@ sw_read(): | } } else if (mode() == PrivilegeMode::VS) { # access in VS mode - if (CSR[hcounteren].HPM29 == 1'b0 && CSR[mcounteren] == 1'b1) { + if (CSR[hcounteren].HPM29 == 1'b0 && CSR[mcounteren].HPM29 == 1'b1) { raise(ExceptionCode::VirtualInstruction, mode(), $encoding); } else if (CSR[mcounteren].HPM29 == 1'b0) { raise(ExceptionCode::IllegalInstruction, mode(), $encoding); diff --git a/arch/csr/Zihpm/hpmcounter3.yaml b/arch/csr/Zihpm/hpmcounter3.yaml index a41868cae5..92c2bea3d8 100644 --- a/arch/csr/Zihpm/hpmcounter3.yaml +++ b/arch/csr/Zihpm/hpmcounter3.yaml @@ -86,7 +86,7 @@ sw_read(): | } } else if (mode() == PrivilegeMode::VS) { # access in VS mode - if (CSR[hcounteren].HPM3 == 1'b0 && CSR[mcounteren] == 1'b1) { + if (CSR[hcounteren].HPM3 == 1'b0 && CSR[mcounteren].HPM3 == 1'b1) { raise(ExceptionCode::VirtualInstruction, mode(), $encoding); } else if (CSR[mcounteren].HPM3 == 1'b0) { raise(ExceptionCode::IllegalInstruction, mode(), $encoding); diff --git a/arch/csr/Zihpm/hpmcounter30.yaml b/arch/csr/Zihpm/hpmcounter30.yaml index 855006edb8..7428dbb693 100644 --- a/arch/csr/Zihpm/hpmcounter30.yaml +++ b/arch/csr/Zihpm/hpmcounter30.yaml @@ -86,7 +86,7 @@ sw_read(): | } } else if (mode() == PrivilegeMode::VS) { # access in VS mode - if (CSR[hcounteren].HPM30 == 1'b0 && CSR[mcounteren] == 1'b1) { + if (CSR[hcounteren].HPM30 == 1'b0 && CSR[mcounteren].HPM30 == 1'b1) { raise(ExceptionCode::VirtualInstruction, mode(), $encoding); } else if (CSR[mcounteren].HPM30 == 1'b0) { raise(ExceptionCode::IllegalInstruction, mode(), $encoding); diff --git a/arch/csr/Zihpm/hpmcounter30h.yaml b/arch/csr/Zihpm/hpmcounter30h.yaml index 8e04748de2..08bc8149a7 100644 --- a/arch/csr/Zihpm/hpmcounter30h.yaml +++ b/arch/csr/Zihpm/hpmcounter30h.yaml @@ -56,7 +56,7 @@ sw_read(): | } } else if (mode() == PrivilegeMode::VS) { # access in VS mode - if (CSR[hcounteren].HPM30 == 1'b0 && CSR[mcounteren] == 1'b1) { + if (CSR[hcounteren].HPM30 == 1'b0 && CSR[mcounteren].HPM30 == 1'b1) { raise(ExceptionCode::VirtualInstruction, mode(), $encoding); } else if (CSR[mcounteren].HPM30 == 1'b0) { raise(ExceptionCode::IllegalInstruction, mode(), $encoding); diff --git a/arch/csr/Zihpm/hpmcounter31.yaml b/arch/csr/Zihpm/hpmcounter31.yaml index 431dd46790..77f88b79f1 100644 --- a/arch/csr/Zihpm/hpmcounter31.yaml +++ b/arch/csr/Zihpm/hpmcounter31.yaml @@ -86,7 +86,7 @@ sw_read(): | } } else if (mode() == PrivilegeMode::VS) { # access in VS mode - if (CSR[hcounteren].HPM31 == 1'b0 && CSR[mcounteren] == 1'b1) { + if (CSR[hcounteren].HPM31 == 1'b0 && CSR[mcounteren].HPM31 == 1'b1) { raise(ExceptionCode::VirtualInstruction, mode(), $encoding); } else if (CSR[mcounteren].HPM31 == 1'b0) { raise(ExceptionCode::IllegalInstruction, mode(), $encoding); diff --git a/arch/csr/Zihpm/hpmcounter31h.yaml b/arch/csr/Zihpm/hpmcounter31h.yaml index 4d1edf7976..02f8f77b23 100644 --- a/arch/csr/Zihpm/hpmcounter31h.yaml +++ b/arch/csr/Zihpm/hpmcounter31h.yaml @@ -56,7 +56,7 @@ sw_read(): | } } else if (mode() == PrivilegeMode::VS) { # access in VS mode - if (CSR[hcounteren].HPM31 == 1'b0 && CSR[mcounteren] == 1'b1) { + if (CSR[hcounteren].HPM31 == 1'b0 && CSR[mcounteren].HPM31 == 1'b1) { raise(ExceptionCode::VirtualInstruction, mode(), $encoding); } else if (CSR[mcounteren].HPM31 == 1'b0) { raise(ExceptionCode::IllegalInstruction, mode(), $encoding); diff --git a/arch/csr/Zihpm/hpmcounter3h.yaml b/arch/csr/Zihpm/hpmcounter3h.yaml index 157cef8459..2c49c0fe4c 100644 --- a/arch/csr/Zihpm/hpmcounter3h.yaml +++ b/arch/csr/Zihpm/hpmcounter3h.yaml @@ -56,7 +56,7 @@ sw_read(): | } } else if (mode() == PrivilegeMode::VS) { # access in VS mode - if (CSR[hcounteren].HPM3 == 1'b0 && CSR[mcounteren] == 1'b1) { + if (CSR[hcounteren].HPM3 == 1'b0 && CSR[mcounteren].HPM3 == 1'b1) { raise(ExceptionCode::VirtualInstruction, mode(), $encoding); } else if (CSR[mcounteren].HPM3 == 1'b0) { raise(ExceptionCode::IllegalInstruction, mode(), $encoding); diff --git a/arch/csr/Zihpm/hpmcounter4.yaml b/arch/csr/Zihpm/hpmcounter4.yaml index 44fc65db97..e453b1739b 100644 --- a/arch/csr/Zihpm/hpmcounter4.yaml +++ b/arch/csr/Zihpm/hpmcounter4.yaml @@ -86,7 +86,7 @@ sw_read(): | } } else if (mode() == PrivilegeMode::VS) { # access in VS mode - if (CSR[hcounteren].HPM4 == 1'b0 && CSR[mcounteren] == 1'b1) { + if (CSR[hcounteren].HPM4 == 1'b0 && CSR[mcounteren].HPM4 == 1'b1) { raise(ExceptionCode::VirtualInstruction, mode(), $encoding); } else if (CSR[mcounteren].HPM4 == 1'b0) { raise(ExceptionCode::IllegalInstruction, mode(), $encoding); diff --git a/arch/csr/Zihpm/hpmcounter4h.yaml b/arch/csr/Zihpm/hpmcounter4h.yaml index 26902cc18d..ac6a575ccc 100644 --- a/arch/csr/Zihpm/hpmcounter4h.yaml +++ b/arch/csr/Zihpm/hpmcounter4h.yaml @@ -56,7 +56,7 @@ sw_read(): | } } else if (mode() == PrivilegeMode::VS) { # access in VS mode - if (CSR[hcounteren].HPM4 == 1'b0 && CSR[mcounteren] == 1'b1) { + if (CSR[hcounteren].HPM4 == 1'b0 && CSR[mcounteren].HPM4 == 1'b1) { raise(ExceptionCode::VirtualInstruction, mode(), $encoding); } else if (CSR[mcounteren].HPM4 == 1'b0) { raise(ExceptionCode::IllegalInstruction, mode(), $encoding); diff --git a/arch/csr/Zihpm/hpmcounter5.yaml b/arch/csr/Zihpm/hpmcounter5.yaml index fd1be13a94..6e9e0c801a 100644 --- a/arch/csr/Zihpm/hpmcounter5.yaml +++ b/arch/csr/Zihpm/hpmcounter5.yaml @@ -86,7 +86,7 @@ sw_read(): | } } else if (mode() == PrivilegeMode::VS) { # access in VS mode - if (CSR[hcounteren].HPM5 == 1'b0 && CSR[mcounteren] == 1'b1) { + if (CSR[hcounteren].HPM5 == 1'b0 && CSR[mcounteren].HPM5 == 1'b1) { raise(ExceptionCode::VirtualInstruction, mode(), $encoding); } else if (CSR[mcounteren].HPM5 == 1'b0) { raise(ExceptionCode::IllegalInstruction, mode(), $encoding); diff --git a/arch/csr/Zihpm/hpmcounter5h.yaml b/arch/csr/Zihpm/hpmcounter5h.yaml index 81d40289f1..1529a2f8a2 100644 --- a/arch/csr/Zihpm/hpmcounter5h.yaml +++ b/arch/csr/Zihpm/hpmcounter5h.yaml @@ -56,7 +56,7 @@ sw_read(): | } } else if (mode() == PrivilegeMode::VS) { # access in VS mode - if (CSR[hcounteren].HPM5 == 1'b0 && CSR[mcounteren] == 1'b1) { + if (CSR[hcounteren].HPM5 == 1'b0 && CSR[mcounteren].HPM5 == 1'b1) { raise(ExceptionCode::VirtualInstruction, mode(), $encoding); } else if (CSR[mcounteren].HPM5 == 1'b0) { raise(ExceptionCode::IllegalInstruction, mode(), $encoding); diff --git a/arch/csr/Zihpm/hpmcounter6.yaml b/arch/csr/Zihpm/hpmcounter6.yaml index 8834e0c214..128202fdb3 100644 --- a/arch/csr/Zihpm/hpmcounter6.yaml +++ b/arch/csr/Zihpm/hpmcounter6.yaml @@ -86,7 +86,7 @@ sw_read(): | } } else if (mode() == PrivilegeMode::VS) { # access in VS mode - if (CSR[hcounteren].HPM6 == 1'b0 && CSR[mcounteren] == 1'b1) { + if (CSR[hcounteren].HPM6 == 1'b0 && CSR[mcounteren].HPM6 == 1'b1) { raise(ExceptionCode::VirtualInstruction, mode(), $encoding); } else if (CSR[mcounteren].HPM6 == 1'b0) { raise(ExceptionCode::IllegalInstruction, mode(), $encoding); diff --git a/arch/csr/Zihpm/hpmcounter6h.yaml b/arch/csr/Zihpm/hpmcounter6h.yaml index b60dee03e0..9995fc0eee 100644 --- a/arch/csr/Zihpm/hpmcounter6h.yaml +++ b/arch/csr/Zihpm/hpmcounter6h.yaml @@ -56,7 +56,7 @@ sw_read(): | } } else if (mode() == PrivilegeMode::VS) { # access in VS mode - if (CSR[hcounteren].HPM6 == 1'b0 && CSR[mcounteren] == 1'b1) { + if (CSR[hcounteren].HPM6 == 1'b0 && CSR[mcounteren].HPM6 == 1'b1) { raise(ExceptionCode::VirtualInstruction, mode(), $encoding); } else if (CSR[mcounteren].HPM6 == 1'b0) { raise(ExceptionCode::IllegalInstruction, mode(), $encoding); diff --git a/arch/csr/Zihpm/hpmcounter7.yaml b/arch/csr/Zihpm/hpmcounter7.yaml index 57841b7dde..c471a1df47 100644 --- a/arch/csr/Zihpm/hpmcounter7.yaml +++ b/arch/csr/Zihpm/hpmcounter7.yaml @@ -86,7 +86,7 @@ sw_read(): | } } else if (mode() == PrivilegeMode::VS) { # access in VS mode - if (CSR[hcounteren].HPM7 == 1'b0 && CSR[mcounteren] == 1'b1) { + if (CSR[hcounteren].HPM7 == 1'b0 && CSR[mcounteren].HPM7 == 1'b1) { raise(ExceptionCode::VirtualInstruction, mode(), $encoding); } else if (CSR[mcounteren].HPM7 == 1'b0) { raise(ExceptionCode::IllegalInstruction, mode(), $encoding); diff --git a/arch/csr/Zihpm/hpmcounter7h.yaml b/arch/csr/Zihpm/hpmcounter7h.yaml index 6f37af9577..416f912193 100644 --- a/arch/csr/Zihpm/hpmcounter7h.yaml +++ b/arch/csr/Zihpm/hpmcounter7h.yaml @@ -56,7 +56,7 @@ sw_read(): | } } else if (mode() == PrivilegeMode::VS) { # access in VS mode - if (CSR[hcounteren].HPM7 == 1'b0 && CSR[mcounteren] == 1'b1) { + if (CSR[hcounteren].HPM7 == 1'b0 && CSR[mcounteren].HPM7 == 1'b1) { raise(ExceptionCode::VirtualInstruction, mode(), $encoding); } else if (CSR[mcounteren].HPM7 == 1'b0) { raise(ExceptionCode::IllegalInstruction, mode(), $encoding); diff --git a/arch/csr/Zihpm/hpmcounter8.yaml b/arch/csr/Zihpm/hpmcounter8.yaml index a99cd0ce8f..a7c22a495c 100644 --- a/arch/csr/Zihpm/hpmcounter8.yaml +++ b/arch/csr/Zihpm/hpmcounter8.yaml @@ -86,7 +86,7 @@ sw_read(): | } } else if (mode() == PrivilegeMode::VS) { # access in VS mode - if (CSR[hcounteren].HPM8 == 1'b0 && CSR[mcounteren] == 1'b1) { + if (CSR[hcounteren].HPM8 == 1'b0 && CSR[mcounteren].HPM8 == 1'b1) { raise(ExceptionCode::VirtualInstruction, mode(), $encoding); } else if (CSR[mcounteren].HPM8 == 1'b0) { raise(ExceptionCode::IllegalInstruction, mode(), $encoding); diff --git a/arch/csr/Zihpm/hpmcounter8h.yaml b/arch/csr/Zihpm/hpmcounter8h.yaml index 83175ce6ea..11a341b4bb 100644 --- a/arch/csr/Zihpm/hpmcounter8h.yaml +++ b/arch/csr/Zihpm/hpmcounter8h.yaml @@ -56,7 +56,7 @@ sw_read(): | } } else if (mode() == PrivilegeMode::VS) { # access in VS mode - if (CSR[hcounteren].HPM8 == 1'b0 && CSR[mcounteren] == 1'b1) { + if (CSR[hcounteren].HPM8 == 1'b0 && CSR[mcounteren].HPM8 == 1'b1) { raise(ExceptionCode::VirtualInstruction, mode(), $encoding); } else if (CSR[mcounteren].HPM8 == 1'b0) { raise(ExceptionCode::IllegalInstruction, mode(), $encoding); diff --git a/arch/csr/Zihpm/hpmcounter9.yaml b/arch/csr/Zihpm/hpmcounter9.yaml index 126d8ad017..09770fa7a6 100644 --- a/arch/csr/Zihpm/hpmcounter9.yaml +++ b/arch/csr/Zihpm/hpmcounter9.yaml @@ -86,7 +86,7 @@ sw_read(): | } } else if (mode() == PrivilegeMode::VS) { # access in VS mode - if (CSR[hcounteren].HPM9 == 1'b0 && CSR[mcounteren] == 1'b1) { + if (CSR[hcounteren].HPM9 == 1'b0 && CSR[mcounteren].HPM9 == 1'b1) { raise(ExceptionCode::VirtualInstruction, mode(), $encoding); } else if (CSR[mcounteren].HPM9 == 1'b0) { raise(ExceptionCode::IllegalInstruction, mode(), $encoding); diff --git a/arch/csr/Zihpm/hpmcounter9h.yaml b/arch/csr/Zihpm/hpmcounter9h.yaml index 100a8c9de1..f3fcfdc25b 100644 --- a/arch/csr/Zihpm/hpmcounter9h.yaml +++ b/arch/csr/Zihpm/hpmcounter9h.yaml @@ -56,7 +56,7 @@ sw_read(): | } } else if (mode() == PrivilegeMode::VS) { # access in VS mode - if (CSR[hcounteren].HPM9 == 1'b0 && CSR[mcounteren] == 1'b1) { + if (CSR[hcounteren].HPM9 == 1'b0 && CSR[mcounteren].HPM9 == 1'b1) { raise(ExceptionCode::VirtualInstruction, mode(), $encoding); } else if (CSR[mcounteren].HPM9 == 1'b0) { raise(ExceptionCode::IllegalInstruction, mode(), $encoding); diff --git a/arch/csr/Zihpm/hpmcounterN.layout b/arch/csr/Zihpm/hpmcounterN.layout index c273c374ec..8fb52d6f63 100644 --- a/arch/csr/Zihpm/hpmcounterN.layout +++ b/arch/csr/Zihpm/hpmcounterN.layout @@ -86,7 +86,7 @@ sw_read(): | } } else if (mode() == PrivilegeMode::VS) { # access in VS mode - if (CSR[hcounteren].HPM<%= hpm_num %> == 1'b0 && CSR[mcounteren] == 1'b1) { + if (CSR[hcounteren].HPM<%= hpm_num %> == 1'b0 && CSR[mcounteren].HPM<%= hpm_num %> == 1'b1) { raise(ExceptionCode::VirtualInstruction, mode(), $encoding); } else if (CSR[mcounteren].HPM<%= hpm_num %> == 1'b0) { raise(ExceptionCode::IllegalInstruction, mode(), $encoding); diff --git a/arch/csr/Zihpm/hpmcounterNh.layout b/arch/csr/Zihpm/hpmcounterNh.layout index b573d35b80..4c30ac4e5c 100644 --- a/arch/csr/Zihpm/hpmcounterNh.layout +++ b/arch/csr/Zihpm/hpmcounterNh.layout @@ -56,7 +56,7 @@ sw_read(): | } } else if (mode() == PrivilegeMode::VS) { # access in VS mode - if (CSR[hcounteren].HPM<%= hpm_num %> == 1'b0 && CSR[mcounteren] == 1'b1) { + if (CSR[hcounteren].HPM<%= hpm_num %> == 1'b0 && CSR[mcounteren].HPM<%= hpm_num %> == 1'b1) { raise(ExceptionCode::VirtualInstruction, mode(), $encoding); } else if (CSR[mcounteren].HPM<%= hpm_num %> == 1'b0) { raise(ExceptionCode::IllegalInstruction, mode(), $encoding); diff --git a/arch/csr/Zihpm/mhpmcounter10h.yaml b/arch/csr/Zihpm/mhpmcounter10h.yaml index c17a6fdcef..3bfbeb11e9 100644 --- a/arch/csr/Zihpm/mhpmcounter10h.yaml +++ b/arch/csr/Zihpm/mhpmcounter10h.yaml @@ -15,7 +15,7 @@ description: | definedBy: Smhpm fields: COUNT: - location: 32-0 + location: 31-0 alias: mhpmcounter.COUNT10[63:32] description: | Upper bits of counter. diff --git a/arch/csr/Zihpm/mhpmcounter11h.yaml b/arch/csr/Zihpm/mhpmcounter11h.yaml index 8514ff4dc9..53d1e8d13b 100644 --- a/arch/csr/Zihpm/mhpmcounter11h.yaml +++ b/arch/csr/Zihpm/mhpmcounter11h.yaml @@ -15,7 +15,7 @@ description: | definedBy: Smhpm fields: COUNT: - location: 32-0 + location: 31-0 alias: mhpmcounter.COUNT11[63:32] description: | Upper bits of counter. diff --git a/arch/csr/Zihpm/mhpmcounter12h.yaml b/arch/csr/Zihpm/mhpmcounter12h.yaml index 2f648514d1..0b94cfb176 100644 --- a/arch/csr/Zihpm/mhpmcounter12h.yaml +++ b/arch/csr/Zihpm/mhpmcounter12h.yaml @@ -15,7 +15,7 @@ description: | definedBy: Smhpm fields: COUNT: - location: 32-0 + location: 31-0 alias: mhpmcounter.COUNT12[63:32] description: | Upper bits of counter. diff --git a/arch/csr/Zihpm/mhpmcounter13h.yaml b/arch/csr/Zihpm/mhpmcounter13h.yaml index 57b60ca1cd..754fb34492 100644 --- a/arch/csr/Zihpm/mhpmcounter13h.yaml +++ b/arch/csr/Zihpm/mhpmcounter13h.yaml @@ -15,7 +15,7 @@ description: | definedBy: Smhpm fields: COUNT: - location: 32-0 + location: 31-0 alias: mhpmcounter.COUNT13[63:32] description: | Upper bits of counter. diff --git a/arch/csr/Zihpm/mhpmcounter14h.yaml b/arch/csr/Zihpm/mhpmcounter14h.yaml index 63791d24f9..5f32a7c7bf 100644 --- a/arch/csr/Zihpm/mhpmcounter14h.yaml +++ b/arch/csr/Zihpm/mhpmcounter14h.yaml @@ -15,7 +15,7 @@ description: | definedBy: Smhpm fields: COUNT: - location: 32-0 + location: 31-0 alias: mhpmcounter.COUNT14[63:32] description: | Upper bits of counter. diff --git a/arch/csr/Zihpm/mhpmcounter15h.yaml b/arch/csr/Zihpm/mhpmcounter15h.yaml index 040f97e017..21184c4cc9 100644 --- a/arch/csr/Zihpm/mhpmcounter15h.yaml +++ b/arch/csr/Zihpm/mhpmcounter15h.yaml @@ -15,7 +15,7 @@ description: | definedBy: Smhpm fields: COUNT: - location: 32-0 + location: 31-0 alias: mhpmcounter.COUNT15[63:32] description: | Upper bits of counter. diff --git a/arch/csr/Zihpm/mhpmcounter16h.yaml b/arch/csr/Zihpm/mhpmcounter16h.yaml index 1bbbb515ae..d0b7ee88a5 100644 --- a/arch/csr/Zihpm/mhpmcounter16h.yaml +++ b/arch/csr/Zihpm/mhpmcounter16h.yaml @@ -15,7 +15,7 @@ description: | definedBy: Smhpm fields: COUNT: - location: 32-0 + location: 31-0 alias: mhpmcounter.COUNT16[63:32] description: | Upper bits of counter. diff --git a/arch/csr/Zihpm/mhpmcounter17h.yaml b/arch/csr/Zihpm/mhpmcounter17h.yaml index d7450adaba..14d6a52195 100644 --- a/arch/csr/Zihpm/mhpmcounter17h.yaml +++ b/arch/csr/Zihpm/mhpmcounter17h.yaml @@ -15,7 +15,7 @@ description: | definedBy: Smhpm fields: COUNT: - location: 32-0 + location: 31-0 alias: mhpmcounter.COUNT17[63:32] description: | Upper bits of counter. diff --git a/arch/csr/Zihpm/mhpmcounter18h.yaml b/arch/csr/Zihpm/mhpmcounter18h.yaml index 4d9cffaac9..3fc41ad052 100644 --- a/arch/csr/Zihpm/mhpmcounter18h.yaml +++ b/arch/csr/Zihpm/mhpmcounter18h.yaml @@ -15,7 +15,7 @@ description: | definedBy: Smhpm fields: COUNT: - location: 32-0 + location: 31-0 alias: mhpmcounter.COUNT18[63:32] description: | Upper bits of counter. diff --git a/arch/csr/Zihpm/mhpmcounter19h.yaml b/arch/csr/Zihpm/mhpmcounter19h.yaml index 63857f32ec..6483d13301 100644 --- a/arch/csr/Zihpm/mhpmcounter19h.yaml +++ b/arch/csr/Zihpm/mhpmcounter19h.yaml @@ -15,7 +15,7 @@ description: | definedBy: Smhpm fields: COUNT: - location: 32-0 + location: 31-0 alias: mhpmcounter.COUNT19[63:32] description: | Upper bits of counter. diff --git a/arch/csr/Zihpm/mhpmcounter20h.yaml b/arch/csr/Zihpm/mhpmcounter20h.yaml index ee8760ed75..73db882e51 100644 --- a/arch/csr/Zihpm/mhpmcounter20h.yaml +++ b/arch/csr/Zihpm/mhpmcounter20h.yaml @@ -15,7 +15,7 @@ description: | definedBy: Smhpm fields: COUNT: - location: 32-0 + location: 31-0 alias: mhpmcounter.COUNT20[63:32] description: | Upper bits of counter. diff --git a/arch/csr/Zihpm/mhpmcounter21h.yaml b/arch/csr/Zihpm/mhpmcounter21h.yaml index 2437ed36bb..f3d81870f8 100644 --- a/arch/csr/Zihpm/mhpmcounter21h.yaml +++ b/arch/csr/Zihpm/mhpmcounter21h.yaml @@ -15,7 +15,7 @@ description: | definedBy: Smhpm fields: COUNT: - location: 32-0 + location: 31-0 alias: mhpmcounter.COUNT21[63:32] description: | Upper bits of counter. diff --git a/arch/csr/Zihpm/mhpmcounter22h.yaml b/arch/csr/Zihpm/mhpmcounter22h.yaml index 2be137d451..6617b98232 100644 --- a/arch/csr/Zihpm/mhpmcounter22h.yaml +++ b/arch/csr/Zihpm/mhpmcounter22h.yaml @@ -15,7 +15,7 @@ description: | definedBy: Smhpm fields: COUNT: - location: 32-0 + location: 31-0 alias: mhpmcounter.COUNT22[63:32] description: | Upper bits of counter. diff --git a/arch/csr/Zihpm/mhpmcounter23h.yaml b/arch/csr/Zihpm/mhpmcounter23h.yaml index 980c373798..36117b668b 100644 --- a/arch/csr/Zihpm/mhpmcounter23h.yaml +++ b/arch/csr/Zihpm/mhpmcounter23h.yaml @@ -15,7 +15,7 @@ description: | definedBy: Smhpm fields: COUNT: - location: 32-0 + location: 31-0 alias: mhpmcounter.COUNT23[63:32] description: | Upper bits of counter. diff --git a/arch/csr/Zihpm/mhpmcounter24h.yaml b/arch/csr/Zihpm/mhpmcounter24h.yaml index c609c26583..d7f37ec8b9 100644 --- a/arch/csr/Zihpm/mhpmcounter24h.yaml +++ b/arch/csr/Zihpm/mhpmcounter24h.yaml @@ -15,7 +15,7 @@ description: | definedBy: Smhpm fields: COUNT: - location: 32-0 + location: 31-0 alias: mhpmcounter.COUNT24[63:32] description: | Upper bits of counter. diff --git a/arch/csr/Zihpm/mhpmcounter25h.yaml b/arch/csr/Zihpm/mhpmcounter25h.yaml index 147c52d261..a8d2fa086d 100644 --- a/arch/csr/Zihpm/mhpmcounter25h.yaml +++ b/arch/csr/Zihpm/mhpmcounter25h.yaml @@ -15,7 +15,7 @@ description: | definedBy: Smhpm fields: COUNT: - location: 32-0 + location: 31-0 alias: mhpmcounter.COUNT25[63:32] description: | Upper bits of counter. diff --git a/arch/csr/Zihpm/mhpmcounter26h.yaml b/arch/csr/Zihpm/mhpmcounter26h.yaml index 11138a32a5..2074cf504d 100644 --- a/arch/csr/Zihpm/mhpmcounter26h.yaml +++ b/arch/csr/Zihpm/mhpmcounter26h.yaml @@ -15,7 +15,7 @@ description: | definedBy: Smhpm fields: COUNT: - location: 32-0 + location: 31-0 alias: mhpmcounter.COUNT26[63:32] description: | Upper bits of counter. diff --git a/arch/csr/Zihpm/mhpmcounter27h.yaml b/arch/csr/Zihpm/mhpmcounter27h.yaml index 8139cf8f7c..cfd7308bdf 100644 --- a/arch/csr/Zihpm/mhpmcounter27h.yaml +++ b/arch/csr/Zihpm/mhpmcounter27h.yaml @@ -15,7 +15,7 @@ description: | definedBy: Smhpm fields: COUNT: - location: 32-0 + location: 31-0 alias: mhpmcounter.COUNT27[63:32] description: | Upper bits of counter. diff --git a/arch/csr/Zihpm/mhpmcounter28h.yaml b/arch/csr/Zihpm/mhpmcounter28h.yaml index 995afc7501..5b89a5cdcd 100644 --- a/arch/csr/Zihpm/mhpmcounter28h.yaml +++ b/arch/csr/Zihpm/mhpmcounter28h.yaml @@ -15,7 +15,7 @@ description: | definedBy: Smhpm fields: COUNT: - location: 32-0 + location: 31-0 alias: mhpmcounter.COUNT28[63:32] description: | Upper bits of counter. diff --git a/arch/csr/Zihpm/mhpmcounter29h.yaml b/arch/csr/Zihpm/mhpmcounter29h.yaml index f6f68ee5b4..8bbcd8a105 100644 --- a/arch/csr/Zihpm/mhpmcounter29h.yaml +++ b/arch/csr/Zihpm/mhpmcounter29h.yaml @@ -15,7 +15,7 @@ description: | definedBy: Smhpm fields: COUNT: - location: 32-0 + location: 31-0 alias: mhpmcounter.COUNT29[63:32] description: | Upper bits of counter. diff --git a/arch/csr/Zihpm/mhpmcounter30h.yaml b/arch/csr/Zihpm/mhpmcounter30h.yaml index 01d5668808..b24609c6f1 100644 --- a/arch/csr/Zihpm/mhpmcounter30h.yaml +++ b/arch/csr/Zihpm/mhpmcounter30h.yaml @@ -15,7 +15,7 @@ description: | definedBy: Smhpm fields: COUNT: - location: 32-0 + location: 31-0 alias: mhpmcounter.COUNT30[63:32] description: | Upper bits of counter. diff --git a/arch/csr/Zihpm/mhpmcounter31h.yaml b/arch/csr/Zihpm/mhpmcounter31h.yaml index 185b961c33..219f540d9f 100644 --- a/arch/csr/Zihpm/mhpmcounter31h.yaml +++ b/arch/csr/Zihpm/mhpmcounter31h.yaml @@ -15,7 +15,7 @@ description: | definedBy: Smhpm fields: COUNT: - location: 32-0 + location: 31-0 alias: mhpmcounter.COUNT31[63:32] description: | Upper bits of counter. diff --git a/arch/csr/Zihpm/mhpmcounter3h.yaml b/arch/csr/Zihpm/mhpmcounter3h.yaml index 5b95135dca..b1d00039fa 100644 --- a/arch/csr/Zihpm/mhpmcounter3h.yaml +++ b/arch/csr/Zihpm/mhpmcounter3h.yaml @@ -15,7 +15,7 @@ description: | definedBy: Smhpm fields: COUNT: - location: 32-0 + location: 31-0 alias: mhpmcounter.COUNT3[63:32] description: | Upper bits of counter. diff --git a/arch/csr/Zihpm/mhpmcounter4h.yaml b/arch/csr/Zihpm/mhpmcounter4h.yaml index ff2ffb6a2b..b810844ab8 100644 --- a/arch/csr/Zihpm/mhpmcounter4h.yaml +++ b/arch/csr/Zihpm/mhpmcounter4h.yaml @@ -15,7 +15,7 @@ description: | definedBy: Smhpm fields: COUNT: - location: 32-0 + location: 31-0 alias: mhpmcounter.COUNT4[63:32] description: | Upper bits of counter. diff --git a/arch/csr/Zihpm/mhpmcounter5h.yaml b/arch/csr/Zihpm/mhpmcounter5h.yaml index e8cc95b13b..f8296cf5a8 100644 --- a/arch/csr/Zihpm/mhpmcounter5h.yaml +++ b/arch/csr/Zihpm/mhpmcounter5h.yaml @@ -15,7 +15,7 @@ description: | definedBy: Smhpm fields: COUNT: - location: 32-0 + location: 31-0 alias: mhpmcounter.COUNT5[63:32] description: | Upper bits of counter. diff --git a/arch/csr/Zihpm/mhpmcounter6h.yaml b/arch/csr/Zihpm/mhpmcounter6h.yaml index 356a42d65f..3305497de4 100644 --- a/arch/csr/Zihpm/mhpmcounter6h.yaml +++ b/arch/csr/Zihpm/mhpmcounter6h.yaml @@ -15,7 +15,7 @@ description: | definedBy: Smhpm fields: COUNT: - location: 32-0 + location: 31-0 alias: mhpmcounter.COUNT6[63:32] description: | Upper bits of counter. diff --git a/arch/csr/Zihpm/mhpmcounter7h.yaml b/arch/csr/Zihpm/mhpmcounter7h.yaml index ae80b017a5..05fc92d3c0 100644 --- a/arch/csr/Zihpm/mhpmcounter7h.yaml +++ b/arch/csr/Zihpm/mhpmcounter7h.yaml @@ -15,7 +15,7 @@ description: | definedBy: Smhpm fields: COUNT: - location: 32-0 + location: 31-0 alias: mhpmcounter.COUNT7[63:32] description: | Upper bits of counter. diff --git a/arch/csr/Zihpm/mhpmcounter8h.yaml b/arch/csr/Zihpm/mhpmcounter8h.yaml index ab07e4a7cd..02b097678b 100644 --- a/arch/csr/Zihpm/mhpmcounter8h.yaml +++ b/arch/csr/Zihpm/mhpmcounter8h.yaml @@ -15,7 +15,7 @@ description: | definedBy: Smhpm fields: COUNT: - location: 32-0 + location: 31-0 alias: mhpmcounter.COUNT8[63:32] description: | Upper bits of counter. diff --git a/arch/csr/Zihpm/mhpmcounter9h.yaml b/arch/csr/Zihpm/mhpmcounter9h.yaml index 307d049b43..277f425300 100644 --- a/arch/csr/Zihpm/mhpmcounter9h.yaml +++ b/arch/csr/Zihpm/mhpmcounter9h.yaml @@ -15,7 +15,7 @@ description: | definedBy: Smhpm fields: COUNT: - location: 32-0 + location: 31-0 alias: mhpmcounter.COUNT9[63:32] description: | Upper bits of counter. diff --git a/arch/csr/Zihpm/mhpmcounterNh.layout b/arch/csr/Zihpm/mhpmcounterNh.layout index c022581fc0..1bda2396de 100644 --- a/arch/csr/Zihpm/mhpmcounterNh.layout +++ b/arch/csr/Zihpm/mhpmcounterNh.layout @@ -15,7 +15,7 @@ description: | definedBy: Smhpm fields: COUNT: - location: 32-0 + location: 31-0 alias: mhpmcounter.COUNT<%= hpm_num %>[63:32] description: | Upper bits of counter. diff --git a/arch/csr/cycle.yaml b/arch/csr/cycle.yaml index 99824ed6e9..0d7d975cf5 100644 --- a/arch/csr/cycle.yaml +++ b/arch/csr/cycle.yaml @@ -54,7 +54,7 @@ sw_read(): | } } else if (mode() == PrivilegeMode::VS) { # access in VS mode - if (CSR[hcounteren].CY == 1'b0 && CSR[mcounteren] == 1'b1) { + if (CSR[hcounteren].CY == 1'b0 && CSR[mcounteren].CY == 1'b1) { raise(ExceptionCode::VirtualInstruction, mode(), $encoding); } else if (CSR[mcounteren].CY == 1'b0) { raise(ExceptionCode::IllegalInstruction, mode(), $encoding); diff --git a/arch/csr/cycleh.yaml b/arch/csr/cycleh.yaml index 720e4b3417..75ed1b438d 100644 --- a/arch/csr/cycleh.yaml +++ b/arch/csr/cycleh.yaml @@ -60,7 +60,7 @@ sw_read(): | } } else if (mode() == PrivilegeMode::VS) { # access in VS mode - if (CSR[hcounteren].CY == 1'b0 && CSR[mcounteren] == 1'b1) { + if (CSR[hcounteren].CY == 1'b0 && CSR[mcounteren].CY == 1'b1) { raise(ExceptionCode::VirtualInstruction, mode(), $encoding); } else if (CSR[mcounteren].CY == 1'b0) { raise(ExceptionCode::IllegalInstruction, mode(), $encoding); diff --git a/arch/csr/instret.yaml b/arch/csr/instret.yaml index 779f47c5f3..b54fedf8d9 100644 --- a/arch/csr/instret.yaml +++ b/arch/csr/instret.yaml @@ -54,7 +54,7 @@ sw_read(): | } } else if (mode() == PrivilegeMode::VS) { # access in VS mode - if (CSR[hcounteren].IR == 1'b0 && CSR[mcounteren] == 1'b1) { + if (CSR[hcounteren].IR == 1'b0 && CSR[mcounteren].IR == 1'b1) { raise(ExceptionCode::VirtualInstruction, mode(), $encoding); } else if (CSR[mcounteren].IR == 1'b0) { raise(ExceptionCode::IllegalInstruction, mode(), $encoding); diff --git a/arch/csr/instreth.yaml b/arch/csr/instreth.yaml index 7a850f01a1..bb4df78053 100644 --- a/arch/csr/instreth.yaml +++ b/arch/csr/instreth.yaml @@ -55,7 +55,7 @@ sw_read(): | } } else if (mode() == PrivilegeMode::VS) { # access in VS mode - if (CSR[hcounteren].IR == 1'b0 && CSR[mcounteren] == 1'b1) { + if (CSR[hcounteren].IR == 1'b0 && CSR[mcounteren].IR == 1'b1) { raise(ExceptionCode::VirtualInstruction, mode(), $encoding); } else if (CSR[mcounteren].IR == 1'b0) { raise(ExceptionCode::IllegalInstruction, mode(), $encoding); diff --git a/arch/csr/mideleg.yaml b/arch/csr/mideleg.yaml index bfa378884e..af53608a13 100644 --- a/arch/csr/mideleg.yaml +++ b/arch/csr/mideleg.yaml @@ -8,6 +8,7 @@ address: 0x303 priv_mode: M length: MXLEN definedBy: + # "In harts without S-mode, the medeleg and mideleg registers should not exist." -- priv # after 1.9.1, mideleg does not exist when S-mode is not implemented # we can represent that by making mideleg an S extension CSR post 1.9.1 oneOf: @@ -85,9 +86,8 @@ description: | corresponding `medeleg` bits should be read-only zero. In particular, `medeleg`[11] is read-only zero. - <%- if ext?(:Smdbltrp) || ext?(:Ssdbltrp) -%> + [when,"ext?(:Smdbltrp) || ext?(:Ssdbltrp)"] The `medeleg`[16] is read-only zero as double trap is not delegatable. - <%- end -%> fields: SSI: diff --git a/arch/csr/minstreth.yaml b/arch/csr/minstreth.yaml index 9930ef02d6..0df4282594 100644 --- a/arch/csr/minstreth.yaml +++ b/arch/csr/minstreth.yaml @@ -4,7 +4,7 @@ $schema: "csr_schema.json#" kind: csr name: minstreth long_name: Machine Instructions Retired Counter -address: 0xB02 +address: 0xB82 description: | Upper half of 64-bit instructions retired counters. diff --git a/arch/csr/mip.yaml b/arch/csr/mip.yaml index 4cd0e65e26..4d831856b4 100644 --- a/arch/csr/mip.yaml +++ b/arch/csr/mip.yaml @@ -403,3 +403,10 @@ fields: type: RW-H reset_value: 0 definedBy: Sscofpmf +sw_read(): | + # OR in the hidden smode external interrupt + return + $bits(CSR[mip]) + | ((CSR[misa].S == 1'b1 && pending_smode_external_interrupt) + ? 10'h200 + : 0); diff --git a/arch/csr/misa.yaml b/arch/csr/misa.yaml index 5b3674d809..4c554b6b06 100644 --- a/arch/csr/misa.yaml +++ b/arch/csr/misa.yaml @@ -15,7 +15,8 @@ fields: location_rv64: 63-62 description: XLEN in M-mode. type: RO - reset_value: 2 + reset_value(): | + return (XLEN == 32) ? 2'b01 : 2'b10; A: location: 0 description: | @@ -25,8 +26,9 @@ fields: Writing 0 to this field will cause all atomic instructions to raise an `IllegalInstruction` exception. type(): | return (implemented?(ExtensionName::A) && MUTABLE_MISA_A) ? CsrFieldType::RW : CsrFieldType::RO; + reset_value(): | + return implemented?(ExtensionName::A) ? 1 : 0; definedBy: A - reset_value: 1 B: location: 1 description: | @@ -36,8 +38,9 @@ fields: Writing 0 to this field will cause all bitmanip instructions to raise an `IllegalInstruction` exception. type(): | return (implemented?(ExtensionName::B) && MUTABLE_MISA_B) ? CsrFieldType::RW : CsrFieldType::RO; + reset_value(): | + return implemented?(ExtensionName::B) ? 1 : 0; definedBy: B - reset_value: 1 C: location: 2 description: | @@ -48,8 +51,9 @@ fields: Additionally, IALIGN becomes 32. type(): | return (implemented?(ExtensionName::C) && MUTABLE_MISA_C) ? CsrFieldType::RW : CsrFieldType::RO; + reset_value(): | + return implemented?(ExtensionName::C) ? 1 : 0; definedBy: C - reset_value: 1 D: location: 3 description: | @@ -63,8 +67,9 @@ fields: -- type(): | return (implemented?(ExtensionName::D) && MUTABLE_MISA_D) ? CsrFieldType::RW : CsrFieldType::RO; + reset_value(): | + return implemented?(ExtensionName::D) ? 1 : 0; definedBy: D - reset_value: 1 F: location: 5 description: | @@ -78,8 +83,9 @@ fields: -- type(): | return (implemented?(ExtensionName::F) && MUTABLE_MISA_F) ? CsrFieldType::RW : CsrFieldType::RO; + reset_value(): | + return implemented?(ExtensionName::F) ? 1 : 0; definedBy: F - reset_value: 1 sw_write(csr_value): | if (csr_value.F == 0 && csr_value.D == 1) { return UNDEFINED_LEGAL_DETERMINISTIC; @@ -118,7 +124,8 @@ fields: type(): | return (implemented?(ExtensionName::H) && MUTABLE_MISA_H) ? CsrFieldType::RW : CsrFieldType::RO; definedBy: H - reset_value: 1 + reset_value(): | + return implemented?(ExtensionName::H) ? 1 : 0; I: location: 8 description: | @@ -135,8 +142,9 @@ fields: Writing 0 to this field will cause all attempts to execute an integer multiply or divide instruction to raise an `IllegalInstruction` exception. type(): | return (implemented?(ExtensionName::M) && MUTABLE_MISA_M) ? CsrFieldType::RW : CsrFieldType::RO; + reset_value(): | + return implemented?(ExtensionName::M) ? 1 : 0; definedBy: M - reset_value: 1 S: location: 19 description: | @@ -146,8 +154,9 @@ fields: Writing 0 to this field will cause all attempts to enter S-mode or access S-mode state to raise an exception. type(): | return (implemented?(ExtensionName::S) && MUTABLE_MISA_S) ? CsrFieldType::RW : CsrFieldType::RO; + reset_value(): | + return implemented?(ExtensionName::S) ? 1 : 0; definedBy: S - reset_value: 1 U: location: 21 description: | @@ -157,8 +166,9 @@ fields: Writing 0 to this field will cause all attempts to enter U-mode to raise an exception. type(): | return (implemented?(ExtensionName::U) && MUTABLE_MISA_U) ? CsrFieldType::RW : CsrFieldType::RO; + reset_value(): | + return implemented?(ExtensionName::U) ? 1 : 0; definedBy: U - reset_value: 1 V: location: 22 description: | @@ -166,12 +176,14 @@ fields: [when,"MUTABLE_MISA_V == true"] Writing 0 to this field will cause all attempts to execute a vector instruction to raise an `IllegalInstruction` trap. - type: RO + type(): | + return (implemented?(ExtensionName::V) && MUTABLE_MISA_V) ? CsrFieldType::RW : CsrFieldType::RO; + reset_value(): | + return implemented?(ExtensionName::V) ? 1 : 0; definedBy: V - reset_value: 1 sw_read(): | return ( - (CSR[misa].MXL << 62) | + (CSR[misa].MXL << (xlen() - 2)) | (CSR[misa].V << 21) | (CSR[misa].U << 20) | (CSR[misa].S << 18) | diff --git a/arch/csr/time.yaml b/arch/csr/time.yaml index 72e4706fa8..5ebccad92d 100644 --- a/arch/csr/time.yaml +++ b/arch/csr/time.yaml @@ -66,7 +66,7 @@ sw_read(): | } } else if (mode() == PrivilegeMode::VS) { # access in VS mode - if (CSR[hcounteren].TM == 1'b0 && CSR[mcounteren] == 1'b1) { + if (CSR[hcounteren].TM == 1'b0 && CSR[mcounteren].TM == 1'b1) { raise(ExceptionCode::VirtualInstruction, mode(), $encoding); } else if (CSR[mcounteren].TM == 1'b0) { raise(ExceptionCode::IllegalInstruction, mode(), $encoding); diff --git a/arch/csr/timeh.yaml b/arch/csr/timeh.yaml index f3aa95bfb0..61dc0857a0 100644 --- a/arch/csr/timeh.yaml +++ b/arch/csr/timeh.yaml @@ -65,7 +65,7 @@ sw_read(): | } } else if (mode() == PrivilegeMode::VS) { # access in VS mode - if (CSR[hcounteren].TM == 1'b0 && CSR[mcounteren] == 1'b1) { + if (CSR[hcounteren].TM == 1'b0 && CSR[mcounteren].TM == 1'b1) { raise(ExceptionCode::VirtualInstruction, mode(), $encoding); } else if (CSR[mcounteren].TM == 1'b0) { raise(ExceptionCode::IllegalInstruction, mode(), $encoding); diff --git a/arch/ext/A.yaml b/arch/ext/A.yaml index ec0ee84087..249259a0a5 100644 --- a/arch/ext/A.yaml +++ b/arch/ext/A.yaml @@ -17,8 +17,10 @@ versions: email: unknown@void.segfault company: Unknown implies: - - [Zaamo, "1.0.0"] - - [Zalrsc, "1.0.0"] + - name: Zaamo + version: "1.0.0" + - name: Zalrsc + version: "1.0.0" description: | The atomic-instruction extension, named `A`, contains diff --git a/arch/ext/B.yaml b/arch/ext/B.yaml index 51b7928c09..ef3376ec93 100644 --- a/arch/ext/B.yaml +++ b/arch/ext/B.yaml @@ -21,9 +21,12 @@ versions: company: Rivos, Inc. url: https://drive.google.com/file/d/1SgLoasaBjs5WboQMaU3wpHkjUwV71UZn/view implies: - - [Zba, "1.0.0"] - - [Zbb, "1.0.0"] - - [Zbs, "1.0.0"] + - name: Zba + version: "1.0.0" + - name: Zbb + version: "1.0.0" + - name: Zbs + version: "1.0.0" description: | The B standard extension comprises instructions provided by the `Zba`, `Zbb`, and `Zbs` extensions. diff --git a/arch/ext/C.yaml b/arch/ext/C.yaml index a59ba53b0c..22f186af72 100644 --- a/arch/ext/C.yaml +++ b/arch/ext/C.yaml @@ -15,6 +15,21 @@ versions: - version: "2.0.0" state: ratified ratification_date: 2019-12 + implies: + - name: Zca + version: "1.0.0" + - if: + name: F + version: ~> 2.2 + then: + name: Zcf + version: "1.0.0" + - if: + name: D + version: ~> 2.2 + then: + name: Zcd + version: "1.0.0" description: | The `C` extension reduces static and dynamic code size by adding short 16-bit instruction encodings for common operations. The C diff --git a/arch/ext/D.yaml b/arch/ext/D.yaml index 849ee00423..a038b98f48 100644 --- a/arch/ext/D.yaml +++ b/arch/ext/D.yaml @@ -11,7 +11,9 @@ versions: ratification_date: 2019-12 changes: - Define NaN-boxing scheme, changed definition of FMAX and FMIN - implies: [F, "2.2.0"] + implies: + name: F + version: "2.2.0" description: | The `D` extension adds double-precision floating-point computational instructions compliant diff --git a/arch/ext/S.yaml b/arch/ext/S.yaml index 5bba5d5f28..de635c42b6 100644 --- a/arch/ext/S.yaml +++ b/arch/ext/S.yaml @@ -84,6 +84,31 @@ params: extra_validation: | assert SXLEN == 32 if XLEN == 32 assert (SXLEN != 32) if UXLEN != 32 + REPORT_VA_IN_MTVAL_ON_LOAD_PAGE_FAULT: + description: | + When true, `mtval` is written with the virtual address of a load when it causes a + `LoadPageFault`. + + WHen false, `mtval` is written with 0 when a load causes a `LoadPageFault`. + schema: + type: boolean + REPORT_VA_IN_MTVAL_ON_STORE_AMO_PAGE_FAULT: + description: | + When true, `mtval` is written with the virtual address of a store when it causes a + `StoreAmoPageFault`. + + WHen false, `mtval` is written with 0 when a store causes a `StoreAmoPageFault`. + schema: + type: boolean + REPORT_VA_IN_MTVAL_ON_INSTRUCTION_PAGE_FAULT: + description: | + When true, `mtval` is written with the virtual PC of an instructino when fetch causes an + `InstructionPageFault`. + + WHen false, `mtval` is written with 0 when an instruction fetch causes an + `InstructionPageFault`. + schema: + type: boolean REPORT_VA_IN_STVAL_ON_BREAKPOINT: description: | When true, `stval` is written with the virtual PC of the EBREAK instruction (same information as `mepc`). diff --git a/arch/ext/Sm.yaml b/arch/ext/Sm.yaml index a2047564f0..10f906c635 100644 --- a/arch/ext/Sm.yaml +++ b/arch/ext/Sm.yaml @@ -377,31 +377,6 @@ params: `InstructionAccessFault`. schema: type: boolean - REPORT_VA_IN_MTVAL_ON_LOAD_PAGE_FAULT: - description: | - When true, `mtval` is written with the virtual address of a load when it causes a - `LoadPageFault`. - - WHen false, `mtval` is written with 0 when a load causes a `LoadPageFault`. - schema: - type: boolean - REPORT_VA_IN_MTVAL_ON_STORE_AMO_PAGE_FAULT: - description: | - When true, `mtval` is written with the virtual address of a store when it causes a - `StoreAmoPageFault`. - - WHen false, `mtval` is written with 0 when a store causes a `StoreAmoPageFault`. - schema: - type: boolean - REPORT_VA_IN_MTVAL_ON_INSTRUCTION_PAGE_FAULT: - description: | - When true, `mtval` is written with the virtual PC of an instructino when fetch causes an - `InstructionPageFault`. - - WHen false, `mtval` is written with 0 when an instruction fetch causes an - `InstructionPageFault`. - schema: - type: boolean REPORT_ENCODING_IN_MTVAL_ON_ILLEGAL_INSTRUCTION: description: | When true, `mtval` is written with the encoding of an instruction that causes an @@ -426,6 +401,8 @@ params: This address is reported in the `mconfigptr` CSR. schema: type: integer + minimum: 0 + maximum: 0xFFFFFFFFFFFFFFFF when: version: ">= 1.12.0" PMA_GRANULARITY: @@ -493,6 +470,7 @@ params: schema: type: integer minimum: 4 + maximum: 64 default: 4 extra_validation: | # must be a power of two diff --git a/arch/ext/Sscofpmf.yaml b/arch/ext/Sscofpmf.yaml index de4584f09d..6cb8389cd8 100644 --- a/arch/ext/Sscofpmf.yaml +++ b/arch/ext/Sscofpmf.yaml @@ -13,3 +13,7 @@ versions: url: https://drive.google.com/file/d/1KcjgbLM5L1ZKY8934aJl8aQwGlMz6Cbo/view?usp=drive_link requires: name: Smhpm +interrupt_codes: + - num: 13 + name: Local counter overflow interrupt + var: LocalCounterOverflow diff --git a/arch/ext/Svade.yaml b/arch/ext/Svade.yaml index 5ea7c4bd98..32c7d55a1c 100644 --- a/arch/ext/Svade.yaml +++ b/arch/ext/Svade.yaml @@ -35,7 +35,6 @@ versions: - name: Paul Donahue - name: Ved Shanbhogue company: Rivos, Inc. -conflicts: Svadu doc_license: name: Creative Commons Attribution 4.0 International License (CC-BY 4.0) url: https://creativecommons.org/licenses/by/4.0/ diff --git a/arch/ext/Zce.yaml b/arch/ext/Zce.yaml index 4eeb34b996..cd63718083 100644 --- a/arch/ext/Zce.yaml +++ b/arch/ext/Zce.yaml @@ -46,10 +46,14 @@ versions: - name: Nicolas Brunie - name: Jiawei implies: - - [Zca, "1.0.0"] - - [Zcb, "1.0.0"] - - [Zcmp, "1.0.0"] - - [Zcmt, "1.0.0"] + - name: Zca + version: "1.0.0" + - name: Zcb + version: "1.0.0" + - name: Zcmp + version: "1.0.0" + - name: Zcmt + version: "1.0.0" # TODO: this implication is conditional!!! (see description) # So it should look something like this: diff --git a/arch/ext/Zcf.yaml b/arch/ext/Zcf.yaml new file mode 100644 index 0000000000..22b9bbc290 --- /dev/null +++ b/arch/ext/Zcf.yaml @@ -0,0 +1,38 @@ +# yaml-language-server: $schema=../../schemas/ext_schema.json + +$schema: "ext_schema.json#" +kind: extension +name: Zcf +long_name: Compressed instructions for single precision floating point +description: | + Zcf is the existing set of compressed single precision floating point loads and stores: + `c.flw`, `c.flwsp`, `c.fsw`, `c.fswsp`. + +type: unprivileged +company: + name: RISC-V International + url: https://riscv.org +versions: + - version: "1.0.0" + state: ratified + ratification_date: 2023-04 + repositories: + - url: https://github.com/riscv/riscv-code-size-reduction + branch: main + contributors: + - name: Tariq Kurd + - name: Ibrahim Abu Kharmeh + - name: Torbjørn Viem Ness + - name: Matteo Perotti + - name: Nidal Faour + - name: Bill Traynor + - name: Rafael Sene + - name: Xinlong Wu + - name: sinan + - name: Jeremy Bennett + - name: Heda Chen + - name: Alasdair Armstrong + - name: Graeme Smecher + - name: Nicolas Brunie + - name: Jiawei + requires: F diff --git a/arch/ext/Zcmp.yaml b/arch/ext/Zcmp.yaml index 115720ec87..d62fe86ccb 100644 --- a/arch/ext/Zcmp.yaml +++ b/arch/ext/Zcmp.yaml @@ -88,3 +88,7 @@ versions: - name: Graeme Smecher - name: Nicolas Brunie - name: Jiawei +conflicts: + anyOf: + - allOf: [C, D] + - Zcd diff --git a/arch/ext/Zcmt.yaml b/arch/ext/Zcmt.yaml index c3a7912dee..f3ed2357e5 100644 --- a/arch/ext/Zcmt.yaml +++ b/arch/ext/Zcmt.yaml @@ -62,10 +62,12 @@ versions: - name: Nicolas Brunie - name: Jiawei implies: - - [Zca, "1.0.0"] - - [Zcb, "1.0.0"] - - [Zcmp, "1.0.0"] - - [Zcmt, "1.0.0"] + - name: Zca + version: "1.0.0" + - name: Zcb + version: "1.0.0" + - name: Zcmp + version: "1.0.0" # TODO: this implication is conditional!!! (see description) # So it should look something like this: diff --git a/arch/ext/Zicbom.yaml b/arch/ext/Zicbom.yaml index c040ce1397..70482c380f 100644 --- a/arch/ext/Zicbom.yaml +++ b/arch/ext/Zicbom.yaml @@ -17,6 +17,8 @@ params: also_defined_in: [Zicboz, Zicbop] schema: type: integer + minimum: 1 + maximum: 0xFFFFFFFFFFFFFFFF FORCE_UPGRADE_CBO_INVAL_TO_FLUSH: description: | When true, an implementation prohibits setting `menvcfg.CBIE` == `11` such that all `cbo.inval` diff --git a/arch/ext/Zicbop.yaml b/arch/ext/Zicbop.yaml index c25d22c907..76f88e181d 100644 --- a/arch/ext/Zicbop.yaml +++ b/arch/ext/Zicbop.yaml @@ -17,3 +17,5 @@ params: also_defined_in: [Zicboz, Zicbom] schema: type: integer + minimum: 1 + maximum: 0xFFFFFFFFFFFFFFFF diff --git a/arch/ext/Zicboz.yaml b/arch/ext/Zicboz.yaml index a6d4c9c179..976dc07920 100644 --- a/arch/ext/Zicboz.yaml +++ b/arch/ext/Zicboz.yaml @@ -17,3 +17,5 @@ params: also_defined_in: [Zicbom, Zicbop] schema: type: integer + minimum: 1 + maximum: 0xFFFFFFFFFFFFFFFF diff --git a/arch/ext/Zk.yaml b/arch/ext/Zk.yaml index 6ffeaf47cd..537bf1782a 100644 --- a/arch/ext/Zk.yaml +++ b/arch/ext/Zk.yaml @@ -17,6 +17,9 @@ versions: state: ratified ratification_date: null implies: - - ["Zkn", "1.0.0"] - - ["Zkr", "1.0.0"] - - ["Zkt", "1.0.0"] + - name: "Zkn" + version: "1.0.0" + - name: "Zkr" + version: "1.0.0" + - name: "Zkt" + version: "1.0.0" diff --git a/arch/ext/Zkn.yaml b/arch/ext/Zkn.yaml index 1ec5a3b464..b2f8008980 100644 --- a/arch/ext/Zkn.yaml +++ b/arch/ext/Zkn.yaml @@ -20,9 +20,15 @@ versions: state: ratified ratification_date: null implies: - - ["Zbkb", "1.0.0"] - - ["Zbkc", "1.0.0"] - - ["Zbkx", "1.0.0"] - - ["Zkne", "1.0.0"] - - ["Zknd", "1.0.0"] - - ["Zknh", "1.0.0"] + - name: "Zbkb" + version: "1.0.0" + - name: "Zbkc" + version: "1.0.0" + - name: "Zbkx" + version: "1.0.0" + - name: "Zkne" + version: "1.0.0" + - name: "Zknd" + version: "1.0.0" + - name: "Zknh" + version: "1.0.0" diff --git a/arch/ext/Zks.yaml b/arch/ext/Zks.yaml index 33f6c69174..53147ac03d 100644 --- a/arch/ext/Zks.yaml +++ b/arch/ext/Zks.yaml @@ -19,9 +19,15 @@ versions: state: ratified ratification_date: null implies: - - ["Zbkb", "1.0.0"] - - ["Zbkc", "1.0.0"] - - ["Zbkx", "1.0.0"] - - ["Zkne", "1.0.0"] - - ["Zknd", "1.0.0"] - - ["Zknh", "1.0.0"] + - name: Zbkb + version: "1.0.0" + - name: Zbkc + version: "1.0.0" + - name: Zbkx + version: "1.0.0" + - name: Zkne + version: "1.0.0" + - name: Zknd + version: "1.0.0" + - name: Zknh + version: "1.0.0" diff --git a/arch/ext/Zvbb.yaml b/arch/ext/Zvbb.yaml index 3ceccfe1ec..163c7c44bf 100644 --- a/arch/ext/Zvbb.yaml +++ b/arch/ext/Zvbb.yaml @@ -11,4 +11,6 @@ versions: - version: "1.0.0" state: ratified ratification_date: null - implies: [Zvkb, "1.0.0"] + implies: + name: Zvkb + version: "1.0.0" diff --git a/arch/ext/Zvkn.yaml b/arch/ext/Zvkn.yaml index 13eec434b8..ecd7acee73 100644 --- a/arch/ext/Zvkn.yaml +++ b/arch/ext/Zvkn.yaml @@ -17,7 +17,11 @@ versions: state: ratified ratification_date: null implies: - - [Zvkned, "1.0.0"] - - [Zvknhb, "1.0.0"] - - [Zvkb, "1.0.0"] - - [Zvkt, "1.0.0"] + - name: Zvkned + version: "1.0.0" + - name: Zvknhb + version: "1.0.0" + - name: Zvkb + version: "1.0.0" + - name: Zvkt + version: "1.0.0" diff --git a/arch/ext/Zvknc.yaml b/arch/ext/Zvknc.yaml index 2e9ce11dc3..a25fa42183 100644 --- a/arch/ext/Zvknc.yaml +++ b/arch/ext/Zvknc.yaml @@ -16,5 +16,7 @@ versions: state: ratified ratification_date: null implies: - - [Zvkn, "1.0.0"] - - [Zvbc, "1.0.0"] + - name: Zvkn + version: "1.0.0" + - name: Zvbc + version: "1.0.0" diff --git a/arch/ext/Zvkng.yaml b/arch/ext/Zvkng.yaml index 5cae637063..74a6b8aafd 100644 --- a/arch/ext/Zvkng.yaml +++ b/arch/ext/Zvkng.yaml @@ -16,5 +16,7 @@ versions: state: ratified ratification_date: null implies: - - [Zvkn, "1.0.0"] - - [Zvkg, "1.0.0"] + - name: Zvkn + version: "1.0.0" + - name: Zvkg + version: "1.0.0" diff --git a/arch/ext/Zvknhb.yaml b/arch/ext/Zvknhb.yaml index 7ca550d956..2fff418295 100644 --- a/arch/ext/Zvknhb.yaml +++ b/arch/ext/Zvknhb.yaml @@ -12,4 +12,6 @@ versions: - version: "1.0.0" state: ratified ratification_date: null - implies: [Zvknha, "1.0.0"] + implies: + name: Zvknha + version: "1.0.0" diff --git a/arch/ext/Zvks.yaml b/arch/ext/Zvks.yaml index 4d1d310133..9d9656005d 100644 --- a/arch/ext/Zvks.yaml +++ b/arch/ext/Zvks.yaml @@ -17,7 +17,11 @@ versions: state: ratified ratification_date: null implies: - - [Zvksed, "1.0.0"] - - [Zvksh, "1.0.0"] - - [Zvkb, "1.0.0"] - - [Zvkt, "1.0.0"] + - name: Zvksed + version: "1.0.0" + - name: Zvksh + version: "1.0.0" + - name: Zvkb + version: "1.0.0" + - name: Zvkt + version: "1.0.0" diff --git a/arch/ext/Zvksc.yaml b/arch/ext/Zvksc.yaml index 2e272745ac..2e20508bcd 100644 --- a/arch/ext/Zvksc.yaml +++ b/arch/ext/Zvksc.yaml @@ -16,5 +16,7 @@ versions: state: ratified ratification_date: null implies: - - [Zvks, "1.0.0"] - - [Zvbc, "1.0.0"] + - name: Zvks + version: "1.0.0" + - name: Zvbc + version: "1.0.0" diff --git a/arch/ext/Zvksg.yaml b/arch/ext/Zvksg.yaml index 4577e0957c..82fc4b371b 100644 --- a/arch/ext/Zvksg.yaml +++ b/arch/ext/Zvksg.yaml @@ -16,5 +16,7 @@ versions: state: ratified ratification_date: null implies: - - [Zvks, "1.0.0"] - - [Zvkg, "1.0.0"] + - name: Zvks + version: "1.0.0" + - name: Zvkg + version: "1.0.0" diff --git a/arch/inst/B/clmul.yaml b/arch/inst/B/clmul.yaml index 58c5a9fadb..29192fff96 100644 --- a/arch/inst/B/clmul.yaml +++ b/arch/inst/B/clmul.yaml @@ -7,7 +7,7 @@ long_name: Carry-less multiply (low-part) description: | `clmul` produces the lower half of the 2*XLEN carry-less product definedBy: - anyOf: [B, Zbc, Zbkc, Zk, Zkn, Zks] + anyOf: [Zbc, Zbkc, Zk, Zkn, Zks] assembly: xd, xs1, xs2 encoding: match: 0000101----------001-----0110011 diff --git a/arch/inst/B/clmulh.yaml b/arch/inst/B/clmulh.yaml index adae02b842..48fc7a7798 100644 --- a/arch/inst/B/clmulh.yaml +++ b/arch/inst/B/clmulh.yaml @@ -7,7 +7,7 @@ long_name: Carry-less multiply (high-part) description: | `clmulh` produces the upper half of the 2*XLEN carry-less product definedBy: - anyOf: [B, Zbc, Zbkc, Zk, Zkn, Zks] + anyOf: [Zbc, Zbkc, Zk, Zkn, Zks] assembly: xd, xs1, xs2 encoding: match: 0000101----------011-----0110011 diff --git a/arch/inst/B/clmulr.yaml b/arch/inst/B/clmulr.yaml index a56b2b8635..d379359e71 100644 --- a/arch/inst/B/clmulr.yaml +++ b/arch/inst/B/clmulr.yaml @@ -6,8 +6,7 @@ name: clmulr long_name: Carry-less multiply (reversed) description: | `clmulr` produces bits 2*XLEN-2:XLEN-1 of the 2*XLEN carry-less product -definedBy: - anyOf: [B, Zbc] +definedBy: Zbc assembly: xd, xs1, xs2 access: s: always diff --git a/arch/inst/B/zext.h.yaml b/arch/inst/B/zext.h.yaml index 247db427c5..dc08ef84e3 100644 --- a/arch/inst/B/zext.h.yaml +++ b/arch/inst/B/zext.h.yaml @@ -14,7 +14,11 @@ description: | [NOTE] The *zext.h* instruction is a pseudo-op for `packw` when `Zbkb` is implemented and XLEN == 64. definedBy: - anyOf: [B, Zbb] + # when The bitmanip cryptography extensions are implemented, then zext.h is an alias + allOf: + - not: + anyOf: [Zbkb, Zk, Zkn, Zks] + - anyOf: [B, Zbb] encoding: RV32: match: 000010000000-----100-----0110011 @@ -30,8 +34,6 @@ encoding: location: 19-15 - name: rd location: 11-7 -excludedBy: - anyOf: [Zk, Zkn, Zks, Zbkb] # zext.h instruction is a pseudo-op for `packw` when `Zbkb` is implemented assembly: xd, xs1 access: s: always diff --git a/arch/inst/C/c.add.yaml b/arch/inst/C/c.add.yaml index c2249358b8..6fc98276d6 100644 --- a/arch/inst/C/c.add.yaml +++ b/arch/inst/C/c.add.yaml @@ -17,8 +17,10 @@ encoding: variables: - name: rs2 location: 6-2 + not: 0 - name: rd location: 11-7 + not: 0 access: s: always u: always diff --git a/arch/inst/C/c.addi.yaml b/arch/inst/C/c.addi.yaml index 6263e0c8f0..dff3d4b10c 100644 --- a/arch/inst/C/c.addi.yaml +++ b/arch/inst/C/c.addi.yaml @@ -33,4 +33,4 @@ operation(): | raise(ExceptionCode::IllegalInstruction, mode(), $encoding); } - X[rd] = X[rd] + imm; + X[rd] = X[rd] + $signed(imm); diff --git a/arch/inst/C/c.addi16sp.yaml b/arch/inst/C/c.addi16sp.yaml index 78cc3d1968..997f686d13 100644 --- a/arch/inst/C/c.addi16sp.yaml +++ b/arch/inst/C/c.addi16sp.yaml @@ -31,4 +31,4 @@ operation(): | raise(ExceptionCode::IllegalInstruction, mode(), $encoding); } - X[2] = X[2] + imm; + X[2] = X[2] + $signed(imm); diff --git a/arch/inst/C/c.addi4spn.yaml b/arch/inst/C/c.addi4spn.yaml index 3da786baf0..0b04cbe7fd 100644 --- a/arch/inst/C/c.addi4spn.yaml +++ b/arch/inst/C/c.addi4spn.yaml @@ -33,4 +33,4 @@ operation(): | raise(ExceptionCode::IllegalInstruction, mode(), $encoding); } - X[rd+8] = X[2] + imm; + X[creg2reg(rd)] = X[2] + imm; diff --git a/arch/inst/C/c.addw.yaml b/arch/inst/C/c.addw.yaml index a78674a86a..095c798116 100644 --- a/arch/inst/C/c.addw.yaml +++ b/arch/inst/C/c.addw.yaml @@ -27,9 +27,9 @@ access: vs: always vu: always operation(): | - Bits<32> t0 = X[rd+8][31:0]; - Bits<32> t1 = X[rs2+8][31:0]; - X[rd+8] = sext(t0 + t1, 31); + Bits<32> t0 = X[creg2reg(rd)][31:0]; + Bits<32> t1 = X[creg2reg(rs2)][31:0]; + X[creg2reg(rd)] = $signed(t0 + t1); sail(): | { diff --git a/arch/inst/C/c.and.yaml b/arch/inst/C/c.and.yaml index ba2648f0b8..5a1d6514d4 100644 --- a/arch/inst/C/c.and.yaml +++ b/arch/inst/C/c.and.yaml @@ -26,9 +26,9 @@ access: vs: always vu: always operation(): | - XReg t0 = X[rd+8]; - XReg t1 = X[rs2+8]; - X[rd+8] = t0 & t1; + XReg t0 = X[creg2reg(rd)]; + XReg t1 = X[creg2reg(rs2)]; + X[creg2reg(rd)] = t0 & t1; sail(): | { diff --git a/arch/inst/C/c.andi.yaml b/arch/inst/C/c.andi.yaml index 3ef3ac838b..e1b807c0a6 100644 --- a/arch/inst/C/c.andi.yaml +++ b/arch/inst/C/c.andi.yaml @@ -27,7 +27,7 @@ access: vu: always operation(): | # shamt is between 0-63 - X[rd+8] = X[rd+8] & imm; + X[creg2reg(rd)] = X[creg2reg(rd)] & $signed(imm); sail(): | { diff --git a/arch/inst/C/c.beqz.yaml b/arch/inst/C/c.beqz.yaml index b831bdf36d..04dd70d9e6 100644 --- a/arch/inst/C/c.beqz.yaml +++ b/arch/inst/C/c.beqz.yaml @@ -17,8 +17,8 @@ encoding: variables: - name: imm location: 12|6-5|2|11-10|4-3 - left_shift: 0 - - name: rs1 + left_shift: 1 + - name: xs1 location: 9-7 access: s: always @@ -29,8 +29,8 @@ operation(): | if (implemented?(ExtensionName::C) && (CSR[misa].C == 1'b0)) { raise(ExceptionCode::IllegalInstruction, mode(), $encoding); } - if (X[rs1] != 0) { - jump($pc + imm); + if (X[creg2reg(xs1)] == 0) { + jump($pc + $signed(imm)); } sail(): | diff --git a/arch/inst/C/c.bnez.yaml b/arch/inst/C/c.bnez.yaml index aa4cb97a0f..fe0384d507 100644 --- a/arch/inst/C/c.bnez.yaml +++ b/arch/inst/C/c.bnez.yaml @@ -6,7 +6,7 @@ name: c.bnez long_name: Branch if NOT Equal Zero description: | C.BEQZ performs conditional control transfers. The offset is sign-extended and added to the pc to form the branch target address. It can therefore target a ±256 B range. C.BEQZ takes the branch if the value in register rs1' is NOT zero. - It expands to `beq` `rs1, x0, offset`. + It expands to `beq` `xs1, x0, offset`. definedBy: anyOf: - C @@ -17,8 +17,8 @@ encoding: variables: - name: imm location: 12|6-5|2|11-10|4-3 - left_shift: 0 - - name: rs1 + left_shift: 1 + - name: xs1 location: 9-7 access: s: always @@ -29,8 +29,8 @@ operation(): | if (implemented?(ExtensionName::C) && (CSR[misa].C == 1'b0)) { raise(ExceptionCode::IllegalInstruction, mode(), $encoding); } - if (X[rs1] != 0) { - jump($pc + imm); + if (X[creg2reg(xs1)] != 0) { + jump($pc + $signed(imm)); } sail(): | diff --git a/arch/inst/C/c.fld.yaml b/arch/inst/C/c.fld.yaml index f4672ed0e9..9a9e87d6be 100644 --- a/arch/inst/C/c.fld.yaml +++ b/arch/inst/C/c.fld.yaml @@ -10,9 +10,11 @@ description: | to the base address in register rs1. It expands to `fld` `rd, offset(rs1)`. definedBy: - allOf: - - C - - D + anyOf: + - allOf: + - C + - D + - Zcd assembly: xd, imm(xs1) encoding: match: 001-----------00 @@ -34,6 +36,6 @@ operation(): | raise(ExceptionCode::IllegalInstruction, mode(), $encoding); } - XReg virtual_address = X[rs1] + imm; + XReg virtual_address = X[creg2reg(rs1)] + imm; - X[rd] = sext(read_memory<64>(virtual_address, $encoding), 64); + X[creg2reg(rd)] = sext(read_memory<64>(virtual_address, $encoding), 64); diff --git a/arch/inst/C/c.fldsp.yaml b/arch/inst/C/c.fldsp.yaml index beb226b790..d9c717a9a8 100644 --- a/arch/inst/C/c.fldsp.yaml +++ b/arch/inst/C/c.fldsp.yaml @@ -10,9 +10,11 @@ description: | to the stack pointer, x2. It expands to `fld` `rd, offset(x2)`. definedBy: - allOf: - - C - - D + anyOf: + - allOf: + - C + - D + - Zcd assembly: fd, imm(sp) encoding: match: 001-----------10 diff --git a/arch/inst/C/c.flw.yaml b/arch/inst/C/c.flw.yaml index 80165e98b1..3866dcbd42 100644 --- a/arch/inst/C/c.flw.yaml +++ b/arch/inst/C/c.flw.yaml @@ -14,6 +14,7 @@ definedBy: - C - F assembly: xd, imm(xs1) +base: 32 encoding: match: 011-----------00 variables: @@ -34,6 +35,6 @@ operation(): | raise(ExceptionCode::IllegalInstruction, mode(), $encoding); } - XReg virtual_address = X[rs1] + imm; + XReg virtual_address = X[creg2reg(rs1)] + imm; - X[rd] = sext(read_memory<32>(virtual_address, $encoding), 32); + X[creg2reg(rd)] = sext(read_memory<32>(virtual_address, $encoding), 32); diff --git a/arch/inst/C/c.flwsp.yaml b/arch/inst/C/c.flwsp.yaml index 0e50dbf1b3..4a46a06016 100644 --- a/arch/inst/C/c.flwsp.yaml +++ b/arch/inst/C/c.flwsp.yaml @@ -14,6 +14,7 @@ definedBy: - C - F assembly: fd, imm(sp) +base: 32 encoding: match: 011-----------10 variables: diff --git a/arch/inst/C/c.fsd.yaml b/arch/inst/C/c.fsd.yaml index 22a1c37854..1c4e1affd3 100644 --- a/arch/inst/C/c.fsd.yaml +++ b/arch/inst/C/c.fsd.yaml @@ -10,9 +10,11 @@ description: | to the base address in register rs1. It expands to `fsd` `rs2, offset(rs1)`. definedBy: - allOf: - - C - - D + anyOf: + - allOf: + - C + - D + - Zcd assembly: xs2, imm(xs1) encoding: match: 101-----------00 @@ -34,6 +36,6 @@ operation(): | raise(ExceptionCode::IllegalInstruction, mode(), $encoding); } - XReg virtual_address = X[rs1] + imm; + XReg virtual_address = X[creg2reg(rs1)] + imm; - write_memory<64>(virtual_address, X[rs2], $encoding); + write_memory<64>(virtual_address, X[creg2reg(rs2)], $encoding); diff --git a/arch/inst/C/c.fsdsp.yaml b/arch/inst/C/c.fsdsp.yaml index 185240e2c1..cc6b28eedd 100644 --- a/arch/inst/C/c.fsdsp.yaml +++ b/arch/inst/C/c.fsdsp.yaml @@ -10,9 +10,11 @@ description: | to the stack pointer, x2. It expands to `fsd` `rs2, offset(x2)`. definedBy: - allOf: - - C - - D + anyOf: + - allOf: + - C + - D + - Zcd assembly: fs2, imm(sp) encoding: match: 101-----------10 diff --git a/arch/inst/C/c.fsw.yaml b/arch/inst/C/c.fsw.yaml index 704652115e..48daecaac1 100644 --- a/arch/inst/C/c.fsw.yaml +++ b/arch/inst/C/c.fsw.yaml @@ -13,6 +13,7 @@ definedBy: allOf: - C - F +base: 32 assembly: xs2, imm(xs1) encoding: match: 111-----------00 @@ -34,6 +35,6 @@ operation(): | raise(ExceptionCode::IllegalInstruction, mode(), $encoding); } - XReg virtual_address = X[rs1] + imm; + XReg virtual_address = X[creg2reg(rs1)] + imm; - write_memory<32>(virtual_address, X[rs2][31:0], $encoding); + write_memory<32>(virtual_address, X[creg2reg(rs2)][31:0], $encoding); diff --git a/arch/inst/C/c.fswsp.yaml b/arch/inst/C/c.fswsp.yaml index 37cca7c629..808a023a4f 100644 --- a/arch/inst/C/c.fswsp.yaml +++ b/arch/inst/C/c.fswsp.yaml @@ -14,6 +14,7 @@ definedBy: - C - F assembly: fs2, imm(sp) +base: 32 encoding: match: 111-----------10 variables: diff --git a/arch/inst/C/c.j.yaml b/arch/inst/C/c.j.yaml index d1fb316eee..f52f324ddb 100644 --- a/arch/inst/C/c.j.yaml +++ b/arch/inst/C/c.j.yaml @@ -29,4 +29,4 @@ operation(): | raise(ExceptionCode::IllegalInstruction, mode(), $encoding); } - jump($pc + imm); + jump($pc + $signed(imm)); diff --git a/arch/inst/C/c.jal.yaml b/arch/inst/C/c.jal.yaml index 6d397fcf0a..6cf527cea0 100644 --- a/arch/inst/C/c.jal.yaml +++ b/arch/inst/C/c.jal.yaml @@ -32,5 +32,5 @@ operation(): | XReg retrun_addr = $pc + 2; - jump_halfword($pc + imm); + jump_halfword($pc + $signed(imm)); X[1] = retrun_addr; diff --git a/arch/inst/C/c.ld.yaml b/arch/inst/C/c.ld.yaml index b16adc0927..06798d7170 100644 --- a/arch/inst/C/c.ld.yaml +++ b/arch/inst/C/c.ld.yaml @@ -35,9 +35,9 @@ operation(): | raise(ExceptionCode::IllegalInstruction, mode(), $encoding); } - XReg virtual_address = X[rs1] + imm; + XReg virtual_address = X[creg2reg(rs1)] + imm; - X[rd] = sext(read_memory<64>(virtual_address, $encoding), 64); + X[creg2reg(rd)] = sext(read_memory<64>(virtual_address, $encoding), 64); sail(): | { diff --git a/arch/inst/C/c.li.yaml b/arch/inst/C/c.li.yaml index 19f3fd4e74..363e1c5b8a 100644 --- a/arch/inst/C/c.li.yaml +++ b/arch/inst/C/c.li.yaml @@ -31,4 +31,4 @@ operation(): | raise(ExceptionCode::IllegalInstruction, mode(), $encoding); } - X[rd] = imm; + X[rd] = $signed(imm); diff --git a/arch/inst/C/c.lui.yaml b/arch/inst/C/c.lui.yaml index bf2cca884f..e1ff446dc3 100644 --- a/arch/inst/C/c.lui.yaml +++ b/arch/inst/C/c.lui.yaml @@ -33,4 +33,4 @@ operation(): | raise(ExceptionCode::IllegalInstruction, mode(), $encoding); } - X[rd] = imm; + X[rd] = $signed(imm); diff --git a/arch/inst/C/c.lw.yaml b/arch/inst/C/c.lw.yaml index da6019aa9d..5cd3340fcc 100644 --- a/arch/inst/C/c.lw.yaml +++ b/arch/inst/C/c.lw.yaml @@ -5,10 +5,10 @@ kind: instruction name: c.lw long_name: Load word description: | - Loads a 32-bit value from memory into register rd. + Loads a 32-bit value from memory into register xd. It computes an effective address by adding the zero-extended offset, scaled by 4, - to the base address in register rs1. - It expands to `lw` `rd, offset(rs1)`. + to the base address in register xs1. + It expands to `lw` `xd, offset(xs1)`. definedBy: anyOf: - C @@ -20,9 +20,9 @@ encoding: - name: imm location: 5|12-10|6 left_shift: 2 - - name: rd + - name: xd location: 4-2 - - name: rs1 + - name: xs1 location: 9-7 access: s: always @@ -34,9 +34,9 @@ operation(): | raise(ExceptionCode::IllegalInstruction, mode(), $encoding); } - XReg virtual_address = X[rs1] + imm; + XReg virtual_address = X[creg2reg(xs1)] + imm; - X[rd] = sext(read_memory<32>(virtual_address, $encoding), 32); + X[creg2reg(xd)] = sext(read_memory<32>(virtual_address, $encoding), 32); sail(): | { diff --git a/arch/inst/C/c.mv.yaml b/arch/inst/C/c.mv.yaml index 36e97e4d10..9f7758cb3d 100644 --- a/arch/inst/C/c.mv.yaml +++ b/arch/inst/C/c.mv.yaml @@ -5,8 +5,8 @@ kind: instruction name: c.mv long_name: Move Register description: | - C.MV (move register) performs copy of the data in register rs2 to register rd - C.MV expands to addi rd, x0, rs2. + C.MV (move register) performs copy of the data in register xs2 to register xd + C.MV expands to addi xd, x0, xs2. definedBy: anyOf: - C @@ -15,10 +15,10 @@ assembly: xd, xs2 encoding: match: 1000----------10 variables: - - name: rd + - name: xd location: 11-7 not: 0 - - name: rs2 + - name: xs2 location: 6-2 not: 0 access: @@ -31,11 +31,11 @@ operation(): | raise(ExceptionCode::IllegalInstruction, mode(), $encoding); } - X[rd] = X[rs2]; + X[xd] = X[xs2]; sail(): | { - let rs2_val = X(rs2); - X(rs) = rs2_val + let xs2_val = X(xs2); + X(rs) = xs2_val RETIRE_SUCCESS } diff --git a/arch/inst/C/c.nop.yaml b/arch/inst/C/c.nop.yaml index 60bdb525ba..57b4df2909 100644 --- a/arch/inst/C/c.nop.yaml +++ b/arch/inst/C/c.nop.yaml @@ -5,18 +5,14 @@ kind: instruction name: c.nop long_name: Non-operation description: | - C.NOP expands into `addi x0, x0, imm`. + C.NOP expands into `addi x0, x0, 0`. definedBy: anyOf: - C - Zca assembly: imm encoding: - match: 000-00000-----01 - variables: - - name: imm - location: 12|6-2 - not: 0 + match: "0000000000000001" access: s: always u: always diff --git a/arch/inst/C/c.or.yaml b/arch/inst/C/c.or.yaml index b14f348c1a..9bac760be4 100644 --- a/arch/inst/C/c.or.yaml +++ b/arch/inst/C/c.or.yaml @@ -26,9 +26,9 @@ access: vs: always vu: always operation(): | - XReg t0 = X[rd+8]; - XReg t1 = X[rs2+8]; - X[rd+8] = t0 | t1; + XReg t0 = X[creg2reg(rd)]; + XReg t1 = X[creg2reg(rs2)]; + X[creg2reg(rd)] = t0 | t1; sail(): | { diff --git a/arch/inst/C/c.sd.yaml b/arch/inst/C/c.sd.yaml index 5b72210fcc..e7b55d03bb 100644 --- a/arch/inst/C/c.sd.yaml +++ b/arch/inst/C/c.sd.yaml @@ -35,6 +35,6 @@ operation(): | raise(ExceptionCode::IllegalInstruction, mode(), $encoding); } - XReg virtual_address = X[rs1] + imm; + XReg virtual_address = X[creg2reg(rs1)] + imm; - write_memory<64>(virtual_address, X[rs2], $encoding); + write_memory<64>(virtual_address, X[creg2reg(rs2)], $encoding); diff --git a/arch/inst/C/c.slli.yaml b/arch/inst/C/c.slli.yaml index 01dad72a7c..69118301a0 100644 --- a/arch/inst/C/c.slli.yaml +++ b/arch/inst/C/c.slli.yaml @@ -11,16 +11,24 @@ definedBy: anyOf: - C - Zca -base: 64 assembly: xd, shamt encoding: - match: 000-----------10 - variables: - - name: shamt - location: 12|6-2 - - name: rd - location: 11-7 - not: 0 + RV32: + match: 0000----------10 + variables: + - name: shamt + location: 6-2 + not: 0 + - name: rd + location: 11-7 + RV64: + match: 000-----------10 + variables: + - name: shamt + location: 12|6-2 + not: 0 + - name: rd + location: 11-7 access: s: always u: always diff --git a/arch/inst/C/c.srai.yaml b/arch/inst/C/c.srai.yaml index 0aa7341844..c96a70a519 100644 --- a/arch/inst/C/c.srai.yaml +++ b/arch/inst/C/c.srai.yaml @@ -12,15 +12,24 @@ definedBy: anyOf: - C - Zca -base: 64 assembly: xd, shamt encoding: - match: 100-01--------01 - variables: - - name: shamt - location: 12|6-2 - - name: rd - location: 9-7 + RV32: + match: 100001--------01 + variables: + - name: shamt + location: 6-2 + not: 0 + - name: rd + location: 9-7 + RV64: + match: 100-01--------01 + variables: + - name: shamt + location: 12|6-2 + not: 0 + - name: rd + location: 9-7 access: s: always u: always @@ -28,7 +37,7 @@ access: vu: always operation(): | # shamt is between 0-63 - X[rd+8] = X[rd+8] >>> shamt; + X[creg2reg(rd)] = X[creg2reg(rd)] >>> shamt; sail(): | { diff --git a/arch/inst/C/c.srli.yaml b/arch/inst/C/c.srli.yaml index 86112f61c0..8d58ef905f 100644 --- a/arch/inst/C/c.srli.yaml +++ b/arch/inst/C/c.srli.yaml @@ -12,15 +12,24 @@ definedBy: anyOf: - C - Zca -base: 64 assembly: xd, shamt encoding: - match: 100-00--------01 - variables: - - name: shamt - location: 12|6-2 - - name: rd - location: 9-7 + RV32: + match: 100000--------01 + variables: + - name: shamt + location: 6-2 + not: 0 + - name: rd + location: 9-7 + RV64: + match: 100-00--------01 + variables: + - name: shamt + location: 12|6-2 + not: 0 + - name: rd + location: 9-7 access: s: always u: always @@ -28,7 +37,7 @@ access: vu: always operation(): | # shamt is between 0-63 - X[rd+8] = X[rd+8] >> shamt; + X[creg2reg(rd)] = X[creg2reg(rd)] >> shamt; sail(): | { diff --git a/arch/inst/C/c.sub.yaml b/arch/inst/C/c.sub.yaml index 32757173a0..eeb1d61ad8 100644 --- a/arch/inst/C/c.sub.yaml +++ b/arch/inst/C/c.sub.yaml @@ -26,9 +26,9 @@ access: vs: always vu: always operation(): | - XReg t0 = X[rd+8]; - XReg t1 = X[rs2+8]; - X[rd+8] = t0 - t1; + XReg t0 = X[creg2reg(rd)]; + XReg t1 = X[creg2reg(rs2)]; + X[creg2reg(rd)] = t0 - t1; sail(): | { diff --git a/arch/inst/C/c.subw.yaml b/arch/inst/C/c.subw.yaml index 36ecadfe50..bdaa2bd5ae 100644 --- a/arch/inst/C/c.subw.yaml +++ b/arch/inst/C/c.subw.yaml @@ -27,9 +27,9 @@ access: vs: always vu: always operation(): | - Bits<32> t0 = X[rd+8][31:0]; - Bits<32> t1 = X[rs2+8][31:0]; - X[rd+8] = sext(t0 - t1, 31); + Bits<32> t0 = X[creg2reg(rd)][31:0]; + Bits<32> t1 = X[creg2reg(rs2)][31:0]; + X[creg2reg(rd)] = sext(t0 - t1, 31); sail(): | { diff --git a/arch/inst/C/c.sw.yaml b/arch/inst/C/c.sw.yaml index f2ac18a3d1..a23ffc7360 100644 --- a/arch/inst/C/c.sw.yaml +++ b/arch/inst/C/c.sw.yaml @@ -34,6 +34,6 @@ operation(): | raise(ExceptionCode::IllegalInstruction, mode(), $encoding); } - XReg virtual_address = X[rs1] + imm; + XReg virtual_address = X[creg2reg(rs1)] + imm; - write_memory<32>(virtual_address, X[rs2][31:0], $encoding); + write_memory<32>(virtual_address, X[creg2reg(rs2)][31:0], $encoding); diff --git a/arch/inst/C/c.xor.yaml b/arch/inst/C/c.xor.yaml index 5700c7b631..ae612de57c 100644 --- a/arch/inst/C/c.xor.yaml +++ b/arch/inst/C/c.xor.yaml @@ -26,9 +26,9 @@ access: vs: always vu: always operation(): | - XReg t0 = X[rd+8]; - XReg t1 = X[rs2+8]; - X[rd+8] = t0 ^ t1; + XReg t0 = X[creg2reg(rd)]; + XReg t1 = X[creg2reg(rs2)]; + X[creg2reg(rd)] = t0 ^ t1; sail(): | { diff --git a/arch/inst/I/add.yaml b/arch/inst/I/add.yaml index 6466549d44..7ae35fc3d6 100644 --- a/arch/inst/I/add.yaml +++ b/arch/inst/I/add.yaml @@ -5,18 +5,18 @@ kind: instruction name: add long_name: Integer add description: | - Add the value in rs1 to rs2, and store the result in rd. + Add the value in xs1 to xs2, and store the result in xd. Any overflow is thrown away. definedBy: I assembly: xd, xs1, xs2 encoding: match: 0000000----------000-----0110011 variables: - - name: rs2 + - name: xs2 location: 24-20 - - name: rs1 + - name: xs1 location: 19-15 - - name: rd + - name: xd location: 11-7 access: s: always @@ -24,30 +24,30 @@ access: vs: always vu: always data_independent_timing: true -operation(): X[rd] = X[rs1] + X[rs2]; +operation(): X[xd] = X[xs1] + X[xs2]; sail(): | { - let rs1_val = X(rs1); - let rs2_val = X(rs2); + let xs1_val = X(xs1); + let xs2_val = X(xs2); let result : xlenbits = match op { - RISCV_ADD => rs1_val + rs2_val, - RISCV_SLT => zero_extend(bool_to_bits(rs1_val <_s rs2_val)), - RISCV_SLTU => zero_extend(bool_to_bits(rs1_val <_u rs2_val)), - RISCV_AND => rs1_val & rs2_val, - RISCV_OR => rs1_val | rs2_val, - RISCV_XOR => rs1_val ^ rs2_val, + RISCV_ADD => xs1_val + xs2_val, + RISCV_SLT => zero_extend(bool_to_bits(xs1_val <_s xs2_val)), + RISCV_SLTU => zero_extend(bool_to_bits(xs1_val <_u xs2_val)), + RISCV_AND => xs1_val & xs2_val, + RISCV_OR => xs1_val | xs2_val, + RISCV_XOR => xs1_val ^ xs2_val, RISCV_SLL => if sizeof(xlen) == 32 - then rs1_val << (rs2_val[4..0]) - else rs1_val << (rs2_val[5..0]), + then xs1_val << (xs2_val[4..0]) + else xs1_val << (xs2_val[5..0]), RISCV_SRL => if sizeof(xlen) == 32 - then rs1_val >> (rs2_val[4..0]) - else rs1_val >> (rs2_val[5..0]), - RISCV_SUB => rs1_val - rs2_val, + then xs1_val >> (xs2_val[4..0]) + else xs1_val >> (xs2_val[5..0]), + RISCV_SUB => xs1_val - xs2_val, RISCV_SRA => if sizeof(xlen) == 32 - then shift_right_arith32(rs1_val, rs2_val[4..0]) - else shift_right_arith64(rs1_val, rs2_val[5..0]) + then shift_right_arith32(xs1_val, xs2_val[4..0]) + else shift_right_arith64(xs1_val, xs2_val[5..0]) }; - X(rd) = result; + X(xd) = result; RETIRE_SUCCESS } diff --git a/arch/inst/I/addi.yaml b/arch/inst/I/addi.yaml index 829a853ab8..259a8f5d10 100644 --- a/arch/inst/I/addi.yaml +++ b/arch/inst/I/addi.yaml @@ -4,7 +4,7 @@ $schema: "inst_schema.json#" kind: instruction name: addi long_name: Add immediate -description: Add an immediate to the value in rs1, and store the result in rd +description: Add an immediate to the value in xs1, and store the result in xd definedBy: I assembly: xd, xs1, imm encoding: @@ -12,9 +12,9 @@ encoding: variables: - name: imm location: 31-20 - - name: rs1 + - name: xs1 location: 19-15 - - name: rd + - name: xd location: 11-7 access: s: always @@ -22,20 +22,20 @@ access: vs: always vu: always data_independent_timing: true -operation(): X[rd] = X[rs1] + imm; +operation(): X[xd] = X[xs1] + $signed(imm); sail(): | { - let rs1_val = X(rs1); + let xs1_val = X(xs1); let immext : xlenbits = sign_extend(imm); let result : xlenbits = match op { - RISCV_ADDI => rs1_val + immext, - RISCV_SLTI => zero_extend(bool_to_bits(rs1_val <_s immext)), - RISCV_SLTIU => zero_extend(bool_to_bits(rs1_val <_u immext)), - RISCV_ANDI => rs1_val & immext, - RISCV_ORI => rs1_val | immext, - RISCV_XORI => rs1_val ^ immext + RISCV_ADDI => xs1_val + immext, + RISCV_SLTI => zero_extend(bool_to_bits(xs1_val <_s immext)), + RISCV_SLTIU => zero_extend(bool_to_bits(xs1_val <_u immext)), + RISCV_ANDI => xs1_val & immext, + RISCV_ORI => xs1_val | immext, + RISCV_XORI => xs1_val ^ immext }; - X(rd) = result; + X(xd) = result; RETIRE_SUCCESS } diff --git a/arch/inst/I/addiw.yaml b/arch/inst/I/addiw.yaml index 40a71c7535..8427667ffd 100644 --- a/arch/inst/I/addiw.yaml +++ b/arch/inst/I/addiw.yaml @@ -4,7 +4,7 @@ $schema: "inst_schema.json#" kind: instruction name: addiw long_name: Add immediate word -description: Add an immediate to the 32-bit value in rs1, and store the sign extended result in rd +description: Add an immediate to the 32-bit value in xs1, and store the sign extended result in xd definedBy: I base: 64 assembly: xd, xs1, imm @@ -13,9 +13,9 @@ encoding: variables: - name: imm location: 31-20 - - name: rs1 + - name: xs1 location: 19-15 - - name: rd + - name: xd location: 11-7 access: s: always @@ -24,12 +24,12 @@ access: vu: always data_independent_timing: true operation(): | - XReg operand = sext(X[rs1], 31); - X[rd] = sext(operand + imm, 31); + XReg operand = sext(X[xs1], 31); + X[xd] = sext(operand + imm, 31); sail(): | { - let result : xlenbits = sign_extend(imm) + X(rs1); - X(rd) = sign_extend(result[31..0]); + let result : xlenbits = sign_extend(imm) + X(xs1); + X(xd) = sign_extend(result[31..0]); RETIRE_SUCCESS } diff --git a/arch/inst/I/addw.yaml b/arch/inst/I/addw.yaml index 4a87e73c9b..0bed9a386e 100644 --- a/arch/inst/I/addw.yaml +++ b/arch/inst/I/addw.yaml @@ -5,7 +5,7 @@ kind: instruction name: addw long_name: Add word description: | - Add the 32-bit values in rs1 to rs2, and store the sign-extended result in rd. + Add the 32-bit values in xs1 to xs2, and store the sign-extended result in xd. Any overflow is thrown away. definedBy: I base: 64 @@ -13,11 +13,11 @@ assembly: xd, xs1, xs2 encoding: match: 0000000----------000-----0111011 variables: - - name: rs2 + - name: xs2 location: 24-20 - - name: rs1 + - name: xs1 location: 19-15 - - name: rd + - name: xd location: 11-7 access: s: always @@ -26,21 +26,21 @@ access: vu: always data_independent_timing: true operation(): | - XReg operand1 = sext(X[rs1], 31); - XReg operand2 = sext(X[rs2], 31); - X[rd] = sext(operand1 + operand2, 31); + XReg operand1 = sext(X[xs1], 31); + XReg operand2 = sext(X[xs2], 31); + X[xd] = sext(operand1 + operand2, 31); sail(): | { - let rs1_val = (X(rs1))[31..0]; - let rs2_val = (X(rs2))[31..0]; + let xs1_val = (X(xs1))[31..0]; + let xs2_val = (X(xs2))[31..0]; let result : bits(32) = match op { - RISCV_ADDW => rs1_val + rs2_val, - RISCV_SUBW => rs1_val - rs2_val, - RISCV_SLLW => rs1_val << (rs2_val[4..0]), - RISCV_SRLW => rs1_val >> (rs2_val[4..0]), - RISCV_SRAW => shift_right_arith32(rs1_val, rs2_val[4..0]) + RISCV_ADDW => xs1_val + xs2_val, + RISCV_SUBW => xs1_val - xs2_val, + RISCV_SLLW => xs1_val << (xs2_val[4..0]), + RISCV_SRLW => xs1_val >> (xs2_val[4..0]), + RISCV_SRAW => shift_right_arith32(xs1_val, xs2_val[4..0]) }; - X(rd) = sign_extend(result); + X(xd) = sign_extend(result); RETIRE_SUCCESS } diff --git a/arch/inst/I/and.yaml b/arch/inst/I/and.yaml index 7a18927797..e61a096346 100644 --- a/arch/inst/I/and.yaml +++ b/arch/inst/I/and.yaml @@ -4,17 +4,17 @@ $schema: "inst_schema.json#" kind: instruction name: and long_name: And -description: And rs1 with rs2, and store the result in rd +description: And xs1 with xs2, and store the result in xd definedBy: I assembly: xd, xs1, xs2 encoding: match: 0000000----------111-----0110011 variables: - - name: rs2 + - name: xs2 location: 24-20 - - name: rs1 + - name: xs1 location: 19-15 - - name: rd + - name: xd location: 11-7 access: s: always @@ -22,30 +22,30 @@ access: vs: always vu: always data_independent_timing: true -operation(): X[rd] = X[rs1] & X[rs2]; +operation(): X[xd] = X[xs1] & X[xs2]; sail(): | { - let rs1_val = X(rs1); - let rs2_val = X(rs2); + let xs1_val = X(xs1); + let xs2_val = X(xs2); let result : xlenbits = match op { - RISCV_ADD => rs1_val + rs2_val, - RISCV_SLT => zero_extend(bool_to_bits(rs1_val <_s rs2_val)), - RISCV_SLTU => zero_extend(bool_to_bits(rs1_val <_u rs2_val)), - RISCV_AND => rs1_val & rs2_val, - RISCV_OR => rs1_val | rs2_val, - RISCV_XOR => rs1_val ^ rs2_val, + RISCV_ADD => xs1_val + xs2_val, + RISCV_SLT => zero_extend(bool_to_bits(xs1_val <_s xs2_val)), + RISCV_SLTU => zero_extend(bool_to_bits(xs1_val <_u xs2_val)), + RISCV_AND => xs1_val & xs2_val, + RISCV_OR => xs1_val | xs2_val, + RISCV_XOR => xs1_val ^ xs2_val, RISCV_SLL => if sizeof(xlen) == 32 - then rs1_val << (rs2_val[4..0]) - else rs1_val << (rs2_val[5..0]), + then xs1_val << (xs2_val[4..0]) + else xs1_val << (xs2_val[5..0]), RISCV_SRL => if sizeof(xlen) == 32 - then rs1_val >> (rs2_val[4..0]) - else rs1_val >> (rs2_val[5..0]), - RISCV_SUB => rs1_val - rs2_val, + then xs1_val >> (xs2_val[4..0]) + else xs1_val >> (xs2_val[5..0]), + RISCV_SUB => xs1_val - xs2_val, RISCV_SRA => if sizeof(xlen) == 32 - then shift_right_arith32(rs1_val, rs2_val[4..0]) - else shift_right_arith64(rs1_val, rs2_val[5..0]) + then shift_right_arith32(xs1_val, xs2_val[4..0]) + else shift_right_arith64(xs1_val, xs2_val[5..0]) }; - X(rd) = result; + X(xd) = result; RETIRE_SUCCESS } diff --git a/arch/inst/I/andi.yaml b/arch/inst/I/andi.yaml index 111918792c..d4fbf18d44 100644 --- a/arch/inst/I/andi.yaml +++ b/arch/inst/I/andi.yaml @@ -4,7 +4,7 @@ $schema: "inst_schema.json#" kind: instruction name: andi long_name: And immediate -description: And an immediate to the value in rs1, and store the result in rd +description: And an immediate to the value in xs1, and store the result in xd definedBy: I assembly: xd, xs1, imm encoding: @@ -12,9 +12,9 @@ encoding: variables: - name: imm location: 31-20 - - name: rs1 + - name: xs1 location: 19-15 - - name: rd + - name: xd location: 11-7 access: s: always @@ -22,20 +22,20 @@ access: vs: always vu: always data_independent_timing: true -operation(): X[rd] = X[rs1] & imm; +operation(): X[xd] = X[xs1] & $signed(imm); sail(): | { - let rs1_val = X(rs1); + let xs1_val = X(xs1); let immext : xlenbits = sign_extend(imm); let result : xlenbits = match op { - RISCV_ADDI => rs1_val + immext, - RISCV_SLTI => zero_extend(bool_to_bits(rs1_val <_s immext)), - RISCV_SLTIU => zero_extend(bool_to_bits(rs1_val <_u immext)), - RISCV_ANDI => rs1_val & immext, - RISCV_ORI => rs1_val | immext, - RISCV_XORI => rs1_val ^ immext + RISCV_ADDI => xs1_val + immext, + RISCV_SLTI => zero_extend(bool_to_bits(xs1_val <_s immext)), + RISCV_SLTIU => zero_extend(bool_to_bits(xs1_val <_u immext)), + RISCV_ANDI => xs1_val & immext, + RISCV_ORI => xs1_val | immext, + RISCV_XORI => xs1_val ^ immext }; - X(rd) = result; + X(xd) = result; RETIRE_SUCCESS } diff --git a/arch/inst/I/auipc.yaml b/arch/inst/I/auipc.yaml index df2cac9f31..a461f09d82 100644 --- a/arch/inst/I/auipc.yaml +++ b/arch/inst/I/auipc.yaml @@ -13,15 +13,17 @@ encoding: - name: imm location: 31-12 left_shift: 12 - - name: rd + - name: xd location: 11-7 +hints: + - { $ref: inst/Zicfilp/lpad.yaml# } access: s: always u: always vs: always vu: always - data_independent_timing: true -operation(): X[rd] = $pc + imm; +data_independent_timing: true +operation(): X[xd] = $pc + $signed(imm); sail(): | { @@ -30,6 +32,6 @@ sail(): | RISCV_LUI => off, RISCV_AUIPC => get_arch_pc() + off }; - X(rd) = ret; + X(xd) = ret; RETIRE_SUCCESS } diff --git a/arch/inst/I/beq.yaml b/arch/inst/I/beq.yaml index de4b337363..b736a1b3eb 100644 --- a/arch/inst/I/beq.yaml +++ b/arch/inst/I/beq.yaml @@ -6,7 +6,7 @@ name: beq long_name: Branch if equal description: | Branch to PC + imm if - the value in register rs1 is equal to the value in register rs2. + the value in register xs1 is equal to the value in register xs2. Raise a `MisalignedAddress` exception if PC + imm is misaligned. definedBy: I @@ -17,9 +17,9 @@ encoding: - name: imm location: 31|7|30-25|11-8 left_shift: 1 - - name: rs2 + - name: xs2 location: 24-20 - - name: rs1 + - name: xs1 location: 19-15 access: s: always @@ -27,24 +27,24 @@ access: vs: always vu: always operation(): | - XReg lhs = X[rs1]; - XReg rhs = X[rs2]; + XReg lhs = X[xs1]; + XReg rhs = X[xs2]; if (lhs == rhs) { - jump_halfword($pc + imm); + jump_halfword($pc + $signed(imm)); } sail(): | { - let rs1_val = X(rs1); - let rs2_val = X(rs2); + let xs1_val = X(xs1); + let xs2_val = X(xs2); let taken : bool = match op { - RISCV_BEQ => rs1_val == rs2_val, - RISCV_BNE => rs1_val != rs2_val, - RISCV_BLT => rs1_val <_s rs2_val, - RISCV_BGE => rs1_val >=_s rs2_val, - RISCV_BLTU => rs1_val <_u rs2_val, - RISCV_BGEU => rs1_val >=_u rs2_val + RISCV_BEQ => xs1_val == xs2_val, + RISCV_BNE => xs1_val != xs2_val, + RISCV_BLT => xs1_val <_s xs2_val, + RISCV_BGE => xs1_val >=_s xs2_val, + RISCV_BLTU => xs1_val <_u xs2_val, + RISCV_BGEU => xs1_val >=_u xs2_val }; let t : xlenbits = PC + sign_extend(imm); if taken then { diff --git a/arch/inst/I/bge.yaml b/arch/inst/I/bge.yaml index d6ed0b2705..a19cedd692 100644 --- a/arch/inst/I/bge.yaml +++ b/arch/inst/I/bge.yaml @@ -6,7 +6,7 @@ name: bge long_name: Branch if greater than or equal description: | Branch to PC + imm if - the signed value in register rs1 is greater than or equal to the signed value in register rs2. + the signed value in register xs1 is greater than or equal to the signed value in register xs2. Raise a `MisalignedAddress` exception if PC + imm is misaligned. definedBy: I @@ -17,9 +17,9 @@ encoding: - name: imm location: 31|7|30-25|11-8 left_shift: 1 - - name: rs2 + - name: xs2 location: 24-20 - - name: rs1 + - name: xs1 location: 19-15 access: s: always @@ -27,24 +27,24 @@ access: vs: always vu: always operation(): | - XReg lhs = X[rs1]; - XReg rhs = X[rs2]; + XReg lhs = X[xs1]; + XReg rhs = X[xs2]; if ($signed(lhs) >= $signed(rhs)) { - jump_halfword($pc + imm); + jump_halfword($pc + $signed(imm)); } sail(): | { - let rs1_val = X(rs1); - let rs2_val = X(rs2); + let xs1_val = X(xs1); + let xs2_val = X(xs2); let taken : bool = match op { - RISCV_BEQ => rs1_val == rs2_val, - RISCV_BNE => rs1_val != rs2_val, - RISCV_BLT => rs1_val <_s rs2_val, - RISCV_BGE => rs1_val >=_s rs2_val, - RISCV_BLTU => rs1_val <_u rs2_val, - RISCV_BGEU => rs1_val >=_u rs2_val + RISCV_BEQ => xs1_val == xs2_val, + RISCV_BNE => xs1_val != xs2_val, + RISCV_BLT => xs1_val <_s xs2_val, + RISCV_BGE => xs1_val >=_s xs2_val, + RISCV_BLTU => xs1_val <_u xs2_val, + RISCV_BGEU => xs1_val >=_u xs2_val }; let t : xlenbits = PC + sign_extend(imm); if taken then { diff --git a/arch/inst/I/bgeu.yaml b/arch/inst/I/bgeu.yaml index e2a2a48214..9a4583a988 100644 --- a/arch/inst/I/bgeu.yaml +++ b/arch/inst/I/bgeu.yaml @@ -6,7 +6,7 @@ name: bgeu long_name: Branch if greater than or equal unsigned description: | Branch to PC + imm if - the unsigned value in register rs1 is greater than or equal to the unsigned value in register rs2. + the unsigned value in register xs1 is greater than or equal to the unsigned value in register xs2. Raise a `MisalignedAddress` exception if PC + imm is misaligned. definedBy: I @@ -17,9 +17,9 @@ encoding: - name: imm location: 31|7|30-25|11-8 left_shift: 1 - - name: rs2 + - name: xs2 location: 24-20 - - name: rs1 + - name: xs1 location: 19-15 access: s: always @@ -27,24 +27,24 @@ access: vs: always vu: always operation(): | - XReg lhs = X[rs1]; - XReg rhs = X[rs2]; + XReg lhs = X[xs1]; + XReg rhs = X[xs2]; if (lhs >= rhs) { - jump_halfword($pc + imm); + jump_halfword($pc + $signed(imm)); } sail(): | { - let rs1_val = X(rs1); - let rs2_val = X(rs2); + let xs1_val = X(xs1); + let xs2_val = X(xs2); let taken : bool = match op { - RISCV_BEQ => rs1_val == rs2_val, - RISCV_BNE => rs1_val != rs2_val, - RISCV_BLT => rs1_val <_s rs2_val, - RISCV_BGE => rs1_val >=_s rs2_val, - RISCV_BLTU => rs1_val <_u rs2_val, - RISCV_BGEU => rs1_val >=_u rs2_val + RISCV_BEQ => xs1_val == xs2_val, + RISCV_BNE => xs1_val != xs2_val, + RISCV_BLT => xs1_val <_s xs2_val, + RISCV_BGE => xs1_val >=_s xs2_val, + RISCV_BLTU => xs1_val <_u xs2_val, + RISCV_BGEU => xs1_val >=_u xs2_val }; let t : xlenbits = PC + sign_extend(imm); if taken then { diff --git a/arch/inst/I/blt.yaml b/arch/inst/I/blt.yaml index 444d76114d..a6898ede89 100644 --- a/arch/inst/I/blt.yaml +++ b/arch/inst/I/blt.yaml @@ -6,7 +6,7 @@ name: blt long_name: Branch if less than description: | Branch to PC + imm if - the signed value in register rs1 is less than the signed value in register rs2. + the signed value in register xs1 is less than the signed value in register xs2. Raise a `MisalignedAddress` exception if PC + imm is misaligned. definedBy: I @@ -17,9 +17,9 @@ encoding: - name: imm location: 31|7|30-25|11-8 left_shift: 1 - - name: rs2 + - name: xs2 location: 24-20 - - name: rs1 + - name: xs1 location: 19-15 access: s: always @@ -27,24 +27,24 @@ access: vs: always vu: always operation(): | - XReg lhs = X[rs1]; - XReg rhs = X[rs2]; + XReg lhs = X[xs1]; + XReg rhs = X[xs2]; if ($signed(lhs) < $signed(rhs)) { - jump_halfword($pc + imm); + jump_halfword($pc + $signed(imm)); } sail(): | { - let rs1_val = X(rs1); - let rs2_val = X(rs2); + let xs1_val = X(xs1); + let xs2_val = X(xs2); let taken : bool = match op { - RISCV_BEQ => rs1_val == rs2_val, - RISCV_BNE => rs1_val != rs2_val, - RISCV_BLT => rs1_val <_s rs2_val, - RISCV_BGE => rs1_val >=_s rs2_val, - RISCV_BLTU => rs1_val <_u rs2_val, - RISCV_BGEU => rs1_val >=_u rs2_val + RISCV_BEQ => xs1_val == xs2_val, + RISCV_BNE => xs1_val != xs2_val, + RISCV_BLT => xs1_val <_s xs2_val, + RISCV_BGE => xs1_val >=_s xs2_val, + RISCV_BLTU => xs1_val <_u xs2_val, + RISCV_BGEU => xs1_val >=_u xs2_val }; let t : xlenbits = PC + sign_extend(imm); if taken then { diff --git a/arch/inst/I/bltu.yaml b/arch/inst/I/bltu.yaml index caedfe0483..a379a838cf 100644 --- a/arch/inst/I/bltu.yaml +++ b/arch/inst/I/bltu.yaml @@ -6,7 +6,7 @@ name: bltu long_name: Branch if less than unsigned description: | Branch to PC + imm if - the unsigned value in register rs1 is less than the unsigned value in register rs2. + the unsigned value in register xs1 is less than the unsigned value in register xs2. Raise a `MisalignedAddress` exception if PC + imm is misaligned. definedBy: I @@ -17,9 +17,9 @@ encoding: - name: imm location: 31|7|30-25|11-8 left_shift: 1 - - name: rs2 + - name: xs2 location: 24-20 - - name: rs1 + - name: xs1 location: 19-15 access: s: always @@ -27,24 +27,24 @@ access: vs: always vu: always operation(): | - XReg lhs = X[rs1]; - XReg rhs = X[rs2]; + XReg lhs = X[xs1]; + XReg rhs = X[xs2]; if (lhs < rhs) { - jump_halfword($pc + imm); + jump_halfword($pc + $signed(imm)); } sail(): | { - let rs1_val = X(rs1); - let rs2_val = X(rs2); + let xs1_val = X(xs1); + let xs2_val = X(xs2); let taken : bool = match op { - RISCV_BEQ => rs1_val == rs2_val, - RISCV_BNE => rs1_val != rs2_val, - RISCV_BLT => rs1_val <_s rs2_val, - RISCV_BGE => rs1_val >=_s rs2_val, - RISCV_BLTU => rs1_val <_u rs2_val, - RISCV_BGEU => rs1_val >=_u rs2_val + RISCV_BEQ => xs1_val == xs2_val, + RISCV_BNE => xs1_val != xs2_val, + RISCV_BLT => xs1_val <_s xs2_val, + RISCV_BGE => xs1_val >=_s xs2_val, + RISCV_BLTU => xs1_val <_u xs2_val, + RISCV_BGEU => xs1_val >=_u xs2_val }; let t : xlenbits = PC + sign_extend(imm); if taken then { diff --git a/arch/inst/I/bne.yaml b/arch/inst/I/bne.yaml index 97f7ed0181..8abd75137e 100644 --- a/arch/inst/I/bne.yaml +++ b/arch/inst/I/bne.yaml @@ -6,7 +6,7 @@ name: bne long_name: Branch if not equal description: | Branch to PC + imm if - the value in register rs1 is not equal to the value in register rs2. + the value in register xs1 is not equal to the value in register xs2. Raise a `MisalignedAddress` exception if PC + imm is misaligned. definedBy: I @@ -17,9 +17,9 @@ encoding: - name: imm location: 31|7|30-25|11-8 left_shift: 1 - - name: rs2 + - name: xs2 location: 24-20 - - name: rs1 + - name: xs1 location: 19-15 access: s: always @@ -27,24 +27,24 @@ access: vs: always vu: always operation(): | - XReg lhs = X[rs1]; - XReg rhs = X[rs2]; + XReg lhs = X[xs1]; + XReg rhs = X[xs2]; if (lhs != rhs) { - jump_halfword($pc + imm); + jump_halfword($pc + $signed(imm)); } sail(): | { - let rs1_val = X(rs1); - let rs2_val = X(rs2); + let xs1_val = X(xs1); + let xs2_val = X(xs2); let taken : bool = match op { - RISCV_BEQ => rs1_val == rs2_val, - RISCV_BNE => rs1_val != rs2_val, - RISCV_BLT => rs1_val <_s rs2_val, - RISCV_BGE => rs1_val >=_s rs2_val, - RISCV_BLTU => rs1_val <_u rs2_val, - RISCV_BGEU => rs1_val >=_u rs2_val + RISCV_BEQ => xs1_val == xs2_val, + RISCV_BNE => xs1_val != xs2_val, + RISCV_BLT => xs1_val <_s xs2_val, + RISCV_BGE => xs1_val >=_s xs2_val, + RISCV_BLTU => xs1_val <_u xs2_val, + RISCV_BGEU => xs1_val >=_u xs2_val }; let t : xlenbits = PC + sign_extend(imm); if taken then { diff --git a/arch/inst/I/ebreak.yaml b/arch/inst/I/ebreak.yaml index 918a6f3927..92155a62f7 100644 --- a/arch/inst/I/ebreak.yaml +++ b/arch/inst/I/ebreak.yaml @@ -10,7 +10,7 @@ description: | EBREAK raises a breakpoint exception and performs no other operation. [NOTE] - As described in the `C` Standard Extension for Compressed Instructions, the `c.ebreak` + As described in the `C` Standaxd Extension for Compressed Instructions, the `c.ebreak` instruction performs the same operation as the EBREAK instruction. EBREAK causes the receiving privilege mode's epc register to be set to the address of diff --git a/arch/inst/I/fence.yaml b/arch/inst/I/fence.yaml index 4302720170..a0b04aa089 100644 --- a/arch/inst/I/fence.yaml +++ b/arch/inst/I/fence.yaml @@ -3,14 +3,14 @@ $schema: "inst_schema.json#" kind: instruction name: fence -long_name: Memory ordering fence +long_name: Memory oxdering fence description: | - Orders memory operations. + Oxders memory operations. - The `fence` instruction is used to order device I/O and memory accesses as + The `fence` instruction is used to oxder device I/O and memory accesses as viewed by other RISC-V harts and external devices or coprocessors. Any combination of device input (I), device output (O), memory reads \(R), - and memory writes (W) may be ordered with respect to any combination of + and memory writes (W) may be oxdered with respect to any combination of the same. Informally, no other RISC-V hart or external device can observe any operation in the _successor_ set following a `fence` before any operation in the _predecessor_ set preceding the `fence`. @@ -30,22 +30,22 @@ description: | |=== |_fm_ field |Mnemonic |Meaning |0000 |_none_ |Normal Fence - |1000 |TSO |With `FENCE RW,RW`: exclude write-to-read ordering; otherwise: _Reserved for future use._ + |1000 |TSO |With `FENCE RW,RW`: exclude write-to-read oxdering; otherwise: _Reserved for future use._ 2+|_other_ |_Reserved for future use._ |=== When the mode field _fm_ is `0001` and both the predecessor and successor sets are 'RW', - then the instruction acts as a special-case `fence.tso`. `fence.tso` orders all load operations + then the instruction acts as a special-case `fence.tso`. `fence.tso` oxders all load operations in its predecessor set before all memory operations in its successor set, and all store operations in its predecessor set before all store operations in its successor set. This leaves non-AMO store - operations in the 'fence.tso's predecessor set unordered with non-AMO loads in its successor set. + operations in the 'fence.tso's predecessor set unoxdered with non-AMO loads in its successor set. When mode field _fm_ is not `0001`, or when mode field _fm_ is `0001` but the _pred_ and _succ_ fields are not both 'RW' (0x3), then the fence acts as a baseline fence (_e.g._, _fm_ is effectively `0000`). This is unaffected by the FIOM bits, described below (implicit promotion does not change how `fence.tso` is decoded). - The `rs1` and `rd` fields are unused and ignored. + The `xs1` and `xd` fields are unused and ignored. In modes other than M-mode, `fence` is further affected by `menvcfg.FIOM`, `senvcfg.FIOM`<% if ext?(:H) %>, and/or `henvcfg.FIOM`<% end %> @@ -131,9 +131,9 @@ encoding: location: 27-24 - name: succ location: 23-20 - - name: rs1 + - name: xs1 location: 19-15 - - name: rd + - name: xd location: 11-7 access: s: always @@ -151,7 +151,7 @@ operation(): | } } if (implemented?(ExtensionName::Zihintpause)) { - if ((pred == 1) && (succ == 0) && (fm == 0) && (rd == 0) && (rs1 == 0)) { + if ((pred == 1) && (succ == 0) && (fm == 0) && (xd == 0) && (xs1 == 0)) { # this is a PAUSE instruction is_pause = true; } @@ -205,7 +205,7 @@ operation(): | pseudoinstructions: - when: (pred == 0x3) && (succ == 0x3) && (fm == 1) to: fence.tso - - when: (pred == 1) && (succ == 0) && (fm == 0) && (rd == 0) && (rs1 == 0) + - when: (pred == 1) && (succ == 0) && (fm == 0) && (xd == 0) && (xs1 == 0) to: pause sail(): | diff --git a/arch/inst/I/jal.yaml b/arch/inst/I/jal.yaml index f0db80a912..d853009db6 100644 --- a/arch/inst/I/jal.yaml +++ b/arch/inst/I/jal.yaml @@ -6,7 +6,7 @@ name: jal long_name: Jump and link description: | Jump to a PC-relative offset and store the return - address in rd. + address in xd. definedBy: I assembly: xd, imm encoding: @@ -16,7 +16,7 @@ encoding: location: 31|19-12|20|30-21 left_shift: 1 sign_extend: true - - name: rd + - name: xd location: 11-7 access: s: always @@ -26,8 +26,8 @@ access: operation(): | XReg retrun_addr = $pc + 4; - jump_halfword($pc + imm); - X[rd] = retrun_addr; + jump_halfword($pc + $signed(imm)); + X[xd] = retrun_addr; sail(): | { @@ -39,13 +39,13 @@ sail(): | RETIRE_FAIL }, Ext_ControlAddr_OK(target) => { - /* Perform standard alignment check */ + /* Perform standaxd alignment check */ if bit_to_bool(target[1]) & not(extension("C")) then { handle_mem_exception(target, E_Fetch_Addr_Align()); RETIRE_FAIL } else { - X(rd) = get_next_pc(); + X(xd) = get_next_pc(); set_next_pc(target); RETIRE_SUCCESS } diff --git a/arch/inst/I/jalr.yaml b/arch/inst/I/jalr.yaml index 55eae9900d..c12fe0881f 100644 --- a/arch/inst/I/jalr.yaml +++ b/arch/inst/I/jalr.yaml @@ -5,20 +5,20 @@ kind: instruction name: jalr long_name: Jump and link register description: | - Jump to an address formed by adding rs1 + Jump to an address formed by adding xs1 to a signed offset then clearing the least significant bit, and store the return address - in rd. + in xd. definedBy: I -assembly: xd, imm(rs1) +assembly: xd, imm(xs1) encoding: match: -----------------000-----1100111 variables: - name: imm location: 31-20 - - name: rs1 + - name: xs1 location: 19-15 - - name: rd + - name: xd location: 11-7 access: s: always @@ -29,18 +29,18 @@ operation(): | XReg returnaddr; returnaddr = $pc + 4; - jump((X[rs1] + imm) & ~XLEN'1); - X[rd] = returnaddr; + jump((X[xs1] + $signed(imm)) & ~XLEN'1); + X[xd] = returnaddr; sail(): | { /* For the sequential model, the memory-model definition doesn't work directly - * if rs1 = rd. We would effectively have to keep a regfile for reads and another for + * if xs1 = xd. We would effectively have to keep a regfile for reads and another for * writes, and swap on instruction completion. This could perhaps be optimized in - * some manner, but for now, we just keep a reordered definition to improve simulator + * some manner, but for now, we just keep a reoxdered definition to improve simulator * performance. */ - let t : xlenbits = X(rs1) + sign_extend(imm); + let t : xlenbits = X(xs1) + sign_extend(imm); /* Extensions get the first checks on the prospective target address. */ match ext_control_check_addr(t) { Ext_ControlAddr_Error(e) => { @@ -53,7 +53,7 @@ sail(): | handle_mem_exception(target, E_Fetch_Addr_Align()); RETIRE_FAIL } else { - X(rd) = get_next_pc(); + X(xd) = get_next_pc(); set_next_pc(target); RETIRE_SUCCESS } diff --git a/arch/inst/I/lb.yaml b/arch/inst/I/lb.yaml index 170707be9a..ce3ea1a077 100644 --- a/arch/inst/I/lb.yaml +++ b/arch/inst/I/lb.yaml @@ -5,19 +5,19 @@ kind: instruction name: lb long_name: Load byte description: | - Load 8 bits of data into register `rd` from an - address formed by adding `rs1` to a signed offset. + Load 8 bits of data into register `xd` from an + address formed by adding `xs1` to a signed offset. Sign extend the result. definedBy: I -assembly: xd, imm(rs1) +assembly: xd, imm(xs1) encoding: match: -----------------000-----0000011 variables: - name: imm location: 31-20 - - name: rs1 + - name: xs1 location: 19-15 - - name: rd + - name: xd location: 11-7 access: s: always @@ -25,16 +25,16 @@ access: vs: always vu: always operation(): | - XReg virtual_address = X[rs1] + imm; + XReg virtual_address = X[xs1] + $signed(imm); - X[rd] = sext(read_memory<8>(virtual_address, $encoding), 8); + X[xd] = sext(read_memory<8>(virtual_address, $encoding), 8); sail(): | { let offset : xlenbits = sign_extend(imm); - /* Get the address, X(rs1) + offset. + /* Get the address, X(xs1) + offset. Some extensions perform additional checks on address validity. */ - match ext_data_get_addr(rs1, offset, Read(Data), width) { + match ext_data_get_addr(xs1, offset, Read(Data), width) { Ext_DataAddr_Error(e) => { ext_handle_data_check_error(e); RETIRE_FAIL }, Ext_DataAddr_OK(vaddr) => if check_misaligned(vaddr, width) @@ -44,13 +44,13 @@ sail(): | TR_Address(paddr, _) => match (width) { BYTE => - process_load(rd, vaddr, mem_read(Read(Data), paddr, 1, aq, rl, false), is_unsigned), + process_load(xd, vaddr, mem_read(Read(Data), paddr, 1, aq, rl, false), is_unsigned), HALF => - process_load(rd, vaddr, mem_read(Read(Data), paddr, 2, aq, rl, false), is_unsigned), + process_load(xd, vaddr, mem_read(Read(Data), paddr, 2, aq, rl, false), is_unsigned), WORD => - process_load(rd, vaddr, mem_read(Read(Data), paddr, 4, aq, rl, false), is_unsigned), + process_load(xd, vaddr, mem_read(Read(Data), paddr, 4, aq, rl, false), is_unsigned), DOUBLE if sizeof(xlen) >= 64 => - process_load(rd, vaddr, mem_read(Read(Data), paddr, 8, aq, rl, false), is_unsigned), + process_load(xd, vaddr, mem_read(Read(Data), paddr, 8, aq, rl, false), is_unsigned), _ => report_invalid_width(__FILE__, __LINE__, width, "load") } } diff --git a/arch/inst/I/lbu.yaml b/arch/inst/I/lbu.yaml index efb7d96972..c13926511d 100644 --- a/arch/inst/I/lbu.yaml +++ b/arch/inst/I/lbu.yaml @@ -5,19 +5,19 @@ kind: instruction name: lbu long_name: Load byte unsigned description: | - Load 8 bits of data into register `rd` from an - address formed by adding `rs1` to a signed offset. + Load 8 bits of data into register `xd` from an + address formed by adding `xs1` to a signed offset. Zero extend the result. definedBy: I -assembly: xd, imm(rs1) +assembly: xd, imm(xs1) encoding: match: -----------------100-----0000011 variables: - name: imm location: 31-20 - - name: rs1 + - name: xs1 location: 19-15 - - name: rd + - name: xd location: 11-7 access: s: always @@ -25,16 +25,16 @@ access: vs: always vu: always operation(): | - XReg virtual_address = X[rs1] + imm; + XReg virtual_address = X[xs1] + $signed(imm); - X[rd] = read_memory<8>(virtual_address, $encoding); + X[xd] = read_memory<8>(virtual_address, $encoding); sail(): | { let offset : xlenbits = sign_extend(imm); - /* Get the address, X(rs1) + offset. + /* Get the address, X(xs1) + offset. Some extensions perform additional checks on address validity. */ - match ext_data_get_addr(rs1, offset, Read(Data), width) { + match ext_data_get_addr(xs1, offset, Read(Data), width) { Ext_DataAddr_Error(e) => { ext_handle_data_check_error(e); RETIRE_FAIL }, Ext_DataAddr_OK(vaddr) => if check_misaligned(vaddr, width) @@ -44,13 +44,13 @@ sail(): | TR_Address(paddr, _) => match (width) { BYTE => - process_load(rd, vaddr, mem_read(Read(Data), paddr, 1, aq, rl, false), is_unsigned), + process_load(xd, vaddr, mem_read(Read(Data), paddr, 1, aq, rl, false), is_unsigned), HALF => - process_load(rd, vaddr, mem_read(Read(Data), paddr, 2, aq, rl, false), is_unsigned), + process_load(xd, vaddr, mem_read(Read(Data), paddr, 2, aq, rl, false), is_unsigned), WORD => - process_load(rd, vaddr, mem_read(Read(Data), paddr, 4, aq, rl, false), is_unsigned), + process_load(xd, vaddr, mem_read(Read(Data), paddr, 4, aq, rl, false), is_unsigned), DOUBLE if sizeof(xlen) >= 64 => - process_load(rd, vaddr, mem_read(Read(Data), paddr, 8, aq, rl, false), is_unsigned), + process_load(xd, vaddr, mem_read(Read(Data), paddr, 8, aq, rl, false), is_unsigned), _ => report_invalid_width(__FILE__, __LINE__, width, "load") } } diff --git a/arch/inst/I/ld.yaml b/arch/inst/I/ld.yaml index e4c39adc06..0a73f77ac8 100644 --- a/arch/inst/I/ld.yaml +++ b/arch/inst/I/ld.yaml @@ -5,19 +5,19 @@ kind: instruction name: ld long_name: Load doubleword description: | - Load 64 bits of data into register `rd` from an - address formed by adding `rs1` to a signed offset. + Load 64 bits of data into register `xd` from an + address formed by adding `xs1` to a signed offset. definedBy: I base: 64 -assembly: xd, imm(rs1) +assembly: xd, imm(xs1) encoding: match: -----------------011-----0000011 variables: - name: imm location: 31-20 - - name: rs1 + - name: xs1 location: 19-15 - - name: rd + - name: xd location: 11-7 access: s: always @@ -25,16 +25,16 @@ access: vs: always vu: always operation(): | - XReg virtual_address = X[rs1] + imm; + XReg virtual_address = X[xs1] + $signed(imm); - X[rd] = read_memory<64>(virtual_address, $encoding); + X[xd] = read_memory<64>(virtual_address, $encoding); sail(): | { let offset : xlenbits = sign_extend(imm); - /* Get the address, X(rs1) + offset. + /* Get the address, X(xs1) + offset. Some extensions perform additional checks on address validity. */ - match ext_data_get_addr(rs1, offset, Read(Data), width) { + match ext_data_get_addr(xs1, offset, Read(Data), width) { Ext_DataAddr_Error(e) => { ext_handle_data_check_error(e); RETIRE_FAIL }, Ext_DataAddr_OK(vaddr) => if check_misaligned(vaddr, width) @@ -44,13 +44,13 @@ sail(): | TR_Address(paddr, _) => match (width) { BYTE => - process_load(rd, vaddr, mem_read(Read(Data), paddr, 1, aq, rl, false), is_unsigned), + process_load(xd, vaddr, mem_read(Read(Data), paddr, 1, aq, rl, false), is_unsigned), HALF => - process_load(rd, vaddr, mem_read(Read(Data), paddr, 2, aq, rl, false), is_unsigned), + process_load(xd, vaddr, mem_read(Read(Data), paddr, 2, aq, rl, false), is_unsigned), WORD => - process_load(rd, vaddr, mem_read(Read(Data), paddr, 4, aq, rl, false), is_unsigned), + process_load(xd, vaddr, mem_read(Read(Data), paddr, 4, aq, rl, false), is_unsigned), DOUBLE if sizeof(xlen) >= 64 => - process_load(rd, vaddr, mem_read(Read(Data), paddr, 8, aq, rl, false), is_unsigned), + process_load(xd, vaddr, mem_read(Read(Data), paddr, 8, aq, rl, false), is_unsigned), _ => report_invalid_width(__FILE__, __LINE__, width, "load") } } diff --git a/arch/inst/I/lh.yaml b/arch/inst/I/lh.yaml index 86b5b95f06..24e7879381 100644 --- a/arch/inst/I/lh.yaml +++ b/arch/inst/I/lh.yaml @@ -5,19 +5,19 @@ kind: instruction name: lh long_name: Load halfword description: | - Load 16 bits of data into register `rd` from an - address formed by adding `rs1` to a signed offset. + Load 16 bits of data into register `xd` from an + address formed by adding `xs1` to a signed offset. Sign extend the result. definedBy: I -assembly: xd, imm(rs1) +assembly: xd, imm(xs1) encoding: match: -----------------001-----0000011 variables: - name: imm location: 31-20 - - name: rs1 + - name: xs1 location: 19-15 - - name: rd + - name: xd location: 11-7 access: s: always @@ -25,16 +25,16 @@ access: vs: always vu: always operation(): | - XReg virtual_address = X[rs1] + imm; + XReg virtual_address = X[xs1] + $signed(imm); - X[rd] = sext(read_memory<16>(virtual_address, $encoding), 16); + X[xd] = sext(read_memory<16>(virtual_address, $encoding), 16); sail(): | { let offset : xlenbits = sign_extend(imm); - /* Get the address, X(rs1) + offset. + /* Get the address, X(xs1) + offset. Some extensions perform additional checks on address validity. */ - match ext_data_get_addr(rs1, offset, Read(Data), width) { + match ext_data_get_addr(xs1, offset, Read(Data), width) { Ext_DataAddr_Error(e) => { ext_handle_data_check_error(e); RETIRE_FAIL }, Ext_DataAddr_OK(vaddr) => if check_misaligned(vaddr, width) @@ -44,13 +44,13 @@ sail(): | TR_Address(paddr, _) => match (width) { BYTE => - process_load(rd, vaddr, mem_read(Read(Data), paddr, 1, aq, rl, false), is_unsigned), + process_load(xd, vaddr, mem_read(Read(Data), paddr, 1, aq, rl, false), is_unsigned), HALF => - process_load(rd, vaddr, mem_read(Read(Data), paddr, 2, aq, rl, false), is_unsigned), + process_load(xd, vaddr, mem_read(Read(Data), paddr, 2, aq, rl, false), is_unsigned), WORD => - process_load(rd, vaddr, mem_read(Read(Data), paddr, 4, aq, rl, false), is_unsigned), + process_load(xd, vaddr, mem_read(Read(Data), paddr, 4, aq, rl, false), is_unsigned), DOUBLE if sizeof(xlen) >= 64 => - process_load(rd, vaddr, mem_read(Read(Data), paddr, 8, aq, rl, false), is_unsigned), + process_load(xd, vaddr, mem_read(Read(Data), paddr, 8, aq, rl, false), is_unsigned), _ => report_invalid_width(__FILE__, __LINE__, width, "load") } } diff --git a/arch/inst/I/lhu.yaml b/arch/inst/I/lhu.yaml index 95e8a60eec..a670098b35 100644 --- a/arch/inst/I/lhu.yaml +++ b/arch/inst/I/lhu.yaml @@ -5,19 +5,19 @@ kind: instruction name: lhu long_name: Load halfword unsigned description: | - Load 16 bits of data into register `rd` from an - address formed by adding `rs1` to a signed offset. + Load 16 bits of data into register `xd` from an + address formed by adding `xs1` to a signed offset. Zero extend the result. definedBy: I -assembly: xd, imm(rs1) +assembly: xd, imm(xs1) encoding: match: -----------------101-----0000011 variables: - name: imm location: 31-20 - - name: rs1 + - name: xs1 location: 19-15 - - name: rd + - name: xd location: 11-7 access: s: always @@ -25,16 +25,16 @@ access: vs: always vu: always operation(): | - XReg virtual_address = X[rs1] + imm; + XReg virtual_address = X[xs1] + $signed(imm); - X[rd] = read_memory<16>(virtual_address, $encoding); + X[xd] = read_memory<16>(virtual_address, $encoding); sail(): | { let offset : xlenbits = sign_extend(imm); - /* Get the address, X(rs1) + offset. + /* Get the address, X(xs1) + offset. Some extensions perform additional checks on address validity. */ - match ext_data_get_addr(rs1, offset, Read(Data), width) { + match ext_data_get_addr(xs1, offset, Read(Data), width) { Ext_DataAddr_Error(e) => { ext_handle_data_check_error(e); RETIRE_FAIL }, Ext_DataAddr_OK(vaddr) => if check_misaligned(vaddr, width) @@ -44,13 +44,13 @@ sail(): | TR_Address(paddr, _) => match (width) { BYTE => - process_load(rd, vaddr, mem_read(Read(Data), paddr, 1, aq, rl, false), is_unsigned), + process_load(xd, vaddr, mem_read(Read(Data), paddr, 1, aq, rl, false), is_unsigned), HALF => - process_load(rd, vaddr, mem_read(Read(Data), paddr, 2, aq, rl, false), is_unsigned), + process_load(xd, vaddr, mem_read(Read(Data), paddr, 2, aq, rl, false), is_unsigned), WORD => - process_load(rd, vaddr, mem_read(Read(Data), paddr, 4, aq, rl, false), is_unsigned), + process_load(xd, vaddr, mem_read(Read(Data), paddr, 4, aq, rl, false), is_unsigned), DOUBLE if sizeof(xlen) >= 64 => - process_load(rd, vaddr, mem_read(Read(Data), paddr, 8, aq, rl, false), is_unsigned), + process_load(xd, vaddr, mem_read(Read(Data), paddr, 8, aq, rl, false), is_unsigned), _ => report_invalid_width(__FILE__, __LINE__, width, "load") } } diff --git a/arch/inst/I/lui.yaml b/arch/inst/I/lui.yaml index be47ea173b..93610f42a2 100644 --- a/arch/inst/I/lui.yaml +++ b/arch/inst/I/lui.yaml @@ -4,7 +4,7 @@ $schema: "inst_schema.json#" kind: instruction name: lui long_name: Load upper immediate -description: Load the zero-extended imm into rd. +description: Load the zero-extended imm into xd. definedBy: I assembly: xd, imm encoding: @@ -13,7 +13,7 @@ encoding: - name: imm location: 31-12 left_shift: 12 - - name: rd + - name: xd location: 11-7 access: s: always @@ -21,7 +21,7 @@ access: vs: always vu: always data_independent_timing: true -operation(): X[rd] = imm; +operation(): X[xd] = imm; sail(): | { @@ -30,6 +30,6 @@ sail(): | RISCV_LUI => off, RISCV_AUIPC => get_arch_pc() + off }; - X(rd) = ret; + X(xd) = ret; RETIRE_SUCCESS } diff --git a/arch/inst/I/lw.yaml b/arch/inst/I/lw.yaml index c950ddbdf2..7ffd96206e 100644 --- a/arch/inst/I/lw.yaml +++ b/arch/inst/I/lw.yaml @@ -5,19 +5,19 @@ kind: instruction name: lw long_name: Load word description: | - Load 32 bits of data into register `rd` from an - address formed by adding `rs1` to a signed offset. + Load 32 bits of data into register `xd` from an + address formed by adding `xs1` to a signed offset. Sign extend the result. definedBy: I -assembly: xd, imm(rs1) +assembly: xd, imm(xs1) encoding: match: -----------------010-----0000011 variables: - name: imm location: 31-20 - - name: rs1 + - name: xs1 location: 19-15 - - name: rd + - name: xd location: 11-7 access: s: always @@ -25,16 +25,16 @@ access: vs: always vu: always operation(): | - XReg virtual_address = X[rs1] + imm; + XReg virtual_address = X[xs1] + $signed(imm); - X[rd] = read_memory<32>(virtual_address, $encoding); + X[xd] = $signed(read_memory<32>(virtual_address, $encoding)); sail(): | { let offset : xlenbits = sign_extend(imm); - /* Get the address, X(rs1) + offset. + /* Get the address, X(xs1) + offset. Some extensions perform additional checks on address validity. */ - match ext_data_get_addr(rs1, offset, Read(Data), width) { + match ext_data_get_addr(xs1, offset, Read(Data), width) { Ext_DataAddr_Error(e) => { ext_handle_data_check_error(e); RETIRE_FAIL }, Ext_DataAddr_OK(vaddr) => if check_misaligned(vaddr, width) @@ -44,13 +44,13 @@ sail(): | TR_Address(paddr, _) => match (width) { BYTE => - process_load(rd, vaddr, mem_read(Read(Data), paddr, 1, aq, rl, false), is_unsigned), + process_load(xd, vaddr, mem_read(Read(Data), paddr, 1, aq, rl, false), is_unsigned), HALF => - process_load(rd, vaddr, mem_read(Read(Data), paddr, 2, aq, rl, false), is_unsigned), + process_load(xd, vaddr, mem_read(Read(Data), paddr, 2, aq, rl, false), is_unsigned), WORD => - process_load(rd, vaddr, mem_read(Read(Data), paddr, 4, aq, rl, false), is_unsigned), + process_load(xd, vaddr, mem_read(Read(Data), paddr, 4, aq, rl, false), is_unsigned), DOUBLE if sizeof(xlen) >= 64 => - process_load(rd, vaddr, mem_read(Read(Data), paddr, 8, aq, rl, false), is_unsigned), + process_load(xd, vaddr, mem_read(Read(Data), paddr, 8, aq, rl, false), is_unsigned), _ => report_invalid_width(__FILE__, __LINE__, width, "load") } } diff --git a/arch/inst/I/lwu.yaml b/arch/inst/I/lwu.yaml index 0c35b3ad94..4b7c60859a 100644 --- a/arch/inst/I/lwu.yaml +++ b/arch/inst/I/lwu.yaml @@ -5,20 +5,20 @@ kind: instruction name: lwu long_name: Load word unsigned description: | - Load 64 bits of data into register `rd` from an - address formed by adding `rs1` to a signed offset. + Load 64 bits of data into register `xd` from an + address formed by adding `xs1` to a signed offset. Zero extend the result. definedBy: I base: 64 -assembly: xd, imm(rs1) +assembly: xd, imm(xs1) encoding: match: -----------------110-----0000011 variables: - name: imm location: 31-20 - - name: rs1 + - name: xs1 location: 19-15 - - name: rd + - name: xd location: 11-7 access: s: always @@ -26,16 +26,16 @@ access: vs: always vu: always operation(): | - XReg virtual_address = X[rs1] + imm; + XReg virtual_address = X[xs1] + $signed(imm); - X[rd] = read_memory<32>(virtual_address, $encoding); + X[xd] = read_memory<32>(virtual_address, $encoding); sail(): | { let offset : xlenbits = sign_extend(imm); - /* Get the address, X(rs1) + offset. + /* Get the address, X(xs1) + offset. Some extensions perform additional checks on address validity. */ - match ext_data_get_addr(rs1, offset, Read(Data), width) { + match ext_data_get_addr(xs1, offset, Read(Data), width) { Ext_DataAddr_Error(e) => { ext_handle_data_check_error(e); RETIRE_FAIL }, Ext_DataAddr_OK(vaddr) => if check_misaligned(vaddr, width) @@ -45,13 +45,13 @@ sail(): | TR_Address(paddr, _) => match (width) { BYTE => - process_load(rd, vaddr, mem_read(Read(Data), paddr, 1, aq, rl, false), is_unsigned), + process_load(xd, vaddr, mem_read(Read(Data), paddr, 1, aq, rl, false), is_unsigned), HALF => - process_load(rd, vaddr, mem_read(Read(Data), paddr, 2, aq, rl, false), is_unsigned), + process_load(xd, vaddr, mem_read(Read(Data), paddr, 2, aq, rl, false), is_unsigned), WORD => - process_load(rd, vaddr, mem_read(Read(Data), paddr, 4, aq, rl, false), is_unsigned), + process_load(xd, vaddr, mem_read(Read(Data), paddr, 4, aq, rl, false), is_unsigned), DOUBLE if sizeof(xlen) >= 64 => - process_load(rd, vaddr, mem_read(Read(Data), paddr, 8, aq, rl, false), is_unsigned), + process_load(xd, vaddr, mem_read(Read(Data), paddr, 8, aq, rl, false), is_unsigned), _ => report_invalid_width(__FILE__, __LINE__, width, "load") } } diff --git a/arch/inst/I/or.yaml b/arch/inst/I/or.yaml index 0eeee6aecb..2957c4252f 100644 --- a/arch/inst/I/or.yaml +++ b/arch/inst/I/or.yaml @@ -4,17 +4,17 @@ $schema: "inst_schema.json#" kind: instruction name: or long_name: Or -description: Or rs1 with rs2, and store the result in rd +description: Or xs1 with xs2, and store the result in xd definedBy: I assembly: xd, xs1, xs2 encoding: match: 0000000----------110-----0110011 variables: - - name: rs2 + - name: xs2 location: 24-20 - - name: rs1 + - name: xs1 location: 19-15 - - name: rd + - name: xd location: 11-7 access: s: always @@ -22,30 +22,30 @@ access: vs: always vu: always data_independent_timing: true -operation(): X[rd] = X[rs1] | X[rs2]; +operation(): X[xd] = X[xs1] | X[xs2]; sail(): | { - let rs1_val = X(rs1); - let rs2_val = X(rs2); + let xs1_val = X(xs1); + let xs2_val = X(xs2); let result : xlenbits = match op { - RISCV_ADD => rs1_val + rs2_val, - RISCV_SLT => zero_extend(bool_to_bits(rs1_val <_s rs2_val)), - RISCV_SLTU => zero_extend(bool_to_bits(rs1_val <_u rs2_val)), - RISCV_AND => rs1_val & rs2_val, - RISCV_OR => rs1_val | rs2_val, - RISCV_XOR => rs1_val ^ rs2_val, + RISCV_ADD => xs1_val + xs2_val, + RISCV_SLT => zero_extend(bool_to_bits(xs1_val <_s xs2_val)), + RISCV_SLTU => zero_extend(bool_to_bits(xs1_val <_u xs2_val)), + RISCV_AND => xs1_val & xs2_val, + RISCV_OR => xs1_val | xs2_val, + RISCV_XOR => xs1_val ^ xs2_val, RISCV_SLL => if sizeof(xlen) == 32 - then rs1_val << (rs2_val[4..0]) - else rs1_val << (rs2_val[5..0]), + then xs1_val << (xs2_val[4..0]) + else xs1_val << (xs2_val[5..0]), RISCV_SRL => if sizeof(xlen) == 32 - then rs1_val >> (rs2_val[4..0]) - else rs1_val >> (rs2_val[5..0]), - RISCV_SUB => rs1_val - rs2_val, + then xs1_val >> (xs2_val[4..0]) + else xs1_val >> (xs2_val[5..0]), + RISCV_SUB => xs1_val - xs2_val, RISCV_SRA => if sizeof(xlen) == 32 - then shift_right_arith32(rs1_val, rs2_val[4..0]) - else shift_right_arith64(rs1_val, rs2_val[5..0]) + then shift_right_arith32(xs1_val, xs2_val[4..0]) + else shift_right_arith64(xs1_val, xs2_val[5..0]) }; - X(rd) = result; + X(xd) = result; RETIRE_SUCCESS } diff --git a/arch/inst/I/ori.yaml b/arch/inst/I/ori.yaml index d2cc79d298..b4e76762b8 100644 --- a/arch/inst/I/ori.yaml +++ b/arch/inst/I/ori.yaml @@ -4,7 +4,7 @@ $schema: "inst_schema.json#" kind: instruction name: ori long_name: Or immediate -description: Or an immediate to the value in rs1, and store the result in rd +description: Or an immediate to the value in xs1, and store the result in xd definedBy: I assembly: xd, xs1, imm encoding: @@ -12,9 +12,9 @@ encoding: variables: - name: imm location: 31-20 - - name: rs1 + - name: xs1 location: 19-15 - - name: rd + - name: xd location: 11-7 access: s: always @@ -24,43 +24,43 @@ access: data_independent_timing: true operation(): | if (implemented?(ExtensionName::Zicbop)) { - if (rd == 0) { + if (xd == 0) { if (imm[4:0] == 0) { # prefetch.i instruction - Bits<12> offset = {imm[11:5], rd}; + Bits<12> offset = {imm[11:5], xd}; prefetch_instruction(offset); } else if (imm[4:0] == 1) { # prefetch.r instruction - Bits<12> offset = {imm[11:5], rd}; + Bits<12> offset = {imm[11:5], xd}; prefetch_read(offset); } else if (imm[4:0] == 3) { # prefetch.r instruction - Bits<12> offset = {imm[11:5], rd}; + Bits<12> offset = {imm[11:5], xd}; prefetch_write(offset); } } } - X[rd] = X[rs1] | imm; + X[xd] = X[xs1] | $signed(imm); pseudoinstructions: - - when: (rd == 0) && (imm[4:0] == 0) + - when: (xd == 0) && (imm[4:0] == 0) to: prefetch.i offset - - when: (rd == 0) && (imm[4:0] == 1) + - when: (xd == 0) && (imm[4:0] == 1) to: prefetch.r offset - - when: (rd == 0) && (imm[4:0] == 3) + - when: (xd == 0) && (imm[4:0] == 3) to: prefetch.w offset sail(): | { - let rs1_val = X(rs1); + let xs1_val = X(xs1); let immext : xlenbits = sign_extend(imm); let result : xlenbits = match op { - RISCV_ADDI => rs1_val + immext, - RISCV_SLTI => zero_extend(bool_to_bits(rs1_val <_s immext)), - RISCV_SLTIU => zero_extend(bool_to_bits(rs1_val <_u immext)), - RISCV_ANDI => rs1_val & immext, - RISCV_ORI => rs1_val | immext, - RISCV_XORI => rs1_val ^ immext + RISCV_ADDI => xs1_val + immext, + RISCV_SLTI => zero_extend(bool_to_bits(xs1_val <_s immext)), + RISCV_SLTIU => zero_extend(bool_to_bits(xs1_val <_u immext)), + RISCV_ANDI => xs1_val & immext, + RISCV_ORI => xs1_val | immext, + RISCV_XORI => xs1_val ^ immext }; - X(rd) = result; + X(xd) = result; RETIRE_SUCCESS } diff --git a/arch/inst/I/sb.yaml b/arch/inst/I/sb.yaml index 303c435c07..980ddff09b 100644 --- a/arch/inst/I/sb.yaml +++ b/arch/inst/I/sb.yaml @@ -5,8 +5,8 @@ kind: instruction name: sb long_name: Store byte description: | - Store 8 bits of data from register `rs2` to an - address formed by adding `rs1` to a signed offset. + Store 8 bits of data from register `xs2` to an + address formed by adding `xs1` to a signed offset. definedBy: I assembly: xs2, imm(xs1) encoding: @@ -14,9 +14,9 @@ encoding: variables: - name: imm location: 31-25|11-7 - - name: rs2 + - name: xs2 location: 24-20 - - name: rs1 + - name: xs1 location: 19-15 access: s: always @@ -24,16 +24,16 @@ access: vs: always vu: always operation(): | - XReg virtual_address = X[rs1] + imm; + XReg virtual_address = X[xs1] + $signed(imm); - write_memory<8>(virtual_address, X[rs2][7:0], $encoding); + write_memory<8>(virtual_address, X[xs2][7:0], $encoding); sail(): | { let offset : xlenbits = sign_extend(imm); - /* Get the address, X(rs1) + offset. + /* Get the address, X(xs1) + offset. Some extensions perform additional checks on address validity. */ - match ext_data_get_addr(rs1, offset, Write(Data), width) { + match ext_data_get_addr(xs1, offset, Write(Data), width) { Ext_DataAddr_Error(e) => { ext_handle_data_check_error(e); RETIRE_FAIL }, Ext_DataAddr_OK(vaddr) => if check_misaligned(vaddr, width) @@ -50,13 +50,13 @@ sail(): | match (eares) { MemException(e) => { handle_mem_exception(vaddr, e); RETIRE_FAIL }, MemValue(_) => { - let rs2_val = X(rs2); + let xs2_val = X(xs2); let res : MemoryOpResult(bool) = match (width) { - BYTE => mem_write_value(paddr, 1, rs2_val[7..0], aq, rl, false), - HALF => mem_write_value(paddr, 2, rs2_val[15..0], aq, rl, false), - WORD => mem_write_value(paddr, 4, rs2_val[31..0], aq, rl, false), + BYTE => mem_write_value(paddr, 1, xs2_val[7..0], aq, rl, false), + HALF => mem_write_value(paddr, 2, xs2_val[15..0], aq, rl, false), + WORD => mem_write_value(paddr, 4, xs2_val[31..0], aq, rl, false), DOUBLE if sizeof(xlen) >= 64 - => mem_write_value(paddr, 8, rs2_val, aq, rl, false), + => mem_write_value(paddr, 8, xs2_val, aq, rl, false), _ => report_invalid_width(__FILE__, __LINE__, width, "store"), }; match (res) { diff --git a/arch/inst/I/sd.yaml b/arch/inst/I/sd.yaml index 8ac4ab4a48..7f2b13d221 100644 --- a/arch/inst/I/sd.yaml +++ b/arch/inst/I/sd.yaml @@ -5,8 +5,8 @@ kind: instruction name: sd long_name: Store doubleword description: | - Store 64 bits of data from register `rs2` to an - address formed by adding `rs1` to a signed offset. + Store 64 bits of data from register `xs2` to an + address formed by adding `xs1` to a signed offset. definedBy: I base: 64 assembly: xs2, imm(xs1) @@ -16,9 +16,9 @@ encoding: - name: imm location: 31-25|11-7 sign_extend: true - - name: rs1 + - name: xs1 location: 19-15 - - name: rs2 + - name: xs2 location: 24-20 access: s: always @@ -26,16 +26,16 @@ access: vs: always vu: always operation(): | - XReg virtual_address = X[rs1] + imm; + XReg virtual_address = X[xs1] + $signed(imm); - write_memory<64>(virtual_address, X[rs2], $encoding); + write_memory<64>(virtual_address, X[xs2], $encoding); sail(): | { let offset : xlenbits = sign_extend(imm); - /* Get the address, X(rs1) + offset. + /* Get the address, X(xs1) + offset. Some extensions perform additional checks on address validity. */ - match ext_data_get_addr(rs1, offset, Write(Data), width) { + match ext_data_get_addr(xs1, offset, Write(Data), width) { Ext_DataAddr_Error(e) => { ext_handle_data_check_error(e); RETIRE_FAIL }, Ext_DataAddr_OK(vaddr) => if check_misaligned(vaddr, width) @@ -52,13 +52,13 @@ sail(): | match (eares) { MemException(e) => { handle_mem_exception(vaddr, e); RETIRE_FAIL }, MemValue(_) => { - let rs2_val = X(rs2); + let xs2_val = X(xs2); let res : MemoryOpResult(bool) = match (width) { - BYTE => mem_write_value(paddr, 1, rs2_val[7..0], aq, rl, false), - HALF => mem_write_value(paddr, 2, rs2_val[15..0], aq, rl, false), - WORD => mem_write_value(paddr, 4, rs2_val[31..0], aq, rl, false), + BYTE => mem_write_value(paddr, 1, xs2_val[7..0], aq, rl, false), + HALF => mem_write_value(paddr, 2, xs2_val[15..0], aq, rl, false), + WORD => mem_write_value(paddr, 4, xs2_val[31..0], aq, rl, false), DOUBLE if sizeof(xlen) >= 64 - => mem_write_value(paddr, 8, rs2_val, aq, rl, false), + => mem_write_value(paddr, 8, xs2_val, aq, rl, false), _ => report_invalid_width(__FILE__, __LINE__, width, "store"), }; match (res) { diff --git a/arch/inst/I/sh.yaml b/arch/inst/I/sh.yaml index f7fb2db7a5..4594779825 100644 --- a/arch/inst/I/sh.yaml +++ b/arch/inst/I/sh.yaml @@ -5,8 +5,8 @@ kind: instruction name: sh long_name: Store halfword description: | - Store 16 bits of data from register `rs2` to an - address formed by adding `rs1` to a signed offset. + Store 16 bits of data from register `xs2` to an + address formed by adding `xs1` to a signed offset. definedBy: I assembly: xs2, imm(xs1) encoding: @@ -14,9 +14,9 @@ encoding: variables: - name: imm location: 31-25|11-7 - - name: rs2 + - name: xs2 location: 24-20 - - name: rs1 + - name: xs1 location: 19-15 access: s: always @@ -24,16 +24,16 @@ access: vs: always vu: always operation(): | - XReg virtual_address = X[rs1] + imm; + XReg virtual_address = X[xs1] + $signed(imm); - write_memory<16>(virtual_address, X[rs2][15:0], $encoding); + write_memory<16>(virtual_address, X[xs2][15:0], $encoding); sail(): | { let offset : xlenbits = sign_extend(imm); - /* Get the address, X(rs1) + offset. + /* Get the address, X(xs1) + offset. Some extensions perform additional checks on address validity. */ - match ext_data_get_addr(rs1, offset, Write(Data), width) { + match ext_data_get_addr(xs1, offset, Write(Data), width) { Ext_DataAddr_Error(e) => { ext_handle_data_check_error(e); RETIRE_FAIL }, Ext_DataAddr_OK(vaddr) => if check_misaligned(vaddr, width) @@ -50,13 +50,13 @@ sail(): | match (eares) { MemException(e) => { handle_mem_exception(vaddr, e); RETIRE_FAIL }, MemValue(_) => { - let rs2_val = X(rs2); + let xs2_val = X(xs2); let res : MemoryOpResult(bool) = match (width) { - BYTE => mem_write_value(paddr, 1, rs2_val[7..0], aq, rl, false), - HALF => mem_write_value(paddr, 2, rs2_val[15..0], aq, rl, false), - WORD => mem_write_value(paddr, 4, rs2_val[31..0], aq, rl, false), + BYTE => mem_write_value(paddr, 1, xs2_val[7..0], aq, rl, false), + HALF => mem_write_value(paddr, 2, xs2_val[15..0], aq, rl, false), + WORD => mem_write_value(paddr, 4, xs2_val[31..0], aq, rl, false), DOUBLE if sizeof(xlen) >= 64 - => mem_write_value(paddr, 8, rs2_val, aq, rl, false), + => mem_write_value(paddr, 8, xs2_val, aq, rl, false), _ => report_invalid_width(__FILE__, __LINE__, width, "store"), }; match (res) { diff --git a/arch/inst/I/sll.yaml b/arch/inst/I/sll.yaml index 53faae3947..fe901e2fee 100644 --- a/arch/inst/I/sll.yaml +++ b/arch/inst/I/sll.yaml @@ -5,17 +5,17 @@ kind: instruction name: sll long_name: Shift left logical description: | - Shift the value in `rs1` left by the value in the lower 6 bits of `rs2`, and store the result in `rd`. + Shift the value in `xs1` left by the value in the lower 6 bits of `xs2`, and store the result in `xd`. definedBy: I assembly: xd, xs1, xs2 encoding: match: 0000000----------001-----0110011 variables: - - name: rs2 + - name: xs2 location: 24-20 - - name: rs1 + - name: xs1 location: 19-15 - - name: rd + - name: xd location: 11-7 access: s: always @@ -25,33 +25,33 @@ access: data_independent_timing: true operation(): | if (xlen() == 64) { - X[rd] = X[rs1] << X[rs2][5:0]; + X[xd] = X[xs1] << X[xs2][5:0]; } else { - X[rd] = X[rs1] << X[rs2][4:0]; + X[xd] = X[xs1] << X[xs2][4:0]; } sail(): | { - let rs1_val = X(rs1); - let rs2_val = X(rs2); + let xs1_val = X(xs1); + let xs2_val = X(xs2); let result : xlenbits = match op { - RISCV_ADD => rs1_val + rs2_val, - RISCV_SLT => zero_extend(bool_to_bits(rs1_val <_s rs2_val)), - RISCV_SLTU => zero_extend(bool_to_bits(rs1_val <_u rs2_val)), - RISCV_AND => rs1_val & rs2_val, - RISCV_OR => rs1_val | rs2_val, - RISCV_XOR => rs1_val ^ rs2_val, + RISCV_ADD => xs1_val + xs2_val, + RISCV_SLT => zero_extend(bool_to_bits(xs1_val <_s xs2_val)), + RISCV_SLTU => zero_extend(bool_to_bits(xs1_val <_u xs2_val)), + RISCV_AND => xs1_val & xs2_val, + RISCV_OR => xs1_val | xs2_val, + RISCV_XOR => xs1_val ^ xs2_val, RISCV_SLL => if sizeof(xlen) == 32 - then rs1_val << (rs2_val[4..0]) - else rs1_val << (rs2_val[5..0]), + then xs1_val << (xs2_val[4..0]) + else xs1_val << (xs2_val[5..0]), RISCV_SRL => if sizeof(xlen) == 32 - then rs1_val >> (rs2_val[4..0]) - else rs1_val >> (rs2_val[5..0]), - RISCV_SUB => rs1_val - rs2_val, + then xs1_val >> (xs2_val[4..0]) + else xs1_val >> (xs2_val[5..0]), + RISCV_SUB => xs1_val - xs2_val, RISCV_SRA => if sizeof(xlen) == 32 - then shift_right_arith32(rs1_val, rs2_val[4..0]) - else shift_right_arith64(rs1_val, rs2_val[5..0]) + then shift_right_arith32(xs1_val, xs2_val[4..0]) + else shift_right_arith64(xs1_val, xs2_val[5..0]) }; - X(rd) = result; + X(xd) = result; RETIRE_SUCCESS } diff --git a/arch/inst/I/slli.yaml b/arch/inst/I/slli.yaml index 12c53e32dd..f55a70eae9 100644 --- a/arch/inst/I/slli.yaml +++ b/arch/inst/I/slli.yaml @@ -4,7 +4,7 @@ $schema: "inst_schema.json#" kind: instruction name: slli long_name: Shift left logical immediate -description: Shift the value in rs1 left by shamt, and store the result in rd +description: Shift the value in xs1 left by shamt, and store the result in xd definedBy: I assembly: xd, xs1, shamt encoding: @@ -13,18 +13,18 @@ encoding: variables: - name: shamt location: 24-20 - - name: rs1 + - name: xs1 location: 19-15 - - name: rd + - name: xd location: 11-7 RV64: match: 000000-----------001-----0010011 variables: - name: shamt location: 25-20 - - name: rs1 + - name: xs1 location: 19-15 - - name: rd + - name: xd location: 11-7 access: s: always @@ -34,23 +34,23 @@ access: data_independent_timing: true operation(): | # shamt is between 0-(XLEN-1) - X[rd] = X[rs1] << shamt; + X[xd] = X[xs1] << shamt; sail(): | { - let rs1_val = X(rs1); - /* the decoder guard should ensure that shamt[5] = 0 for RV32 */ + let xs1_val = X(xs1); + /* the decoder guaxd should ensure that shamt[5] = 0 for RV32 */ let result : xlenbits = match op { RISCV_SLLI => if sizeof(xlen) == 32 - then rs1_val << shamt[4..0] - else rs1_val << shamt, + then xs1_val << shamt[4..0] + else xs1_val << shamt, RISCV_SRLI => if sizeof(xlen) == 32 - then rs1_val >> shamt[4..0] - else rs1_val >> shamt, + then xs1_val >> shamt[4..0] + else xs1_val >> shamt, RISCV_SRAI => if sizeof(xlen) == 32 - then shift_right_arith32(rs1_val, shamt[4..0]) - else shift_right_arith64(rs1_val, shamt) + then shift_right_arith32(xs1_val, shamt[4..0]) + else shift_right_arith64(xs1_val, shamt) }; - X(rd) = result; + X(xd) = result; RETIRE_SUCCESS } diff --git a/arch/inst/I/slliw.yaml b/arch/inst/I/slliw.yaml index b952b97df6..bc035f6f0c 100644 --- a/arch/inst/I/slliw.yaml +++ b/arch/inst/I/slliw.yaml @@ -4,7 +4,7 @@ $schema: "inst_schema.json#" kind: instruction name: slliw long_name: Shift left logical immediate word -description: Shift the 32-bit value in rs1 left by shamt, and store the sign-extended result in rd +description: Shift the 32-bit value in xs1 left by shamt, and store the sign-extended result in xd definedBy: I base: 64 assembly: xd, xs1, shamt @@ -13,9 +13,9 @@ encoding: variables: - name: shamt location: 24-20 - - name: rs1 + - name: xs1 location: 19-15 - - name: rd + - name: xd location: 11-7 access: s: always @@ -25,16 +25,16 @@ access: data_independent_timing: true operation(): | # shamt is between 0-32 - X[rd] = sext(X[rs1] << shamt, 31); + X[xd] = sext(X[xs1] << shamt, 31); sail(): | { - let rs1_val = (X(rs1))[31..0]; + let xs1_val = (X(xs1))[31..0]; let result : bits(32) = match op { - RISCV_SLLIW => rs1_val << shamt, - RISCV_SRLIW => rs1_val >> shamt, - RISCV_SRAIW => shift_right_arith32(rs1_val, shamt) + RISCV_SLLIW => xs1_val << shamt, + RISCV_SRLIW => xs1_val >> shamt, + RISCV_SRAIW => shift_right_arith32(xs1_val, shamt) }; - X(rd) = sign_extend(result); + X(xd) = sign_extend(result); RETIRE_SUCCESS } diff --git a/arch/inst/I/sllw.yaml b/arch/inst/I/sllw.yaml index 1e354d13d6..b288ee9b2c 100644 --- a/arch/inst/I/sllw.yaml +++ b/arch/inst/I/sllw.yaml @@ -5,18 +5,18 @@ kind: instruction name: sllw long_name: Shift left logical word description: | - Shift the 32-bit value in `rs1` left by the value in the lower 5 bits of `rs2`, and store the sign-extended result in `rd`. + Shift the 32-bit value in `xs1` left by the value in the lower 5 bits of `xs2`, and store the sign-extended result in `xd`. definedBy: I base: 64 assembly: xd, xs1, xs2 encoding: match: 0000000----------001-----0111011 variables: - - name: rs2 + - name: xs2 location: 24-20 - - name: rs1 + - name: xs1 location: 19-15 - - name: rd + - name: xd location: 11-7 access: s: always @@ -24,19 +24,19 @@ access: vs: always vu: always data_independent_timing: true -operation(): X[rd] = sext(X[rs1] << X[rs2][4:0], 31); +operation(): X[xd] = sext(X[xs1] << X[xs2][4:0], 31); sail(): | { - let rs1_val = (X(rs1))[31..0]; - let rs2_val = (X(rs2))[31..0]; + let xs1_val = (X(xs1))[31..0]; + let xs2_val = (X(xs2))[31..0]; let result : bits(32) = match op { - RISCV_ADDW => rs1_val + rs2_val, - RISCV_SUBW => rs1_val - rs2_val, - RISCV_SLLW => rs1_val << (rs2_val[4..0]), - RISCV_SRLW => rs1_val >> (rs2_val[4..0]), - RISCV_SRAW => shift_right_arith32(rs1_val, rs2_val[4..0]) + RISCV_ADDW => xs1_val + xs2_val, + RISCV_SUBW => xs1_val - xs2_val, + RISCV_SLLW => xs1_val << (xs2_val[4..0]), + RISCV_SRLW => xs1_val >> (xs2_val[4..0]), + RISCV_SRAW => shift_right_arith32(xs1_val, xs2_val[4..0]) }; - X(rd) = sign_extend(result); + X(xd) = sign_extend(result); RETIRE_SUCCESS } diff --git a/arch/inst/I/slt.yaml b/arch/inst/I/slt.yaml index d24ce3e2d3..4ec55f3234 100644 --- a/arch/inst/I/slt.yaml +++ b/arch/inst/I/slt.yaml @@ -5,18 +5,18 @@ kind: instruction name: slt long_name: Set on less than description: | - Places the value 1 in register `rd` if register `rs1` is less than the value in register `rs2`, where - both sources are treated as signed numbers, else 0 is written to `rd`. + Places the value 1 in register `xd` if register `xs1` is less than the value in register `xs2`, where + both sources are treated as signed numbers, else 0 is written to `xd`. definedBy: I -assembly: xd, xs1, rs2 +assembly: xd, xs1, xs2 encoding: match: 0000000----------010-----0110011 variables: - - name: rs2 + - name: xs2 location: 24-20 - - name: rs1 + - name: xs1 location: 19-15 - - name: rd + - name: xd location: 11-7 access: s: always @@ -25,33 +25,33 @@ access: vu: always data_independent_timing: true operation(): | - XReg src1 = X[rs1]; - XReg src2 = X[rs2]; + XReg src1 = X[xs1]; + XReg src2 = X[xs2]; - X[rd] = ($signed(src1) < $signed(src2)) ? '1 : '0; + X[xd] = ($signed(src1) < $signed(src2)) ? '1 : '0; sail(): | { - let rs1_val = X(rs1); - let rs2_val = X(rs2); + let xs1_val = X(xs1); + let xs2_val = X(xs2); let result : xlenbits = match op { - RISCV_ADD => rs1_val + rs2_val, - RISCV_SLT => zero_extend(bool_to_bits(rs1_val <_s rs2_val)), - RISCV_SLTU => zero_extend(bool_to_bits(rs1_val <_u rs2_val)), - RISCV_AND => rs1_val & rs2_val, - RISCV_OR => rs1_val | rs2_val, - RISCV_XOR => rs1_val ^ rs2_val, + RISCV_ADD => xs1_val + xs2_val, + RISCV_SLT => zero_extend(bool_to_bits(xs1_val <_s xs2_val)), + RISCV_SLTU => zero_extend(bool_to_bits(xs1_val <_u xs2_val)), + RISCV_AND => xs1_val & xs2_val, + RISCV_OR => xs1_val | xs2_val, + RISCV_XOR => xs1_val ^ xs2_val, RISCV_SLL => if sizeof(xlen) == 32 - then rs1_val << (rs2_val[4..0]) - else rs1_val << (rs2_val[5..0]), + then xs1_val << (xs2_val[4..0]) + else xs1_val << (xs2_val[5..0]), RISCV_SRL => if sizeof(xlen) == 32 - then rs1_val >> (rs2_val[4..0]) - else rs1_val >> (rs2_val[5..0]), - RISCV_SUB => rs1_val - rs2_val, + then xs1_val >> (xs2_val[4..0]) + else xs1_val >> (xs2_val[5..0]), + RISCV_SUB => xs1_val - xs2_val, RISCV_SRA => if sizeof(xlen) == 32 - then shift_right_arith32(rs1_val, rs2_val[4..0]) - else shift_right_arith64(rs1_val, rs2_val[5..0]) + then shift_right_arith32(xs1_val, xs2_val[4..0]) + else shift_right_arith64(xs1_val, xs2_val[5..0]) }; - X(rd) = result; + X(xd) = result; RETIRE_SUCCESS } diff --git a/arch/inst/I/slti.yaml b/arch/inst/I/slti.yaml index 9431cca6ea..a4a610b8fa 100644 --- a/arch/inst/I/slti.yaml +++ b/arch/inst/I/slti.yaml @@ -5,8 +5,8 @@ kind: instruction name: slti long_name: Set on less than immediate description: | - Places the value 1 in register `rd` if register `rs1` is less than the sign-extended immediate - when both are treated as signed numbers, else 0 is written to `rd`. + Places the value 1 in register `xd` if register `xs1` is less than the sign-extended immediate + when both are treated as signed numbers, else 0 is written to `xd`. definedBy: I assembly: xd, xs1, imm encoding: @@ -14,9 +14,9 @@ encoding: variables: - name: imm location: 31-20 - - name: rs1 + - name: xs1 location: 19-15 - - name: rd + - name: xd location: 11-7 access: s: always @@ -25,20 +25,20 @@ access: vu: always data_independent_timing: true operation(): | - X[rd] = ($signed(X[rs1]) < $signed(imm)) ? '1 : '0; + X[xd] = ($signed(X[xs1]) < $signed(imm)) ? '1 : '0; sail(): | { - let rs1_val = X(rs1); + let xs1_val = X(xs1); let immext : xlenbits = sign_extend(imm); let result : xlenbits = match op { - RISCV_ADDI => rs1_val + immext, - RISCV_SLTI => zero_extend(bool_to_bits(rs1_val <_s immext)), - RISCV_SLTIU => zero_extend(bool_to_bits(rs1_val <_u immext)), - RISCV_ANDI => rs1_val & immext, - RISCV_ORI => rs1_val | immext, - RISCV_XORI => rs1_val ^ immext + RISCV_ADDI => xs1_val + immext, + RISCV_SLTI => zero_extend(bool_to_bits(xs1_val <_s immext)), + RISCV_SLTIU => zero_extend(bool_to_bits(xs1_val <_u immext)), + RISCV_ANDI => xs1_val & immext, + RISCV_ORI => xs1_val | immext, + RISCV_XORI => xs1_val ^ immext }; - X(rd) = result; + X(xd) = result; RETIRE_SUCCESS } diff --git a/arch/inst/I/sltiu.yaml b/arch/inst/I/sltiu.yaml index 33e3c0d88e..ea48c6c92d 100644 --- a/arch/inst/I/sltiu.yaml +++ b/arch/inst/I/sltiu.yaml @@ -5,12 +5,12 @@ kind: instruction name: sltiu long_name: Set on less than immediate unsigned description: | - Places the value 1 in register `rd` if register `rs1` is less than the sign-extended immediate + Places the value 1 in register `xd` if register `xs1` is less than the sign-extended immediate when both are treated as unsigned numbers (_i.e._, the immediate is first sign-extended to - XLEN bits then treated as an unsigned number), else 0 is written to `rd`. + XLEN bits then treated as an unsigned number), else 0 is written to `xd`. - NOTE: `sltiu rd, rs1, 1` sets `rd` to 1 if `rs1` equals zero, otherwise sets `rd` to 0 - (assembler pseudoinstruction `SEQZ rd, rs`). + NOTE: `sltiu xd, xs1, 1` sets `xd` to 1 if `xs1` equals zero, otherwise sets `xd` to 0 + (assembler pseudoinstruction `SEQZ xd, rs`). definedBy: I assembly: xd, xs1, imm encoding: @@ -18,9 +18,9 @@ encoding: variables: - name: imm location: 31-20 - - name: rs1 + - name: xs1 location: 19-15 - - name: rd + - name: xd location: 11-7 access: s: always @@ -29,20 +29,21 @@ access: vu: always data_independent_timing: true operation(): | - X[rd] = (X[rs1] < imm) ? 1 : 0; + Bits sign_extend_imm = $signed(imm); + X[xd] = (X[xs1] < sign_extend_imm) ? 1 : 0; sail(): | { - let rs1_val = X(rs1); + let xs1_val = X(xs1); let immext : xlenbits = sign_extend(imm); let result : xlenbits = match op { - RISCV_ADDI => rs1_val + immext, - RISCV_SLTI => zero_extend(bool_to_bits(rs1_val <_s immext)), - RISCV_SLTIU => zero_extend(bool_to_bits(rs1_val <_u immext)), - RISCV_ANDI => rs1_val & immext, - RISCV_ORI => rs1_val | immext, - RISCV_XORI => rs1_val ^ immext + RISCV_ADDI => xs1_val + immext, + RISCV_SLTI => zero_extend(bool_to_bits(xs1_val <_s immext)), + RISCV_SLTIU => zero_extend(bool_to_bits(xs1_val <_u immext)), + RISCV_ANDI => xs1_val & immext, + RISCV_ORI => xs1_val | immext, + RISCV_XORI => xs1_val ^ immext }; - X(rd) = result; + X(xd) = result; RETIRE_SUCCESS } diff --git a/arch/inst/I/sltu.yaml b/arch/inst/I/sltu.yaml index e07da0b3e6..a0721b2168 100644 --- a/arch/inst/I/sltu.yaml +++ b/arch/inst/I/sltu.yaml @@ -5,18 +5,18 @@ kind: instruction name: sltu long_name: Set on less than unsigned description: | - Places the value 1 in register `rd` if register `rs1` is less than the value in register `rs2`, where - both sources are treated as unsigned numbers, else 0 is written to `rd`. + Places the value 1 in register `xd` if register `xs1` is less than the value in register `xs2`, where + both sources are treated as unsigned numbers, else 0 is written to `xd`. definedBy: I assembly: xd, xs1, xs2 encoding: match: 0000000----------011-----0110011 variables: - - name: rs2 + - name: xs2 location: 24-20 - - name: rs1 + - name: xs1 location: 19-15 - - name: rd + - name: xd location: 11-7 access: s: always @@ -25,30 +25,30 @@ access: vu: always data_independent_timing: true operation(): | - X[rd] = (X[rs1] < X[rs2]) ? 1 : 0; + X[xd] = (X[xs1] < X[xs2]) ? 1 : 0; sail(): | { - let rs1_val = X(rs1); - let rs2_val = X(rs2); + let xs1_val = X(xs1); + let xs2_val = X(xs2); let result : xlenbits = match op { - RISCV_ADD => rs1_val + rs2_val, - RISCV_SLT => zero_extend(bool_to_bits(rs1_val <_s rs2_val)), - RISCV_SLTU => zero_extend(bool_to_bits(rs1_val <_u rs2_val)), - RISCV_AND => rs1_val & rs2_val, - RISCV_OR => rs1_val | rs2_val, - RISCV_XOR => rs1_val ^ rs2_val, + RISCV_ADD => xs1_val + xs2_val, + RISCV_SLT => zero_extend(bool_to_bits(xs1_val <_s xs2_val)), + RISCV_SLTU => zero_extend(bool_to_bits(xs1_val <_u xs2_val)), + RISCV_AND => xs1_val & xs2_val, + RISCV_OR => xs1_val | xs2_val, + RISCV_XOR => xs1_val ^ xs2_val, RISCV_SLL => if sizeof(xlen) == 32 - then rs1_val << (rs2_val[4..0]) - else rs1_val << (rs2_val[5..0]), + then xs1_val << (xs2_val[4..0]) + else xs1_val << (xs2_val[5..0]), RISCV_SRL => if sizeof(xlen) == 32 - then rs1_val >> (rs2_val[4..0]) - else rs1_val >> (rs2_val[5..0]), - RISCV_SUB => rs1_val - rs2_val, + then xs1_val >> (xs2_val[4..0]) + else xs1_val >> (xs2_val[5..0]), + RISCV_SUB => xs1_val - xs2_val, RISCV_SRA => if sizeof(xlen) == 32 - then shift_right_arith32(rs1_val, rs2_val[4..0]) - else shift_right_arith64(rs1_val, rs2_val[5..0]) + then shift_right_arith32(xs1_val, xs2_val[4..0]) + else shift_right_arith64(xs1_val, xs2_val[5..0]) }; - X(rd) = result; + X(xd) = result; RETIRE_SUCCESS } diff --git a/arch/inst/I/sra.yaml b/arch/inst/I/sra.yaml index 855bbc9f16..66a3f951e3 100644 --- a/arch/inst/I/sra.yaml +++ b/arch/inst/I/sra.yaml @@ -5,17 +5,17 @@ kind: instruction name: sra long_name: Shift right arithmetic description: | - Arithmetic shift the value in `rs1` right by the value in the lower 5 bits of `rs2`, and store the result in `rd`. + Arithmetic shift the value in `xs1` right by the value in the lower 5 bits of `xs2`, and store the result in `xd`. definedBy: I assembly: xd, xs1, xs2 encoding: match: 0100000----------101-----0110011 variables: - - name: rs2 + - name: xs2 location: 24-20 - - name: rs1 + - name: xs1 location: 19-15 - - name: rd + - name: xd location: 11-7 access: s: always @@ -25,33 +25,33 @@ access: data_independent_timing: true operation(): | if (xlen() == 64) { - X[rd] = X[rs1] >>> X[rs2][5:0]; + X[xd] = X[xs1] >>> X[xs2][5:0]; } else { - X[rd] = X[rs1] >>> X[rs2][4:0]; + X[xd] = X[xs1] >>> X[xs2][4:0]; } sail(): | { - let rs1_val = X(rs1); - let rs2_val = X(rs2); + let xs1_val = X(xs1); + let xs2_val = X(xs2); let result : xlenbits = match op { - RISCV_ADD => rs1_val + rs2_val, - RISCV_SLT => zero_extend(bool_to_bits(rs1_val <_s rs2_val)), - RISCV_SLTU => zero_extend(bool_to_bits(rs1_val <_u rs2_val)), - RISCV_AND => rs1_val & rs2_val, - RISCV_OR => rs1_val | rs2_val, - RISCV_XOR => rs1_val ^ rs2_val, + RISCV_ADD => xs1_val + xs2_val, + RISCV_SLT => zero_extend(bool_to_bits(xs1_val <_s xs2_val)), + RISCV_SLTU => zero_extend(bool_to_bits(xs1_val <_u xs2_val)), + RISCV_AND => xs1_val & xs2_val, + RISCV_OR => xs1_val | xs2_val, + RISCV_XOR => xs1_val ^ xs2_val, RISCV_SLL => if sizeof(xlen) == 32 - then rs1_val << (rs2_val[4..0]) - else rs1_val << (rs2_val[5..0]), + then xs1_val << (xs2_val[4..0]) + else xs1_val << (xs2_val[5..0]), RISCV_SRL => if sizeof(xlen) == 32 - then rs1_val >> (rs2_val[4..0]) - else rs1_val >> (rs2_val[5..0]), - RISCV_SUB => rs1_val - rs2_val, + then xs1_val >> (xs2_val[4..0]) + else xs1_val >> (xs2_val[5..0]), + RISCV_SUB => xs1_val - xs2_val, RISCV_SRA => if sizeof(xlen) == 32 - then shift_right_arith32(rs1_val, rs2_val[4..0]) - else shift_right_arith64(rs1_val, rs2_val[5..0]) + then shift_right_arith32(xs1_val, xs2_val[4..0]) + else shift_right_arith64(xs1_val, xs2_val[5..0]) }; - X(rd) = result; + X(xd) = result; RETIRE_SUCCESS } diff --git a/arch/inst/I/srai.yaml b/arch/inst/I/srai.yaml index 33ae74ee00..55ac5d7f69 100644 --- a/arch/inst/I/srai.yaml +++ b/arch/inst/I/srai.yaml @@ -6,7 +6,7 @@ name: srai long_name: Shift right arithmetic immediate description: | Arithmetic shift (the original sign bit is copied into the vacated upper bits) the - value in rs1 right by shamt, and store the result in rd. + value in xs1 right by shamt, and store the result in xd. definedBy: I assembly: xd, xs1, shamt encoding: @@ -15,18 +15,18 @@ encoding: variables: - name: shamt location: 24-20 - - name: rs1 + - name: xs1 location: 19-15 - - name: rd + - name: xd location: 11-7 RV64: match: 010000-----------101-----0010011 variables: - name: shamt location: 25-20 - - name: rs1 + - name: xs1 location: 19-15 - - name: rd + - name: xd location: 11-7 access: s: always @@ -36,23 +36,23 @@ access: data_independent_timing: true operation(): | # shamt is between 0-63 - X[rd] = X[rs1] >>> shamt; + X[xd] = X[xs1] >>> shamt; sail(): | { - let rs1_val = X(rs1); - /* the decoder guard should ensure that shamt[5] = 0 for RV32 */ + let xs1_val = X(xs1); + /* the decoder guaxd should ensure that shamt[5] = 0 for RV32 */ let result : xlenbits = match op { RISCV_SLLI => if sizeof(xlen) == 32 - then rs1_val << shamt[4..0] - else rs1_val << shamt, + then xs1_val << shamt[4..0] + else xs1_val << shamt, RISCV_SRLI => if sizeof(xlen) == 32 - then rs1_val >> shamt[4..0] - else rs1_val >> shamt, + then xs1_val >> shamt[4..0] + else xs1_val >> shamt, RISCV_SRAI => if sizeof(xlen) == 32 - then shift_right_arith32(rs1_val, shamt[4..0]) - else shift_right_arith64(rs1_val, shamt) + then shift_right_arith32(xs1_val, shamt[4..0]) + else shift_right_arith64(xs1_val, shamt) }; - X(rd) = result; + X(xd) = result; RETIRE_SUCCESS } diff --git a/arch/inst/I/sraiw.yaml b/arch/inst/I/sraiw.yaml index 89e413c3d5..a93d1d4bca 100644 --- a/arch/inst/I/sraiw.yaml +++ b/arch/inst/I/sraiw.yaml @@ -6,7 +6,7 @@ name: sraiw long_name: Shift right arithmetic immediate word description: | Arithmetic shift (the original sign bit is copied into the vacated upper bits) the - 32-bit value in rs1 right by shamt, and store the sign-extended result in rd. + 32-bit value in xs1 right by shamt, and store the sign-extended result in xd. definedBy: I base: 64 assembly: xd, xs1, shamt @@ -15,9 +15,9 @@ encoding: variables: - name: shamt location: 24-20 - - name: rs1 + - name: xs1 location: 19-15 - - name: rd + - name: xd location: 11-7 access: s: always @@ -27,17 +27,17 @@ access: data_independent_timing: true operation(): | # shamt is between 0-32 - XReg operand = sext(X[rs1], 31); - X[rd] = sext(operand >>> shamt, 31); + XReg operand = sext(X[xs1], 31); + X[xd] = sext(operand >>> shamt, 31); sail(): | { - let rs1_val = (X(rs1))[31..0]; + let xs1_val = (X(xs1))[31..0]; let result : bits(32) = match op { - RISCV_SLLIW => rs1_val << shamt, - RISCV_SRLIW => rs1_val >> shamt, - RISCV_SRAIW => shift_right_arith32(rs1_val, shamt) + RISCV_SLLIW => xs1_val << shamt, + RISCV_SRLIW => xs1_val >> shamt, + RISCV_SRAIW => shift_right_arith32(xs1_val, shamt) }; - X(rd) = sign_extend(result); + X(xd) = sign_extend(result); RETIRE_SUCCESS } diff --git a/arch/inst/I/sraw.yaml b/arch/inst/I/sraw.yaml index 46ff0b5cdf..5e2529d9ae 100644 --- a/arch/inst/I/sraw.yaml +++ b/arch/inst/I/sraw.yaml @@ -5,18 +5,18 @@ kind: instruction name: sraw long_name: Shift right arithmetic word description: | - Arithmetic shift the 32-bit value in `rs1` right by the value in the lower 5 bits of `rs2`, and store the sign-extended result in `rd`. + Arithmetic shift the 32-bit value in `xs1` right by the value in the lower 5 bits of `xs2`, and store the sign-extended result in `xd`. definedBy: I base: 64 assembly: xd, xs1, xs2 encoding: match: 0100000----------101-----0111011 variables: - - name: rs2 + - name: xs2 location: 24-20 - - name: rs1 + - name: xs1 location: 19-15 - - name: rd + - name: xd location: 11-7 access: s: always @@ -25,21 +25,21 @@ access: vu: always data_independent_timing: true operation(): | - XReg operand1 = sext(X[rs1], 31); + XReg operand1 = sext(X[xs1], 31); - X[rd] = sext(operand1 >>> X[rs2][4:0], 31); + X[xd] = sext(operand1 >>> X[xs2][4:0], 31); sail(): | { - let rs1_val = (X(rs1))[31..0]; - let rs2_val = (X(rs2))[31..0]; + let xs1_val = (X(xs1))[31..0]; + let xs2_val = (X(xs2))[31..0]; let result : bits(32) = match op { - RISCV_ADDW => rs1_val + rs2_val, - RISCV_SUBW => rs1_val - rs2_val, - RISCV_SLLW => rs1_val << (rs2_val[4..0]), - RISCV_SRLW => rs1_val >> (rs2_val[4..0]), - RISCV_SRAW => shift_right_arith32(rs1_val, rs2_val[4..0]) + RISCV_ADDW => xs1_val + xs2_val, + RISCV_SUBW => xs1_val - xs2_val, + RISCV_SLLW => xs1_val << (xs2_val[4..0]), + RISCV_SRLW => xs1_val >> (xs2_val[4..0]), + RISCV_SRAW => shift_right_arith32(xs1_val, xs2_val[4..0]) }; - X(rd) = sign_extend(result); + X(xd) = sign_extend(result); RETIRE_SUCCESS } diff --git a/arch/inst/I/srl.yaml b/arch/inst/I/srl.yaml index aac0008550..9eede2f378 100644 --- a/arch/inst/I/srl.yaml +++ b/arch/inst/I/srl.yaml @@ -5,17 +5,17 @@ kind: instruction name: srl long_name: Shift right logical description: | - Logical shift the value in `rs1` right by the value in the lower bits of `rs2`, and store the result in `rd`. + Logical shift the value in `xs1` right by the value in the lower bits of `xs2`, and store the result in `xd`. definedBy: I assembly: xd, xs1, xs2 encoding: match: 0000000----------101-----0110011 variables: - - name: rs2 + - name: xs2 location: 24-20 - - name: rs1 + - name: xs1 location: 19-15 - - name: rd + - name: xd location: 11-7 access: s: always @@ -25,33 +25,33 @@ access: data_independent_timing: true operation(): | if (xlen() == 64) { - X[rd] = X[rs1] >> X[rs2][5:0]; + X[xd] = X[xs1] >> X[xs2][5:0]; } else { - X[rd] = X[rs1] >> X[rs2][4:0]; + X[xd] = X[xs1] >> X[xs2][4:0]; } sail(): | { - let rs1_val = X(rs1); - let rs2_val = X(rs2); + let xs1_val = X(xs1); + let xs2_val = X(xs2); let result : xlenbits = match op { - RISCV_ADD => rs1_val + rs2_val, - RISCV_SLT => zero_extend(bool_to_bits(rs1_val <_s rs2_val)), - RISCV_SLTU => zero_extend(bool_to_bits(rs1_val <_u rs2_val)), - RISCV_AND => rs1_val & rs2_val, - RISCV_OR => rs1_val | rs2_val, - RISCV_XOR => rs1_val ^ rs2_val, + RISCV_ADD => xs1_val + xs2_val, + RISCV_SLT => zero_extend(bool_to_bits(xs1_val <_s xs2_val)), + RISCV_SLTU => zero_extend(bool_to_bits(xs1_val <_u xs2_val)), + RISCV_AND => xs1_val & xs2_val, + RISCV_OR => xs1_val | xs2_val, + RISCV_XOR => xs1_val ^ xs2_val, RISCV_SLL => if sizeof(xlen) == 32 - then rs1_val << (rs2_val[4..0]) - else rs1_val << (rs2_val[5..0]), + then xs1_val << (xs2_val[4..0]) + else xs1_val << (xs2_val[5..0]), RISCV_SRL => if sizeof(xlen) == 32 - then rs1_val >> (rs2_val[4..0]) - else rs1_val >> (rs2_val[5..0]), - RISCV_SUB => rs1_val - rs2_val, + then xs1_val >> (xs2_val[4..0]) + else xs1_val >> (xs2_val[5..0]), + RISCV_SUB => xs1_val - xs2_val, RISCV_SRA => if sizeof(xlen) == 32 - then shift_right_arith32(rs1_val, rs2_val[4..0]) - else shift_right_arith64(rs1_val, rs2_val[5..0]) + then shift_right_arith32(xs1_val, xs2_val[4..0]) + else shift_right_arith64(xs1_val, xs2_val[5..0]) }; - X(rd) = result; + X(xd) = result; RETIRE_SUCCESS } diff --git a/arch/inst/I/srli.yaml b/arch/inst/I/srli.yaml index d0e525725c..f83bd8059e 100644 --- a/arch/inst/I/srli.yaml +++ b/arch/inst/I/srli.yaml @@ -3,7 +3,7 @@ $schema: "inst_schema.json#" kind: instruction name: srli long_name: Shift right logical immediate -description: Shift the value in rs1 right by shamt, and store the result in rd +description: Shift the value in xs1 right by shamt, and store the result in xd definedBy: I assembly: xd, xs1, shamt encoding: @@ -12,18 +12,18 @@ encoding: variables: - name: shamt location: 24-20 - - name: rs1 + - name: xs1 location: 19-15 - - name: rd + - name: xd location: 11-7 RV64: match: 000000-----------101-----0010011 variables: - name: shamt location: 25-20 - - name: rs1 + - name: xs1 location: 19-15 - - name: rd + - name: xd location: 11-7 access: s: always @@ -33,23 +33,23 @@ access: data_independent_timing: true operation(): | # shamt is between 0-63 - X[rd] = X[rs1] >> shamt; + X[xd] = X[xs1] >> shamt; sail(): | { - let rs1_val = X(rs1); - /* the decoder guard should ensure that shamt[5] = 0 for RV32 */ + let xs1_val = X(xs1); + /* the decoder guaxd should ensure that shamt[5] = 0 for RV32 */ let result : xlenbits = match op { RISCV_SLLI => if sizeof(xlen) == 32 - then rs1_val << shamt[4..0] - else rs1_val << shamt, + then xs1_val << shamt[4..0] + else xs1_val << shamt, RISCV_SRLI => if sizeof(xlen) == 32 - then rs1_val >> shamt[4..0] - else rs1_val >> shamt, + then xs1_val >> shamt[4..0] + else xs1_val >> shamt, RISCV_SRAI => if sizeof(xlen) == 32 - then shift_right_arith32(rs1_val, shamt[4..0]) - else shift_right_arith64(rs1_val, shamt) + then shift_right_arith32(xs1_val, shamt[4..0]) + else shift_right_arith64(xs1_val, shamt) }; - X(rd) = result; + X(xd) = result; RETIRE_SUCCESS } diff --git a/arch/inst/I/srliw.yaml b/arch/inst/I/srliw.yaml index b859e0fa49..554559d06d 100644 --- a/arch/inst/I/srliw.yaml +++ b/arch/inst/I/srliw.yaml @@ -4,7 +4,7 @@ $schema: "inst_schema.json#" kind: instruction name: srliw long_name: Shift right logical immediate word -description: Shift the 32-bit value in rs1 right by shamt, and store the sign-extended result in rd +description: Shift the 32-bit value in xs1 right by shamt, and store the sign-extended result in xd definedBy: I base: 64 assembly: xd, xs1, shamt @@ -13,9 +13,9 @@ encoding: variables: - name: shamt location: 24-20 - - name: rs1 + - name: xs1 location: 19-15 - - name: rd + - name: xd location: 11-7 access: s: always @@ -25,18 +25,18 @@ access: data_independent_timing: true operation(): | # shamt is between 0-31 - XReg operand = X[rs1][31:0]; + XReg operand = X[xs1][31:0]; - X[rd] = sext(operand >> shamt, 31); + X[xd] = sext(operand >> shamt, 31); sail(): | { - let rs1_val = (X(rs1))[31..0]; + let xs1_val = (X(xs1))[31..0]; let result : bits(32) = match op { - RISCV_SLLIW => rs1_val << shamt, - RISCV_SRLIW => rs1_val >> shamt, - RISCV_SRAIW => shift_right_arith32(rs1_val, shamt) + RISCV_SLLIW => xs1_val << shamt, + RISCV_SRLIW => xs1_val >> shamt, + RISCV_SRAIW => shift_right_arith32(xs1_val, shamt) }; - X(rd) = sign_extend(result); + X(xd) = sign_extend(result); RETIRE_SUCCESS } diff --git a/arch/inst/I/srlw.yaml b/arch/inst/I/srlw.yaml index 5e24f68d83..974a53fdf6 100644 --- a/arch/inst/I/srlw.yaml +++ b/arch/inst/I/srlw.yaml @@ -5,18 +5,18 @@ kind: instruction name: srlw long_name: Shift right logical word description: | - Logical shift the 32-bit value in `rs1` right by the value in the lower 5 bits of `rs2`, and store the sign-extended result in `rd`. + Logical shift the 32-bit value in `xs1` right by the value in the lower 5 bits of `xs2`, and store the sign-extended result in `xd`. definedBy: I base: 64 assembly: xd, xs1, xs2 encoding: match: 0000000----------101-----0111011 variables: - - name: rs2 + - name: xs2 location: 24-20 - - name: rs1 + - name: xs1 location: 19-15 - - name: rd + - name: xd location: 11-7 access: s: always @@ -24,19 +24,19 @@ access: vs: always vu: always data_independent_timing: true -operation(): X[rd] = sext(X[rs1][31:0] >> X[rs2][4:0], 31); +operation(): X[xd] = sext(X[xs1][31:0] >> X[xs2][4:0], 31); sail(): | { - let rs1_val = (X(rs1))[31..0]; - let rs2_val = (X(rs2))[31..0]; + let xs1_val = (X(xs1))[31..0]; + let xs2_val = (X(xs2))[31..0]; let result : bits(32) = match op { - RISCV_ADDW => rs1_val + rs2_val, - RISCV_SUBW => rs1_val - rs2_val, - RISCV_SLLW => rs1_val << (rs2_val[4..0]), - RISCV_SRLW => rs1_val >> (rs2_val[4..0]), - RISCV_SRAW => shift_right_arith32(rs1_val, rs2_val[4..0]) + RISCV_ADDW => xs1_val + xs2_val, + RISCV_SUBW => xs1_val - xs2_val, + RISCV_SLLW => xs1_val << (xs2_val[4..0]), + RISCV_SRLW => xs1_val >> (xs2_val[4..0]), + RISCV_SRAW => shift_right_arith32(xs1_val, xs2_val[4..0]) }; - X(rd) = sign_extend(result); + X(xd) = sign_extend(result); RETIRE_SUCCESS } diff --git a/arch/inst/I/sub.yaml b/arch/inst/I/sub.yaml index a4bbb98a7e..eb30a4bfce 100644 --- a/arch/inst/I/sub.yaml +++ b/arch/inst/I/sub.yaml @@ -4,17 +4,17 @@ $schema: "inst_schema.json#" kind: instruction name: sub long_name: Subtract -description: Subtract the value in rs2 from rs1, and store the result in rd +description: Subtract the value in xs2 from xs1, and store the result in xd definedBy: I assembly: xd, xs1, xs2 encoding: match: 0100000----------000-----0110011 variables: - - name: rs2 + - name: xs2 location: 24-20 - - name: rs1 + - name: xs1 location: 19-15 - - name: rd + - name: xd location: 11-7 access: s: always @@ -23,32 +23,32 @@ access: vu: always data_independent_timing: true operation(): | - XReg t0 = X[rs1]; - XReg t1 = X[rs2]; - X[rd] = t0 - t1; + XReg t0 = X[xs1]; + XReg t1 = X[xs2]; + X[xd] = t0 - t1; sail(): | { - let rs1_val = X(rs1); - let rs2_val = X(rs2); + let xs1_val = X(xs1); + let xs2_val = X(xs2); let result : xlenbits = match op { - RISCV_ADD => rs1_val + rs2_val, - RISCV_SLT => zero_extend(bool_to_bits(rs1_val <_s rs2_val)), - RISCV_SLTU => zero_extend(bool_to_bits(rs1_val <_u rs2_val)), - RISCV_AND => rs1_val & rs2_val, - RISCV_OR => rs1_val | rs2_val, - RISCV_XOR => rs1_val ^ rs2_val, + RISCV_ADD => xs1_val + xs2_val, + RISCV_SLT => zero_extend(bool_to_bits(xs1_val <_s xs2_val)), + RISCV_SLTU => zero_extend(bool_to_bits(xs1_val <_u xs2_val)), + RISCV_AND => xs1_val & xs2_val, + RISCV_OR => xs1_val | xs2_val, + RISCV_XOR => xs1_val ^ xs2_val, RISCV_SLL => if sizeof(xlen) == 32 - then rs1_val << (rs2_val[4..0]) - else rs1_val << (rs2_val[5..0]), + then xs1_val << (xs2_val[4..0]) + else xs1_val << (xs2_val[5..0]), RISCV_SRL => if sizeof(xlen) == 32 - then rs1_val >> (rs2_val[4..0]) - else rs1_val >> (rs2_val[5..0]), - RISCV_SUB => rs1_val - rs2_val, + then xs1_val >> (xs2_val[4..0]) + else xs1_val >> (xs2_val[5..0]), + RISCV_SUB => xs1_val - xs2_val, RISCV_SRA => if sizeof(xlen) == 32 - then shift_right_arith32(rs1_val, rs2_val[4..0]) - else shift_right_arith64(rs1_val, rs2_val[5..0]) + then shift_right_arith32(xs1_val, xs2_val[4..0]) + else shift_right_arith64(xs1_val, xs2_val[5..0]) }; - X(rd) = result; + X(xd) = result; RETIRE_SUCCESS } diff --git a/arch/inst/I/subw.yaml b/arch/inst/I/subw.yaml index d39c1b7e52..a05518f6b2 100644 --- a/arch/inst/I/subw.yaml +++ b/arch/inst/I/subw.yaml @@ -4,18 +4,18 @@ $schema: "inst_schema.json#" kind: instruction name: subw long_name: Subtract word -description: Subtract the 32-bit values in rs2 from rs1, and store the sign-extended result in rd +description: Subtract the 32-bit values in xs2 from xs1, and store the sign-extended result in xd definedBy: I base: 64 assembly: xd, xs1, xs2 encoding: match: 0100000----------000-----0111011 variables: - - name: rs2 + - name: xs2 location: 24-20 - - name: rs1 + - name: xs1 location: 19-15 - - name: rd + - name: xd location: 11-7 access: s: always @@ -24,21 +24,21 @@ access: vu: always data_independent_timing: true operation(): | - Bits<32> t0 = X[rs1][31:0]; - Bits<32> t1 = X[rs2][31:0]; - X[rd] = sext(t0 - t1, 31); + Bits<32> t0 = X[xs1][31:0]; + Bits<32> t1 = X[xs2][31:0]; + X[xd] = sext(t0 - t1, 31); sail(): | { - let rs1_val = (X(rs1))[31..0]; - let rs2_val = (X(rs2))[31..0]; + let xs1_val = (X(xs1))[31..0]; + let xs2_val = (X(xs2))[31..0]; let result : bits(32) = match op { - RISCV_ADDW => rs1_val + rs2_val, - RISCV_SUBW => rs1_val - rs2_val, - RISCV_SLLW => rs1_val << (rs2_val[4..0]), - RISCV_SRLW => rs1_val >> (rs2_val[4..0]), - RISCV_SRAW => shift_right_arith32(rs1_val, rs2_val[4..0]) + RISCV_ADDW => xs1_val + xs2_val, + RISCV_SUBW => xs1_val - xs2_val, + RISCV_SLLW => xs1_val << (xs2_val[4..0]), + RISCV_SRLW => xs1_val >> (xs2_val[4..0]), + RISCV_SRAW => shift_right_arith32(xs1_val, xs2_val[4..0]) }; - X(rd) = sign_extend(result); + X(xd) = sign_extend(result); RETIRE_SUCCESS } diff --git a/arch/inst/I/sw.yaml b/arch/inst/I/sw.yaml index a95dcf3f83..e40ddfa4f0 100644 --- a/arch/inst/I/sw.yaml +++ b/arch/inst/I/sw.yaml @@ -5,8 +5,8 @@ kind: instruction name: sw long_name: Store word description: | - Store 32 bits of data from register `rs2` to an - address formed by adding `rs1` to a signed offset. + Store 32 bits of data from register `xs2` to an + address formed by adding `xs1` to a signed offset. definedBy: I assembly: xs2, imm(xs1) encoding: @@ -14,9 +14,9 @@ encoding: variables: - name: imm location: 31-25|11-7 - - name: rs2 + - name: xs2 location: 24-20 - - name: rs1 + - name: xs1 location: 19-15 access: s: always @@ -24,16 +24,16 @@ access: vs: always vu: always operation(): | - XReg virtual_address = X[rs1] + imm; + XReg virtual_address = X[xs1] + $signed(imm); - write_memory<32>(virtual_address, X[rs2][31:0], $encoding); + write_memory<32>(virtual_address, X[xs2][31:0], $encoding); sail(): | { let offset : xlenbits = sign_extend(imm); - /* Get the address, X(rs1) + offset. + /* Get the address, X(xs1) + offset. Some extensions perform additional checks on address validity. */ - match ext_data_get_addr(rs1, offset, Write(Data), width) { + match ext_data_get_addr(xs1, offset, Write(Data), width) { Ext_DataAddr_Error(e) => { ext_handle_data_check_error(e); RETIRE_FAIL }, Ext_DataAddr_OK(vaddr) => if check_misaligned(vaddr, width) @@ -50,13 +50,13 @@ sail(): | match (eares) { MemException(e) => { handle_mem_exception(vaddr, e); RETIRE_FAIL }, MemValue(_) => { - let rs2_val = X(rs2); + let xs2_val = X(xs2); let res : MemoryOpResult(bool) = match (width) { - BYTE => mem_write_value(paddr, 1, rs2_val[7..0], aq, rl, false), - HALF => mem_write_value(paddr, 2, rs2_val[15..0], aq, rl, false), - WORD => mem_write_value(paddr, 4, rs2_val[31..0], aq, rl, false), + BYTE => mem_write_value(paddr, 1, xs2_val[7..0], aq, rl, false), + HALF => mem_write_value(paddr, 2, xs2_val[15..0], aq, rl, false), + WORD => mem_write_value(paddr, 4, xs2_val[31..0], aq, rl, false), DOUBLE if sizeof(xlen) >= 64 - => mem_write_value(paddr, 8, rs2_val, aq, rl, false), + => mem_write_value(paddr, 8, xs2_val, aq, rl, false), _ => report_invalid_width(__FILE__, __LINE__, width, "store"), }; match (res) { diff --git a/arch/inst/I/xor.yaml b/arch/inst/I/xor.yaml index ba2efc7264..5b4a30686c 100644 --- a/arch/inst/I/xor.yaml +++ b/arch/inst/I/xor.yaml @@ -4,17 +4,17 @@ $schema: "inst_schema.json#" kind: instruction name: xor long_name: Exclusive Or -description: Exclusive or rs1 with rs2, and store the result in rd +description: Exclusive or xs1 with xs2, and store the result in xd definedBy: I assembly: xd, xs1, xs2 encoding: match: 0000000----------100-----0110011 variables: - - name: rs2 + - name: xs2 location: 24-20 - - name: rs1 + - name: xs1 location: 19-15 - - name: rd + - name: xd location: 11-7 access: s: always @@ -22,30 +22,30 @@ access: vs: always vu: always data_independent_timing: true -operation(): X[rd] = X[rs1] ^ X[rs2]; +operation(): X[xd] = X[xs1] ^ X[xs2]; sail(): | { - let rs1_val = X(rs1); - let rs2_val = X(rs2); + let xs1_val = X(xs1); + let xs2_val = X(xs2); let result : xlenbits = match op { - RISCV_ADD => rs1_val + rs2_val, - RISCV_SLT => zero_extend(bool_to_bits(rs1_val <_s rs2_val)), - RISCV_SLTU => zero_extend(bool_to_bits(rs1_val <_u rs2_val)), - RISCV_AND => rs1_val & rs2_val, - RISCV_OR => rs1_val | rs2_val, - RISCV_XOR => rs1_val ^ rs2_val, + RISCV_ADD => xs1_val + xs2_val, + RISCV_SLT => zero_extend(bool_to_bits(xs1_val <_s xs2_val)), + RISCV_SLTU => zero_extend(bool_to_bits(xs1_val <_u xs2_val)), + RISCV_AND => xs1_val & xs2_val, + RISCV_OR => xs1_val | xs2_val, + RISCV_XOR => xs1_val ^ xs2_val, RISCV_SLL => if sizeof(xlen) == 32 - then rs1_val << (rs2_val[4..0]) - else rs1_val << (rs2_val[5..0]), + then xs1_val << (xs2_val[4..0]) + else xs1_val << (xs2_val[5..0]), RISCV_SRL => if sizeof(xlen) == 32 - then rs1_val >> (rs2_val[4..0]) - else rs1_val >> (rs2_val[5..0]), - RISCV_SUB => rs1_val - rs2_val, + then xs1_val >> (xs2_val[4..0]) + else xs1_val >> (xs2_val[5..0]), + RISCV_SUB => xs1_val - xs2_val, RISCV_SRA => if sizeof(xlen) == 32 - then shift_right_arith32(rs1_val, rs2_val[4..0]) - else shift_right_arith64(rs1_val, rs2_val[5..0]) + then shift_right_arith32(xs1_val, xs2_val[4..0]) + else shift_right_arith64(xs1_val, xs2_val[5..0]) }; - X(rd) = result; + X(xd) = result; RETIRE_SUCCESS } diff --git a/arch/inst/I/xori.yaml b/arch/inst/I/xori.yaml index 2a588aa50a..032530b3d7 100644 --- a/arch/inst/I/xori.yaml +++ b/arch/inst/I/xori.yaml @@ -4,7 +4,7 @@ $schema: "inst_schema.json#" kind: instruction name: xori long_name: Exclusive Or immediate -description: Exclusive or an immediate to the value in rs1, and store the result in rd +description: Exclusive or an immediate to the value in xs1, and store the result in xd definedBy: I assembly: xd, xs1, imm encoding: @@ -12,9 +12,9 @@ encoding: variables: - name: imm location: 31-20 - - name: rs1 + - name: xs1 location: 19-15 - - name: rd + - name: xd location: 11-7 access: s: always @@ -22,20 +22,20 @@ access: vs: always vu: always data_independent_timing: true -operation(): X[rd] = X[rs1] ^ imm; +operation(): X[xd] = X[xs1] ^ $signed(imm); sail(): | { - let rs1_val = X(rs1); + let xs1_val = X(xs1); let immext : xlenbits = sign_extend(imm); let result : xlenbits = match op { - RISCV_ADDI => rs1_val + immext, - RISCV_SLTI => zero_extend(bool_to_bits(rs1_val <_s immext)), - RISCV_SLTIU => zero_extend(bool_to_bits(rs1_val <_u immext)), - RISCV_ANDI => rs1_val & immext, - RISCV_ORI => rs1_val | immext, - RISCV_XORI => rs1_val ^ immext + RISCV_ADDI => xs1_val + immext, + RISCV_SLTI => zero_extend(bool_to_bits(xs1_val <_s immext)), + RISCV_SLTIU => zero_extend(bool_to_bits(xs1_val <_u immext)), + RISCV_ANDI => xs1_val & immext, + RISCV_ORI => xs1_val | immext, + RISCV_XORI => xs1_val ^ immext }; - X(rd) = result; + X(xd) = result; RETIRE_SUCCESS } diff --git a/arch/inst/M/div.yaml b/arch/inst/M/div.yaml index 652784552c..d48a144a45 100644 --- a/arch/inst/M/div.yaml +++ b/arch/inst/M/div.yaml @@ -35,15 +35,18 @@ operation(): | XReg src1 = X[rs1]; XReg src2 = X[rs2]; + # smallest signed value + XReg signed_min = (xlen() == 32) ? $signed({1'b1, {31{1'b0}}}) : {1'b1, {63{1'b0}}}; + if (src2 == 0) { # division by zero. Since RISC-V does not have arithmetic exceptions, the result is defined # to be -1 X[rd] = {XLEN{1'b1}}; - } else if ((src1 == {1'b1, {XLEN-1{1'b0}}}) && (src2 == {XLEN{1'b1}})) { + } else if ((src1 == signed_min) && (src2 == {XLEN{1'b1}})) { # signed overflow. Since RISC-V does not have arithmetic exceptions, the result is defined # to be the most negative number (-2^(XLEN-1)) - X[rd] = {1'b1, {XLEN-1{1'b0}}}; + X[rd] = signed_min; } else { # no special case, just divide diff --git a/arch/inst/Zcb/c.lbu.yaml b/arch/inst/Zcb/c.lbu.yaml index e02c328573..bf7d0d6f54 100644 --- a/arch/inst/Zcb/c.lbu.yaml +++ b/arch/inst/Zcb/c.lbu.yaml @@ -32,9 +32,9 @@ operation(): | raise(ExceptionCode::IllegalInstruction, mode(), $encoding); } - XReg virtual_address = X[rs1+8] + imm; + XReg virtual_address = X[creg2reg(rs1)] + imm; - X[rd+8] = zext(read_memory<8>(virtual_address, $encoding), 8); + X[creg2reg(rd)] = read_memory<8>(virtual_address, $encoding); sail(): | { diff --git a/arch/inst/Zcb/c.lh.yaml b/arch/inst/Zcb/c.lh.yaml index a96c92ef80..8edcd6a5fa 100644 --- a/arch/inst/Zcb/c.lh.yaml +++ b/arch/inst/Zcb/c.lh.yaml @@ -33,9 +33,9 @@ operation(): | raise(ExceptionCode::IllegalInstruction, mode(), $encoding); } - XReg virtual_address = X[rs1+8] + imm; + XReg virtual_address = X[creg2reg(rs1)] + imm; - X[rd+8] = sext(read_memory<16>(virtual_address, $encoding), 16); + X[creg2reg(rd)] = sext(read_memory<16>(virtual_address, $encoding), 16); sail(): | { diff --git a/arch/inst/Zcb/c.lhu.yaml b/arch/inst/Zcb/c.lhu.yaml index 6d6e7d819c..c1660ff3e2 100644 --- a/arch/inst/Zcb/c.lhu.yaml +++ b/arch/inst/Zcb/c.lhu.yaml @@ -33,9 +33,9 @@ operation(): | raise(ExceptionCode::IllegalInstruction, mode(), $encoding); } - XReg virtual_address = X[rs1+8] + imm; + XReg virtual_address = X[creg2reg(rs1)] + imm; - X[rd+8] = zext(read_memory<16>(virtual_address, $encoding), 16); + X[creg2reg(rd)] = read_memory<16>(virtual_address, $encoding); sail(): | { diff --git a/arch/inst/Zcb/c.mul.yaml b/arch/inst/Zcb/c.mul.yaml index f70a887120..194c0875d6 100644 --- a/arch/inst/Zcb/c.mul.yaml +++ b/arch/inst/Zcb/c.mul.yaml @@ -34,7 +34,7 @@ operation(): | raise(ExceptionCode::IllegalInstruction, mode(), $encoding); } - X[rd+8] = X[rd+8] * X[rs2+8]; + X[creg2reg(rd)] = X[creg2reg(rd)] * X[creg2reg(rs2)]; sail(): | { diff --git a/arch/inst/Zcb/c.not.yaml b/arch/inst/Zcb/c.not.yaml index 97df445c63..7f18d1ec80 100644 --- a/arch/inst/Zcb/c.not.yaml +++ b/arch/inst/Zcb/c.not.yaml @@ -29,7 +29,7 @@ operation(): | raise(ExceptionCode::IllegalInstruction, mode(), $encoding); } - X[rd+8] = ~X[rd+8]; + X[creg2reg(rd)] = ~X[creg2reg(rd)]; sail(): | { diff --git a/arch/inst/Zcb/c.sb.yaml b/arch/inst/Zcb/c.sb.yaml index 18ef857322..1af01a657c 100644 --- a/arch/inst/Zcb/c.sb.yaml +++ b/arch/inst/Zcb/c.sb.yaml @@ -32,6 +32,6 @@ operation(): | raise(ExceptionCode::IllegalInstruction, mode(), $encoding); } - XReg virtual_address = X[rs1+8] + imm; + XReg virtual_address = X[creg2reg(rs1)] + imm; - write_memory<8>(virtual_address, X[rs2+8][7:0], $encoding); + write_memory<8>(virtual_address, X[creg2reg(rs2)][7:0], $encoding); diff --git a/arch/inst/Zcb/c.sext.b.yaml b/arch/inst/Zcb/c.sext.b.yaml index 9dd0dae51f..659f8cf977 100644 --- a/arch/inst/Zcb/c.sext.b.yaml +++ b/arch/inst/Zcb/c.sext.b.yaml @@ -34,7 +34,7 @@ operation(): | raise(ExceptionCode::IllegalInstruction, mode(), $encoding); } - X[rd+8] = sext(X[rd+8][15:0],8); + X[creg2reg(rd)] = $signed(X[creg2reg(rd)][7:0]); sail(): | { diff --git a/arch/inst/Zcb/c.sext.h.yaml b/arch/inst/Zcb/c.sext.h.yaml index 734cb8bd35..70ef2fc8da 100644 --- a/arch/inst/Zcb/c.sext.h.yaml +++ b/arch/inst/Zcb/c.sext.h.yaml @@ -34,7 +34,7 @@ operation(): | raise(ExceptionCode::IllegalInstruction, mode(), $encoding); } - X[rd+8] = sext(X[rd+8][15:0],16); + X[creg2reg(rd)] = $signed(X[creg2reg(rd)][15:0]); sail(): | { diff --git a/arch/inst/Zcb/c.sh.yaml b/arch/inst/Zcb/c.sh.yaml index f4e34aef88..372a12c1fe 100644 --- a/arch/inst/Zcb/c.sh.yaml +++ b/arch/inst/Zcb/c.sh.yaml @@ -33,6 +33,6 @@ operation(): | raise(ExceptionCode::IllegalInstruction, mode(), $encoding); } - XReg virtual_address = X[rs1+8] + imm; + XReg virtual_address = X[creg2reg(rs1)] + imm; - write_memory<16>(virtual_address, X[rs2+8][15:0], $encoding); + write_memory<16>(virtual_address, X[creg2reg(rs2)][15:0], $encoding); diff --git a/arch/inst/Zcb/c.zext.b.yaml b/arch/inst/Zcb/c.zext.b.yaml index f048a52c58..8fb59d01e2 100644 --- a/arch/inst/Zcb/c.zext.b.yaml +++ b/arch/inst/Zcb/c.zext.b.yaml @@ -34,7 +34,7 @@ operation(): | raise(ExceptionCode::IllegalInstruction, mode(), $encoding); } - X[rd+8] = X[rd+8][7:0]; + X[creg2reg(rd)] = X[creg2reg(rd)][7:0]; sail(): | { diff --git a/arch/inst/Zcb/c.zext.h.yaml b/arch/inst/Zcb/c.zext.h.yaml index 57fb520e67..6f04096415 100644 --- a/arch/inst/Zcb/c.zext.h.yaml +++ b/arch/inst/Zcb/c.zext.h.yaml @@ -34,7 +34,7 @@ operation(): | raise(ExceptionCode::IllegalInstruction, mode(), $encoding); } - X[rd+8] = X[rd+8][15:0]; + X[creg2reg(rd)] = X[creg2reg(rd)][15:0]; sail(): | { diff --git a/arch/inst/Zcb/c.zext.w.yaml b/arch/inst/Zcb/c.zext.w.yaml index cd6e4526e1..2a1a287f46 100644 --- a/arch/inst/Zcb/c.zext.w.yaml +++ b/arch/inst/Zcb/c.zext.w.yaml @@ -34,7 +34,7 @@ operation(): | raise(ExceptionCode::IllegalInstruction, mode(), $encoding); } - X[rd+8] = X[rd+8][31:0]; + X[creg2reg(rd)] = X[creg2reg(rd)][31:0]; sail(): | { diff --git a/arch/inst/Zcmp/cm.mva01s.yaml b/arch/inst/Zcmp/cm.mva01s.yaml index 58b115a83d..d73f6b97f7 100644 --- a/arch/inst/Zcmp/cm.mva01s.yaml +++ b/arch/inst/Zcmp/cm.mva01s.yaml @@ -7,13 +7,7 @@ long_name: Move two s0-s7 registers into a0-a1 description: | This instruction moves r1s' into a0 and r2s' into a1. The execution is atomic, so it is not possible to observe state where only one of a0 or a1 have been updated. The encoding uses sreg number specifiers instead of xreg number specifiers to save encoding space. The mapping between them is specified in the pseudo-code below. -definedBy: - anyOf: - - Zcmp -excludedBy: - anyOf: - - allOf: [C, D] - - Zcd +definedBy: Zcmp assembly: r1s, r2s encoding: match: 101011---11---10 diff --git a/arch/inst/Zcmp/cm.mvsa01.yaml b/arch/inst/Zcmp/cm.mvsa01.yaml index 1c3e19d367..0b7a3b5d0b 100644 --- a/arch/inst/Zcmp/cm.mvsa01.yaml +++ b/arch/inst/Zcmp/cm.mvsa01.yaml @@ -9,13 +9,7 @@ description: | The execution is atomic, so it is not possible to observe state where only one of r1s' or r2s' has been updated. The encoding uses sreg number specifiers instead of xreg number specifiers to save encoding space. The mapping between them is specified in the pseudo-code below. -definedBy: - anyOf: - - Zcmp -excludedBy: - anyOf: - - allOf: [C, D] - - Zcd +definedBy: Zcmp assembly: r1s, r2s encoding: match: 101011---01---10 diff --git a/arch/inst/Zcmp/cm.pop.yaml b/arch/inst/Zcmp/cm.pop.yaml index 975861da9a..e08daac742 100644 --- a/arch/inst/Zcmp/cm.pop.yaml +++ b/arch/inst/Zcmp/cm.pop.yaml @@ -14,13 +14,7 @@ description: | * it must be a multiple of 16 (bytes): ** for RV32 the allowed values are: 16, 32, 48, 64, 80, 96, 112 ** for RV64 the allowed values are: 16, 32, 48, 64, 80, 96, 112, 128, 144, 160 -definedBy: - anyOf: - - Zcmp -excludedBy: - anyOf: - - allOf: [C, D] - - Zcd +definedBy: Zcmp assembly: reg_list, stack_adj encoding: match: 10111010------10 @@ -41,47 +35,47 @@ operation(): | raise(ExceptionCode::IllegalInstruction, mode(), $encoding); } - XReg size = xlen(); + XReg size = xlen() / 8; XReg nreg = (rlist == 15) ? 13 : (rlist - 3); XReg stack_aligned_adj = (nreg * 4 + 15) & ~0xF; XReg virtual_address_sp = X[2]; XReg virtual_address_new_sp = virtual_address_sp + stack_aligned_adj + spimm; XReg virtual_address_base = virtual_address_new_sp - (nreg * size); - X[ 1] = read_memory_xlen(virtual_address_base + 0*size, $encoding); + X[ 1] = read_memory_xlen_aligned(virtual_address_base + 0*size, $encoding); if (nreg > 1) { - X[ 8] = read_memory_xlen(virtual_address_base + 1*size, $encoding); + X[ 8] = read_memory_xlen_aligned(virtual_address_base + 1*size, $encoding); } if (nreg > 2) { - X[ 9] = read_memory_xlen(virtual_address_base + 2*size, $encoding); + X[ 9] = read_memory_xlen_aligned(virtual_address_base + 2*size, $encoding); } if (nreg > 3) { - X[18] = read_memory_xlen(virtual_address_base + 3*size, $encoding); + X[18] = read_memory_xlen_aligned(virtual_address_base + 3*size, $encoding); } if (nreg > 4) { - X[19] = read_memory_xlen(virtual_address_base + 4*size, $encoding); + X[19] = read_memory_xlen_aligned(virtual_address_base + 4*size, $encoding); } if (nreg > 5) { - X[20] = read_memory_xlen(virtual_address_base + 5*size, $encoding); + X[20] = read_memory_xlen_aligned(virtual_address_base + 5*size, $encoding); } if (nreg > 6) { - X[21] = read_memory_xlen(virtual_address_base + 6*size, $encoding); + X[21] = read_memory_xlen_aligned(virtual_address_base + 6*size, $encoding); } if (nreg > 7) { - X[22] = read_memory_xlen(virtual_address_base + 7*size, $encoding); + X[22] = read_memory_xlen_aligned(virtual_address_base + 7*size, $encoding); } if (nreg > 8) { - X[23] = read_memory_xlen(virtual_address_base + 8*size, $encoding); + X[23] = read_memory_xlen_aligned(virtual_address_base + 8*size, $encoding); } if (nreg > 9) { - X[24] = read_memory_xlen(virtual_address_base + 9*size, $encoding); + X[24] = read_memory_xlen_aligned(virtual_address_base + 9*size, $encoding); } if (nreg > 10) { - X[25] = read_memory_xlen(virtual_address_base + 10*size, $encoding); + X[25] = read_memory_xlen_aligned(virtual_address_base + 10*size, $encoding); } if (nreg > 11) { - X[26] = read_memory_xlen(virtual_address_base + 11*size, $encoding); - X[27] = read_memory_xlen(virtual_address_base + 12*size, $encoding); + X[26] = read_memory_xlen_aligned(virtual_address_base + 11*size, $encoding); + X[27] = read_memory_xlen_aligned(virtual_address_base + 12*size, $encoding); } X[2] = virtual_address_new_sp; diff --git a/arch/inst/Zcmp/cm.popret.yaml b/arch/inst/Zcmp/cm.popret.yaml index a5c7069967..99d4b1179e 100644 --- a/arch/inst/Zcmp/cm.popret.yaml +++ b/arch/inst/Zcmp/cm.popret.yaml @@ -14,13 +14,7 @@ description: | * it must be a multiple of 16 (bytes): ** for RV32 the allowed values are: 16, 32, 48, 64, 80, 96, 112 ** for RV64 the allowed values are: 16, 32, 48, 64, 80, 96, 112, 128, 144, 160 -definedBy: - anyOf: - - Zcmp -excludedBy: - anyOf: - - allOf: [C, D] - - Zcd +definedBy: Zcmp assembly: reg_list, stack_adj encoding: match: 10111110------10 @@ -41,47 +35,47 @@ operation(): | raise(ExceptionCode::IllegalInstruction, mode(), $encoding); } - XReg size = xlen(); + XReg size = xlen() / 8; XReg nreg = (rlist == 15) ? 13 : (rlist - 3); XReg stack_aligned_adj = (nreg * 4 + 15) & ~0xF; XReg virtual_address_sp = X[2]; XReg virtual_address_new_sp = virtual_address_sp + stack_aligned_adj + spimm; XReg virtual_address_base = virtual_address_new_sp - (nreg * size); - X[ 1] = read_memory_xlen(virtual_address_base + 0*size, $encoding); + X[ 1] = read_memory_xlen_aligned(virtual_address_base + 0*size, $encoding); if (nreg > 1) { - X[ 8] = read_memory_xlen(virtual_address_base + 1*size, $encoding); + X[ 8] = read_memory_xlen_aligned(virtual_address_base + 1*size, $encoding); } if (nreg > 2) { - X[ 9] = read_memory_xlen(virtual_address_base + 2*size, $encoding); + X[ 9] = read_memory_xlen_aligned(virtual_address_base + 2*size, $encoding); } if (nreg > 3) { - X[18] = read_memory_xlen(virtual_address_base + 3*size, $encoding); + X[18] = read_memory_xlen_aligned(virtual_address_base + 3*size, $encoding); } if (nreg > 4) { - X[19] = read_memory_xlen(virtual_address_base + 4*size, $encoding); + X[19] = read_memory_xlen_aligned(virtual_address_base + 4*size, $encoding); } if (nreg > 5) { - X[20] = read_memory_xlen(virtual_address_base + 5*size, $encoding); + X[20] = read_memory_xlen_aligned(virtual_address_base + 5*size, $encoding); } if (nreg > 6) { - X[21] = read_memory_xlen(virtual_address_base + 6*size, $encoding); + X[21] = read_memory_xlen_aligned(virtual_address_base + 6*size, $encoding); } if (nreg > 7) { - X[22] = read_memory_xlen(virtual_address_base + 7*size, $encoding); + X[22] = read_memory_xlen_aligned(virtual_address_base + 7*size, $encoding); } if (nreg > 8) { - X[23] = read_memory_xlen(virtual_address_base + 8*size, $encoding); + X[23] = read_memory_xlen_aligned(virtual_address_base + 8*size, $encoding); } if (nreg > 9) { - X[24] = read_memory_xlen(virtual_address_base + 9*size, $encoding); + X[24] = read_memory_xlen_aligned(virtual_address_base + 9*size, $encoding); } if (nreg > 10) { - X[25] = read_memory_xlen(virtual_address_base + 10*size, $encoding); + X[25] = read_memory_xlen_aligned(virtual_address_base + 10*size, $encoding); } if (nreg > 11) { - X[26] = read_memory_xlen(virtual_address_base + 11*size, $encoding); - X[27] = read_memory_xlen(virtual_address_base + 12*size, $encoding); + X[26] = read_memory_xlen_aligned(virtual_address_base + 11*size, $encoding); + X[27] = read_memory_xlen_aligned(virtual_address_base + 12*size, $encoding); } X[2] = virtual_address_new_sp; diff --git a/arch/inst/Zcmp/cm.popretz.yaml b/arch/inst/Zcmp/cm.popretz.yaml index 2b656db3b4..a53d22532d 100644 --- a/arch/inst/Zcmp/cm.popretz.yaml +++ b/arch/inst/Zcmp/cm.popretz.yaml @@ -14,13 +14,7 @@ description: | * it must be a multiple of 16 (bytes): ** for RV32 the allowed values are: 16, 32, 48, 64, 80, 96, 112 ** for RV64 the allowed values are: 16, 32, 48, 64, 80, 96, 112, 128, 144, 160 -definedBy: - anyOf: - - Zcmp -excludedBy: - anyOf: - - allOf: [C, D] - - Zcd +definedBy: Zcmp assembly: reg_list, stack_adj encoding: match: 10111100------10 @@ -41,47 +35,47 @@ operation(): | raise(ExceptionCode::IllegalInstruction, mode(), $encoding); } - XReg size = xlen(); + XReg size = xlen() / 8; XReg nreg = (rlist == 15) ? 13 : (rlist - 3); XReg stack_aligned_adj = (nreg * 4 + 15) & ~0xF; XReg virtual_address_sp = X[2]; XReg virtual_address_new_sp = virtual_address_sp + stack_aligned_adj + spimm; XReg virtual_address_base = virtual_address_new_sp - (nreg * size); - X[ 1] = read_memory_xlen(virtual_address_base + 0*size, $encoding); + X[ 1] = read_memory_xlen_aligned(virtual_address_base + 0*size, $encoding); if (nreg > 1) { - X[ 8] = read_memory_xlen(virtual_address_base + 1*size, $encoding); + X[ 8] = read_memory_xlen_aligned(virtual_address_base + 1*size, $encoding); } if (nreg > 2) { - X[ 9] = read_memory_xlen(virtual_address_base + 2*size, $encoding); + X[ 9] = read_memory_xlen_aligned(virtual_address_base + 2*size, $encoding); } if (nreg > 3) { - X[18] = read_memory_xlen(virtual_address_base + 3*size, $encoding); + X[18] = read_memory_xlen_aligned(virtual_address_base + 3*size, $encoding); } if (nreg > 4) { - X[19] = read_memory_xlen(virtual_address_base + 4*size, $encoding); + X[19] = read_memory_xlen_aligned(virtual_address_base + 4*size, $encoding); } if (nreg > 5) { - X[20] = read_memory_xlen(virtual_address_base + 5*size, $encoding); + X[20] = read_memory_xlen_aligned(virtual_address_base + 5*size, $encoding); } if (nreg > 6) { - X[21] = read_memory_xlen(virtual_address_base + 6*size, $encoding); + X[21] = read_memory_xlen_aligned(virtual_address_base + 6*size, $encoding); } if (nreg > 7) { - X[22] = read_memory_xlen(virtual_address_base + 7*size, $encoding); + X[22] = read_memory_xlen_aligned(virtual_address_base + 7*size, $encoding); } if (nreg > 8) { - X[23] = read_memory_xlen(virtual_address_base + 8*size, $encoding); + X[23] = read_memory_xlen_aligned(virtual_address_base + 8*size, $encoding); } if (nreg > 9) { - X[24] = read_memory_xlen(virtual_address_base + 9*size, $encoding); + X[24] = read_memory_xlen_aligned(virtual_address_base + 9*size, $encoding); } if (nreg > 10) { - X[25] = read_memory_xlen(virtual_address_base + 10*size, $encoding); + X[25] = read_memory_xlen_aligned(virtual_address_base + 10*size, $encoding); } if (nreg > 11) { - X[26] = read_memory_xlen(virtual_address_base + 11*size, $encoding); - X[27] = read_memory_xlen(virtual_address_base + 12*size, $encoding); + X[26] = read_memory_xlen_aligned(virtual_address_base + 11*size, $encoding); + X[27] = read_memory_xlen_aligned(virtual_address_base + 12*size, $encoding); } X[2] = virtual_address_new_sp; diff --git a/arch/inst/Zcmp/cm.push.yaml b/arch/inst/Zcmp/cm.push.yaml index 49cafa5ff4..6a7386124f 100644 --- a/arch/inst/Zcmp/cm.push.yaml +++ b/arch/inst/Zcmp/cm.push.yaml @@ -15,13 +15,7 @@ description: | * it must be a multiple of 16 (bytes): ** for RV32 the allowed values are: 16, 32, 48, 64, 80, 96, 112 ** for RV64 the allowed values are: 16, 32, 48, 64, 80, 96, 112, 128, 144, 160 -definedBy: - anyOf: - - Zcmp -excludedBy: - anyOf: - - allOf: [C, D] - - Zcd +definedBy: Zcmp assembly: reg_list, -stack_adj encoding: match: 10111000------10 @@ -42,47 +36,47 @@ operation(): | raise(ExceptionCode::IllegalInstruction, mode(), $encoding); } - XReg size = xlen(); + XReg size = xlen() / 8; XReg nreg = (rlist == 15) ? 13 : (rlist - 3); XReg stack_aligned_adj = (nreg * 4 + 15) & ~0xF; XReg virtual_address_sp = X[2]; XReg virtual_address_new_sp = virtual_address_sp - stack_aligned_adj - spimm; XReg virtual_address_base = virtual_address_sp - (nreg * size); - write_memory_xlen(virtual_address_base + 0*size, X[ 1], $encoding); + write_memory_xlen_aligned(virtual_address_base + 0*size, X[ 1], $encoding); if (nreg > 1) { - write_memory_xlen(virtual_address_base + 1*size, X[ 8], $encoding); + write_memory_xlen_aligned(virtual_address_base + 1*size, X[ 8], $encoding); } if (nreg > 2) { - write_memory_xlen(virtual_address_base + 2*size, X[ 9], $encoding); + write_memory_xlen_aligned(virtual_address_base + 2*size, X[ 9], $encoding); } if (nreg > 3) { - write_memory_xlen(virtual_address_base + 3*size, X[18], $encoding); + write_memory_xlen_aligned(virtual_address_base + 3*size, X[18], $encoding); } if (nreg > 4) { - write_memory_xlen(virtual_address_base + 4*size, X[19], $encoding); + write_memory_xlen_aligned(virtual_address_base + 4*size, X[19], $encoding); } if (nreg > 5) { - write_memory_xlen(virtual_address_base + 5*size, X[20], $encoding); + write_memory_xlen_aligned(virtual_address_base + 5*size, X[20], $encoding); } if (nreg > 6) { - write_memory_xlen(virtual_address_base + 6*size, X[21], $encoding); + write_memory_xlen_aligned(virtual_address_base + 6*size, X[21], $encoding); } if (nreg > 7) { - write_memory_xlen(virtual_address_base + 7*size, X[22], $encoding); + write_memory_xlen_aligned(virtual_address_base + 7*size, X[22], $encoding); } if (nreg > 8) { - write_memory_xlen(virtual_address_base + 8*size, X[23], $encoding); + write_memory_xlen_aligned(virtual_address_base + 8*size, X[23], $encoding); } if (nreg > 9) { - write_memory_xlen(virtual_address_base + 9*size, X[24], $encoding); + write_memory_xlen_aligned(virtual_address_base + 9*size, X[24], $encoding); } if (nreg > 10) { - write_memory_xlen(virtual_address_base + 10*size, X[25], $encoding); + write_memory_xlen_aligned(virtual_address_base + 10*size, X[25], $encoding); } if (nreg > 11) { - write_memory_xlen(virtual_address_base + 11*size, X[26], $encoding); - write_memory_xlen(virtual_address_base + 12*size, X[27], $encoding); + write_memory_xlen_aligned(virtual_address_base + 11*size, X[26], $encoding); + write_memory_xlen_aligned(virtual_address_base + 12*size, X[27], $encoding); } X[2] = virtual_address_new_sp; diff --git a/arch/inst/Zicsr/csrrc.yaml b/arch/inst/Zicsr/csrrc.yaml index 1246882309..3ab6aaf2b3 100644 --- a/arch/inst/Zicsr/csrrc.yaml +++ b/arch/inst/Zicsr/csrrc.yaml @@ -13,9 +13,9 @@ encoding: variables: - name: csr location: 31-20 - - name: rs1 + - name: xs1 location: 19-15 - - name: rd + - name: xd location: 11-7 access: s: always @@ -24,3 +24,16 @@ access: vu: always data_independent_timing: false operation(): | + Boolean will_write = xs1 != 0; + check_csr(csr, will_write, $encoding); + + XReg initial_csr_value = CSR[csr].sw_read(); + + if (xs1 != 0) { + # clear bits using the mask + # performing any WARL transformations first + XReg mask = X[xs1]; + CSR[csr].sw_write(initial_csr_value & ~mask); + } + + X[xd] = initial_csr_value; diff --git a/arch/inst/Zicsr/csrrci.yaml b/arch/inst/Zicsr/csrrci.yaml index 52f008dbe3..d45728539c 100644 --- a/arch/inst/Zicsr/csrrci.yaml +++ b/arch/inst/Zicsr/csrrci.yaml @@ -15,7 +15,7 @@ encoding: location: 31-20 - name: uimm location: 19-15 - - name: rd + - name: xd location: 11-7 access: s: always @@ -24,3 +24,16 @@ access: vu: always data_independent_timing: false operation(): | + Boolean will_write = uimm != 0; + check_csr(csr, will_write, $encoding); + + XReg initial_csr_value = CSR[csr].sw_read(); + + if (uimm != 0) { + # set bits using the mask + # performing any WARL transformations first + XReg mask = uimm; + CSR[csr].sw_write(initial_csr_value & ~mask); + } + + X[xd] = initial_csr_value; diff --git a/arch/inst/Zicsr/csrrs.yaml b/arch/inst/Zicsr/csrrs.yaml index 49595cdb70..9ee1f34d8f 100644 --- a/arch/inst/Zicsr/csrrs.yaml +++ b/arch/inst/Zicsr/csrrs.yaml @@ -30,14 +30,17 @@ access: vs: always vu: always operation(): | - # TODO: permission checks + Boolean will_write = rs1 != 0; + check_csr(csr, will_write, $encoding); XReg initial_csr_value = CSR[csr].sw_read(); - XReg mask = X[rs1]; - # set bits using the mask - # performing any WARL transformations first - CSR[csr].sw_write(initial_csr_value | mask); + if (will_write) { + # set bits using the mask + # performing any WARL transformations first + XReg mask = X[rs1]; + CSR[csr].sw_write(initial_csr_value | mask); + } X[rd] = initial_csr_value; diff --git a/arch/inst/Zicsr/csrrsi.yaml b/arch/inst/Zicsr/csrrsi.yaml index 54ca92ebdc..5543777132 100644 --- a/arch/inst/Zicsr/csrrsi.yaml +++ b/arch/inst/Zicsr/csrrsi.yaml @@ -15,7 +15,7 @@ encoding: location: 31-20 - name: uimm location: 19-15 - - name: rd + - name: xd location: 11-7 access: s: always @@ -24,3 +24,16 @@ access: vu: always data_independent_timing: false operation(): | + Boolean will_write = uimm != 0; + check_csr(csr, will_write, $encoding); + + XReg initial_csr_value = CSR[csr].sw_read(); + + if (will_write) { + # set bits using the mask + # performing any WARL transformations first + XReg mask = uimm; + CSR[csr].sw_write(initial_csr_value | mask); + } + + X[xd] = initial_csr_value; diff --git a/arch/inst/Zicsr/csrrw.yaml b/arch/inst/Zicsr/csrrw.yaml index 97a98b3433..d7479d80b1 100644 --- a/arch/inst/Zicsr/csrrw.yaml +++ b/arch/inst/Zicsr/csrrw.yaml @@ -19,9 +19,9 @@ encoding: variables: - name: csr location: 31-20 - - name: rs1 + - name: xs1 location: 19-15 - - name: rd + - name: xd location: 11-7 access: s: always @@ -29,13 +29,17 @@ access: vs: always vu: always operation(): | - if (rd != 0) { - X[rd] = CSR[csr].sw_read(); + check_csr(csr, true, $encoding); + + Bits initial_value = X[xs1]; + + if (xd != 0) { + X[xd] = CSR[csr].sw_read(); } - # writes the value in X[rs1] to the CSR, + # writes the value in X[xs1] to the CSR, # performing any WARL transformations first - CSR[csr].sw_write(X[rs1]); + CSR[csr].sw_write(initial_value); sail(): | { diff --git a/arch/inst/Zicsr/csrrwi.yaml b/arch/inst/Zicsr/csrrwi.yaml index 473236a8bc..f0c768347d 100644 --- a/arch/inst/Zicsr/csrrwi.yaml +++ b/arch/inst/Zicsr/csrrwi.yaml @@ -29,6 +29,8 @@ access: vs: always vu: always operation(): | + check_csr(csr, true, $encoding); + if (rd != 0) { X[rd] = CSR[csr].sw_read(); } diff --git a/arch/inst/Zimop/mop.r.n.yaml b/arch/inst/Zimop/mop.r.n.yaml index 6b7ef69541..cbca8d2dfb 100644 --- a/arch/inst/Zimop/mop.r.n.yaml +++ b/arch/inst/Zimop/mop.r.n.yaml @@ -27,6 +27,10 @@ access: vs: always vu: always data_independent_timing: false +hints: + - { $ref: inst/Zicfilp/sspopchk.x1.yaml# } + - { $ref: inst/Zicfilp/sspopchk.x5.yaml# } + - { $ref: inst/Zicfilp/ssrdp.yaml# } pseudoinstructions: - when: (mop_r_t_30 == 0x0) && (mop_r_t_21_20 == 0x0) && (mop_r_t_27_26 == 0x0) to: mop.r.0 diff --git a/arch/inst/Zimop/mop.rr.n.yaml b/arch/inst/Zimop/mop.rr.n.yaml index 01aeab425b..6c80c31b90 100644 --- a/arch/inst/Zimop/mop.rr.n.yaml +++ b/arch/inst/Zimop/mop.rr.n.yaml @@ -27,6 +27,9 @@ access: vs: always vu: always data_independent_timing: false +hints: + - { $ref: inst/Zicfilp/sspush.x1.yaml# } + - { $ref: inst/Zicfilp/sspush.x5.yaml# } pseudoinstructions: - when: (mop_rr_t_30 == 0x0) && (mop_rr_t_27_26 == 0x0) to: mop.rr.0 diff --git a/arch/inst/Zk/pack.yaml b/arch/inst/Zk/pack.yaml index a8b886764c..c54a9a24ac 100644 --- a/arch/inst/Zk/pack.yaml +++ b/arch/inst/Zk/pack.yaml @@ -24,4 +24,7 @@ access: vs: always vu: always data_independent_timing: true +pseudoinstructions: + - when: (rs2 == 0) && (xlen() == 32) + to: zext.h xd, xs1 operation(): | diff --git a/arch/isa/builtin_functions.idl b/arch/isa/builtin_functions.idl index 9c47a0cb48..6a11479338 100644 --- a/arch/isa/builtin_functions.idl +++ b/arch/isa/builtin_functions.idl @@ -1,6 +1,6 @@ %version: 1.0 -builtin function implemented? { +generated function implemented? { returns Boolean arguments ExtensionName extension description { @@ -8,6 +8,22 @@ builtin function implemented? { } } +generated function implemented_version? { + returns Boolean + arguments ExtensionName extension, String version_requirement + description { + Return true if the implementation supports `extension` meeting 'version_requirement'. + } +} + +generated function implemented_csr? { + returns Boolean + arguments Bits<12> csr_addr + description { + Return true if csr_addr is an implemented CSR + } +} + builtin function unpredictable { arguments String why description { @@ -176,33 +192,12 @@ builtin function abort_current_instruction { } } - -builtin function zext { - returns XReg - arguments XReg value, XReg first_extended_bit - description { - Zero extend `value` starting at `first_extended_bit`. - - Bits [`XLEN-1`:`first_extended_bit`] of the return value - should get the value `0`; - } -} - builtin function ebreak { description { Raise an `Environment Break` exception, returning control to the debug environment. } } -builtin function saturate { - returns XReg - arguments XReg value, XReg max_value - description { - If unsigned `value` is less than `max_value`, return `value`. - Otherwise, return `max_value`. - } -} - builtin function prefetch_instruction { arguments XReg virtual_address # virtual cache block address @@ -256,7 +251,7 @@ builtin function pause { } } -builtin function pow { +generated function pow { returns XReg arguments XReg value, XReg exponent description { @@ -264,8 +259,7 @@ builtin function pow { } } - -builtin function maybe_cache_translation { +generated function maybe_cache_translation { arguments XReg vaddr, MemoryOperation op, @@ -276,19 +270,17 @@ builtin function maybe_cache_translation { } } -builtin function cached_translation { +generated function cached_translation { returns - Boolean, # whether or not the cached translation is valid - TranslationResult # cached result + CachedTranslationResult # cached result arguments XReg vaddr, # virtual address MemoryOperation op # operation description { Possibly returns a cached translation result matching +vaddr+. - If the result is value, the first return value will be true. - +paddr+ is the full translation of +vaddr+, including the page offset - +pbmt+ is the result of any paged-based attributes for the translation + CachedTranslationResult contains a Boolean 'valid' field. If valid, + 'result' is a usable translation. Otherwise, the cache lookup failed. } } @@ -320,7 +312,7 @@ builtin function order_pgtbl_reads_after_vmafence { } } -builtin function invalidate_translations { +generated function invalidate_translations { arguments VmaOrderType inval_type description { @@ -331,21 +323,107 @@ builtin function invalidate_translations { } } -builtin function read_physical_memory { +function read_physical_memory { template U32 len returns Bits arguments XReg paddr description { Read from physical memory. } + body { + if (len == 8) { + return read_physical_memory_8(paddr); + } else if (len == 16) { + return read_physical_memory_16(paddr); + } else if (len == 32) { + return read_physical_memory_32(paddr); + } else if (len == 64) { + return read_physical_memory_64(paddr); + } else { + assert(false, "Invalid len"); + } + } } -builtin function write_physical_memory { +builtin function read_physical_memory_8 { + returns Bits<8> + arguments XReg paddr + description { + Read a byte from physical memory. + } +} + +builtin function read_physical_memory_16 { + returns Bits<16> + arguments XReg paddr + description { + Read two bytes from physical memory. + } +} + +builtin function read_physical_memory_32 { + returns Bits<32> + arguments XReg paddr + description { + Read four bytes from physical memory. + } +} + +builtin function read_physical_memory_64 { + returns Bits<64> + arguments XReg paddr + description { + Read eight bytes from physical memory. + } +} + +function write_physical_memory { template U32 len arguments XReg paddr, Bits value description { Write to physical memory. } + body { + if (len == 8) { + write_physical_memory_8(paddr, value); + } else if (len == 16) { + write_physical_memory_16(paddr, value); + } else if (len == 32) { + write_physical_memory_32(paddr, value); + } else if (len == 64) { + write_physical_memory_64(paddr, value); + } else { + assert(false, "Invalid len"); + } + } +} + +builtin function write_physical_memory_8 { + arguments XReg paddr, Bits<8> value + description { + Write a byte to physical memory. + } +} + +builtin function write_physical_memory_16 { + arguments XReg paddr, Bits<16> value + description { + Write two bytes to physical memory. + } +} + +builtin function write_physical_memory_32 { + arguments XReg paddr, Bits<32> value + description { + Write four bytes to physical memory. + } +} + +builtin function write_physical_memory_64 { + arguments XReg paddr, Bits<64> value + description { + Write eight bytes to physical memory. + } } builtin function wfi { diff --git a/arch/isa/fetch.idl b/arch/isa/fetch.idl new file mode 100644 index 0000000000..4a1a42f57a --- /dev/null +++ b/arch/isa/fetch.idl @@ -0,0 +1,71 @@ +%version: 1.0 + +function fetch_memory_aligned_16 { + returns Bits<16> + arguments + XReg virtual_address + description { + Fetch 16 bits from virtual memory using a known aligned address. + } + body { + TranslationResult result; + + if (CSR[misa].S == 1) { + result = translate(virtual_address, MemoryOperation::Fetch, mode(), virtual_address); + } else { + result.paddr = virtual_address; + } + + # may raise an exception + access_check(result.paddr, 16, virtual_address, MemoryOperation::Fetch, ExceptionCode::InstructionAccessFault, mode()); + + return read_physical_memory<16>(result.paddr); + } +} + +function fetch_memory_aligned_32 { + returns Bits<32> + arguments + XReg virtual_address + description { + Fetch 32 bits from virtual memory using a known aligned address. + } + body { + TranslationResult result; + + if (CSR[misa].S == 1) { + result = translate(virtual_address, MemoryOperation::Fetch, mode(), virtual_address); + } else { + result.paddr = virtual_address; + } + + # may raise an exception + access_check(result.paddr, 32, virtual_address, MemoryOperation::Fetch, ExceptionCode::InstructionAccessFault, mode()); + + return read_physical_memory<32>(result.paddr); + } +} + +fetch { + Bits enc = 0; + + if (pending_and_enabled_interrupts != 0) { + # this will adjust CSRs and set the PC to the interrupt handler + take_interrupt(); + } + + if (ialign() == 16) { + # must fetch 16 bits at a time + enc = fetch_memory_aligned_16($pc); + if (enc[1:0] == 2'b11) { + Bits<32> upper = fetch_memory_aligned_16($pc + 2); + enc = (upper << 16) | enc; + } + } else if (ialign() == 32) { + enc = fetch_memory_aligned_32($pc); + } else { + assert(ialign() == 16 || ialign() == 32, "Unsupported IALIGN"); + } + + return enc; +} diff --git a/arch/isa/fp.idl b/arch/isa/fp.idl index e49ce20585..335c73ef2f 100644 --- a/arch/isa/fp.idl +++ b/arch/isa/fp.idl @@ -86,7 +86,7 @@ function rm_to_mode { } else if (rm == $bits(RoundingMode::RMM)) { return RoundingMode::RMM; } else if (rm == $bits(RoundingMode::DYN)) { - return CSR[fcsr].FRM; + return $enum(RoundingMode, CSR[fcsr].FRM); } else { raise(ExceptionCode::IllegalInstruction, mode(), encoding); } @@ -412,7 +412,7 @@ function softfloat_normSubnormalF16Sig { } body { Bits<8> shift_dist = count_leading_zeros<16>(hp_value); - return {1 - shift_dist, hp_value << shift_dist}; + return 1 - shift_dist, hp_value << shift_dist; } } @@ -593,25 +593,11 @@ function softfloat_propagateNaNF32UI { Boolean isSigNaN_a = is_sp_signaling_nan?(a); Boolean isSigNaN_b = is_sp_signaling_nan?(b); - # get non Signalling versions of a and b - U32 nonsig_a = returnNonSignalingNaN(a); - U32 nonsig_b = returnNonSignalingNaN(b); - if (isSigNaN_a || isSigNaN_b) { # raise invalid flag if either number is NaN set_fp_flag(FpFlag::NV); - if ( isSigNaN_a ) { - if ( isSigNaN_b ) { - # if both numbers are NaN return larger magnitude and remove NaN signaling - return returnLargerMag(a, b); - } - # if b is NaN return non signaling value of b - return is_sp_nan?(b) ? nonsig_b : nonsig_a; - } else { - return is_sp_nan?(a) ? nonsig_a : nonsig_b; - } } - + return SP_CANONICAL_NAN; } } diff --git a/arch/isa/globals.isa b/arch/isa/globals.isa index c5aab80051..c208dedec7 100644 --- a/arch/isa/globals.isa +++ b/arch/isa/globals.isa @@ -1,6 +1,8 @@ %version: 1.0 include "builtin_functions.idl" +include "interrupts.idl" +include "fetch.idl" include "util.idl" include "fp.idl" @@ -104,13 +106,10 @@ enum CsrFieldType { } # generated from extension information in arch definition -builtin enum ExtensionName; +generated enum ExtensionName; # generated from extension information in arch definition -builtin enum InterruptCode; - -# generated from extension information in arch definition -builtin enum ExceptionCode; +generated enum ExceptionCode; # XLEN encoding, as defined in CSR[mstatus].mxl, etc. enum XRegWidth { @@ -181,6 +180,11 @@ struct TranslationResult { PteFlags pte_flags; } +struct CachedTranslationResult { + Boolean valid; + TranslationResult result; +} + # options associated with a translation (TLB) invalidation struct VmaOrderType { Boolean global; # include global mappings? @@ -198,7 +202,6 @@ struct VmaOrderType { XReg gpaddr; # when single_gpaddr is true, the guest physical address to order } - bitfield (64) Sv39PageTableEntry { N 63 PBMT 62-61 @@ -237,6 +240,19 @@ function mode { } +function set_mode_no_refresh { + arguments PrivilegeMode new_mode + description { + Set the current privilege mode to `new_mode`, but don't refresh interrupts + } + body { + if (new_mode != current_mode) { + notify_mode_change(new_mode, current_mode); + current_mode = new_mode; + } + } +} + function set_mode { arguments PrivilegeMode new_mode description { @@ -246,6 +262,7 @@ function set_mode { if (new_mode != current_mode) { notify_mode_change(new_mode, current_mode); current_mode = new_mode; + refresh_pending_interrupts(); } } } @@ -283,6 +300,43 @@ function exception_handling_mode { } } +function creg2reg { + returns Bits<5> # X register index + arguments Bits<3> creg_idx + description { + Maps a C register index (_e.g._, `rs1'` in the specification) to an X register index. From the + specification: + + .Registers specified by the three-bit _rs1_′, _rs2_′, and _rd_′ fields of the CIW, CL, CS, CA, and CB formats. + //[cols="20%,10%,10%,10%,10%,10%,10%,10%,10%"] + [float="center",align="center",cols="1a, 1a",frame="none",grid="none"] + |=== + | + [%autowidth,cols="<",frame="none",grid="none",options="noheader"] + !=== + !RVC Register Number + !Integer Register Number + !Integer Register ABI Name + !Floating-Point Register Number + !Floating-Point Register ABI Name + !=== + | + + [%autowidth,cols="^,^,^,^,^,^,^,^",options="noheader"] + !=== + !`000` !`001` !`010` !`011` !`100` !`101` !`110` !`111` + !`x8` !`x9` !`x10` !`x11` !`x12` !`x13` !`x14`!`x15` + !`s0` !`s1` !`a0` !`a1` !`a2` !`a3` !`a4`!`a5` + !`f8` !`f9` !`f10` !`f11` !`f12` !`f13`!`f14` !`f15` + !`fs0` !`fs1` !`fa0` !`fa1` !`fa2`!`fa3` !`fa4` !`fa5` + !=== + |=== + } + body { + return {2'b01, creg_idx}; + } +} + function unimplemented_csr { arguments Bits encoding description { @@ -461,6 +515,78 @@ function stval_for { } } +function csr? { + returns Boolean + arguments Bits<12> csr_addr + description { + Returns true if csr_addr is an implemented csr, and the defining extension is not disabled + } + body { + if (!implemented_csr?(csr_addr)) { + return false; + } + + if (implemented?(ExtensionName::S) + && !CSR[csr_addr].implemented_without?(ExtensionName::S)) { + + return CSR[misa].S == 1'b1; + + } else if (implemented?(ExtensionName::U) + && !CSR[csr_addr].implemented_without?(ExtensionName::U)) { + + return CSR[misa].U == 1'b1; + + } else if (implemented?(ExtensionName::H) + && !CSR[csr_addr].implemented_without?(ExtensionName::H)) { + + return CSR[misa].H == 1'b1; + } + + return true; + } +} + +function check_csr { + arguments Bits<12> csr_addr, Boolean for_write, Bits encoding + description { + Checks if 'csr_addr' is a valid address, can be read in the current mode, + and, if for_write is true, can be written in the current mode. + + If the check fails, will either raise IllegalInsruction or cause + unpredictable behavior, depending on TRAP_ON_UNIMPLEMENTED_CSR + } + body { + if (!csr?(csr_addr)) { + if (TRAP_ON_UNIMPLEMENTED_CSR) { + raise(ExceptionCode::IllegalInstruction, mode(), encoding); + } else { + unpredictable("Attempt to read unimplemented CSR"); + } + } + PrivilegeMode priv_mode; + if (csr_addr[9:8] == 2'b00) { + priv_mode = PrivilegeMode::M; + } else if (csr_addr[9:8] == 2'b01 || csr_addr[9:8] == 2'b10) { + priv_mode = PrivilegeMode::S; + } else { + priv_mode = PrivilegeMode::U; + } + if (priv_mode == PrivilegeMode::M) { + if (mode() != PrivilegeMode::M) { + raise(ExceptionCode::IllegalInstruction, mode(), encoding); + } + } else if (priv_mode == PrivilegeMode::S) { + if (mode() == PrivilegeMode::U || mode() == PrivilegeMode::VU) { + raise(ExceptionCode::IllegalInstruction, mode(), encoding); + } + } + + if (for_write && csr_addr[11:10] == 2'b11) { + # write to read-only CSR + raise(ExceptionCode::IllegalInstruction, mode(), encoding); + } + } +} function vstval_for { returns XReg @@ -704,7 +830,7 @@ function jump { if ((ialign() == 16) && ((target_addr & 0x1) != 0)) { # the target PC is not halfword-aligned raise(ExceptionCode::InstructionAddressMisaligned, mode(), target_addr); - } else if ((target_addr & 0x3) != 0) { + } else if ((ialign() == 32) && (target_addr & 0x3) != 0) { # the target PC is not word-aligned raise(ExceptionCode::InstructionAddressMisaligned, mode(), target_addr); } @@ -882,9 +1008,8 @@ enum PmpMatchResult { } function pmp_match_64 { - template U32 access_size returns PmpMatchResult, PmpCfg - arguments Bits paddr + arguments Bits paddr, U32 access_size description { Given a physical address, see if any PMP entry matches. @@ -913,9 +1038,9 @@ function pmp_match_64 { range_lo = 0; } else { # otherwise, it's the address in the next lowest pmpaddr register - range_lo = (CSR[pmpaddr_idx - 1] << 2)[PHYS_ADDR_WIDTH-1:0]; + range_lo = ($bits(CSR[pmpaddr_idx - 1]) << 2)[PHYS_ADDR_WIDTH-1:0]; } - range_hi = (CSR[pmpaddr_idx] << 2)[PHYS_ADDR_WIDTH-1:0]; + range_hi = ($bits(CSR[pmpaddr_idx]) << 2)[PHYS_ADDR_WIDTH-1:0]; } else if (cfg.A == $bits(PmpCfg_A::NAPOT)) { # Example pmpaddr: 0b00010101111 @@ -931,7 +1056,7 @@ function pmp_match_64 { range_hi = ((pmpaddr_value & ~mask) + len) << 2; } else if (cfg.A == $bits(PmpCfg_A::NA4)) { - range_lo = (CSR[pmpaddr_idx] << 2)[PHYS_ADDR_WIDTH-1:0]; + range_lo = ($bits(CSR[pmpaddr_idx]) << 2)[PHYS_ADDR_WIDTH-1:0]; range_hi = range_lo + 4; } @@ -950,9 +1075,8 @@ function pmp_match_64 { } function pmp_match_32 { - template U32 access_size returns PmpMatchResult, PmpCfg - arguments Bits paddr + arguments Bits paddr, U32 access_size description { Given a physical address, see if any PMP entry matches. @@ -981,9 +1105,9 @@ function pmp_match_32 { range_lo = 0; } else { # otherwise, it's the address in the next lowest pmpaddr register - range_lo = (CSR[pmpaddr_idx - 1] << 2)[PHYS_ADDR_WIDTH-1:0]; + range_lo = ($bits(CSR[pmpaddr_idx - 1]) << 2)[PHYS_ADDR_WIDTH-1:0]; } - range_hi = (CSR[pmpaddr_idx] << 2)[PHYS_ADDR_WIDTH-1:0]; + range_hi = ($bits(CSR[pmpaddr_idx]) << 2)[PHYS_ADDR_WIDTH-1:0]; } else if (cfg.A == $bits(PmpCfg_A::NAPOT)) { # Example pmpaddr: 0b00010101111 @@ -1018,9 +1142,8 @@ function pmp_match_32 { } function pmp_match { - template U32 access_size returns PmpMatchResult, PmpCfg - arguments Bits paddr + arguments Bits paddr, U32 access_size description { Given a physical address, see if any PMP entry matches. @@ -1029,9 +1152,9 @@ function pmp_match { } body { if (XLEN == 64) { - return pmp_match_64(paddr); + return pmp_match_64(paddr, access_size); } else { - return pmp_match_32(paddr); + return pmp_match_32(paddr, access_size); } } } @@ -1085,9 +1208,8 @@ function effective_ldst_mode { function pmp_check { - template U32 access_size returns Boolean - arguments Bits paddr, MemoryOperation type + arguments Bits paddr, U32 access_size, MemoryOperation type description { Given a physical address and operation type, return whether or not the access is allowed by PMP. } @@ -1096,7 +1218,7 @@ function pmp_check { PmpMatchResult match_result; PmpCfg cfg; - (match_result, cfg) = pmp_match(paddr); + (match_result, cfg) = pmp_match(paddr, access_size); if (match_result == PmpMatchResult::FullMatch) { if (mode == PrivilegeMode::M && (cfg.L == 0)) { @@ -1151,8 +1273,10 @@ function access_check { } # check PMP - if (!pmp_check(paddr[PHYS_ADDR_WIDTH-1:0], type)) { - raise(fault_type, from_mode, vaddr); + if (implemented?(ExtensionName::Smpmp)) { + if (!pmp_check(paddr[PHYS_ADDR_WIDTH-1:0], access_size, type)) { + raise(fault_type, from_mode, vaddr); + } } } } @@ -1287,77 +1411,78 @@ function current_translation_mode { return SatpMode::Reserved; } } - } + } else if (CSR[misa].S == 1'b1) { - # if we reach here, then the effective mode is S or U - assert(effective_mode == PrivilegeMode::S || effective_mode == PrivilegeMode::U, "unexpected priv mode"); - Bits<4> mode_val = CSR[vsatp].MODE; - if (mode_val == $bits(SatpMode::Sv32)) { - # Sv32 is only defined when XLEN == 32 - if (XLEN == 64) { - if (effective_mode == PrivilegeMode::S && CSR[mstatus].SXL != $bits(XRegWidth::XLEN32)) { + # if we reach here, then the effective mode is S or U + assert(effective_mode == PrivilegeMode::S || effective_mode == PrivilegeMode::U, "unexpected priv mode"); + Bits<4> mode_val = CSR[satp].MODE; + if (mode_val == $bits(SatpMode::Sv32)) { + # Sv32 is only defined when XLEN == 32 + if (XLEN == 64) { + if (effective_mode == PrivilegeMode::S && CSR[mstatus].SXL != $bits(XRegWidth::XLEN32)) { + # not supported in this XLEN + return SatpMode::Reserved; + } + if (effective_mode == PrivilegeMode::U && CSR[sstatus].UXL != $bits(XRegWidth::XLEN32)) { + # not supported in this XLEN + return SatpMode::Reserved; + } + } + if (!implemented?(ExtensionName::Sv32)) { + # not supported in this configuration + return SatpMode::Reserved; + } + } else if ((XLEN == 64) && (mode_val == $bits(SatpMode::Sv39))) { + # Sv39 is only defined when XLEN == 64 + if (effective_mode == PrivilegeMode::S && CSR[mstatus].SXL != $bits(XRegWidth::XLEN64)) { # not supported in this XLEN return SatpMode::Reserved; } - if (effective_mode == PrivilegeMode::U && CSR[sstatus].UXL != $bits(XRegWidth::XLEN32)) { + if (effective_mode == PrivilegeMode::U && CSR[sstatus].UXL != $bits(XRegWidth::XLEN64)) { # not supported in this XLEN return SatpMode::Reserved; } - } - if (!implemented?(ExtensionName::Sv32)) { - # not supported in this configuration - return SatpMode::Reserved; - } - } else if ((XLEN == 64) && (mode_val == $bits(SatpMode::Sv39))) { - # Sv39 is only defined when XLEN == 64 - if (effective_mode == PrivilegeMode::S && CSR[mstatus].SXL != $bits(XRegWidth::XLEN64)) { - # not supported in this XLEN - return SatpMode::Reserved; - } - if (effective_mode == PrivilegeMode::U && CSR[sstatus].UXL != $bits(XRegWidth::XLEN64)) { - # not supported in this XLEN - return SatpMode::Reserved; - } - if (!implemented?(ExtensionName::Sv39)) { - # not supported in this configuration - return SatpMode::Reserved; - } - # OK - return SatpMode::Sv39; - } else if ((XLEN == 64) && (mode_val == $bits(SatpMode::Sv48))) { - # Sv48 is only defined when XLEN == 64 - if (effective_mode == PrivilegeMode::S && CSR[mstatus].SXL != $bits(XRegWidth::XLEN64)) { - # not supported in this XLEN - return SatpMode::Reserved; - } - if (effective_mode == PrivilegeMode::U && CSR[sstatus].UXL != $bits(XRegWidth::XLEN64)) { - # not supported in this XLEN - return SatpMode::Reserved; - } - if (!implemented?(ExtensionName::Sv48)) { - # not supported in this configuration - return SatpMode::Reserved; - } - # OK - return SatpMode::Sv48; - } else if ((XLEN == 64) && (mode_val == $bits(SatpMode::Sv57))) { - # Sv57 is only defined when XLEN == 64 - if (effective_mode == PrivilegeMode::S && CSR[mstatus].SXL != $bits(XRegWidth::XLEN64)) { - # not supported in this XLEN - return SatpMode::Reserved; - } - if (effective_mode == PrivilegeMode::U && CSR[sstatus].UXL != $bits(XRegWidth::XLEN64)) { - # not supported in this XLEN - return SatpMode::Reserved; - } - if (!implemented?(ExtensionName::Sv57)) { - # not supported in this configuration + if (!implemented?(ExtensionName::Sv39)) { + # not supported in this configuration + return SatpMode::Reserved; + } + # OK + return SatpMode::Sv39; + } else if ((XLEN == 64) && (mode_val == $bits(SatpMode::Sv48))) { + # Sv48 is only defined when XLEN == 64 + if (effective_mode == PrivilegeMode::S && CSR[mstatus].SXL != $bits(XRegWidth::XLEN64)) { + # not supported in this XLEN + return SatpMode::Reserved; + } + if (effective_mode == PrivilegeMode::U && CSR[sstatus].UXL != $bits(XRegWidth::XLEN64)) { + # not supported in this XLEN + return SatpMode::Reserved; + } + if (!implemented?(ExtensionName::Sv48)) { + # not supported in this configuration + return SatpMode::Reserved; + } + # OK + return SatpMode::Sv48; + } else if ((XLEN == 64) && (mode_val == $bits(SatpMode::Sv57))) { + # Sv57 is only defined when XLEN == 64 + if (effective_mode == PrivilegeMode::S && CSR[mstatus].SXL != $bits(XRegWidth::XLEN64)) { + # not supported in this XLEN + return SatpMode::Reserved; + } + if (effective_mode == PrivilegeMode::U && CSR[sstatus].UXL != $bits(XRegWidth::XLEN64)) { + # not supported in this XLEN + return SatpMode::Reserved; + } + if (!implemented?(ExtensionName::Sv57)) { + # not supported in this configuration + return SatpMode::Reserved; + } + # OK + return SatpMode::Sv57; + } else { return SatpMode::Reserved; } - # OK - return SatpMode::Sv57; - } else { - return SatpMode::Reserved; } } } @@ -1369,7 +1494,7 @@ function current_gstage_translation_mode { from VS-mode or VU-mode. } body { - return CSR[hgatp].MODE; + return $enum(HgatpMode, CSR[hgatp].MODE); } } @@ -1832,7 +1957,7 @@ function gstage_page_walk { # successful translation and update result.paddr = pte_paddr; if (PTESIZE >= 64) { - result.pbmt = pte[62:61]; + result.pbmt = $enum(Pbmt, pte[62:61]); } result.pte_flags = pte_flags; return result; @@ -2151,7 +2276,7 @@ function stage1_page_walk { encoding ); result.paddr = pte_phys.paddr; - result.pbmt = pte_phys.pbmt == 0 ? pte[62:61] : pte_phys.pbmt; + result.pbmt = pte_phys.pbmt == Pbmt::PMA ? $enum(Pbmt, pte[62:61]) : pte_phys.pbmt; result.pte_flags = pte_flags; return result; } @@ -2232,13 +2357,13 @@ function translate { ##################################################################### Boolean cached_translation_valid; - TranslationResult cached_translation_result; + CachedTranslationResult cached_translation_result; - (cached_translation_valid, cached_translation_result) = + cached_translation_result = cached_translation(vaddr, op); - if (cached_translation_valid) { - return cached_translation_result; + if (cached_translation_result.valid) { + return cached_translation_result.result; } ##################################################################### @@ -2249,7 +2374,8 @@ function translate { if (effective_mode == PrivilegeMode::M) { # there is no translation in M-mode - return vaddr; + result.paddr = vaddr; + return result; } SatpMode translation_mode = @@ -2305,9 +2431,9 @@ function canonical_vaddr? { # canonical depends on the virtual address size in the current translation mode SatpMode satp_mode; if (virtual_mode?()) { - satp_mode = CSR[vsatp].MODE; + satp_mode = $enum(SatpMode, CSR[vsatp].MODE); } else { - satp_mode = CSR[satp].MODE; + satp_mode = $enum(SatpMode, CSR[satp].MODE); } # calculate the effective address after pointer masking @@ -2337,7 +2463,7 @@ function canonical_gpaddr? { body { # canonical depends on the virtual address size in the current translation mode - SatpMode satp_mode = CSR[satp].MODE; + SatpMode satp_mode = $enum(SatpMode, CSR[satp].MODE); if (satp_mode == SatpMode::Bare) { return true; @@ -2393,7 +2519,7 @@ function read_memory_aligned { returns Bits arguments XReg virtual_address, - Bits encoding # the encoding of an instruction causing this access, or 0 if a fetch + Bits encoding # the encoding of an instruction causing this access description { Read from virtual memory using a known aligned address. } @@ -2466,7 +2592,7 @@ function read_memory { # misaligned, must break into multiple reads if (MISALIGNED_SPLIT_STRATEGY == "by_byte") { Bits result = 0; - for (U32 i = 0; i <= LEN; i++) { + for (U32 i = 0; i <= (LEN/8); i++) { result = result | (read_memory_aligned<8>(virtual_address + i, encoding) << (8*i)); } return result; @@ -2478,12 +2604,46 @@ function read_memory { } function read_memory_xlen { + returns Bits + arguments + XReg virtual_address, + Bits encoding + description { + Read XLEN bits from memory + } + body { + if (xlen() == 32) { + return read_memory<32>(virtual_address, encoding); + } else { + return read_memory<64>(virtual_address, encoding); + } + } +} + +function write_memory_xlen { + arguments + XReg virtual_address, + Bits value, + Bits encoding + description { + Read XLEN bits from memory + } + body { + if (xlen() == 32) { + return write_memory<32>(virtual_address, value, encoding); + } else { + return write_memory<64>(virtual_address, value, encoding); + } + } +} + +function read_memory_xlen_aligned { returns Bits arguments XReg virtual_address, Bits encoding # the encoding of an instruction causing this access, or 0 if a fetch description { - Read from virtual memory XLEN bits using a known aligned address. + Read from virtual memory XLEN (which may be runtime-determined) bits using a known aligned address. } body { TranslationResult result; @@ -2495,9 +2655,13 @@ function read_memory_xlen { } # may raise an exception - access_check(result.paddr, XLEN, virtual_address, MemoryOperation::Read, ExceptionCode::LoadAccessFault, effective_ldst_mode()); + access_check(result.paddr, xlen(), virtual_address, MemoryOperation::Read, ExceptionCode::LoadAccessFault, effective_ldst_mode()); - return read_physical_memory(result.paddr); + if (xlen() == 32) { + return read_physical_memory<32>(result.paddr); + } else { + return read_physical_memory<64>(result.paddr); + } } } @@ -2777,6 +2941,7 @@ function write_memory { if (aligned) { write_memory_aligned(virtual_address, value, encoding); + return; } # access isn't naturally aligned, but it still might be atomic if this hart supports @@ -2794,6 +2959,7 @@ function write_memory { if (misaligned_is_atomic?(physical_address)) { access_check(physical_address, LEN, virtual_address, MemoryOperation::Write, ExceptionCode::StoreAmoAccessFault, effective_ldst_mode()); write_physical_memory(physical_address, value); + return; } } @@ -2812,7 +2978,7 @@ function write_memory { } else { # misaligned, must break into multiple reads if (MISALIGNED_SPLIT_STRATEGY == "by_byte") { - for (U32 i = 0; i <= LEN; i++) { + for (U32 i = 0; i <= (LEN/8); i++) { write_memory_aligned<8>(virtual_address + i, (value >> (8*i))[7:0], encoding); } } else if (MISALIGNED_SPLIT_STRATEGY == "custom") { @@ -2822,13 +2988,13 @@ function write_memory { } } -function write_memory_xlen { +function write_memory_xlen_aligned { arguments XReg virtual_address, Bits value, Bits encoding # encoding of the instruction causing this access description { - Write to virtual memory XLEN bits using a known aligned address. + Write to virtual memory XLEN bits (which may be runtime determined) using a known aligned address. } body { XReg physical_address; @@ -2838,9 +3004,13 @@ function write_memory_xlen { : virtual_address; # may raise an exception - access_check(physical_address, XLEN, virtual_address, MemoryOperation::Write, ExceptionCode::StoreAmoAccessFault, effective_ldst_mode()); + access_check(physical_address, xlen(), virtual_address, MemoryOperation::Write, ExceptionCode::StoreAmoAccessFault, effective_ldst_mode()); - write_physical_memory(physical_address, value); + if (xlen() == 32) { + write_physical_memory<32>(physical_address, value); + } else { + write_physical_memory<64>(physical_address, value); + } } } diff --git a/arch/isa/interrupts.idl b/arch/isa/interrupts.idl new file mode 100644 index 0000000000..ae55420eac --- /dev/null +++ b/arch/isa/interrupts.idl @@ -0,0 +1,386 @@ +%version: 1.0 + +# generated from extension information in arch definition +generated enum InterruptCode; + +# the "hidden" part of mip.SEIP +Boolean pending_smode_external_interrupt = false; + +# the "hidden" part of hip.VSTIP +Boolean pending_vsmode_timer_interrupt = false; + +# bitmask of interrupts that are both pending and enabled +# updated by calls to refresh_pending_interrupts() +Bits pending_and_enabled_interrupts = 0; + +external function set_external_interrupt { + arguments PrivilegeMode target_mode + description { + Set an external interrupt targeting target_mode + } + body { + if (target_mode == PrivilegeMode::M) { + CSR[mip].MEIP = 1'b1; + } else if ((CSR[misa].S == 1'b1) && (target_mode == PrivilegeMode::S)) { + # set the "hidden" bit, not the software-writeable bit + pending_smode_external_interrupt = true; + } else if ((CSR[misa].H == 1'b1) && (target_mode == PrivilegeMode::VS)) { + CSR[mip].VSEIP = 1'b1; + } else { + assert(false, "Invalid target_mode"); + } + + refresh_pending_interrupts(); + } +} + +external function clear_external_interrupt { + arguments PrivilegeMode target_mode + description { + Clear an external interrupt targeting target_mode + } + body { + if (target_mode == PrivilegeMode::M) { + CSR[mip].MEIP = 1'b0; + } else if ((CSR[misa].S == 1'b1) && (target_mode == PrivilegeMode::S)) { + # clear the "hidden" bit, not the software-writeable bit + pending_smode_external_interrupt = false; + } else if ((CSR[misa].H == 1'b1) && (target_mode == PrivilegeMode::VS)) { + CSR[mip].VSEIP = 1'b0; + } else { + assert(false, "Invalid target_mode"); + } + + refresh_pending_interrupts(); + } +} + +external function set_software_interrupt { + arguments PrivilegeMode target_mode + description { + Set a software interrupt targeting target_mode + } + body { + if (target_mode == PrivilegeMode::M) { + CSR[mip].MSIP = 1'b1; + } else if ((CSR[misa].S == 1'b1) && (target_mode == PrivilegeMode::S)) { + # set the "hidden" bit, not the software-writeable bit + CSR[mip].SSIP = 1'b1; + # } else if ((CSR[misa].H == 1'b1) && (target_mode == PrivilegeMode::VS)) { + # CSR[hvip].VSSIP = 1'b1; + } else { + assert(false, "Invalid target_mode"); + } + + refresh_pending_interrupts(); + } +} + +external function clear_software_interrupt { + arguments PrivilegeMode target_mode + description { + Clear a software interrupt targeting target_mode + } + body { + if (target_mode == PrivilegeMode::M) { + CSR[mip].MSIP = 1'b0; + } else if ((CSR[misa].S == 1'b1) && (target_mode == PrivilegeMode::S)) { + # set the "hidden" bit, not the software-writeable bit + CSR[mip].SSIP = 1'b0; + # } else if ((CSR[misa].H == 1'b1) && (target_mode == PrivilegeMode::VS)) { + # CSR[hvip].VSSIP = 1'b0; + } else { + assert(false, "Invalid target_mode"); + } + + refresh_pending_interrupts(); + } +} + +external function set_timer_interrupt { + arguments PrivilegeMode target_mode + description { + Set a timer interrupt from the platform targeting target_mode + } + body { + if (target_mode == PrivilegeMode::M) { + CSR[mip].MTIP = 1'b1; + } else if ((CSR[misa].S == 1'b1) && (target_mode == PrivilegeMode::S)) { + # set the "hidden" bit, not the software-writeable bit + CSR[mip].STIP = 1'b1; + } else if ((CSR[misa].H == 1'b1) && (target_mode == PrivilegeMode::VS)) { + pending_vsmode_timer_interrupt = true; + } else { + assert(false, "Invalid target_mode"); + } + + refresh_pending_interrupts(); + } +} + +external function clear_timer_interrupt { + arguments PrivilegeMode target_mode + description { + Set a timer interrupt from the platform targeting target_mode + } + body { + if (target_mode == PrivilegeMode::M) { + CSR[mip].MTIP = 1'b0; + } else if ((CSR[misa].S == 1'b1) && (target_mode == PrivilegeMode::S)) { + # set the "hidden" bit, not the software-writeable bit + CSR[mip].STIP = 1'b0; + } else if ((CSR[misa].H == 1'b1) && (target_mode == PrivilegeMode::VS)) { + pending_vsmode_timer_interrupt = false; + } else { + assert(false, "Invalid target_mode"); + } + + refresh_pending_interrupts(); + } +} + +function refresh_pending_interrupts { + description { + refreshes the calculation of a pending interrupt + + needs to be called after any state update that could change a pending interrupt. This includes: + - CSR[mip] + - CSR[mie] + - CSR[mstatus].MIE + - CSR[mstatus].SIE + - CSR[vsstatus].SIE + - CSR[mideleg] + - CSR[sideleg] + - CSR[hideleg] + - CSR[hvip] + - CSR[hgeip] + - CSR[hgeie] + - mode changes + } + body { + # by using sw_read, we'll get, e.g., the combined value of mip.SEIP + Bits pending_ints = CSR[mip].sw_read() & $bits(CSR[mie]); + if (pending_ints == 0) { + # there are no pending interrupts + pending_and_enabled_interrupts = 0; + return; + } + + Boolean HAS_MIDELEG = + implemented_version?(ExtensionName::S, "<= 1.9.1") || + (implemented_version?(ExtensionName::S, "> 1.9.1") && implemented_version?(ExtensionName::Sm, "> 1.9.1")); + + Bits mmode_enabled_ints = + ((mode() == PrivilegeMode::M) && (CSR[mstatus].MIE == 1'b0)) + ? 0 + : ($bits(CSR[mie]) & (HAS_MIDELEG ? ~$bits(CSR[mideleg]) : ~XLEN'0)); + Bits mmode_pending_and_enabled = pending_ints & mmode_enabled_ints; + if (mmode_pending_and_enabled != 0) { + pending_and_enabled_interrupts = mmode_pending_and_enabled; + return; + } + + if (CSR[misa].S == 1'b1) { + Bits smode_enabled_ints = + ((mode() == PrivilegeMode::M) || (CSR[mstatus].SIE == 1'b0)) + ? 0 + : $bits(CSR[mie]) & ($bits(CSR[mideleg])); + Bits smode_pending_and_enabled = pending_ints & smode_enabled_ints; + if (smode_pending_and_enabled != 0) { + pending_and_enabled_interrupts = smode_pending_and_enabled; + return; + } + } + + # if (CSR[misa].H == 1'b1) { + # Boolean sgei_pending_and_enabled = ($bits(CSR[hgeip]) & $bits(CSR[heie])) != 0; + # if (sgei_pending_and_enabled) { + + # } + # Bits vsmode_enabled_ints = + # ((mode() == PrivilegeMode::M) || (mode() == PrivilegeMode::S) || (CSR[vsstatus].SIE == 1'b0) + # ? 0 + # : $bits(CSR[mie]) & ($bits[CSR[mideleg]] & $bits[CSR[hideleg]])); + # Bits vsmode_pending_and_enabled = pending_ints & vsmode_enabled_ints; + + # if (vsmode_pending_and_enabled != 0) { + # pending_and_enabled_interrupts = vsmode_pending_and_enabled; + # return; + # } + # } + + # nothing is enabled + pending_and_enabled_interrupts = 0; + } +} + +function highest_priority_interrupt { + returns InterruptCode + arguments Bits int_mask + description { + Given a bitmask of interrupts in the format of MIE/MIP, return the highest + priority interrupt code that is set + + Interrupt priority is: + MEI, MSI, MTI, SEI, SSI, STI, SGEI, VSEI, VSSI, VSTI, LCOFI + } + body{ + if (int_mask[$bits(InterruptCode::MachineExternal)] == 1'b1) { + return InterruptCode::MachineExternal; + } else if (int_mask[$bits(InterruptCode::MachineSoftware)] == 1'b1) { + return InterruptCode::MachineSoftware; + } else if (int_mask[$bits(InterruptCode::MachineTimer)] == 1'b1) { + return InterruptCode::MachineTimer; + } + if (CSR[misa].S == 1'b1) { + if (int_mask[$bits(InterruptCode::SupervisorExternal)] == 1'b1) { + return InterruptCode::SupervisorExternal; + } else if (int_mask[$bits(InterruptCode::SupervisorSoftware)] == 1'b1) { + return InterruptCode::SupervisorSoftware; + } else if (int_mask[$bits(InterruptCode::SupervisorTimer)] == 1'b1) { + return InterruptCode::SupervisorTimer; + } + } + # if (CSR[misa].H == 1'b1) { + # if (int_mask[$bits(InterruptCode::SupervisorGuestExternal)] == 1'b1) { + # return InterruptCode::SupervisorGuestExternal; + # } else if (int_mask[$bits(InterruptCode::VirtualSupervisorExternal)] == 1'b1) { + # return InterruptCode::VirtualSupervisorExternal; + # } else if (int_mask[$bits(InterruptCode::VirtualSupervisorSoftware)] == 1'b1) { + # return InterruptCode::VirtualSupervisorSoftware; + # } else if (int_mask[$bits(InterruptCode::VirtualSupervisorTimer)] == 1'b1) { + # return InterruptCode::VirtualSupervisorTimer; + # } + # } + if (implemented?(ExtensionName::Sscofpmf)) { + if (int_mask[$bits(InterruptCode::LocalCounterOverflow)] == 1'b1) { + return InterruptCode::LocalCounterOverflow; + } + } + assert(false, "There is no valid interrupt"); + } +} + +function choose_interrupt { + returns InterruptCode, PrivilegeMode + description { + Return the highest priority interrupt + that is both pending and enabled and the mode it will be taken in + } + body { + InterruptCode chosen; + + Boolean HAS_MIDELEG = + implemented_version?(ExtensionName::S, "<= 1.9.1") || + (implemented_version?(ExtensionName::S, "> 1.9.1") && implemented_version?(ExtensionName::Sm, "> 1.9.1")); + + + # check M mode interrupts + Bits mmode_pending_and_enabled = + pending_and_enabled_interrupts & ~(HAS_MIDELEG ? $bits(CSR[mideleg]) : XLEN'0); + if (mmode_pending_and_enabled != 0) { + assert((mode() != PrivilegeMode::M) || (CSR[mstatus].MIE == 1'b1), + "M-mode interrupts are not enabled"); + + chosen = highest_priority_interrupt(mmode_pending_and_enabled); + + # check S-mode interrupts + } else if (CSR[misa].S == 1'b1) { + Bits smode_pending_and_enabled = + (pending_and_enabled_interrupts & $bits(CSR[mideleg])) + # & ((CSR[misa].H == 1'b1) ? ~$bits(CSR[hideleg]) : ~XLEN'0) + ; + + if (smode_pending_and_enabled != 0) { + assert((mode() == PrivilegeMode::U) || (mode() == PrivilegeMode::VU) || (mode() == PrivilegeMode::VS) || + (mode() == PrivilegeMode::S) && (CSR[mstatus].SIE == 1'b1), + "S-mode interrupt can't be triggered"); + + chosen = highest_priority_interrupt(smode_pending_and_enabled); + } + } + + assert($bits(chosen) != 0, "Didn't pick interrupt?"); + + # check the delegation register + PrivilegeMode to_mode; + + Bits chosen_mask = (XLEN'1 << $bits(chosen)); + if (((HAS_MIDELEG ? $bits(CSR[mideleg]) : XLEN'0) & chosen_mask) == 0) { + to_mode = PrivilegeMode::M; + } else { + # delegated from M + if (CSR[misa].S == 1'b1) { + to_mode = PrivilegeMode::S; + } else { + to_mode = PrivilegeMode::U; + } + } + + return chosen, to_mode; + } +} + +function take_interrupt { + description { + Take (adjust CSRs and set PC to handler) the highest priority interrupt + that is both pending and enabled + } + body { + PrivilegeMode to_mode; + InterruptCode code; + + (code, to_mode) = choose_interrupt(); + + if (to_mode == PrivilegeMode::M) { + CSR[mepc].PC = $pc; + CSR[mstatus].MPP = $bits(mode())[1:0]; + if (CSR[misa].H == 1'b1) { + CSR[mstatus].MPV = $bits(mode())[2]; + CSR[mtval2].VALUE = 0; + CSR[mtinst].VALUE = 0; + } + CSR[mcause].CODE = $bits(code); + CSR[mcause].INT = 1'b1; + CSR[mtval].VALUE = 0; + if (CSR[mtvec].MODE == 0) { + # direct mode + $pc = {CSR[mtvec].BASE, 2'b00}; + } else if (CSR[mtvec].MODE == 1'b1) { + # vectored + $pc = {CSR[mtvec].BASE, 2'b00} + ($bits(code)*4); + } + } else if ((CSR[misa].S == 1'b1) && (to_mode == PrivilegeMode::S)) { + CSR[sepc].PC = $pc; + CSR[mstatus].SPP = $bits(mode())[0]; + if (CSR[misa].H == 1'b1) { + CSR[hstatus].SPV = $bits(mode())[2]; + } + CSR[scause].CODE = $bits(code); + CSR[scause].INT = 1'b1; + CSR[stval].VALUE = 0; + if (CSR[stvec].MODE == 0) { + # direct mode + $pc = {CSR[stvec].BASE, 2'b00}; + } else if (CSR[stvec].MODE == 1'b1) { + # vectored + $pc = {CSR[stvec].BASE, 2'b00} + ($bits(code)*4); + } + } else if ((CSR[misa].H == 1'b1) && (to_mode == PrivilegeMode::VS)) { + CSR[vsepc].PC = $pc; + CSR[vsstatus].SPP = $bits(mode())[0]; + CSR[vscause].CODE = $bits(code); + CSR[vscause].INT = 1'b1; + CSR[vstval].VALUE = 0; + if (CSR[vstvec].MODE == 0) { + # direct mode + $pc = {CSR[vstvec].BASE, 2'b00}; + } else if (CSR[vstvec].MODE == 1'b1) { + # vectored + $pc = {CSR[vstvec].BASE, 2'b00} + ($bits(code)*4); + } + } + + set_mode_no_refresh(to_mode); + } +} diff --git a/arch/isa/util.idl b/arch/isa/util.idl index 3c7f423d40..086c7e63cb 100644 --- a/arch/isa/util.idl +++ b/arch/isa/util.idl @@ -82,6 +82,28 @@ function lowest_set_bit { } } +function bit_length { + returns XReg + arguments XReg value + description { + Returns the minimum number of bits needed to represent value. + + Only works on unsigned values. + + The value 0 returns 1. + } + body { + for (XReg i = 63; i > 0; i--) { + if (value[i] == 1) { + return i; + } + } + + # if we get here, the value is 0 or 1. either way, say we need one bit + return 1; + } +} + function count_leading_zeros { template U32 N returns @@ -129,28 +151,6 @@ function sext { } } -function bit_length { - returns XReg - arguments XReg value - description { - Returns the minimum number of bits needed to represent value. - - Only works on unsigned values. - - The value 0 returns 1. - } - body { - for (XReg i = 63; i > 0; i--) { - if (value[i] == 1) { - return i; - } - } - - # if we get here, the value is 0 or 1. either way, say we need one bit - return 1; - } -} - function is_naturally_aligned { template U32 N returns Boolean diff --git a/arch/manual_version/isa/20240411/isa_20240411.yaml b/arch/manual_version/isa/20240411/isa_20240411.yaml index ca04085167..cf4ad81006 100644 --- a/arch/manual_version/isa/20240411/isa_20240411.yaml +++ b/arch/manual_version/isa/20240411/isa_20240411.yaml @@ -66,103 +66,103 @@ volumes: #/End of Vector appendices - src/index.adoc extensions: - - [I, "2.1.0"] - - [U, "1.12.0"] - # - [E, "2.0"] - # - [RVI64, "2.1"] - # - [RVI128, "1.7"] - - [Zifencei, "2.0.0"] - - [Zicsr, "2.0.0"] - - [Zicntr, "2.0.0"] - - [Zihpm, "2.0.0"] - - [Zihintntl, "1.0.0"] - - [Zihintpause, "2.0.0"] - - [Zimop, "2.0.0"] - - [Zicond, "1.0.0"] - - [M, "2.0.0"] - - [A, "2.1.0"] - - [Zawrs, "1.0.1"] - - [Zacas, "1.0.0"] - - [Zabha, "1.0.0"] - # - [RVWMO, "2.0"] - - [Ztso, "1.0.0"] - - [Zicbom, "1.0.0"] - - [Zicboz, "1.0.0"] - - [Zicbop, "1.0.0"] - - [F, "2.2.0"] - - [D, "2.2.0"] - # - [Q, "2.2"] - - [Zfh, "1.0.0"] - - [Zfhmin, "1.0.0"] - - [Zfbfmin, "1.0.0"] - - [Zvfbfmin, "1.0.0"] - - [Zvfbfwma, "1.0.0"] - - [Zfa, "1.0.0"] - - [Zfinx, "1.0.0"] - - [Zdinx, "1.0.0"] - - [Zhinx, "1.0.0"] - - [C, "2.0.0"] - - [Zca, "1.0.0"] - - [Zcf, "1.0.0"] - - [Zcd, "1.0.0"] - - [Zcb, "1.0.0"] - - [Zcmp, "1.0.0"] - - [Zcmt, "1.0.0"] - - [B, "1.0.0"] - - [Zba, "1.0.0"] - - [Zbb, "1.0.0"] - - [Zbc, "1.0.0"] - - [Zbs, "1.0.0"] - - [Zbkb, "1.0.0"] - - [Zbkc, "1.0.0"] - - [Zbkx, "1.0.0"] - # - [J, "0.0"] - # - [P, "0.2"] - - [V, "1.0.0"] - - [Zvl32b, "1.0.0"] - - [Zvl64b, "1.0.0"] - - [Zvl128b, "1.0.0"] - - [Zvl256b, "1.0.0"] - - [Zvl512b, "1.0.0"] - - [Zvl1024b, "1.0.0"] - - [Zve32x, "1.0.0"] - - [Zve32f, "1.0.0"] - - [Zve64x, "1.0.0"] - - [Zve64f, "1.0.0"] - - [Zve64d, "1.0.0"] - - [Zvfhmin, "1.0.0"] - - [Zvfh, "1.0.0"] - - [Zvknha, "1.0.0"] - - [Zvknhb, "1.0.0"] - - [Zknd, "1.0.0"] - - [Zkne, "1.0.0"] - - [Zknh, "1.0.0"] - - [Zksed, "1.0.0"] - - [Zksh, "1.0.0"] - - [Zkr, "1.0.0"] - - [Zkn, "1.0.0"] - - [Zks, "1.0.0"] - - [Zk, "1.0.0"] - - [Zkt, "1.0.0"] - - [Zvbb, "1.0.0"] - - [Zvbc, "1.0.0"] - - [Zvkb, "1.0.0"] - - [Zvkg, "1.0.0"] - - [Zvkned, "1.0.0"] - - [Zvknha, "1.0.0"] - - [Zvknhb, "1.0.0"] - - [Zvksed, "1.0.0"] - - [Zvksh, "1.0.0"] - - [Zvkn, "1.0.0"] - - [Zvknc, "1.0.0"] - - [Zvkng, "1.0.0"] - - [Zvks, "1.0.0"] - - [Zvksc, "1.0.0"] - - [Zvksg, "1.0.0"] - - [Zvkt, "1.0.0"] - - [Zicfilp, "1.0.0"] - - [Zicfiss, "1.0.0"] - # - [Zam, "0.1"] + - { name: I, version: "2.1.0" } + - { name: U, version: "1.12.0" } + # - { name: E, version: "2.0" } + # - { name: RVI64, version: "2.1" } + # - { name: RVI128, version: "1.7" } + - { name: Zifencei, version: "2.0.0" } + - { name: Zicsr, version: "2.0.0" } + - { name: Zicntr, version: "2.0.0" } + - { name: Zihpm, version: "2.0.0" } + - { name: Zihintntl, version: "1.0.0" } + - { name: Zihintpause, version: "2.0.0" } + - { name: Zimop, version: "2.0.0" } + - { name: Zicond, version: "1.0.0" } + - { name: M, version: "2.0.0" } + - { name: A, version: "2.1.0" } + - { name: Zawrs, version: "1.0.1" } + - { name: Zacas, version: "1.0.0" } + - { name: Zabha, version: "1.0.0" } + # - { name: RVWMO, version: "2.0" } + - { name: Ztso, version: "1.0.0" } + - { name: Zicbom, version: "1.0.0" } + - { name: Zicboz, version: "1.0.0" } + - { name: Zicbop, version: "1.0.0" } + - { name: F, version: "2.2.0" } + - { name: D, version: "2.2.0" } + # - { name: Q, version: "2.2" } + - { name: Zfh, version: "1.0.0" } + - { name: Zfhmin, version: "1.0.0" } + - { name: Zfbfmin, version: "1.0.0" } + - { name: Zvfbfmin, version: "1.0.0" } + - { name: Zvfbfwma, version: "1.0.0" } + - { name: Zfa, version: "1.0.0" } + - { name: Zfinx, version: "1.0.0" } + - { name: Zdinx, version: "1.0.0" } + - { name: Zhinx, version: "1.0.0" } + - { name: C, version: "2.0.0" } + - { name: Zca, version: "1.0.0" } + - { name: Zcf, version: "1.0.0" } + - { name: Zcd, version: "1.0.0" } + - { name: Zcb, version: "1.0.0" } + - { name: Zcmp, version: "1.0.0" } + - { name: Zcmt, version: "1.0.0" } + - { name: B, version: "1.0.0" } + - { name: Zba, version: "1.0.0" } + - { name: Zbb, version: "1.0.0" } + - { name: Zbc, version: "1.0.0" } + - { name: Zbs, version: "1.0.0" } + - { name: Zbkb, version: "1.0.0" } + - { name: Zbkc, version: "1.0.0" } + - { name: Zbkx, version: "1.0.0" } + # - { name: J, version: "0.0" } + # - { name: P, version: "0.2" } + - { name: V, version: "1.0.0" } + - { name: Zvl32b, version: "1.0.0" } + - { name: Zvl64b, version: "1.0.0" } + - { name: Zvl128b, version: "1.0.0" } + - { name: Zvl256b, version: "1.0.0" } + - { name: Zvl512b, version: "1.0.0" } + - { name: Zvl1024b, version: "1.0.0" } + - { name: Zve32x, version: "1.0.0" } + - { name: Zve32f, version: "1.0.0" } + - { name: Zve64x, version: "1.0.0" } + - { name: Zve64f, version: "1.0.0" } + - { name: Zve64d, version: "1.0.0" } + - { name: Zvfhmin, version: "1.0.0" } + - { name: Zvfh, version: "1.0.0" } + - { name: Zvknha, version: "1.0.0" } + - { name: Zvknhb, version: "1.0.0" } + - { name: Zknd, version: "1.0.0" } + - { name: Zkne, version: "1.0.0" } + - { name: Zknh, version: "1.0.0" } + - { name: Zksed, version: "1.0.0" } + - { name: Zksh, version: "1.0.0" } + - { name: Zkr, version: "1.0.0" } + - { name: Zkn, version: "1.0.0" } + - { name: Zks, version: "1.0.0" } + - { name: Zk, version: "1.0.0" } + - { name: Zkt, version: "1.0.0" } + - { name: Zvbb, version: "1.0.0" } + - { name: Zvbc, version: "1.0.0" } + - { name: Zvkb, version: "1.0.0" } + - { name: Zvkg, version: "1.0.0" } + - { name: Zvkned, version: "1.0.0" } + - { name: Zvknha, version: "1.0.0" } + - { name: Zvknhb, version: "1.0.0" } + - { name: Zvksed, version: "1.0.0" } + - { name: Zvksh, version: "1.0.0" } + - { name: Zvkn, version: "1.0.0" } + - { name: Zvknc, version: "1.0.0" } + - { name: Zvkng, version: "1.0.0" } + - { name: Zvks, version: "1.0.0" } + - { name: Zvksc, version: "1.0.0" } + - { name: Zvksg, version: "1.0.0" } + - { name: Zvkt, version: "1.0.0" } + - { name: Zicfilp, version: "1.0.0" } + - { name: Zicfiss, version: "1.0.0" } + # - { name: Zam, version: "0.1" } changes: - The inclusion of all ratified extensions through March 2024. - The draft Zam extension has been removed, in favor of the definition of a misaligned atomicity granule PMA. @@ -194,25 +194,25 @@ volumes: - src/priv-history.adoc - src/bibliography.adoc extensions: - - [Smstateen, "1.0.0"] - - [Smcsrind, "1.0.0"] - - [Sscsrind, "1.0.0"] - - [Smepmp, "1.0.0"] - - [Smcntrpmf, "1.0.0"] - - [Smrnmi, "0.5.0"] - - [Smcdeleg, "1.0.0"] - - [S, "1.12.0"] - - [Sm, "1.12.0"] - - [Smhpm, "1.12.0"] - - [Smpmp, "1.12.0"] - - [Sv32, "1.12.0"] - - [Sv39, "1.12.0"] - - [Sv48, "1.12.0"] - - [Sv57, "1.12.0"] - - [Svnapot, "1.0.0"] - - [Svpbmt, "1.0.0"] - - [Svinval, "1.0.0"] - - [Svadu, "1.0.0"] - - [Svvptc, "1.0.0"] - - [Sscofpmf, "1.0.0"] - - [H, "1.0.0"] + - { name: Smstateen, version: "1.0.0" } + - { name: Smcsrind, version: "1.0.0" } + - { name: Sscsrind, version: "1.0.0" } + - { name: Smepmp, version: "1.0.0" } + - { name: Smcntrpmf, version: "1.0.0" } + - { name: Smrnmi, version: "0.5.0" } + - { name: Smcdeleg, version: "1.0.0" } + - { name: S, version: "1.12.0" } + - { name: Sm, version: "1.12.0" } + - { name: Smhpm, version: "1.12.0" } + - { name: Smpmp, version: "1.12.0" } + - { name: Sv32, version: "1.12.0" } + - { name: Sv39, version: "1.12.0" } + - { name: Sv48, version: "1.12.0" } + - { name: Sv57, version: "1.12.0" } + - { name: Svnapot, version: "1.0.0" } + - { name: Svpbmt, version: "1.0.0" } + - { name: Svinval, version: "1.0.0" } + - { name: Svadu, version: "1.0.0" } + - { name: Svvptc, version: "1.0.0" } + - { name: Sscofpmf, version: "1.0.0" } + - { name: H, version: "1.0.0" } diff --git a/cfgs/generic_rv64/arch_overlay/csr/marchid.yaml b/arch_overlay/example/csr/marchid.yaml similarity index 100% rename from cfgs/generic_rv64/arch_overlay/csr/marchid.yaml rename to arch_overlay/example/csr/marchid.yaml diff --git a/cfgs/generic_rv64/arch_overlay/csr/mcustom0.yaml b/arch_overlay/example/csr/mcustom0.yaml similarity index 100% rename from cfgs/generic_rv64/arch_overlay/csr/mcustom0.yaml rename to arch_overlay/example/csr/mcustom0.yaml diff --git a/cfgs/generic_rv64/arch_overlay/ext/Xcustom.yaml b/arch_overlay/example/ext/Xcustom.yaml similarity index 100% rename from cfgs/generic_rv64/arch_overlay/ext/Xcustom.yaml rename to arch_overlay/example/ext/Xcustom.yaml diff --git a/cfgs/qc_iu/arch_overlay/csr/Xqci/gen_mcliciX.rb b/arch_overlay/qc_iu/csr/Xqci/gen_mcliciX.rb similarity index 100% rename from cfgs/qc_iu/arch_overlay/csr/Xqci/gen_mcliciX.rb rename to arch_overlay/qc_iu/csr/Xqci/gen_mcliciX.rb diff --git a/cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mcause.yaml b/arch_overlay/qc_iu/csr/Xqci/qc.mcause.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mcause.yaml rename to arch_overlay/qc_iu/csr/Xqci/qc.mcause.yaml diff --git a/cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicie0.yaml b/arch_overlay/qc_iu/csr/Xqci/qc.mclicie0.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicie0.yaml rename to arch_overlay/qc_iu/csr/Xqci/qc.mclicie0.yaml diff --git a/cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicie1.yaml b/arch_overlay/qc_iu/csr/Xqci/qc.mclicie1.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicie1.yaml rename to arch_overlay/qc_iu/csr/Xqci/qc.mclicie1.yaml diff --git a/cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicie2.yaml b/arch_overlay/qc_iu/csr/Xqci/qc.mclicie2.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicie2.yaml rename to arch_overlay/qc_iu/csr/Xqci/qc.mclicie2.yaml diff --git a/cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicie3.yaml b/arch_overlay/qc_iu/csr/Xqci/qc.mclicie3.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicie3.yaml rename to arch_overlay/qc_iu/csr/Xqci/qc.mclicie3.yaml diff --git a/cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicie4.yaml b/arch_overlay/qc_iu/csr/Xqci/qc.mclicie4.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicie4.yaml rename to arch_overlay/qc_iu/csr/Xqci/qc.mclicie4.yaml diff --git a/cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicie5.yaml b/arch_overlay/qc_iu/csr/Xqci/qc.mclicie5.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicie5.yaml rename to arch_overlay/qc_iu/csr/Xqci/qc.mclicie5.yaml diff --git a/cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicie6.yaml b/arch_overlay/qc_iu/csr/Xqci/qc.mclicie6.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicie6.yaml rename to arch_overlay/qc_iu/csr/Xqci/qc.mclicie6.yaml diff --git a/cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicie7.yaml b/arch_overlay/qc_iu/csr/Xqci/qc.mclicie7.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicie7.yaml rename to arch_overlay/qc_iu/csr/Xqci/qc.mclicie7.yaml diff --git a/cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicilvl00.yaml b/arch_overlay/qc_iu/csr/Xqci/qc.mclicilvl00.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicilvl00.yaml rename to arch_overlay/qc_iu/csr/Xqci/qc.mclicilvl00.yaml diff --git a/cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicilvl01.yaml b/arch_overlay/qc_iu/csr/Xqci/qc.mclicilvl01.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicilvl01.yaml rename to arch_overlay/qc_iu/csr/Xqci/qc.mclicilvl01.yaml diff --git a/cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicilvl02.yaml b/arch_overlay/qc_iu/csr/Xqci/qc.mclicilvl02.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicilvl02.yaml rename to arch_overlay/qc_iu/csr/Xqci/qc.mclicilvl02.yaml diff --git a/cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicilvl03.yaml b/arch_overlay/qc_iu/csr/Xqci/qc.mclicilvl03.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicilvl03.yaml rename to arch_overlay/qc_iu/csr/Xqci/qc.mclicilvl03.yaml diff --git a/cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicilvl04.yaml b/arch_overlay/qc_iu/csr/Xqci/qc.mclicilvl04.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicilvl04.yaml rename to arch_overlay/qc_iu/csr/Xqci/qc.mclicilvl04.yaml diff --git a/cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicilvl05.yaml b/arch_overlay/qc_iu/csr/Xqci/qc.mclicilvl05.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicilvl05.yaml rename to arch_overlay/qc_iu/csr/Xqci/qc.mclicilvl05.yaml diff --git a/cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicilvl06.yaml b/arch_overlay/qc_iu/csr/Xqci/qc.mclicilvl06.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicilvl06.yaml rename to arch_overlay/qc_iu/csr/Xqci/qc.mclicilvl06.yaml diff --git a/cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicilvl07.yaml b/arch_overlay/qc_iu/csr/Xqci/qc.mclicilvl07.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicilvl07.yaml rename to arch_overlay/qc_iu/csr/Xqci/qc.mclicilvl07.yaml diff --git a/cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicilvl08.yaml b/arch_overlay/qc_iu/csr/Xqci/qc.mclicilvl08.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicilvl08.yaml rename to arch_overlay/qc_iu/csr/Xqci/qc.mclicilvl08.yaml diff --git a/cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicilvl09.yaml b/arch_overlay/qc_iu/csr/Xqci/qc.mclicilvl09.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicilvl09.yaml rename to arch_overlay/qc_iu/csr/Xqci/qc.mclicilvl09.yaml diff --git a/cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicilvl10.yaml b/arch_overlay/qc_iu/csr/Xqci/qc.mclicilvl10.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicilvl10.yaml rename to arch_overlay/qc_iu/csr/Xqci/qc.mclicilvl10.yaml diff --git a/cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicilvl11.yaml b/arch_overlay/qc_iu/csr/Xqci/qc.mclicilvl11.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicilvl11.yaml rename to arch_overlay/qc_iu/csr/Xqci/qc.mclicilvl11.yaml diff --git a/cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicilvl12.yaml b/arch_overlay/qc_iu/csr/Xqci/qc.mclicilvl12.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicilvl12.yaml rename to arch_overlay/qc_iu/csr/Xqci/qc.mclicilvl12.yaml diff --git a/cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicilvl13.yaml b/arch_overlay/qc_iu/csr/Xqci/qc.mclicilvl13.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicilvl13.yaml rename to arch_overlay/qc_iu/csr/Xqci/qc.mclicilvl13.yaml diff --git a/cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicilvl14.yaml b/arch_overlay/qc_iu/csr/Xqci/qc.mclicilvl14.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicilvl14.yaml rename to arch_overlay/qc_iu/csr/Xqci/qc.mclicilvl14.yaml diff --git a/cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicilvl15.yaml b/arch_overlay/qc_iu/csr/Xqci/qc.mclicilvl15.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicilvl15.yaml rename to arch_overlay/qc_iu/csr/Xqci/qc.mclicilvl15.yaml diff --git a/cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicilvl16.yaml b/arch_overlay/qc_iu/csr/Xqci/qc.mclicilvl16.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicilvl16.yaml rename to arch_overlay/qc_iu/csr/Xqci/qc.mclicilvl16.yaml diff --git a/cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicilvl17.yaml b/arch_overlay/qc_iu/csr/Xqci/qc.mclicilvl17.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicilvl17.yaml rename to arch_overlay/qc_iu/csr/Xqci/qc.mclicilvl17.yaml diff --git a/cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicilvl18.yaml b/arch_overlay/qc_iu/csr/Xqci/qc.mclicilvl18.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicilvl18.yaml rename to arch_overlay/qc_iu/csr/Xqci/qc.mclicilvl18.yaml diff --git a/cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicilvl19.yaml b/arch_overlay/qc_iu/csr/Xqci/qc.mclicilvl19.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicilvl19.yaml rename to arch_overlay/qc_iu/csr/Xqci/qc.mclicilvl19.yaml diff --git a/cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicilvl20.yaml b/arch_overlay/qc_iu/csr/Xqci/qc.mclicilvl20.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicilvl20.yaml rename to arch_overlay/qc_iu/csr/Xqci/qc.mclicilvl20.yaml diff --git a/cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicilvl21.yaml b/arch_overlay/qc_iu/csr/Xqci/qc.mclicilvl21.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicilvl21.yaml rename to arch_overlay/qc_iu/csr/Xqci/qc.mclicilvl21.yaml diff --git a/cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicilvl22.yaml b/arch_overlay/qc_iu/csr/Xqci/qc.mclicilvl22.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicilvl22.yaml rename to arch_overlay/qc_iu/csr/Xqci/qc.mclicilvl22.yaml diff --git a/cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicilvl23.yaml b/arch_overlay/qc_iu/csr/Xqci/qc.mclicilvl23.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicilvl23.yaml rename to arch_overlay/qc_iu/csr/Xqci/qc.mclicilvl23.yaml diff --git a/cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicilvl24.yaml b/arch_overlay/qc_iu/csr/Xqci/qc.mclicilvl24.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicilvl24.yaml rename to arch_overlay/qc_iu/csr/Xqci/qc.mclicilvl24.yaml diff --git a/cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicilvl25.yaml b/arch_overlay/qc_iu/csr/Xqci/qc.mclicilvl25.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicilvl25.yaml rename to arch_overlay/qc_iu/csr/Xqci/qc.mclicilvl25.yaml diff --git a/cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicilvl26.yaml b/arch_overlay/qc_iu/csr/Xqci/qc.mclicilvl26.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicilvl26.yaml rename to arch_overlay/qc_iu/csr/Xqci/qc.mclicilvl26.yaml diff --git a/cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicilvl27.yaml b/arch_overlay/qc_iu/csr/Xqci/qc.mclicilvl27.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicilvl27.yaml rename to arch_overlay/qc_iu/csr/Xqci/qc.mclicilvl27.yaml diff --git a/cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicilvl28.yaml b/arch_overlay/qc_iu/csr/Xqci/qc.mclicilvl28.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicilvl28.yaml rename to arch_overlay/qc_iu/csr/Xqci/qc.mclicilvl28.yaml diff --git a/cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicilvl29.yaml b/arch_overlay/qc_iu/csr/Xqci/qc.mclicilvl29.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicilvl29.yaml rename to arch_overlay/qc_iu/csr/Xqci/qc.mclicilvl29.yaml diff --git a/cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicilvl30.yaml b/arch_overlay/qc_iu/csr/Xqci/qc.mclicilvl30.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicilvl30.yaml rename to arch_overlay/qc_iu/csr/Xqci/qc.mclicilvl30.yaml diff --git a/cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicilvl31.yaml b/arch_overlay/qc_iu/csr/Xqci/qc.mclicilvl31.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicilvl31.yaml rename to arch_overlay/qc_iu/csr/Xqci/qc.mclicilvl31.yaml diff --git a/cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicip0.yaml b/arch_overlay/qc_iu/csr/Xqci/qc.mclicip0.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicip0.yaml rename to arch_overlay/qc_iu/csr/Xqci/qc.mclicip0.yaml diff --git a/cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicip1.yaml b/arch_overlay/qc_iu/csr/Xqci/qc.mclicip1.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicip1.yaml rename to arch_overlay/qc_iu/csr/Xqci/qc.mclicip1.yaml diff --git a/cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicip2.yaml b/arch_overlay/qc_iu/csr/Xqci/qc.mclicip2.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicip2.yaml rename to arch_overlay/qc_iu/csr/Xqci/qc.mclicip2.yaml diff --git a/cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicip3.yaml b/arch_overlay/qc_iu/csr/Xqci/qc.mclicip3.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicip3.yaml rename to arch_overlay/qc_iu/csr/Xqci/qc.mclicip3.yaml diff --git a/cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicip4.yaml b/arch_overlay/qc_iu/csr/Xqci/qc.mclicip4.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicip4.yaml rename to arch_overlay/qc_iu/csr/Xqci/qc.mclicip4.yaml diff --git a/cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicip5.yaml b/arch_overlay/qc_iu/csr/Xqci/qc.mclicip5.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicip5.yaml rename to arch_overlay/qc_iu/csr/Xqci/qc.mclicip5.yaml diff --git a/cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicip6.yaml b/arch_overlay/qc_iu/csr/Xqci/qc.mclicip6.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicip6.yaml rename to arch_overlay/qc_iu/csr/Xqci/qc.mclicip6.yaml diff --git a/cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicip7.yaml b/arch_overlay/qc_iu/csr/Xqci/qc.mclicip7.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mclicip7.yaml rename to arch_overlay/qc_iu/csr/Xqci/qc.mclicip7.yaml diff --git a/cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mmcr.yaml b/arch_overlay/qc_iu/csr/Xqci/qc.mmcr.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mmcr.yaml rename to arch_overlay/qc_iu/csr/Xqci/qc.mmcr.yaml diff --git a/cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mntvec.yaml b/arch_overlay/qc_iu/csr/Xqci/qc.mntvec.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mntvec.yaml rename to arch_overlay/qc_iu/csr/Xqci/qc.mntvec.yaml diff --git a/cfgs/qc_iu/arch_overlay/csr/qc.mstkbottomaddr.yaml b/arch_overlay/qc_iu/csr/Xqci/qc.mstkbottomaddr.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/csr/qc.mstkbottomaddr.yaml rename to arch_overlay/qc_iu/csr/Xqci/qc.mstkbottomaddr.yaml diff --git a/cfgs/qc_iu/arch_overlay/csr/qc.mstktopaddr.yaml b/arch_overlay/qc_iu/csr/Xqci/qc.mstktopaddr.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/csr/qc.mstktopaddr.yaml rename to arch_overlay/qc_iu/csr/Xqci/qc.mstktopaddr.yaml diff --git a/cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mthreadptr.yaml b/arch_overlay/qc_iu/csr/Xqci/qc.mthreadptr.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mthreadptr.yaml rename to arch_overlay/qc_iu/csr/Xqci/qc.mthreadptr.yaml diff --git a/cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mwpendaddr0.yaml b/arch_overlay/qc_iu/csr/Xqci/qc.mwpendaddr0.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mwpendaddr0.yaml rename to arch_overlay/qc_iu/csr/Xqci/qc.mwpendaddr0.yaml diff --git a/cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mwpendaddr1.yaml b/arch_overlay/qc_iu/csr/Xqci/qc.mwpendaddr1.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mwpendaddr1.yaml rename to arch_overlay/qc_iu/csr/Xqci/qc.mwpendaddr1.yaml diff --git a/cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mwpendaddr2.yaml b/arch_overlay/qc_iu/csr/Xqci/qc.mwpendaddr2.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mwpendaddr2.yaml rename to arch_overlay/qc_iu/csr/Xqci/qc.mwpendaddr2.yaml diff --git a/cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mwpendaddr3.yaml b/arch_overlay/qc_iu/csr/Xqci/qc.mwpendaddr3.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mwpendaddr3.yaml rename to arch_overlay/qc_iu/csr/Xqci/qc.mwpendaddr3.yaml diff --git a/cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mwpstartaddr0.yaml b/arch_overlay/qc_iu/csr/Xqci/qc.mwpstartaddr0.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mwpstartaddr0.yaml rename to arch_overlay/qc_iu/csr/Xqci/qc.mwpstartaddr0.yaml diff --git a/cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mwpstartaddr1.yaml b/arch_overlay/qc_iu/csr/Xqci/qc.mwpstartaddr1.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mwpstartaddr1.yaml rename to arch_overlay/qc_iu/csr/Xqci/qc.mwpstartaddr1.yaml diff --git a/cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mwpstartaddr2.yaml b/arch_overlay/qc_iu/csr/Xqci/qc.mwpstartaddr2.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mwpstartaddr2.yaml rename to arch_overlay/qc_iu/csr/Xqci/qc.mwpstartaddr2.yaml diff --git a/cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mwpstartaddr3.yaml b/arch_overlay/qc_iu/csr/Xqci/qc.mwpstartaddr3.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/csr/Xqci/qc.mwpstartaddr3.yaml rename to arch_overlay/qc_iu/csr/Xqci/qc.mwpstartaddr3.yaml diff --git a/cfgs/qc_iu/arch_overlay/ext/Xqccmp.yaml b/arch_overlay/qc_iu/ext/Xqccmp.yaml similarity index 82% rename from cfgs/qc_iu/arch_overlay/ext/Xqccmp.yaml rename to arch_overlay/qc_iu/ext/Xqccmp.yaml index 4b8c05f219..9c3a71e9a9 100644 --- a/cfgs/qc_iu/arch_overlay/ext/Xqccmp.yaml +++ b/arch_overlay/qc_iu/ext/Xqccmp.yaml @@ -1,4 +1,4 @@ -# yaml-language-server: $schema=../../../../schemas/ext_schema.json +# yaml-language-server: $schema=../../../schemas/ext_schema.json $schema: ext_schema.json# kind: extension @@ -50,6 +50,28 @@ versions: requires: name: Zca version: ">= 1.0.0" +- version: "0.3.0" + state: frozen + ratification_date: null + contributors: + - name: Albert Yosher + company: Qualcomm Technologies, Inc. + email: ayosher@qti.qualcomm.com + - name: Derek Hower + company: Qualcomm Technologies, Inc. + email: dhower@qti.qualcomm.com + - name: Ana Pazos + company: Qualcomm Technologies, Inc. + email: apazos@qti.qualcomm.com + - name: James Ball + company: Qualcomm Technologies, Inc. + email: jameball@qti.qualcomm.com + changes: + - Fix all push and pop instructions IDL code to take in consideration that xlen() returns bits and not bytes + - Declare state of extension as "frozen" + requires: + name: Zca + version: ">= 1.0.0" description: | The Xqccmp extension is a set of instructions which may be executed as a series of existing 32-bit RISC-V instructions. @@ -123,3 +145,8 @@ doc_license: company: name: Qualcomm Technologies, Inc. url: https://qualcomm.com +conflicts: + anyOf: + - allOf: [C, D] + - Zcd + - Zcmp diff --git a/cfgs/qc_iu/arch_overlay/ext/Xqci.yaml b/arch_overlay/qc_iu/ext/Xqci.yaml similarity index 66% rename from cfgs/qc_iu/arch_overlay/ext/Xqci.yaml rename to arch_overlay/qc_iu/ext/Xqci.yaml index eca732b992..28afe4569d 100644 --- a/cfgs/qc_iu/arch_overlay/ext/Xqci.yaml +++ b/arch_overlay/qc_iu/ext/Xqci.yaml @@ -44,21 +44,21 @@ versions: - Rename mnemonics to match toolchain guidelines - Ensure every instruction belongs to at least one sub extension implies: - - [Xqcia, "0.1.0"] - - [Xqciac, "0.1.0"] - - [Xqcibi, "0.1.0"] - - [Xqcibm, "0.1.0"] - - [Xqcicli, "0.1.0"] - - [Xqcicm, "0.1.0"] - - [Xqcics, "0.1.0"] - - [Xqcicsr, "0.1.0"] - - [Xqciint, "0.1.0"] - - [Xqcilb, "0.1.0"] - - [Xqcili, "0.1.0"] - - [Xqcilia, "0.1.0"] - - [Xqcilo, "0.1.0"] - - [Xqcilsm, "0.1.0"] - - [Xqcisls, "0.1.0"] + - { name: Xqcia, version: "0.1.0" } + - { name: Xqciac, version: "0.1.0" } + - { name: Xqcibi, version: "0.1.0" } + - { name: Xqcibm, version: "0.1.0" } + - { name: Xqcicli, version: "0.1.0" } + - { name: Xqcicm, version: "0.1.0" } + - { name: Xqcics, version: "0.1.0" } + - { name: Xqcicsr, version: "0.1.0" } + - { name: Xqciint, version: "0.1.0" } + - { name: Xqcilb, version: "0.1.0" } + - { name: Xqcili, version: "0.1.0" } + - { name: Xqcilia, version: "0.1.0" } + - { name: Xqcilo, version: "0.1.0" } + - { name: Xqcilsm, version: "0.1.0" } + - { name: Xqcisls, version: "0.1.0" } - version: "0.4.0" state: frozen ratification_date: null @@ -74,21 +74,21 @@ versions: - Fix description and functionality of qc.c.extu instruction - Fix description and functionality of qc.shladd instruction implies: - - [Xqcia, "0.2.0"] - - [Xqciac, "0.2.0"] - - [Xqcibi, "0.2.0"] - - [Xqcibm, "0.2.0"] - - [Xqcicli, "0.2.0"] - - [Xqcicm, "0.2.0"] - - [Xqcics, "0.2.0"] - - [Xqcicsr, "0.2.0"] - - [Xqciint, "0.2.0"] - - [Xqcilb, "0.2.0"] - - [Xqcili, "0.2.0"] - - [Xqcilia, "0.2.0"] - - [Xqcilo, "0.2.0"] - - [Xqcilsm, "0.2.0"] - - [Xqcisls, "0.2.0"] + - { name: Xqcia, version: "0.2.0" } + - { name: Xqciac, version: "0.2.0" } + - { name: Xqcibi, version: "0.2.0" } + - { name: Xqcibm, version: "0.2.0" } + - { name: Xqcicli, version: "0.2.0" } + - { name: Xqcicm, version: "0.2.0" } + - { name: Xqcics, version: "0.2.0" } + - { name: Xqcicsr, version: "0.2.0" } + - { name: Xqciint, version: "0.2.0" } + - { name: Xqcilb, version: "0.2.0" } + - { name: Xqcili, version: "0.2.0" } + - { name: Xqcilia, version: "0.2.0" } + - { name: Xqcilo, version: "0.2.0" } + - { name: Xqcilsm, version: "0.2.0" } + - { name: Xqcisls, version: "0.2.0" } requires: name: Zca version: ">= 1.0.0" @@ -114,25 +114,26 @@ versions: - Fix description of qc.swmi, qc.lwmi and qc.setwmi instructions - Fix description of qc.mclici* CSRs to reflect being part of Xqciint custom extension - Fix description of qc.setinti and qc.clrinti instructions + - Fix to make qc.c.mret and qc.c.mnret visible implies: - - [Xqcia, "0.3.0"] - - [Xqciac, "0.3.0"] - - [Xqcibi, "0.2.0"] - - [Xqcibm, "0.3.0"] - - [Xqcicli, "0.2.0"] - - [Xqcicm, "0.2.0"] - - [Xqcics, "0.2.0"] - - [Xqcicsr, "0.2.0"] - - [Xqciint, "0.3.0"] - - [Xqciio, "0.1.0"] - - [Xqcilb, "0.2.0"] - - [Xqcili, "0.2.0"] - - [Xqcilia, "0.2.0"] - - [Xqcilo, "0.2.0"] - - [Xqcilsm, "0.3.0"] - - [Xqcisim, "0.1.0"] - - [Xqcisls, "0.2.0"] - - [Xqcisync, "0.1.0"] + - { name: Xqcia, version: "0.3.0" } + - { name: Xqciac, version: "0.3.0" } + - { name: Xqcibi, version: "0.2.0" } + - { name: Xqcibm, version: "0.3.0" } + - { name: Xqcicli, version: "0.2.0" } + - { name: Xqcicm, version: "0.2.0" } + - { name: Xqcics, version: "0.2.0" } + - { name: Xqcicsr, version: "0.2.0" } + - { name: Xqciint, version: "0.3.0" } + - { name: Xqciio, version: "0.1.0" } + - { name: Xqcilb, version: "0.2.0" } + - { name: Xqcili, version: "0.2.0" } + - { name: Xqcilia, version: "0.2.0" } + - { name: Xqcilo, version: "0.2.0" } + - { name: Xqcilsm, version: "0.3.0" } + - { name: Xqcisim, version: "0.1.0" } + - { name: Xqcisls, version: "0.2.0" } + - { name: Xqcisync, version: "0.1.0" } requires: name: Zca version: ">= 1.0.0" @@ -157,23 +158,24 @@ versions: - Add requirement to include Zca extension for Xqcisync since it has 16-bit instructions - Remove qc.flags CSR implies: - - [Xqcia, "0.4.0"] - - [Xqciac, "0.3.0"] - - [Xqcibi, "0.2.0"] - - [Xqcibm, "0.4.0"] - - [Xqcicli, "0.2.0"] - - [Xqcicm, "0.2.0"] - - [Xqcics, "0.2.0"] - - [Xqcicsr, "0.3.0"] - - [Xqciint, "0.3.0"] - - [Xqcilb, "0.2.0"] - - [Xqcili, "0.2.0"] - - [Xqcilia, "0.2.0"] - - [Xqcilo, "0.2.0"] - - [Xqcilsm, "0.4.0"] - - [Xqcisim, "0.2.0"] - - [Xqcisls, "0.2.0"] - - [Xqcisync, "0.2.0"] + - { name: Xqcia, version: "0.4.0" } + - { name: Xqciac, version: "0.3.0" } + - { name: Xqcibi, version: "0.2.0" } + - { name: Xqcibm, version: "0.4.0" } + - { name: Xqcicli, version: "0.2.0" } + - { name: Xqcicm, version: "0.2.0" } + - { name: Xqcics, version: "0.2.0" } + - { name: Xqcicsr, version: "0.3.0" } + - { name: Xqciint, version: "0.3.0" } + - { name: Xqciio, version: "0.1.0" } + - { name: Xqcilb, version: "0.2.0" } + - { name: Xqcili, version: "0.2.0" } + - { name: Xqcilia, version: "0.2.0" } + - { name: Xqcilo, version: "0.2.0" } + - { name: Xqcilsm, version: "0.4.0" } + - { name: Xqcisim, version: "0.2.0" } + - { name: Xqcisls, version: "0.2.0" } + - { name: Xqcisync, version: "0.2.0" } requires: name: Zca version: ">= 1.0.0" @@ -199,28 +201,70 @@ versions: - Add missing CSR registers qc.mmcr, qc.mthreadptr, qc.mcause - Remove CSR registers qc.mncause, qc.mnepc - Fix IDL code for qc.c.mienter, qc.c.mienter.nest, qc.c.mileaveret, qc.c.mret, qc.c.mnret + - Fix IDL code for qc.c.ei, qc.c.eir, qc.c.di, qc.c.dir - Add list of supported custom exceptions - Fix qc.cto instruction IDL code - Change width calculations for qc.extdpr, qc.extdprh, qc.extdr, qc.extdupr, qc.extduprh, qc.extdur, - Change width calculations for qc.insbhr, qc.insbpr, qc.insbprh, qc.insbr, qc.insbri implies: - - [Xqcia, "0.5.0"] - - [Xqciac, "0.3.0"] - - [Xqcibi, "0.2.0"] - - [Xqcibm, "0.5.0"] - - [Xqcicli, "0.2.0"] - - [Xqcicm, "0.2.0"] - - [Xqcics, "0.2.0"] - - [Xqcicsr, "0.3.0"] - - [Xqciint, "0.4.0"] - - [Xqcilb, "0.2.0"] - - [Xqcili, "0.2.0"] - - [Xqcilia, "0.2.0"] - - [Xqcilo, "0.2.0"] - - [Xqcilsm, "0.4.0"] - - [Xqcisim, "0.2.0"] - - [Xqcisls, "0.2.0"] - - [Xqcisync, "0.2.0"] + - { name: Xqcia, version: "0.5.0" } + - { name: Xqciac, version: "0.3.0" } + - { name: Xqcibi, version: "0.2.0" } + - { name: Xqcibm, version: "0.5.0" } + - { name: Xqcicli, version: "0.2.0" } + - { name: Xqcicm, version: "0.2.0" } + - { name: Xqcics, version: "0.2.0" } + - { name: Xqcicsr, version: "0.3.0" } + - { name: Xqciint, version: "0.4.0" } + - { name: Xqciio, version: "0.1.0" } + - { name: Xqcilb, version: "0.2.0" } + - { name: Xqcili, version: "0.2.0" } + - { name: Xqcilia, version: "0.2.0" } + - { name: Xqcilo, version: "0.2.0" } + - { name: Xqcilsm, version: "0.4.0" } + - { name: Xqcisim, version: "0.2.0" } + - { name: Xqcisls, version: "0.2.0" } + - { name: Xqcisync, version: "0.2.0" } + requires: + name: Zca + version: ">= 1.0.0" +- version: "0.8.0" + state: frozen + ratification_date: null + contributors: + - name: Albert Yosher + company: Qualcomm Technologies, Inc. + email: ayosher@qti.qualcomm.com + - name: Derek Hower + company: Qualcomm Technologies, Inc. + email: dhower@qti.qualcomm.com + changes: + - Fix rs1 cannot be 31 for qc.extdu, qc.extd, qc.extdur, instructions + - Fix rs1 cannot be 31 for qc.extduprh, qc.extdprh, qc.extdupr, qc.extdr qc.extdpr instructions + - Fix typos in IDL code (missing ')' ) for qc.extdpr, qc.extdr instructions + - Fix IDL code to look correct in PDF for qc.insbhr and qc.insbh instructions + - Fix Xqci extension description to reflect correct 48-bit format field names + - Fix IDL code to to match description for qc.insbr instruction + - Add stack checks to qc.c.mienter, qc.c.mienter.nest, qc.c.mileaveret + implies: + - { name: Xqcia, version: "0.5.0" } + - { name: Xqciac, version: "0.3.0" } + - { name: Xqcibi, version: "0.2.0" } + - { name: Xqcibm, version: "0.6.0" } + - { name: Xqcicli, version: "0.2.0" } + - { name: Xqcicm, version: "0.2.0" } + - { name: Xqcics, version: "0.2.0" } + - { name: Xqcicsr, version: "0.3.0" } + - { name: Xqciint, version: "0.5.0" } + - { name: Xqciio, version: "0.1.0" } + - { name: Xqcilb, version: "0.2.0" } + - { name: Xqcili, version: "0.2.0" } + - { name: Xqcilia, version: "0.2.0" } + - { name: Xqcilo, version: "0.2.0" } + - { name: Xqcilsm, version: "0.4.0" } + - { name: Xqcisim, version: "0.2.0" } + - { name: Xqcisls, version: "0.2.0" } + - { name: Xqcisync, version: "0.2.0" } requires: name: Zca version: ">= 1.0.0" @@ -286,7 +330,7 @@ description: | QC.EAI format used for 48-bit instructions that operate on 32-bit immediate argument. -- - [%autowidth, cols="4*", options="header"] + [%autowidth, cols="4*", options="header" } |=== ^|Field ^|Start bit @@ -303,7 +347,7 @@ description: | ^|5 |Destination register - |func3 + |funct3 ^|12 ^|3 |Function field identifying instruction group @@ -323,7 +367,7 @@ description: | QC.EI format used for 48-bit instructions that operate on 26-bit immediate argument, including loads. -- - [%autowidth, cols="4*", options="header"] + [%autowidth, cols="4*", options="header" } |=== ^|Field ^|Start bit @@ -340,7 +384,7 @@ description: | ^|5 |Destination register - |func3 + |funct3 ^|12 ^|3 |Function field identifying instruction group @@ -355,7 +399,7 @@ description: | ^|10 |Immediate operand of 26 bits, the 10 LSBs - |func2 + |funct2 ^|30 ^|2 |Secondary function field @@ -370,7 +414,7 @@ description: | QC.EB format used for 48-bit branch instructions that compare register with 16-bit immediate. -- - [%autowidth, cols="4*", options="header"] + [%autowidth, cols="4*", options="header" } |=== ^|Field ^|Start bit @@ -387,7 +431,7 @@ description: | ^|5 |Bits of immediate value of branch target - |func3 + |funct3 ^|12 ^|3 |Function field identifying instruction group @@ -397,7 +441,7 @@ description: | ^|5 |Register argument - |func5 + |funct5 ^|20 ^|5 |Secondary function field @@ -417,7 +461,7 @@ description: | QC.EJ format used for 48-bit jump/call instructions with 32-bit immediate target address. -- - [%autowidth, cols="4*", options="header"] + [%autowidth, cols="4*", options="header" } |=== ^|Field ^|Start bit @@ -434,12 +478,12 @@ description: | ^|5 |Bits of immediate value of branch target - |func3 + |funct3 ^|12 ^|3 |Function field identifying instruction group - |func2 + |funct2 ^|15 ^|2 |Secondary function field @@ -449,7 +493,7 @@ description: | ^|3 |Bits of immediate value of branch target - |func5 + |funct5 ^|20 ^|5 |Secondary function field @@ -483,7 +527,7 @@ description: | ^|5 |Immediate operand of 26 bits offset, the 5 LSBs - |func3 + |funct3 ^|12 ^|3 |Function field identifying instruction group @@ -503,7 +547,7 @@ description: | ^|5 |Immediate operand of 26 bits offset, the 5 bits - |func2 + |funct2 ^|30 ^|2 |Secondary function field @@ -519,4 +563,4 @@ doc_license: company: name: Qualcomm Technologies, Inc. url: https://qualcomm.com -conflicts: [D] +conflicts: D diff --git a/cfgs/qc_iu/arch_overlay/ext/Xqcia.yaml b/arch_overlay/qc_iu/ext/Xqcia.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/ext/Xqcia.yaml rename to arch_overlay/qc_iu/ext/Xqcia.yaml diff --git a/cfgs/qc_iu/arch_overlay/ext/Xqciac.yaml b/arch_overlay/qc_iu/ext/Xqciac.yaml similarity index 97% rename from cfgs/qc_iu/arch_overlay/ext/Xqciac.yaml rename to arch_overlay/qc_iu/ext/Xqciac.yaml index 7caa649484..38916ece3d 100644 --- a/cfgs/qc_iu/arch_overlay/ext/Xqciac.yaml +++ b/arch_overlay/qc_iu/ext/Xqciac.yaml @@ -47,7 +47,10 @@ versions: description: | The Xqciac extension includes three instructions to accelerate common address calculations. -conflicts: [D] +conflicts: + anyOf: + - allOf: [C, D] + - Zcd doc_license: name: Creative Commons Attribution 4.0 International License url: https://creativecommons.org/licenses/by/4.0/ diff --git a/cfgs/qc_iu/arch_overlay/ext/Xqcibi.yaml b/arch_overlay/qc_iu/ext/Xqcibi.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/ext/Xqcibi.yaml rename to arch_overlay/qc_iu/ext/Xqcibi.yaml diff --git a/cfgs/qc_iu/arch_overlay/ext/Xqcibm.yaml b/arch_overlay/qc_iu/ext/Xqcibm.yaml similarity index 77% rename from cfgs/qc_iu/arch_overlay/ext/Xqcibm.yaml rename to arch_overlay/qc_iu/ext/Xqcibm.yaml index 5eec173530..290c76445e 100644 --- a/cfgs/qc_iu/arch_overlay/ext/Xqcibm.yaml +++ b/arch_overlay/qc_iu/ext/Xqcibm.yaml @@ -71,6 +71,23 @@ versions: - Change width calculations for qc.extdpr, qc.extdprh, qc.extdr, qc.extdupr, qc.extduprh, qc.extdur, - Change width calculations for qc.insbhr, qc.insbpr, qc.insbprh, qc.insbr, qc.insbri requires: { name: Zca, version: ">= 1.0.0" } +- version: "0.6.0" + state: frozen + ratification_date: null + contributors: + - name: Albert Yosher + company: Qualcomm Technologies, Inc. + email: ayosher@qti.qualcomm.com + - name: Derek Hower + company: Qualcomm Technologies, Inc. + email: dhower@qti.qualcomm.com + changes: + - Fix rs1 cannot be 31 for qc.extdu, qc.extd, qc.extdur, instructions + - Fix rs1 cannot be 31 for qc.extduprh, qc.extdprh, qc.extdupr, qc.extdr qc.extdpr instructions + - Fix typos in IDL code (missing ')' ) for qc.extdpr, qc.extdr instructions + - Fix IDL code to look correct in PDF for qc.insbhr and qc.insbh instructions + - Fix IDL code to to match description for qc.insbr instruction + requires: { name: Zca, version: ">= 1.0.0" } description: | The Xqcibm extension includes thirty eight instructions that perform bit manipulation, include insertion and extraction. diff --git a/cfgs/qc_iu/arch_overlay/ext/Xqcicli.yaml b/arch_overlay/qc_iu/ext/Xqcicli.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/ext/Xqcicli.yaml rename to arch_overlay/qc_iu/ext/Xqcicli.yaml diff --git a/cfgs/qc_iu/arch_overlay/ext/Xqcicm.yaml b/arch_overlay/qc_iu/ext/Xqcicm.yaml similarity index 95% rename from cfgs/qc_iu/arch_overlay/ext/Xqcicm.yaml rename to arch_overlay/qc_iu/ext/Xqcicm.yaml index a93b092698..b9f4cec827 100644 --- a/cfgs/qc_iu/arch_overlay/ext/Xqcicm.yaml +++ b/arch_overlay/qc_iu/ext/Xqcicm.yaml @@ -32,6 +32,10 @@ versions: description: | The Xqcicm extension includes thirteen conditional move instructions. +conflicts: + anyOf: + - allOf: [C, D] + - Zcd doc_license: name: Creative Commons Attribution 4.0 International License url: https://creativecommons.org/licenses/by/4.0/ diff --git a/cfgs/qc_iu/arch_overlay/ext/Xqcics.yaml b/arch_overlay/qc_iu/ext/Xqcics.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/ext/Xqcics.yaml rename to arch_overlay/qc_iu/ext/Xqcics.yaml diff --git a/cfgs/qc_iu/arch_overlay/ext/Xqcicsr.yaml b/arch_overlay/qc_iu/ext/Xqcicsr.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/ext/Xqcicsr.yaml rename to arch_overlay/qc_iu/ext/Xqcicsr.yaml diff --git a/cfgs/qc_iu/arch_overlay/ext/Xqciint.yaml b/arch_overlay/qc_iu/ext/Xqciint.yaml similarity index 84% rename from cfgs/qc_iu/arch_overlay/ext/Xqciint.yaml rename to arch_overlay/qc_iu/ext/Xqciint.yaml index 679b30761c..61b0c62f01 100644 --- a/cfgs/qc_iu/arch_overlay/ext/Xqciint.yaml +++ b/arch_overlay/qc_iu/ext/Xqciint.yaml @@ -42,6 +42,7 @@ versions: changes: - Fix description of qc.mclici* CSRs to reflect being part of Xqciint custom extension - Fix description of qc.setinti and qc.clrinti instructions + - Fix to make qc.c.mret and qc.c.mnret visible requires: { name: Zca, version: ">= 1.0.0" } - version: "0.4.0" state: frozen @@ -62,9 +63,23 @@ versions: - Add missing CSR registers qc.mmcr, qc.mthreadptr, qc.mcause - Remove CSR registers qc.mncause, qc.mnepc - Fix IDL code for qc.c.mienter, qc.c.mienter.nest, qc.c.mileaveret, qc.c.mret, qc.c.mnret + - Fix IDL code for qc.c.ei, qc.c.eir, qc.c.di, qc.c.dir - Add list of supported custom exceptions - Add dependency on Smrnmi extension requires: { name: Zca, version: ">= 1.0.0" } +- version: "0.5.0" + state: frozen + ratification_date: null + contributors: + - name: Albert Yosher + company: Qualcomm Technologies, Inc. + email: ayosher@qti.qualcomm.com + - name: Derek Hower + company: Qualcomm Technologies, Inc. + email: dhower@qti.qualcomm.com + changes: + - Add stack checks to qc.c.mienter, qc.c.mienter.nest, qc.c.mileaveret + requires: { name: Zca, version: ">= 1.0.0" } description: | The Xqciint extension includes eleven instructions to accelerate interrupt servicing by performing common actions during ISR prologue/epilogue. diff --git a/cfgs/qc_iu/arch_overlay/ext/Xqciio.yaml b/arch_overlay/qc_iu/ext/Xqciio.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/ext/Xqciio.yaml rename to arch_overlay/qc_iu/ext/Xqciio.yaml diff --git a/cfgs/qc_iu/arch_overlay/ext/Xqcilb.yaml b/arch_overlay/qc_iu/ext/Xqcilb.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/ext/Xqcilb.yaml rename to arch_overlay/qc_iu/ext/Xqcilb.yaml diff --git a/cfgs/qc_iu/arch_overlay/ext/Xqcili.yaml b/arch_overlay/qc_iu/ext/Xqcili.yaml similarity index 92% rename from cfgs/qc_iu/arch_overlay/ext/Xqcili.yaml rename to arch_overlay/qc_iu/ext/Xqcili.yaml index fa5c702ad6..97517a6fb1 100644 --- a/cfgs/qc_iu/arch_overlay/ext/Xqcili.yaml +++ b/arch_overlay/qc_iu/ext/Xqcili.yaml @@ -16,6 +16,7 @@ versions: - name: Derek Hower company: Qualcomm Technologies, Inc. email: dhower@qti.qualcomm.com + requires: { name: Zca, version: ">= 1.0.0" } - version: "0.2.0" state: frozen ratification_date: null @@ -28,6 +29,7 @@ versions: email: dhower@qti.qualcomm.com changes: - Add information about instruction formats of each instruction + requires: { name: Zca, version: ">= 1.0.0" } description: | The Xqcili extension includes a two instructions that load large immediates than is available with the base RISC-V ISA. diff --git a/cfgs/qc_iu/arch_overlay/ext/Xqcilia.yaml b/arch_overlay/qc_iu/ext/Xqcilia.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/ext/Xqcilia.yaml rename to arch_overlay/qc_iu/ext/Xqcilia.yaml diff --git a/cfgs/qc_iu/arch_overlay/ext/Xqcilo.yaml b/arch_overlay/qc_iu/ext/Xqcilo.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/ext/Xqcilo.yaml rename to arch_overlay/qc_iu/ext/Xqcilo.yaml diff --git a/cfgs/qc_iu/arch_overlay/ext/Xqcilsm.yaml b/arch_overlay/qc_iu/ext/Xqcilsm.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/ext/Xqcilsm.yaml rename to arch_overlay/qc_iu/ext/Xqcilsm.yaml diff --git a/cfgs/qc_iu/arch_overlay/ext/Xqcisim.yaml b/arch_overlay/qc_iu/ext/Xqcisim.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/ext/Xqcisim.yaml rename to arch_overlay/qc_iu/ext/Xqcisim.yaml diff --git a/cfgs/qc_iu/arch_overlay/ext/Xqcisls.yaml b/arch_overlay/qc_iu/ext/Xqcisls.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/ext/Xqcisls.yaml rename to arch_overlay/qc_iu/ext/Xqcisls.yaml diff --git a/cfgs/qc_iu/arch_overlay/ext/Xqcisync.yaml b/arch_overlay/qc_iu/ext/Xqcisync.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/ext/Xqcisync.yaml rename to arch_overlay/qc_iu/ext/Xqcisync.yaml diff --git a/arch_overlay/qc_iu/inst/C/c.slli.yaml b/arch_overlay/qc_iu/inst/C/c.slli.yaml new file mode 100644 index 0000000000..5bc677a75c --- /dev/null +++ b/arch_overlay/qc_iu/inst/C/c.slli.yaml @@ -0,0 +1,3 @@ +hints: + - { $ref: inst/Xqci/qc.c.delay.yaml# } + - { $ref: inst/Xqci/qc.c.ptrace.yaml# } diff --git a/arch_overlay/qc_iu/inst/C/c.srai.yaml b/arch_overlay/qc_iu/inst/C/c.srai.yaml new file mode 100644 index 0000000000..ed08a00625 --- /dev/null +++ b/arch_overlay/qc_iu/inst/C/c.srai.yaml @@ -0,0 +1,2 @@ +hints: + - { $ref: inst/Xqci/qc.c.syncr.yaml# } diff --git a/arch_overlay/qc_iu/inst/C/c.srli.yaml b/arch_overlay/qc_iu/inst/C/c.srli.yaml new file mode 100644 index 0000000000..36daeec3d0 --- /dev/null +++ b/arch_overlay/qc_iu/inst/C/c.srli.yaml @@ -0,0 +1,2 @@ +hints: + - { $ref: inst/Xqci/qc.c.sync.yaml# } diff --git a/arch_overlay/qc_iu/inst/I/slti.yaml b/arch_overlay/qc_iu/inst/I/slti.yaml new file mode 100644 index 0000000000..6e2ff5212b --- /dev/null +++ b/arch_overlay/qc_iu/inst/I/slti.yaml @@ -0,0 +1,12 @@ +# add hints using slti + +hints: + - { $ref: inst/Xqci/qc.pcoredump.yaml# } + - { $ref: inst/Xqci/qc.psyscalli.yaml# } + - { $ref: inst/Xqci/qc.pputci.yaml# } + - { $ref: inst/Xqci/qc.ppregs.yaml# } + - { $ref: inst/Xqci/qc.ppreg.yaml# } + - { $ref: inst/Xqci/qc.pputc.yaml# } + - { $ref: inst/Xqci/qc.pputs.yaml# } + - { $ref: inst/Xqci/qc.psyscall.yaml# } + - { $ref: inst/Xqci/qc.pexit.yaml# } diff --git a/arch_overlay/qc_iu/inst/I/sltiu.yaml b/arch_overlay/qc_iu/inst/I/sltiu.yaml new file mode 100644 index 0000000000..d553cf494b --- /dev/null +++ b/arch_overlay/qc_iu/inst/I/sltiu.yaml @@ -0,0 +1,5 @@ +hints: + - { $ref: inst/Xqci/qc.sync.yaml# } + - { $ref: inst/Xqci/qc.syncr.yaml# } + - { $ref: inst/Xqci/qc.syncwf.yaml# } + - { $ref: inst/Xqci/qc.syncwl.yaml# } diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqccmp/qc.cm.mva01s.yaml b/arch_overlay/qc_iu/inst/Xqccmp/qc.cm.mva01s.yaml similarity index 85% rename from cfgs/qc_iu/arch_overlay/inst/Xqccmp/qc.cm.mva01s.yaml rename to arch_overlay/qc_iu/inst/Xqccmp/qc.cm.mva01s.yaml index f6eef31eb4..0327c1a482 100644 --- a/cfgs/qc_iu/arch_overlay/inst/Xqccmp/qc.cm.mva01s.yaml +++ b/arch_overlay/qc_iu/inst/Xqccmp/qc.cm.mva01s.yaml @@ -1,4 +1,4 @@ -# yaml-language-server: $schema=../../../../../schemas/inst_schema.json +# yaml-language-server: $schema=../../../../schemas/inst_schema.json $schema: "inst_schema.json#" kind: instruction @@ -7,14 +7,7 @@ long_name: Move two s0-s7 registers into a0-a1 description: | This instruction moves r1s' into a0 and r2s' into a1. The execution is atomic, so it is not possible to observe state where only one of a0 or a1 have been updated. The encoding uses sreg number specifiers instead of xreg number specifiers to save encoding space. The mapping between them is specified in the pseudo-code below. -definedBy: - anyOf: - - Xqccmp -excludedBy: - anyOf: - - allOf: [C, D] - - Zcd - - Zcmp +definedBy: Xqccmp assembly: r1s, r2s encoding: match: 101011---11---10 diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqccmp/qc.cm.mvsa01.yaml b/arch_overlay/qc_iu/inst/Xqccmp/qc.cm.mvsa01.yaml similarity index 85% rename from cfgs/qc_iu/arch_overlay/inst/Xqccmp/qc.cm.mvsa01.yaml rename to arch_overlay/qc_iu/inst/Xqccmp/qc.cm.mvsa01.yaml index 2fbb3d1f64..6c69d0f4df 100644 --- a/cfgs/qc_iu/arch_overlay/inst/Xqccmp/qc.cm.mvsa01.yaml +++ b/arch_overlay/qc_iu/inst/Xqccmp/qc.cm.mvsa01.yaml @@ -1,4 +1,4 @@ -# yaml-language-server: $schema=../../../../../schemas/inst_schema.json +# yaml-language-server: $schema=../../../../schemas/inst_schema.json $schema: "inst_schema.json#" kind: instruction @@ -9,14 +9,7 @@ description: | The execution is atomic, so it is not possible to observe state where only one of r1s' or r2s' has been updated. The encoding uses sreg number specifiers instead of xreg number specifiers to save encoding space. The mapping between them is specified in the pseudo-code below. -definedBy: - anyOf: - - Xqccmp -excludedBy: - anyOf: - - allOf: [C, D] - - Zcd - - Zcmp +definedBy: Xqccmp assembly: r1s, r2s encoding: match: 101011---01---10 diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqccmp/qc.cm.pop.yaml b/arch_overlay/qc_iu/inst/Xqccmp/qc.cm.pop.yaml similarity index 90% rename from cfgs/qc_iu/arch_overlay/inst/Xqccmp/qc.cm.pop.yaml rename to arch_overlay/qc_iu/inst/Xqccmp/qc.cm.pop.yaml index 034a7bfd4b..b149fd35b1 100644 --- a/cfgs/qc_iu/arch_overlay/inst/Xqccmp/qc.cm.pop.yaml +++ b/arch_overlay/qc_iu/inst/Xqccmp/qc.cm.pop.yaml @@ -1,4 +1,4 @@ -# yaml-language-server: $schema=../../../../../schemas/inst_schema.json +# yaml-language-server: $schema=../../../../schemas/inst_schema.json $schema: "inst_schema.json#" kind: instruction @@ -14,14 +14,7 @@ description: | * it must be a multiple of 16 (bytes): ** for RV32 the allowed values are: 16, 32, 48, 64, 80, 96, 112 ** for RV64 the allowed values are: 16, 32, 48, 64, 80, 96, 112, 128, 144, 160 -definedBy: - anyOf: - - Xqccmp -excludedBy: - anyOf: - - allOf: [C, D] - - Zcd - - Zcmp +definedBy: Xqccmp assembly: reg_list, stack_adj encoding: match: 10111010------10 @@ -41,8 +34,8 @@ operation(): | raise(ExceptionCode::IllegalInstruction, mode(), $encoding); } - XReg virtual_address_sp = get_and_validate_stack_pointer($encoding); - XReg size = xlen(); + XReg virtual_address_sp = get_and_validate_stack_pointer(X[2], $encoding); + XReg size = xlen() / 8; XReg nreg = (rlist == 15) ? 13 : (rlist - 3); XReg stack_aligned_adj = (nreg * size + 15) & ~0xF; XReg virtual_address_new_sp = virtual_address_sp + stack_aligned_adj + spimm; diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqccmp/qc.cm.popret.yaml b/arch_overlay/qc_iu/inst/Xqccmp/qc.cm.popret.yaml similarity index 90% rename from cfgs/qc_iu/arch_overlay/inst/Xqccmp/qc.cm.popret.yaml rename to arch_overlay/qc_iu/inst/Xqccmp/qc.cm.popret.yaml index 5d451e7a22..cefaacab1b 100644 --- a/cfgs/qc_iu/arch_overlay/inst/Xqccmp/qc.cm.popret.yaml +++ b/arch_overlay/qc_iu/inst/Xqccmp/qc.cm.popret.yaml @@ -1,4 +1,4 @@ -# yaml-language-server: $schema=../../../../../schemas/inst_schema.json +# yaml-language-server: $schema=../../../../schemas/inst_schema.json $schema: "inst_schema.json#" kind: instruction @@ -14,14 +14,7 @@ description: | * it must be a multiple of 16 (bytes): ** for RV32 the allowed values are: 16, 32, 48, 64, 80, 96, 112 ** for RV64 the allowed values are: 16, 32, 48, 64, 80, 96, 112, 128, 144, 160 -definedBy: - anyOf: - - Xqccmp -excludedBy: - anyOf: - - allOf: [C, D] - - Zcd - - Zcmp +definedBy: Xqccmp assembly: reg_list, stack_adj encoding: match: 10111110------10 @@ -41,8 +34,8 @@ operation(): | raise(ExceptionCode::IllegalInstruction, mode(), $encoding); } - XReg virtual_address_sp = get_and_validate_stack_pointer($encoding); - XReg size = xlen(); + XReg virtual_address_sp = get_and_validate_stack_pointer(X[2], $encoding); + XReg size = xlen() / 8; XReg nreg = (rlist == 15) ? 13 : (rlist - 3); XReg stack_aligned_adj = (nreg * size + 15) & ~0xF; XReg virtual_address_new_sp = virtual_address_sp + stack_aligned_adj + spimm; diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqccmp/qc.cm.popretz.yaml b/arch_overlay/qc_iu/inst/Xqccmp/qc.cm.popretz.yaml similarity index 91% rename from cfgs/qc_iu/arch_overlay/inst/Xqccmp/qc.cm.popretz.yaml rename to arch_overlay/qc_iu/inst/Xqccmp/qc.cm.popretz.yaml index 894bf1c15d..54a97188f5 100644 --- a/cfgs/qc_iu/arch_overlay/inst/Xqccmp/qc.cm.popretz.yaml +++ b/arch_overlay/qc_iu/inst/Xqccmp/qc.cm.popretz.yaml @@ -1,4 +1,4 @@ -# yaml-language-server: $schema=../../../../../schemas/inst_schema.json +# yaml-language-server: $schema=../../../../schemas/inst_schema.json $schema: "inst_schema.json#" kind: instruction @@ -14,14 +14,7 @@ description: | * it must be a multiple of 16 (bytes): ** for RV32 the allowed values are: 16, 32, 48, 64, 80, 96, 112 ** for RV64 the allowed values are: 16, 32, 48, 64, 80, 96, 112, 128, 144, 160 -definedBy: - anyOf: - - Xqccmp -excludedBy: - anyOf: - - allOf: [C, D] - - Zcd - - Zcmp +definedBy: Xqccmp assembly: reg_list, stack_adj encoding: match: 10111100------10 @@ -41,8 +34,8 @@ operation(): | raise(ExceptionCode::IllegalInstruction, mode(), $encoding); } - XReg virtual_address_sp = get_and_validate_stack_pointer($encoding); - XReg size = xlen(); + XReg virtual_address_sp = get_and_validate_stack_pointer(X[2], $encoding); + XReg size = xlen() / 8; XReg nreg = (rlist == 15) ? 13 : (rlist - 3); XReg stack_aligned_adj = (nreg * size + 15) & ~0xF; XReg virtual_address_new_sp = virtual_address_sp + stack_aligned_adj + spimm; diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqccmp/qc.cm.push.yaml b/arch_overlay/qc_iu/inst/Xqccmp/qc.cm.push.yaml similarity index 91% rename from cfgs/qc_iu/arch_overlay/inst/Xqccmp/qc.cm.push.yaml rename to arch_overlay/qc_iu/inst/Xqccmp/qc.cm.push.yaml index 94a053394e..272e9f440f 100644 --- a/cfgs/qc_iu/arch_overlay/inst/Xqccmp/qc.cm.push.yaml +++ b/arch_overlay/qc_iu/inst/Xqccmp/qc.cm.push.yaml @@ -1,4 +1,4 @@ -# yaml-language-server: $schema=../../../../../schemas/inst_schema.json +# yaml-language-server: $schema=../../../../schemas/inst_schema.json $schema: "inst_schema.json#" kind: instruction @@ -15,14 +15,7 @@ description: | * it must be a multiple of 16 (bytes): ** for RV32 the allowed values are: 16, 32, 48, 64, 80, 96, 112 ** for RV64 the allowed values are: 16, 32, 48, 64, 80, 96, 112, 128, 144, 160 -definedBy: - anyOf: - - Xqccmp -excludedBy: - anyOf: - - allOf: [C, D] - - Zcd - - Zcmp +definedBy: Xqccmp assembly: reg_list, -stack_adj encoding: match: 10111000------10 @@ -43,8 +36,8 @@ operation(): | raise(ExceptionCode::IllegalInstruction, mode(), $encoding); } - XReg virtual_address_sp = get_and_validate_stack_pointer($encoding); - XReg size = xlen(); + XReg virtual_address_sp = get_and_validate_stack_pointer(X[2], $encoding); + XReg size = xlen() / 8; XReg nreg = (rlist == 15) ? 13 : (rlist - 3); XReg stack_aligned_adj = (nreg * size + 15) & ~0xF; XReg virtual_address_new_sp = virtual_address_sp - stack_aligned_adj - spimm; diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqccmp/qc.cm.pushfp.yaml b/arch_overlay/qc_iu/inst/Xqccmp/qc.cm.pushfp.yaml similarity index 91% rename from cfgs/qc_iu/arch_overlay/inst/Xqccmp/qc.cm.pushfp.yaml rename to arch_overlay/qc_iu/inst/Xqccmp/qc.cm.pushfp.yaml index 9db67ac036..823020b7ff 100644 --- a/cfgs/qc_iu/arch_overlay/inst/Xqccmp/qc.cm.pushfp.yaml +++ b/arch_overlay/qc_iu/inst/Xqccmp/qc.cm.pushfp.yaml @@ -1,4 +1,4 @@ -# yaml-language-server: $schema=../../../../../schemas/inst_schema.json +# yaml-language-server: $schema=../../../../schemas/inst_schema.json $schema: "inst_schema.json#" kind: instruction @@ -16,14 +16,7 @@ description: | * it must be a multiple of 16 (bytes): ** for RV32 the allowed values are: 16, 32, 48, 64, 80, 96, 112 ** for RV64 the allowed values are: 16, 32, 48, 64, 80, 96, 112, 128, 144, 160 -definedBy: - anyOf: - - Xqccmp -excludedBy: - anyOf: - - allOf: [C, D] - - Zcd - - Zcmp +definedBy: Xqccmp assembly: reg_list, -stack_adj encoding: match: 10111001------10 @@ -43,8 +36,8 @@ operation(): | raise(ExceptionCode::IllegalInstruction, mode(), $encoding); } - XReg virtual_address_sp = get_and_validate_stack_pointer($encoding); - XReg size = xlen(); + XReg virtual_address_sp = get_and_validate_stack_pointer(X[2], $encoding); + XReg size = xlen() / 8; XReg nreg = (rlist == 15) ? 13 : (rlist - 3); XReg stack_aligned_adj = (nreg * size + 15) & ~0xF; XReg virtual_address_new_sp = virtual_address_sp - stack_aligned_adj - spimm; diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.addsat.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.addsat.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.addsat.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.addsat.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.addusat.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.addusat.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.addusat.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.addusat.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.beqi.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.beqi.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.beqi.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.beqi.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.bgei.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.bgei.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.bgei.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.bgei.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.bgeui.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.bgeui.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.bgeui.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.bgeui.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.blti.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.blti.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.blti.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.blti.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.bltui.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.bltui.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.bltui.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.bltui.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.bnei.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.bnei.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.bnei.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.bnei.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.brev32.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.brev32.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.brev32.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.brev32.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.c.bexti.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.c.bexti.yaml similarity index 96% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.c.bexti.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.c.bexti.yaml index b3faa3d87c..f3ae2c1c4b 100644 --- a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.c.bexti.yaml +++ b/arch_overlay/qc_iu/inst/Xqci/qc.c.bexti.yaml @@ -29,5 +29,5 @@ access: vu: always operation(): | XReg index = shamt & (xlen() - 1); - XReg reg = rd + 8; + XReg reg = creg2reg(rd); X[reg] = (X[reg] >> index) & 1; diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.c.bseti.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.c.bseti.yaml similarity index 96% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.c.bseti.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.c.bseti.yaml index f1bf5140a5..ad505c9fac 100644 --- a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.c.bseti.yaml +++ b/arch_overlay/qc_iu/inst/Xqci/qc.c.bseti.yaml @@ -29,5 +29,5 @@ access: vu: always operation(): | XReg index = shamt & (xlen() - 1); - XReg reg = rd + 8; + XReg reg = creg2reg(rd); X[reg] = X[reg] | (1 << index); diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.c.clrint.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.c.clrint.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.c.clrint.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.c.clrint.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.c.delay.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.c.delay.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.c.delay.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.c.delay.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.c.di.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.c.di.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.c.di.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.c.di.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.c.dir.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.c.dir.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.c.dir.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.c.dir.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.c.ei.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.c.ei.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.c.ei.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.c.ei.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.c.eir.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.c.eir.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.c.eir.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.c.eir.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.c.extu.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.c.extu.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.c.extu.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.c.extu.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.c.mienter.nest.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.c.mienter.nest.yaml similarity index 96% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.c.mienter.nest.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.c.mienter.nest.yaml index 745579e5d4..b033799f2b 100644 --- a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.c.mienter.nest.yaml +++ b/arch_overlay/qc_iu/inst/Xqci/qc.c.mienter.nest.yaml @@ -21,7 +21,7 @@ base: 32 encoding: match: "0001100010010010" operation(): | - XReg virtual_address = X[2]; + XReg virtual_address = get_and_validate_stack_pointer(X[2], $encoding); XReg mepc_val = CSR[mepc].sw_read(); XReg mnepc_val = CSR[mnepc].sw_read(); XReg qc_mcause_val = CSR[qc.mcause].sw_read(); diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.c.mienter.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.c.mienter.yaml similarity index 96% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.c.mienter.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.c.mienter.yaml index ad96a534c6..4c02ce9e05 100644 --- a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.c.mienter.yaml +++ b/arch_overlay/qc_iu/inst/Xqci/qc.c.mienter.yaml @@ -23,7 +23,7 @@ base: 32 encoding: match: "0001100000010010" operation(): | - XReg virtual_address = X[2]; + XReg virtual_address = get_and_validate_stack_pointer(X[2], $encoding); XReg mepc_val = CSR[mepc].sw_read(); XReg mnepc_val = CSR[mnepc].sw_read(); XReg qc_mcause_val = CSR[qc.mcause].sw_read(); diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.c.mileaveret.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.c.mileaveret.yaml similarity index 96% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.c.mileaveret.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.c.mileaveret.yaml index 041713bcfe..dfe0859d46 100644 --- a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.c.mileaveret.yaml +++ b/arch_overlay/qc_iu/inst/Xqci/qc.c.mileaveret.yaml @@ -22,7 +22,8 @@ base: 32 encoding: match: "0001101000010010" operation(): | - XReg virtual_address = X[2] + 96; + XReg virtual_address_sp = get_and_validate_stack_pointer(X[2], $encoding); + XReg virtual_address = virtual_address_sp + 96; XReg prev_retpc = read_memory<32>(virtual_address - 4, $encoding); XReg qc_mcause_val = read_memory<32>(virtual_address - 12, $encoding); Bits<1> nmie_val = CSR[mnstatus].NMIE; diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.c.mnret.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.c.mnret.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.c.mnret.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.c.mnret.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.c.mret.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.c.mret.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.c.mret.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.c.mret.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.c.muliadd.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.c.muliadd.yaml similarity index 74% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.c.muliadd.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.c.muliadd.yaml index aaf9e433bc..85cc60a6eb 100644 --- a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.c.muliadd.yaml +++ b/arch_overlay/qc_iu/inst/Xqci/qc.c.muliadd.yaml @@ -1,4 +1,4 @@ -# yaml-language-server: $schema=../../../../../schemas/inst_schema.json +# yaml-language-server: $schema=../../../../schemas/inst_schema.json $schema: inst_schema.json# kind: instruction @@ -11,10 +11,6 @@ definedBy: anyOf: - Xqci - Xqciac -excludedBy: - anyOf: - - allOf: [C, D] - - Zcd base: 32 encoding: match: 001-----------10 @@ -32,5 +28,7 @@ access: vs: always vu: always operation(): | - XReg orig_val = X[rd+8]; - X[rd+8] = orig_val + (X[rs1+8] * uimm); + XReg reg = creg2reg(rd); + XReg src = creg2reg(rs1); + XReg orig_val = X[reg]; + X[reg] = orig_val + (X[src] * uimm); diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.c.mveqz.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.c.mveqz.yaml similarity index 69% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.c.mveqz.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.c.mveqz.yaml index 95b41c5e1c..9b8600a370 100644 --- a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.c.mveqz.yaml +++ b/arch_overlay/qc_iu/inst/Xqci/qc.c.mveqz.yaml @@ -1,4 +1,4 @@ -# yaml-language-server: $schema=../../../../../schemas/inst_schema.json +# yaml-language-server: $schema=../../../../schemas/inst_schema.json $schema: inst_schema.json# kind: instruction @@ -10,11 +10,7 @@ description: | definedBy: anyOf: - Xqci - - Xqciac -excludedBy: - anyOf: - - allOf: [C, D] - - Zcd + - Xqcicm base: 32 encoding: match: 101011---00---10 @@ -30,5 +26,7 @@ access: vs: always vu: always operation(): | - XReg orig_val = X[rd+8]; - X[rd+8] = (orig_val == 0) ? X[rs1+8] : orig_val; + XReg reg = creg2reg(rd); + XReg src = creg2reg(rs1); + XReg orig_val = X[reg]; + X[reg] = (orig_val == 0) ? X[src] : orig_val; diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.c.ptrace.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.c.ptrace.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.c.ptrace.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.c.ptrace.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.c.setint.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.c.setint.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.c.setint.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.c.setint.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.c.sync.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.c.sync.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.c.sync.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.c.sync.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.c.syncr.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.c.syncr.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.c.syncr.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.c.syncr.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.c.syncwf.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.c.syncwf.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.c.syncwf.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.c.syncwf.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.c.syncwl.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.c.syncwl.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.c.syncwl.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.c.syncwl.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.clo.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.clo.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.clo.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.clo.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.clrinti.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.clrinti.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.clrinti.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.clrinti.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.compress2.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.compress2.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.compress2.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.compress2.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.compress3.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.compress3.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.compress3.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.compress3.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.csrrwr.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.csrrwr.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.csrrwr.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.csrrwr.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.csrrwri.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.csrrwri.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.csrrwri.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.csrrwri.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.cto.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.cto.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.cto.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.cto.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.e.addai.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.e.addai.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.e.addai.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.e.addai.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.e.addi.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.e.addi.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.e.addi.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.e.addi.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.e.andai.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.e.andai.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.e.andai.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.e.andai.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.e.andi.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.e.andi.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.e.andi.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.e.andi.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.e.beqi.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.e.beqi.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.e.beqi.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.e.beqi.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.e.bgei.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.e.bgei.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.e.bgei.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.e.bgei.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.e.bgeui.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.e.bgeui.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.e.bgeui.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.e.bgeui.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.e.blti.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.e.blti.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.e.blti.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.e.blti.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.e.bltui.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.e.bltui.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.e.bltui.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.e.bltui.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.e.bnei.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.e.bnei.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.e.bnei.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.e.bnei.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.e.j.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.e.j.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.e.j.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.e.j.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.e.jal.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.e.jal.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.e.jal.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.e.jal.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.e.lb.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.e.lb.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.e.lb.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.e.lb.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.e.lbu.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.e.lbu.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.e.lbu.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.e.lbu.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.e.lh.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.e.lh.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.e.lh.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.e.lh.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.e.lhu.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.e.lhu.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.e.lhu.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.e.lhu.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.e.li.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.e.li.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.e.li.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.e.li.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.e.lw.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.e.lw.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.e.lw.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.e.lw.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.e.orai.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.e.orai.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.e.orai.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.e.orai.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.e.ori.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.e.ori.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.e.ori.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.e.ori.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.e.sb.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.e.sb.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.e.sb.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.e.sb.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.e.sh.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.e.sh.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.e.sh.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.e.sh.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.e.sw.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.e.sw.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.e.sw.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.e.sw.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.e.xorai.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.e.xorai.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.e.xorai.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.e.xorai.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.e.xori.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.e.xori.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.e.xori.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.e.xori.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.expand2.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.expand2.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.expand2.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.expand2.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.expand3.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.expand3.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.expand3.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.expand3.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.ext.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.ext.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.ext.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.ext.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.extd.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.extd.yaml similarity index 98% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.extd.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.extd.yaml index 55de009194..8a87f5119b 100644 --- a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.extd.yaml +++ b/arch_overlay/qc_iu/inst/Xqci/qc.extd.yaml @@ -23,6 +23,7 @@ encoding: location: 24-20 - name: rs1 location: 19-15 + not: 31 - name: rd location: 11-7 not: 0 diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.extdpr.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.extdpr.yaml similarity index 98% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.extdpr.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.extdpr.yaml index 68928fbe37..9137471e06 100644 --- a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.extdpr.yaml +++ b/arch_overlay/qc_iu/inst/Xqci/qc.extdpr.yaml @@ -24,6 +24,7 @@ encoding: not: 0 - name: rs1 location: 19-15 + not: 31 - name: rd location: 11-7 not: 0 diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.extdprh.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.extdprh.yaml similarity index 98% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.extdprh.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.extdprh.yaml index f867e06170..7b105c3108 100644 --- a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.extdprh.yaml +++ b/arch_overlay/qc_iu/inst/Xqci/qc.extdprh.yaml @@ -24,6 +24,7 @@ encoding: not: 0 - name: rs1 location: 19-15 + not: 31 - name: rd location: 11-7 not: 0 diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.extdr.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.extdr.yaml similarity index 98% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.extdr.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.extdr.yaml index 059bd12938..1e510665e8 100644 --- a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.extdr.yaml +++ b/arch_overlay/qc_iu/inst/Xqci/qc.extdr.yaml @@ -24,6 +24,7 @@ encoding: not: 0 - name: rs1 location: 19-15 + not: 31 - name: rd location: 11-7 not: 0 diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.extdu.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.extdu.yaml similarity index 98% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.extdu.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.extdu.yaml index 57f0ea22ee..88bcd46354 100644 --- a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.extdu.yaml +++ b/arch_overlay/qc_iu/inst/Xqci/qc.extdu.yaml @@ -23,6 +23,7 @@ encoding: location: 24-20 - name: rs1 location: 19-15 + not: 31 - name: rd location: 11-7 not: 0 diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.extdupr.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.extdupr.yaml similarity index 98% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.extdupr.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.extdupr.yaml index 33355efb73..5ff7acd6ee 100644 --- a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.extdupr.yaml +++ b/arch_overlay/qc_iu/inst/Xqci/qc.extdupr.yaml @@ -24,6 +24,7 @@ encoding: not: 0 - name: rs1 location: 19-15 + not: 31 - name: rd location: 11-7 not: 0 diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.extduprh.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.extduprh.yaml similarity index 98% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.extduprh.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.extduprh.yaml index 127392224c..74d875bd63 100644 --- a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.extduprh.yaml +++ b/arch_overlay/qc_iu/inst/Xqci/qc.extduprh.yaml @@ -24,6 +24,7 @@ encoding: not: 0 - name: rs1 location: 19-15 + not: 31 - name: rd location: 11-7 not: 0 diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.extdur.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.extdur.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.extdur.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.extdur.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.extu.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.extu.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.extu.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.extu.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.insb.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.insb.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.insb.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.insb.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.insbh.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.insbh.yaml similarity index 86% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.insbh.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.insbh.yaml index 38782be7f8..2a4471865e 100644 --- a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.insbh.yaml +++ b/arch_overlay/qc_iu/inst/Xqci/qc.insbh.yaml @@ -40,7 +40,9 @@ access: operation(): | XReg width = width_minus1 + 1; if (width + shamt > 32) { - XReg mask = ((1 << (width + shamt - 32)) - 1); + XReg shifted_one = 1 << (width + shamt - 32); + XReg mask = (shifted_one - 1); XReg orig_val = X[rd]; - X[rd] = (orig_val & ~mask) | ((X[rs1] >> (32 - shamt)) & mask); + XReg shifted_rs1 = X[rs1] >> (32 - shamt); + X[rd] = (orig_val & ~mask) | (shifted_rs1 & mask); } diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.insbhr.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.insbhr.yaml similarity index 87% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.insbhr.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.insbhr.yaml index 49b6c54fe4..b2270628df 100644 --- a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.insbhr.yaml +++ b/arch_overlay/qc_iu/inst/Xqci/qc.insbhr.yaml @@ -42,7 +42,9 @@ operation(): | XReg width = (width_bits > 32) ? 32 : width_bits; XReg shamt = X[rs2][4:0]; if ((width + shamt > 32) && (width > 0)) { - XReg mask = ((1 << (width + shamt - 32)) - 1); + XReg shifted_one = 1 << (width + shamt - 32); + XReg mask = (shifted_one - 1); XReg orig_val = X[rd]; - X[rd] = (orig_val & ~mask) | ((X[rs1] >> (32 - shamt)) & mask); + XReg shifted_rs1 = X[rs1] >> (32 - shamt); + X[rd] = (orig_val & ~mask) | (shifted_rs1 & mask); } diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.insbi.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.insbi.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.insbi.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.insbi.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.insbpr.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.insbpr.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.insbpr.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.insbpr.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.insbprh.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.insbprh.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.insbprh.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.insbprh.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.insbr.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.insbr.yaml similarity index 97% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.insbr.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.insbr.yaml index 92845cac6c..e88008a5d2 100644 --- a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.insbr.yaml +++ b/arch_overlay/qc_iu/inst/Xqci/qc.insbr.yaml @@ -34,7 +34,7 @@ access: vs: always vu: always operation(): | - XReg width_bits = X[rs2][5:0]; + XReg width_bits = X[rs2][21:16]; XReg width = (width_bits > 32) ? 32 : width_bits; XReg shamt = X[rs2][4:0]; if (width > 0) { diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.insbri.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.insbri.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.insbri.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.insbri.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.inw.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.inw.yaml similarity index 95% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.inw.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.inw.yaml index f782005207..6698c24b30 100644 --- a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.inw.yaml +++ b/arch_overlay/qc_iu/inst/Xqci/qc.inw.yaml @@ -32,4 +32,4 @@ access: vu: always operation(): | XReg device_address = X[rs1] + imm; - X[rd] = read_device<32>(device_address); + X[rd] = read_device_32(device_address); diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.li.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.li.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.li.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.li.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.lieq.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.lieq.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.lieq.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.lieq.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.lieqi.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.lieqi.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.lieqi.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.lieqi.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.lige.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.lige.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.lige.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.lige.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.ligei.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.ligei.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.ligei.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.ligei.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.ligeu.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.ligeu.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.ligeu.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.ligeu.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.ligeui.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.ligeui.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.ligeui.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.ligeui.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.lilt.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.lilt.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.lilt.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.lilt.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.lilti.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.lilti.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.lilti.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.lilti.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.liltu.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.liltu.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.liltu.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.liltu.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.liltui.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.liltui.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.liltui.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.liltui.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.line.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.line.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.line.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.line.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.linei.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.linei.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.linei.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.linei.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.lrb.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.lrb.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.lrb.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.lrb.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.lrbu.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.lrbu.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.lrbu.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.lrbu.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.lrh.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.lrh.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.lrh.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.lrh.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.lrhu.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.lrhu.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.lrhu.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.lrhu.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.lrw.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.lrw.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.lrw.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.lrw.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.lwm.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.lwm.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.lwm.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.lwm.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.lwmi.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.lwmi.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.lwmi.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.lwmi.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.muliadd.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.muliadd.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.muliadd.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.muliadd.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.mveq.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.mveq.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.mveq.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.mveq.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.mveqi.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.mveqi.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.mveqi.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.mveqi.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.mvge.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.mvge.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.mvge.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.mvge.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.mvgei.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.mvgei.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.mvgei.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.mvgei.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.mvgeu.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.mvgeu.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.mvgeu.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.mvgeu.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.mvgeui.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.mvgeui.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.mvgeui.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.mvgeui.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.mvlt.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.mvlt.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.mvlt.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.mvlt.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.mvlti.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.mvlti.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.mvlti.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.mvlti.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.mvltu.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.mvltu.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.mvltu.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.mvltu.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.mvltui.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.mvltui.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.mvltui.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.mvltui.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.mvne.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.mvne.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.mvne.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.mvne.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.mvnei.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.mvnei.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.mvnei.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.mvnei.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.norm.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.norm.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.norm.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.norm.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.normeu.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.normeu.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.normeu.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.normeu.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.normu.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.normu.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.normu.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.normu.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.outw.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.outw.yaml similarity index 95% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.outw.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.outw.yaml index aab60cca76..aef96e6c77 100644 --- a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.outw.yaml +++ b/arch_overlay/qc_iu/inst/Xqci/qc.outw.yaml @@ -32,4 +32,4 @@ access: vu: always operation(): | XReg device_address = X[rs1] + imm; - write_device<32>(device_address, X[rs2]); + write_device_32(device_address, X[rs2]); diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.pcoredump.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.pcoredump.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.pcoredump.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.pcoredump.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.pexit.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.pexit.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.pexit.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.pexit.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.ppreg.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.ppreg.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.ppreg.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.ppreg.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.ppregs.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.ppregs.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.ppregs.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.ppregs.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.pputc.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.pputc.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.pputc.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.pputc.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.pputci.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.pputci.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.pputci.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.pputci.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.pputs.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.pputs.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.pputs.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.pputs.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.psyscall.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.psyscall.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.psyscall.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.psyscall.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.psyscalli.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.psyscalli.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.psyscalli.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.psyscalli.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.selecteqi.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.selecteqi.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.selecteqi.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.selecteqi.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.selectieq.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.selectieq.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.selectieq.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.selectieq.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.selectieqi.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.selectieqi.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.selectieqi.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.selectieqi.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.selectiieq.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.selectiieq.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.selectiieq.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.selectiieq.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.selectiine.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.selectiine.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.selectiine.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.selectiine.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.selectine.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.selectine.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.selectine.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.selectine.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.selectinei.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.selectinei.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.selectinei.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.selectinei.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.selectnei.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.selectnei.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.selectnei.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.selectnei.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.setinti.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.setinti.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.setinti.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.setinti.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.setwm.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.setwm.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.setwm.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.setwm.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.setwmi.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.setwmi.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.setwmi.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.setwmi.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.shladd.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.shladd.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.shladd.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.shladd.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.shlsat.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.shlsat.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.shlsat.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.shlsat.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.shlusat.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.shlusat.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.shlusat.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.shlusat.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.srb.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.srb.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.srb.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.srb.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.srh.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.srh.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.srh.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.srh.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.srw.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.srw.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.srw.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.srw.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.subsat.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.subsat.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.subsat.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.subsat.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.subusat.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.subusat.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.subusat.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.subusat.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.swm.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.swm.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.swm.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.swm.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.swmi.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.swmi.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.swmi.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.swmi.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.sync.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.sync.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.sync.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.sync.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.syncr.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.syncr.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.syncr.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.syncr.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.syncwf.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.syncwf.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.syncwf.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.syncwf.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.syncwl.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.syncwl.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.syncwl.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.syncwl.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.wrap.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.wrap.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.wrap.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.wrap.yaml diff --git a/cfgs/qc_iu/arch_overlay/inst/Xqci/qc.wrapi.yaml b/arch_overlay/qc_iu/inst/Xqci/qc.wrapi.yaml similarity index 100% rename from cfgs/qc_iu/arch_overlay/inst/Xqci/qc.wrapi.yaml rename to arch_overlay/qc_iu/inst/Xqci/qc.wrapi.yaml diff --git a/cfgs/qc_iu/arch_overlay/isa/globals.isa b/arch_overlay/qc_iu/isa/globals.isa similarity index 69% rename from cfgs/qc_iu/arch_overlay/isa/globals.isa rename to arch_overlay/qc_iu/isa/globals.isa index 7aac254234..b09195f267 100644 --- a/cfgs/qc_iu/arch_overlay/isa/globals.isa +++ b/arch_overlay/qc_iu/isa/globals.isa @@ -1,6 +1,6 @@ %version: 1.0 -include "../../../../arch/isa/globals.isa" +include "../../../arch/isa/globals.isa" builtin function delay { arguments XReg cycles @@ -16,21 +16,19 @@ builtin function iss_syscall { } } -builtin function read_device { - template U32 len - returns Bits +builtin function read_device_32 { + returns Bits<32> arguments XReg dev_addr description { - Read from non-memory-mapped device. - Such devices have own addresses not related to memory map. + Read 32 bits from non-memory-mapped device. + Such devices have own addresses not related to memory map. } } -builtin function write_device { - template U32 len - arguments XReg dev_addr, Bits value +builtin function write_device_32 { + arguments XReg dev_addr, Bits<32> value description { - Write to non-memory-mapped device. + Write 32 bits to non-memory-mapped device. Such devices have own addresses not related to memory map. } } @@ -59,7 +57,7 @@ builtin function sync_write_after_read_device { function get_and_validate_stack_pointer { returns XReg - arguments Bits encoding + arguments XReg sp, Bits encoding description { Validate and return stack pointer. Stack pointer is validated to fit in range, defined by qc.mstktopaddr and qc.mstkbottomaddr CSRs. @@ -68,16 +66,15 @@ function get_and_validate_stack_pointer { In case when stack pointer is not 16-bytes aligned, raising exception IllegalStackPointer. } body { - XReg sp = X[2]; - if (sp < CSR[qc.mstkbottomaddr]) { - raise(ExceptionCode::SpOutOfRange, mode(), encoding); - } - else if (sp > CSR[qc.mstktopaddr]) { - raise(ExceptionCode::SpOutOfRange, mode(), encoding); - } - else if (sp & 0xf != 0) { - raise(ExceptionCode::IllegalStackPointer, mode(), encoding); - } - return sp; - } -} + if (sp < $bits(CSR[qc.mstkbottomaddr])) { + raise(ExceptionCode::SpOutOfRange, mode(), encoding); + } + else if (sp > $bits(CSR[qc.mstktopaddr])) { + raise(ExceptionCode::SpOutOfRange, mode(), encoding); + } + else if ((sp & 0xf) != 0) { + raise(ExceptionCode::IllegalStackPointer, mode(), encoding); + } + return sp; + } + } diff --git a/backends/certificate_doc/templates/certificate.adoc.erb b/backends/certificate_doc/templates/certificate.adoc.erb index 4b695f7224..b9a3897f55 100644 --- a/backends/certificate_doc/templates/certificate.adoc.erb +++ b/backends/certificate_doc/templates/certificate.adoc.erb @@ -510,9 +510,7 @@ Requirement <%= req.name %> only apply when <%= req.when_pretty %>. <% end -%> <% if v.implications.size > 0 -%> Implies::: - <% v.implications.each do |i| -%> - * `<%= i.name %>` version <%= i.version_spec %> - <% end -%> + <%= v.implications.each { |i| "* #{i[:ext_ver].name} (#{i[:ext_ver].version_spec}) #{i[:cond].empty? ? '' : i[:cond].to_asciidoc(join: ', ')}" }.join("\n* ") %> <% end -%> <% end -%> @@ -532,7 +530,15 @@ 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 = cfg_arch.instructions.select do |i| + i.defined_by_condition.satisfied_by? do |defining_ext_req| + cert_model.in_scope_ext_reqs.any? do |in_scope_ext_req| + in_scope_ext_req.satisfying_versions.any? { |ext_ver| defining_ext_req.satisfied_by?(ext_ver) } + end + end + end +-%> <% unless insts.empty? -%> ==== Instructions @@ -681,13 +687,13 @@ RV64:: <% if inst.key?("operation()") -%> [source,idl,subs="specialchars,macros"] ---- -<%= inst.operation_ast(cfg_arch.symtab).gen_adoc %> +<%= inst.operation_ast.gen_adoc %> ---- <% end -%> ==== Exceptions -<%- exception_list = inst.reachable_exceptions_str(cfg_arch.symtab) -%> +<%- exception_list = inst.reachable_exceptions_str -%> <%- if exception_list.empty? -%> This instruction does not generate synchronous exceptions. <%- else -%> @@ -735,17 +741,17 @@ 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? -%> +h| Length | <%= csr.length_pretty %> <% else -%> -h| Length | <%= csr.length_pretty(cfg_arch) %> +h| Length | <%= csr.length_pretty %> <% 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? || csr.possible_fields.any? { |f| f.dynamic_location? } -%> <%# CSR has a known static length, so there is only one format to display -%> .<%= csr.name %> format [wavedrom, ,svg,subs='attributes',width="100%"] @@ -779,34 +785,34 @@ This CSR format changes dynamically with XLEN. |=== @ Name @ Location @ Type @ Reset Value -<%- csr.implemented_fields(cfg_arch).each do |field| -%> +<%- csr.possible_fields.each do |field| -%> @ xref:<%=csr.name%>-<%=field.name%>-def[`<%= field.name %>`] a@ -<%- if field.dynamic_location?(cfg_arch) -%> +<%- if field.dynamic_location? -%> [when,"<%= field.location_cond32 %>"] -- -<%= field.location_pretty(cfg_arch, 32) %> +<%= field.location_pretty(32) %> -- [when,"<%= field.location_cond64 %>"] -- -<%= field.location_pretty(cfg_arch, 64) %> +<%= field.location_pretty(64) %> -- <%- else -%> -<%= field.location_pretty(cfg_arch) %> +<%= field.location_pretty %> <%- end -%> a@ -- -<%= field.type_pretty(cfg_arch.symtab) %> +<%= field.type_pretty %> -- a@ -- -<%= field.reset_value_pretty(cfg_arch) %> +<%= field.reset_value_pretty %> -- <%- end -%> @@ -814,11 +820,11 @@ a@ ==== Fields -<%- if csr.implemented_fields(cfg_arch).empty? -%> +<%- if csr.possible_fields.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.possible_fields.each do |field| -%> [[<%=csr.name%>-<%=field.name%>-def]] ===== `<%= field.name %>` @@ -828,23 +834,23 @@ IMPORTANT: <%= field.name %> is only defined in <%= field.base32_only? ? "RV32" **** Location:: -<%= field.location_pretty(cfg_arch) %> +<%= field.location_pretty %> Description:: <%= field.description.gsub("\n", " +\n") %> Type:: -<%= field.type_pretty(cfg_arch.symtab) %> +<%= field.type_pretty %> Reset value:: -<%= field.reset_value_pretty(cfg_arch) %> +<%= field.reset_value_pretty %> **** <%- end -%> <%- end -%> -<%- if csr.implemented_fields(cfg_arch).map(&:has_custom_sw_write?).any? -%> +<%- if csr.possible_fields.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 +860,7 @@ written value: [idl] ---- -<%- csr.implemented_fields(cfg_arch).each do |field| -%> +<%- csr.possible_fields.each do |field| -%> <%- if field.has_custom_sw_write? -%> <%= field.name %> = <%= field["sw_write(csr_value)"] %> <%- else -%> diff --git a/backends/cfg_html_doc/adoc_gen.rake b/backends/cfg_html_doc/adoc_gen.rake index 5279271828..61effb3d12 100644 --- a/backends/cfg_html_doc/adoc_gen.rake +++ b/backends/cfg_html_doc/adoc_gen.rake @@ -41,7 +41,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_extension_versions.each do |ext_version| ext = cfg_arch.extension(ext_version.name) path = dir_path / "#{ext.name}.adoc" puts " Generating #{path}" @@ -89,7 +89,7 @@ require "ruby-prof" end when "ext" puts "Generating full extension list" - cfg_arch.transitive_implemented_extensions.each do |ext_version| + cfg_arch.transitive_implemented_extension_versions.each do |ext_version| lines << " * `#{ext_version.name}` #{ext_version.ext.long_name}" end when "inst" @@ -128,7 +128,7 @@ end namespace :gen do desc "Generate Asciidoc source for config into gen/CONFIG_NAME/adoc" task :adoc, [:config_name] do |_t, args| - raise "No config named #{args[:config_name]}" unless File.directory?($root / "cfgs" / args[:config_name]) + raise "No config named #{args[:config_name]}" unless File.file?($root / "cfgs" / "#{args[:config_name]}.yaml") ["inst", "csr", "ext", "func"].each do |type| Rake::Task["#{$root}/.stamps/adoc-gen-#{type}s-#{args[:config_name]}.stamp"].invoke diff --git a/backends/cfg_html_doc/templates/config.adoc.erb b/backends/cfg_html_doc/templates/config.adoc.erb index e160d313a3..17cc795a20 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_extension_versions.sort{ |a,b| a.name <=> b.name }.each do |e| -%> | `<%= e.name %>` | <%= e.version_spec %> <%- end -%> |=== diff --git a/backends/cfg_html_doc/templates/csr.adoc.erb b/backends/cfg_html_doc/templates/csr.adoc.erb index a2ef0c9ea8..3ac8f60150 100644 --- a/backends/cfg_html_doc/templates/csr.adoc.erb +++ b/backends/cfg_html_doc/templates/csr.adoc.erb @@ -15,16 +15,12 @@ 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) %> -<%- else -%> -h| Length | <%= csr.length_pretty(cfg_arch) %> -<%- end -%> +h| Length | <%= csr.length_pretty() %> 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? || csr.fields.any? { |f| f.dynamic_location? } -%> <%# CSR has a known static length, so there is only one format to display -%> .<%= csr.name %> format [wavedrom, ,svg,subs='attributes',width="100%"] @@ -57,29 +53,29 @@ This CSR format changes dynamically. |=== |Name | Location | Type | Reset Value -<%- csr.implemented_fields(cfg_arch).each do |field| -%> +<%- csr.possible_fields.each do |field| -%> | `xref:<%=csr.name%>-<%=field.name%>-def[<%= field.name %>]` -| <%= field.location_pretty(cfg_arch) %> -| <%= field.type(cfg_arch.symtab) %> -| <%= field.reset_value(cfg_arch) %> +| <%= field.location_pretty(cfg_arch.multi_xlen? ? nil : cfg_arch.possible_xlens[0]) %> +| <%= field.type_pretty(cfg_arch.multi_xlen? ? nil : cfg_arch.possible_xlens[0]) %> +| <%= field.reset_value %> <%- end -%> |=== == Fields -<%- if csr.implemented_fields(cfg_arch).empty? -%> +<%- if csr.possible_fields.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.possible_fields.each do |field| -%> [[<%=csr.name%>-<%=field.name%>-def]] === `<%= field.name %>` [.csr-field-info] -- Location:: -`<%=field.csr.name%>[<%= field.location_pretty(cfg_arch) %>]` +`<%=field.csr.name%>[<%= field.location_pretty %>]` Description:: <%= cfg_arch.render_erb(field.description).gsub("\n\n", "\n+\n") %> @@ -88,11 +84,11 @@ Type:: [%autowidth] |=== -| <%= field.type(cfg_arch.symtab) %> | <%= field.type_desc(cfg_arch) %> +| <%= field.type %> | <%= field.type_desc %> |=== Reset value:: -<%= field.reset_value(cfg_arch) %> +<%= field.reset_value %> <%- if field.has_custom_sw_write? -%> Software write:: @@ -107,20 +103,20 @@ RV32:: + [source,idl,subs="specialchars,macros"] ---- -<%= field.pruned_sw_write_ast(cfg_arch, 32).gen_adoc %> +<%= field.pruned_sw_write_ast(32).gen_adoc %> ---- RV64:: + [source,idl,subs="specialchars,macros"] ---- -<%= field.pruned_sw_write_ast(cfg_arch, 64).gen_adoc %> +<%= field.pruned_sw_write_ast(64).gen_adoc %> ---- <%- else -%> <%- xlen = !cfg_arch.multi_xlen? ? cfg_arch.mxlen : (!csr.defined_in_all_bases? ? csr.base : field.base) -%> [source,idl,subs="specialchars,macros"] ---- -<%= field.pruned_sw_write_ast(cfg_arch, xlen).gen_adoc %> +<%= field.pruned_sw_write_ast(xlen).gen_adoc %> ---- <%- end -%> <%- end -%> @@ -141,14 +137,14 @@ Pruned:: + [source,idl,subs="specialchars,macros"] ---- -<%= csr.pruned_sw_read_ast(cfg_arch).gen_adoc %> +<%= csr.pruned_sw_read_ast(cfg_arch.multi_xlen? ? nil : cfg_arch.possible_xlens[0]).gen_adoc %> ---- Original:: + [source,idl,subs="specialchars,macros"] ---- -<%= csr.type_checked_sw_read_ast(cfg_arch.symtab).gen_adoc %> +<%= csr.type_checked_sw_read_ast(cfg_arch.multi_xlen? ? nil : cfg_arch.possible_xlens[0]).gen_adoc %> ---- ==== <%- end -%> diff --git a/backends/cfg_html_doc/templates/ext.adoc.erb b/backends/cfg_html_doc/templates/ext.adoc.erb index 8111a2ae57..b4866ed92a 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_extension_versions.include?(v) -%> <%= v.version_str %>:: Ratification date::: <%= v.ratification_date %> @@ -26,7 +26,8 @@ Implemented Version:: <%= ext_version.version_str %> <%- unless v.implications.empty? -%> Implies::: <%- v.implications.each do |i| -%> - * `<%= i.name %>` version <%= i.version_str %> + <%- next unless i[:cond].satisfied_by? { |ext_req| cfg_arch.transitive_implemented_extension_versions.any? { |ext_ver| ext_req.satisfied_by?(ext_ver)}} -%> + * `<%= i[:ext_ver].name %>` version <%= i[:ext_ver].version_str %> <%- end -%> <%- end -%> <%- end -%> diff --git a/backends/cfg_html_doc/templates/func.adoc.erb b/backends/cfg_html_doc/templates/func.adoc.erb index 40b87e3846..c1b2f02a37 100644 --- a/backends/cfg_html_doc/templates/func.adoc.erb +++ b/backends/cfg_html_doc/templates/func.adoc.erb @@ -6,7 +6,7 @@ <%- cfg_arch.implemented_functions.each do |f| -%> [#<%= f.name %>-func-def] -== <%= f.name %><%- if f.builtin? -%> (builtin)<%- end -%> +== <%= f.name %><%- if f.builtin? -%> (builtin)<%- end -%><%- if f.generated? -%> (generated)<%- end -%> <%= f.description %> @@ -15,7 +15,7 @@ h| Return Type l| <%= f.return_type_list_str.join(', ') %> h| Arguments l| <%= f.arguments_list_str.join (', ') %> |=== -<%- unless f.builtin? -%> +<%- unless f.builtin? || f.generated? -%> <%- body_ast = f.body -%> [tabs] ==== diff --git a/backends/cfg_html_doc/templates/inst.adoc.erb b/backends/cfg_html_doc/templates/inst.adoc.erb index d34aa21243..7e0ac3f67b 100644 --- a/backends/cfg_html_doc/templates/inst.adoc.erb +++ b/backends/cfg_html_doc/templates/inst.adoc.erb @@ -108,7 +108,7 @@ Pruned, XLEN == <%= effective_xlen %>:: + [source,idl,subs="specialchars,macros"] ---- -<%= inst.pruned_operation_ast(cfg_arch.symtab, effective_xlen).gen_adoc %> +<%= inst.pruned_operation_ast(effective_xlen).gen_adoc %> ---- <%- end -%> @@ -116,12 +116,12 @@ Original:: + [source,idl,subs="specialchars,macros"] ---- -<%= inst.operation_ast(cfg_arch.idl_compiler).gen_adoc %> +<%= inst.operation_ast().gen_adoc %> ---- ==== <%- end -%> -<%- exception_list = inst.reachable_exceptions_str(cfg_arch.symtab) -%> +<%- exception_list = inst.reachable_exceptions_str() -%> <%- unless exception_list.empty? -%> == Exceptions diff --git a/backends/cfg_html_doc/templates/toc.adoc.erb b/backends/cfg_html_doc/templates/toc.adoc.erb index aac4215f0f..445d0a5c61 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_extension_versions.sort { |a, b| a.name <=> b.name }.each do |ext| -%> * %%LINK%ext;<%= ext.name %>;<%= ext.name %>%% <%- end -%> diff --git a/backends/common_templates/adoc/csr.adoc.erb b/backends/common_templates/adoc/csr.adoc.erb index 0f9187dede..13d57b3362 100644 --- a/backends/common_templates/adoc/csr.adoc.erb +++ b/backends/common_templates/adoc/csr.adoc.erb @@ -18,7 +18,7 @@ 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? || csr.fields.any? { |f| f.dynamic_location? } -%> <%# CSR has a known static length, so there is only one format to display -%> .<%= csr.name %> format [wavedrom, ,svg,subs='attributes',width="100%"] @@ -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 %> +@ <%= field.type_pretty %> +@ <%= field.reset_value_pretty %> <%- 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 %> Description:: <%= field.description %> Type:: -<%= field.type_pretty(cfg_arch.symtab) %> +<%= field.type_pretty %> Reset value:: -<%= field.reset_value_pretty(cfg_arch) %> +<%= field.reset_value_pretty %> **** diff --git a/backends/common_templates/adoc/inst.adoc.erb b/backends/common_templates/adoc/inst.adoc.erb index caf9a42bac..6e33f8c3e8 100644 --- a/backends/common_templates/adoc/inst.adoc.erb +++ b/backends/common_templates/adoc/inst.adoc.erb @@ -89,7 +89,7 @@ RV64:: Operation:: [source,idl,subs="specialchars,macros"] ---- -<%= inst.operation_ast(cfg_arch.symtab).gen_adoc %> +<%= inst.operation_ast.gen_adoc %> ---- <% end %> <% end %> diff --git a/backends/cpp_hart_gen/CMakeLists.txt b/backends/cpp_hart_gen/CMakeLists.txt new file mode 100644 index 0000000000..6a4d039b65 --- /dev/null +++ b/backends/cpp_hart_gen/CMakeLists.txt @@ -0,0 +1,175 @@ +cmake_minimum_required(VERSION 3.20) +cmake_policy(SET CMP0067 NEW) # Force check_cxx_source_compiles to respect CMAKE_CXX_STANDARD + +include(ExternalProject) + +project(udb LANGUAGES CXX C) + +# Add Asan build type +SET(CMAKE_CXX_FLAGS_ASAN + "-g -Og -fsanitize=address" + CACHE STRING "Flags used by the C++ compiler during address sanitizer builds." + FORCE ) +SET(CMAKE_C_FLAGS_ASAN + "-g -Og -fsanitize=address" + CACHE STRING "Flags used by the C compiler during address address sanitizer builds." + FORCE ) +SET(CMAKE_EXE_LINKER_FLAGS_ASAN + "" + CACHE STRING "Flags used for linking binaries during address address sanitizer builds." + FORCE ) +SET(CMAKE_SHARED_LINKER_FLAGS_ASAN + "" + CACHE STRING "Flags used by the shared libraries linker during address sanitizer builds." + FORCE ) +MARK_AS_ADVANCED( + CMAKE_CXX_FLAGS_ASAN + CMAKE_C_FLAGS_ASAN + CMAKE_EXE_LINKER_FLAGS_ASAN + CMAKE_SHARED_LINKER_FLAGS_ASAN ) +IF(NOT CMAKE_BUILD_TYPE) + SET(CMAKE_BUILD_TYPE RelWithDebInfo + CACHE STRING "Choose the type of build : None Debug Release RelWithDebInfo MinSizeRel Asan." + FORCE) +ENDIF(NOT CMAKE_BUILD_TYPE) +message("* Current build type is : ${CMAKE_BUILD_TYPE}") + +set(CMAKE_CXX_STANDARD 23) +set(CMAKE_CXX_STANDARD_REQUIRED True) +set(CMAKE_POSITION_INDEPENDENT_CODE ON) +set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} -no-pie) + +include(CheckCXXSourceCompiles) + +check_cxx_source_compiles( + " + #include + #if !defined(__cpp_concepts) || (__cpp_concepts < 201907) + #error \"No concepts\" + #endif + int main(void) { return 0; } + " + HAVE_CONCEPTS +) +if (NOT HAVE_CONCEPTS) + message(FATAL_ERROR "Compiler (${CMAKE_CXX_COMPILER}) does not support concepts") +endif() + +include(FetchContent) + +FetchContent_Declare(fmt + GIT_REPOSITORY https://github.com/fmtlib/fmt.git + GIT_TAG 10.2.1 +) +FetchContent_MakeAvailable(fmt) + +FetchContent_Declare( + yaml-cpp + GIT_REPOSITORY https://github.com/jbeder/yaml-cpp.git + GIT_TAG yaml-cpp-0.7.0 +) +FetchContent_MakeAvailable(yaml-cpp) + +FetchContent_Declare( + cli11_proj + QUIET + GIT_REPOSITORY https://github.com/CLIUtils/CLI11.git + GIT_TAG f4d0731 +) + +FetchContent_MakeAvailable(cli11_proj) + +FetchContent_Declare( + Catch2 + GIT_REPOSITORY https://github.com/catchorg/Catch2.git + GIT_TAG v3.7.1 +) + +FetchContent_MakeAvailable(Catch2) + +FetchContent_Declare( + nlohmann_json_schema_validator + GIT_REPOSITORY https://github.com/pboettch/json-schema-validator.git + GIT_TAG 2.3.0 +) + +FetchContent_MakeAvailable(nlohmann_json_schema_validator) + +FetchContent_Declare( + compile_time_regular_expressions + GIT_REPOSITORY https://github.com/hanickadot/compile-time-regular-expressions.git + GIT_TAG v3.9.0 +) +FetchContent_MakeAvailable(compile_time_regular_expressions) + +set(GENERATED_SRCS "") +foreach(config ${CONFIG_LIST}) + list(APPEND GENERATED_SRCS "${CMAKE_SOURCE_DIR}/src/cfgs/${config}/params.cxx") +endforeach() + +add_library(hart + ${CMAKE_SOURCE_DIR}/src/db_data.cxx + ${CMAKE_SOURCE_DIR}/src/enum.cxx + ${CMAKE_SOURCE_DIR}/src/memory.cpp + ${GENERATED_SRCS} +# ../gen/iss/oryon/src/types.cxx +# ../gen/iss/oryon/src/csr_types.cxx +# ../gen/iss/oryon/src/hart_funcs.cxx +# ../gen/iss/oryon/src/decode.cxx +) +target_include_directories(hart PUBLIC ${CMAKE_SOURCE_DIR}/include) +target_link_libraries(hart PUBLIC yaml-cpp::yaml-cpp nlohmann_json_schema_validator fmt::fmt ctre::ctre -lgmp -lgmpxx) + +# add_library(hart_c ${CMAKE_SOURCE_DIR}/src/libhart_c.cpp) +# target_include_directories(hart_c PUBLIC ${CMAKE_SOURCE_DIR}/include) +# target_link_libraries(hart_c hart) + +add_library(hart_renode SHARED ${CMAKE_SOURCE_DIR}/src/libhart_renode.cpp) +target_compile_options(hart_renode PRIVATE -fvisibility=hidden) +target_include_directories(hart_renode PUBLIC + ${CMAKE_SOURCE_DIR}/include +) +target_link_libraries(hart_renode PUBLIC hart) + +add_executable(iss + ${CMAKE_SOURCE_DIR}/src/iss.cpp + ${CMAKE_SOURCE_DIR}/src/elf_reader.cpp +) +target_link_libraries(iss PRIVATE hart elf CLI11::CLI11) + + +## TESTS + +add_executable(test_bits + ${CMAKE_SOURCE_DIR}/test/test_bits.cpp +) +target_include_directories(test_bits PUBLIC ${CMAKE_SOURCE_DIR}/include) +target_link_libraries(test_bits PRIVATE hart Catch2::Catch2WithMain) + +add_executable(test_util + ${CMAKE_SOURCE_DIR}/test/test_util.cpp +) +target_include_directories(test_util PUBLIC ${CMAKE_SOURCE_DIR}/include) +target_link_libraries(test_util PRIVATE hart Catch2::Catch2WithMain) + +add_executable(test_csr + ${CMAKE_SOURCE_DIR}/test/test_csr.cpp +) +target_include_directories(test_csr PUBLIC ${CMAKE_SOURCE_DIR}/include) + target_link_libraries(test_csr PRIVATE hart Catch2::Catch2WithMain) + +add_executable(test_version + ${CMAKE_SOURCE_DIR}/test/test_version.cpp +) +target_include_directories(test_version PUBLIC ${CMAKE_SOURCE_DIR}/include) +target_link_libraries(test_version PRIVATE hart Catch2::Catch2WithMain) + +# add_executable(test_decode +# ${CMAKE_SOURCE_DIR}/test/test_decode.cpp +# ) +# target_include_directories(test_decode PUBLIC ${CMAKE_SOURCE_DIR}/include) +# target_link_libraries(test_decode PRIVATE hart Catch2::Catch2WithMain) + +include(CTest) +include(Catch) +catch_discover_tests(test_bits test_util test_csr test_version) # test_decode) diff --git a/backends/cpp_hart_gen/c/include/udb/renode_imports.h b/backends/cpp_hart_gen/c/include/udb/renode_imports.h new file mode 100644 index 0000000000..90111925da --- /dev/null +++ b/backends/cpp_hart_gen/c/include/udb/renode_imports.h @@ -0,0 +1,134 @@ +/* + * SPDX-FileCopyrightText: Antmicro + * + * SPDX-License-Identifier: MIT + */ + +#ifndef RENODE_IMPORTS_H_ +#define RENODE_IMPORTS_H_ + +#ifdef __cplusplus +#define EXTERN_C extern "C" +#else +#define EXTERN_C +#endif + +/* If this header is used as a part of tlib, we will call this hook after every callback */ +#ifdef TARGET_LONG_BITS +EXTERN_C void tlib_try_interrupt_translation_block(void); +#else +#define tlib_try_interrupt_translation_block() +#endif + +#include "udb/renode_map.h" + +#define VA_NARGS_IMPL(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N +#define VA_NARGS(ARGS...) VA_NARGS_IMPL(_, ##ARGS, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) + +#define CONCAT_0() +#define CONCAT_1(A) A +#define CONCAT_2(A, B) A##B +#define CONCAT_3(A, B, C) A##B##C +#define CONCAT_4(A, B, C, D) A##B##C##D +#define CONCAT_5(A, B, C, D, E) A##B##C##D##E +#define CONCAT_6(A, B, C, D, E, F) A##B##C##D##E##F +#define CONCAT_EXP_2(A, B) CONCAT_2(A, B) +#define CONCAT_EXP_5(A, B, C, D, E) CONCAT_5(A, B, C, D, E) + +#define CONCAT_IMPL2(N, XS...) CONCAT_##N(XS) +#define CONCAT_IMPL(N, XS...) CONCAT_IMPL2(N, XS) +#define CONCAT(XS...) CONCAT_IMPL(VA_NARGS(XS), XS) + +#define IF_THEN_ELSE(COND, THEN, ELSE) CONCAT_2(IF_THEN_ELSE_, COND)(THEN, ELSE) +#define IF_THEN_ELSE_0(THEN, ELSE) ELSE +#define IF_THEN_ELSE_1(THEN, ELSE) THEN + +// We need these typedefs in order for the type name mapping based on token pasting to work +typedef void *voidptr; +typedef char *charptr; + +#define CSHARP_TYPE_uint8_t Byte +#define CSHARP_TYPE_uint16_t UInt16 +#define CSHARP_TYPE_uint32_t UInt32 +#define CSHARP_TYPE_uint64_t UInt64 +#define CSHARP_TYPE_int8_t SByte +#define CSHARP_TYPE_int16_t Int16 +#define CSHARP_TYPE_int32_t Int32 +#define CSHARP_TYPE_int64_t Int64 +#define CSHARP_TYPE_charptr String +#define CSHARP_TYPE_voidptr IntPtr /* a pointer is a pointer */ +#define CSHARP_TYPE_void /* for return types */ +#define CSHARP_TYPE_ /* for zero-arg functions */ +#define CSHARP_TYPE(TYPE) CSHARP_TYPE_##TYPE + +#define CSHARP_PREFIX_uint8_t Func +#define CSHARP_PREFIX_uint16_t Func +#define CSHARP_PREFIX_uint32_t Func +#define CSHARP_PREFIX_uint64_t Func +#define CSHARP_PREFIX_int8_t Func +#define CSHARP_PREFIX_int16_t Func +#define CSHARP_PREFIX_int32_t Func +#define CSHARP_PREFIX_int64_t Func +#define CSHARP_PREFIX_charptr Func +#define CSHARP_PREFIX_voidptr Func +#define CSHARP_PREFIX_void Action +#define CSHARP_PREFIX(TYPE) CSHARP_PREFIX_##TYPE + +#define PARAMS_0() void +#define PARAMS_1(T0) T0 a0 +#define PARAMS_2(T0, T1) PARAMS_1(T0), T1 a1 +#define PARAMS_3(T0, T1, T2) PARAMS_2(T0, T1), T2 a2 +#define PARAMS_4(T0, T1, T2, T3) PARAMS_3(T0, T1, T2), T3 a3 +#define PARAMS_5(T0, T1, T2, T3, T4) PARAMS_4(T0, T1, T2, T3), T4 a4 +#define PARAMS_6(T0, T1, T2, T3, T4, T5) PARAMS_5(T0, T1, T2, T3, T4), T5 a5 +#define PARAMS_IMPL(N, TYPES...) CONCAT_EXP_2(PARAMS_, N)(TYPES) +#define PARAMS(TYPES...) PARAMS_IMPL(VA_NARGS(TYPES), TYPES) + +#define PARAM_NAMES_0() +#define PARAM_NAMES_1(T0) a0 +#define PARAM_NAMES_2(T0, T1) PARAM_NAMES_1(T0), a1 +#define PARAM_NAMES_3(T0, T1, T2) PARAM_NAMES_2(T0, T1), a2 +#define PARAM_NAMES_4(T0, T1, T2, T3) PARAM_NAMES_3(T0, T1, T2), a3 +#define PARAM_NAMES_5(T0, T1, T2, T3, T4) PARAM_NAMES_4(T0, T1, T2, T3), a4 +#define PARAM_NAMES_6(T0, T1, T2, T3, T4, T5) PARAM_NAMES_5(T0, T1, T2, T3, T4), a5 +#define PARAM_NAMES_IMPL(N, TYPES...) CONCAT_EXP_2(PARAM_NAMES_, N)(TYPES) +#define PARAM_NAMES(TYPES...) PARAM_NAMES_IMPL(VA_NARGS(TYPES), TYPES) + +#define RETURN_KEYWORD_Action +#define RETURN_KEYWORD_Func return +#define HAS_RETURN_Action 0 +#define HAS_RETURN_Func 1 +#define RETURN_KEYWORD(TYPE) CONCAT_EXP_2(RETURN_KEYWORD_, CSHARP_PREFIX(TYPE)) +#define HAS_RETURN(TYPE) CONCAT_EXP_2(HAS_RETURN_, CSHARP_PREFIX(TYPE)) + +// Usage example: EXTERNAL_AS(int32_t, CSharpName, c_name, uint32_t, voidptr) +// +// Warning: for historical reasons, the return type goes FIRST in the generated +// renode_external_attach function name, which is reversed from the C# approach +// seen in delegate type parameters (as in Func) +#define EXTERNAL_AS(RETURN_TYPE, IMPORTED_NAME, LOCAL_NAME, PARAMETER_TYPES...) \ + static RETURN_TYPE (*LOCAL_NAME##_callback$)(PARAMS(PARAMETER_TYPES)); \ + \ + RETURN_TYPE LOCAL_NAME(PARAMS(PARAMETER_TYPES)) \ + { \ + /* If this function returns a value, generate code of the form \ + * uint32_t retval = (*callback)(); return retval;, possibly with something in between. \ + * Otherwise, just call it. */ \ + IF_THEN_ELSE(HAS_RETURN(RETURN_TYPE), RETURN_TYPE retval =,) \ + LOCAL_NAME##_callback$(PARAM_NAMES(PARAMETER_TYPES)); \ + tlib_try_interrupt_translation_block(); \ + IF_THEN_ELSE(HAS_RETURN(RETURN_TYPE), return retval;,) \ + } \ + \ + EXTERN_C UDB_EXPORT void CONCAT_EXP_5(renode_external_attach__, CSHARP_PREFIX(RETURN_TYPE), \ + CSHARP_TYPE(RETURN_TYPE), \ + CONCAT(MAP_LIST(CSHARP_TYPE, PARAMETER_TYPES)), \ + __##IMPORTED_NAME(RETURN_TYPE (*param)(PARAMS(PARAMETER_TYPES)))) \ + { \ + LOCAL_NAME##_callback$ = param; \ + } + +#define EXTERNAL(RETURN_TYPE, NAME, PARAMETER_TYPES...) \ + EXTERNAL_AS(RETURN_TYPE, $##NAME, NAME, PARAMETER_TYPES) + +#endif diff --git a/backends/cpp_hart_gen/c/include/udb/renode_map.h b/backends/cpp_hart_gen/c/include/udb/renode_map.h new file mode 100644 index 0000000000..eb02acc1d8 --- /dev/null +++ b/backends/cpp_hart_gen/c/include/udb/renode_map.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2025 Antmicro + * Originally created by William Swanson in 2012, donated to the public domain. + */ + +#ifndef MAP_H_INCLUDED +#define MAP_H_INCLUDED + +#define EVAL0(...) __VA_ARGS__ +#define EVAL1(...) EVAL0(EVAL0(EVAL0(__VA_ARGS__))) +#define EVAL2(...) EVAL1(EVAL1(EVAL1(__VA_ARGS__))) +#define EVAL3(...) EVAL2(EVAL2(EVAL2(__VA_ARGS__))) +#define EVAL4(...) EVAL3(EVAL3(EVAL3(__VA_ARGS__))) +#define EVAL(...) EVAL4(EVAL4(EVAL4(__VA_ARGS__))) + +#define MAP_END(...) +#define MAP_OUT +#define MAP_COMMA , + +#define MAP_GET_END2() 0, MAP_END +#define MAP_GET_END1(...) MAP_GET_END2 +#define MAP_GET_END(...) MAP_GET_END1 +#define MAP_NEXT0(test, next, ...) next MAP_OUT +#define MAP_NEXT1(test, next) MAP_NEXT0(test, next, 0) +#define MAP_NEXT(test, next) MAP_NEXT1(MAP_GET_END test, next) + +#define MAP0(f, x, peek, ...) f(x) MAP_NEXT(peek, MAP1)(f, peek, __VA_ARGS__) +#define MAP1(f, x, peek, ...) f(x) MAP_NEXT(peek, MAP0)(f, peek, __VA_ARGS__) + +#define MAP_LIST_NEXT1(test, next) MAP_NEXT0(test, MAP_COMMA next, 0) +#define MAP_LIST_NEXT(test, next) MAP_LIST_NEXT1(MAP_GET_END test, next) + +#define MAP_LIST0(f, x, peek, ...) f(x) MAP_LIST_NEXT(peek, MAP_LIST1)(f, peek, __VA_ARGS__) +#define MAP_LIST1(f, x, peek, ...) f(x) MAP_LIST_NEXT(peek, MAP_LIST0)(f, peek, __VA_ARGS__) + +/** + * Applies the function macro `f` to each of the remaining parameters. + */ +#define MAP(f, ...) EVAL(MAP1(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) + +/** + * Applies the function macro `f` to each of the remaining parameters and + * inserts commas between the results. + */ +#define MAP_LIST(f, ...) EVAL(MAP_LIST1(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) + +#endif diff --git a/backends/cpp_hart_gen/c/include/udb/stop_reason.h b/backends/cpp_hart_gen/c/include/udb/stop_reason.h new file mode 100644 index 0000000000..8a029459c4 --- /dev/null +++ b/backends/cpp_hart_gen/c/include/udb/stop_reason.h @@ -0,0 +1,12 @@ +#pragma once + +typedef struct _StopReason { + static const int ExitSuccess = 0; // Guest program exited successfully (only occurs with certain tracers) + static const int InstLimitReached = 1; // Normal exit from run_one/run_bb/run_n + static const int Wfi = 2; // Executed WFI + static const int Pause = 3; // Executed PAUSE + static const int Ebreak = 4; // Executed EBREAK + static const int ExitFailure = -1; // Guest program exited with failure (only occurs with certain tracers) + static const int Exception = -2; // Hit exception while in run_one/run_bb/run_n + static const int UnpredictableBehavior = -3; // Hart tried to do something that is unpredictable according to the standard/config +} StopReason; diff --git a/backends/cpp_hart_gen/cpp/include/udb/bb_cache.hpp b/backends/cpp_hart_gen/cpp/include/udb/bb_cache.hpp new file mode 100644 index 0000000000..68ac8ec903 --- /dev/null +++ b/backends/cpp_hart_gen/cpp/include/udb/bb_cache.hpp @@ -0,0 +1,81 @@ +#pragma once + +namespace udb { + class InstBase; + + template + class BasicBlock { + public: + static constexpr const unsigned MAX_BASIC_BLOCK_SIZE = 40; + + using InstStorage = std::array; + + BasicBlock() + : m_size(0), m_head(0), m_start_pc(~static_cast(0)) {} + + void recycle(uint64_t start_pc) { + m_start_pc = start_pc; + m_size = 0; + m_head = 0; + } + + uint64_t start_pc() const { return m_start_pc; } + unsigned size() const { return m_size; } + void reset() { m_head = 0; } + void invalidate() { m_start_pc = ~static_cast(0); } + + bool full() const { return m_size == MAX_BASIC_BLOCK_SIZE; } + + InstBase* alloc_inst() { + InstBase* inst = reinterpret_cast(m_insts[m_size].data()); + m_size++; + return inst; + } + + // return the next instruction + // does *not* check that the bb has another instruction + InstBase* pop() { + return reinterpret_cast(m_insts[m_head++].data()); + } + + private: + unsigned m_size; + unsigned m_head; + uint64_t m_start_pc; + std::array m_insts; + }; + + template + class BasicBlockCache { + static const constexpr unsigned NUM_BASIC_BLOCKS = 2048; + static_assert(__builtin_popcount(NUM_BASIC_BLOCKS) == 1, + "NUM_BASIC_BLOCKS must be a power of two"); + + public: + using BasicBlockType = BasicBlock; + static constexpr unsigned MAX_BASIC_BLOCK_SIZE = + BasicBlockType::MAX_BASIC_BLOCK_SIZE; + + BasicBlockCache() {} + + BasicBlock* get(uint64_t pc) { + unsigned idx = hash(pc); + return &m_bbs[idx]; + } + + void invalidate() { + for (unsigned i = 0; i < NUM_BASIC_BLOCKS; i++) { + m_bbs[i].invalidate(); + } + } + + private: + unsigned hash(uint64_t pc) { + // return number between [0..NUM_BASIC_BLOCKS) + return (pc >> 2) & (NUM_BASIC_BLOCKS - 1); + } + + private: + std::array m_bbs; + }; +} // namespace udb diff --git a/backends/cpp_hart_gen/cpp/include/udb/bitfield.hpp b/backends/cpp_hart_gen/cpp/include/udb/bitfield.hpp new file mode 100644 index 0000000000..c1c0aa7c95 --- /dev/null +++ b/backends/cpp_hart_gen/cpp/include/udb/bitfield.hpp @@ -0,0 +1,230 @@ +#pragma once + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace udb { + + using uint128_t = unsigned __int128; + using int128_t = __int128; + + struct __not_impl_type { + constexpr __not_impl_type() = default; + constexpr explicit __not_impl_type(const __not_impl_type &o) {} + + template + requires(!std::same_as) + constexpr __not_impl_type(T a, Args... args) {} + + template + requires(!std::convertible_to) + constexpr // required + // since C++14 + void + operator=(T &&) const noexcept {} + + template + constexpr // required since C++14 + bool + operator==(T &&) const noexcept { + return false; + } + }; + + template + using Bits = _Bits; + + // static_assert(static_cast(_Bits<1023> { 5 }) == 5); + +} // namespace udb + +namespace udb { + template + class Bitfield; + + template + class BitfieldMember { + public: + static constexpr unsigned Width = Size; + + BitfieldMember(Bitfield &parent) : m_parent(parent) {} + BitfieldMember(const BitfieldMember &other) : m_parent(other.m_parent) {} + + static constexpr Bits MaximumValue = + (Bits<1>(1).template sll()) - 1; + static constexpr Bits Mask = MaximumValue.template sll(); + + template + requires(N >= Size) + operator Bits() const; + + template + requires(N >= Size) + operator PossiblyUnknownBits() const; + + bool operator!() const { return !static_cast>(*this).get(); } + + template + BitfieldMember &operator=(const _Bits &value); + + BitfieldMember &operator=(const BitfieldMember &other); + + template + bool operator==(const Type &other) const { + return other == static_cast>(*this); + } + + template + bool operator==(const _Bits &other) const { + return other == static_cast>(*this); + } + + template + bool operator==(const _PossiblyUnknownBits &other) const { + return other == static_cast>(*this); + } + + template + bool operator>(const _Bits &other) const { + return static_cast>(*this) > other; + } + + template + bool operator>(const _PossiblyUnknownBits &other) const { + return static_cast>(*this) > other; + } + + template + friend bool operator<(const _PossiblyUnknownBits &lhs, + const BitfieldMember &rhs); + + template + bool operator>=(const _Bits &other) const { + return static_cast>(*this) >= other; + } + + template + bool operator>=(const _PossiblyUnknownBits &other) const { + return static_cast>(*this) >= other; + } + + template + bool operator<=(const _Bits &other) const { + return static_cast>(*this) <= other; + } + + template + bool operator<=(const _PossiblyUnknownBits &other) const { + return static_cast>(*this) <= other; + } + + template + bool operator<(const BitfieldMember + &other) const { + return static_cast>(*this) > + static_cast>(*this); + } + + template + Bits operator>>(const _Bits &shamt) const { + return static_cast>(*this) >> shamt; + } + template + requires(std::integral) + Bits operator>>(const T &shamt) const { + return static_cast>(*this) >> shamt; + } + + template + Bits operator&(const _Bits &other) const { + return static_cast>(*this) & other; + } + + Bits::InfinitePrecision> operator<<(const int &shamt) const { + return static_cast>(*this) << shamt; + } + + template + Bits sll() const { + return static_cast>(*this).template sll(); + } + + private: + Bitfield &m_parent; + }; + + template + class Bitfield { + public: + Bitfield() = default; + Bitfield(const Bits &value) : m_value(value) {} + + Bitfield &operator=(const Bits &value) { + m_value = value; + return *this; + } + template + Bitfield &operator=(const Type &value) { + m_value = value; + return *this; + } + operator Bits &() { return m_value; } + operator Bits() const { return m_value; } + + protected: + Bits m_value; + }; + + static_assert(std::is_copy_constructible_v>); + + template + template + requires(N >= Size) + BitfieldMember::template operator Bits() const { + return (static_cast>(m_parent) >> Start) & MaximumValue; + } + + template + template + requires(N >= Size) + BitfieldMember::template + operator PossiblyUnknownBits() const { + return (static_cast>(m_parent) >> Start) & MaximumValue; + } + + template + template + BitfieldMember + &BitfieldMember::template operator=( + const _Bits &value) { + m_parent = (static_cast>(m_parent) & ~Mask) | + ((value.template sll()) & Mask); + return *this; + } + + template + BitfieldMember + &BitfieldMember::template operator=( + const BitfieldMember &other) { + m_parent = (static_cast>(m_parent) & ~Mask) | + ((static_cast>(other).template sll()) & Mask); + return *this; + } + + template + bool operator<(const _PossiblyUnknownBits &lhs, + const BitfieldMember &rhs) { + return lhs < static_cast>(rhs); + } + +} // namespace udb diff --git a/backends/cpp_hart_gen/cpp/include/udb/bits-yaml.hpp b/backends/cpp_hart_gen/cpp/include/udb/bits-yaml.hpp new file mode 100644 index 0000000000..459d41cacf --- /dev/null +++ b/backends/cpp_hart_gen/cpp/include/udb/bits-yaml.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include + +#include "bits.hpp" + +// convert functions so that we can encode/decode a Bits type in YAML +namespace YAML { + template + struct convert> { + static Node encode(const udb::_Bits& rhs) { + Node node; + node = std::to_string(rhs); + return node; + } + + static bool decode(const Node& node, udb::_Bits& rhs) { + if (!node.IsScalar()) { + return false; + } + + rhs = udb::_Bits::from_string(node.as()); + return true; + } + }; +} // namespace YAML diff --git a/backends/cpp_hart_gen/cpp/include/udb/bits.hpp b/backends/cpp_hart_gen/cpp/include/udb/bits.hpp new file mode 100644 index 0000000000..3cfebbbd4b --- /dev/null +++ b/backends/cpp_hart_gen/cpp/include/udb/bits.hpp @@ -0,0 +1,2126 @@ +#pragma once + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "udb/cpp_exceptions.hpp" +#include "udb/defines.hpp" + +// Bits classes implement the IDL Bits type. +// +// There are four Bits types in C++: +// +// _Bits: Compile-time known vector length +// holding a known value _PossiblyUnknownBits: Compile-time known +// vector length holding a possibly unknown value _RuntimeBits: +// Compile-time unknown vector length, at most MaxN, holding a known value +// _PossiblyUnknownRuntimeBits: Compile-time unknown vector +// length, at most MaxN, holding a possibly unknown value +// +// You can convert: +// +// - _Bits -> *any +// - _PossiblyUnknownBits -> +// _PossiblyUnknownRuntimeBits +// - _RuntimeBits -> +// _PossiblyUnknownRuntimeBits +// - _PossiblyUnknownRuntimeBits -> none +// +// The bits classes attempt to hold the smallest native type to hold the value, +// falling back on GMP when it will not fit in any native type. The bits classes +// attempt to drop state (and checks) for unknown values when the value must be +// known; thus the multiple class types. The bits classes handle when the vector +// length isn't known at compile-time (e.g., because the length is a config +// parameter). +// + +// we need this to be true for GMP +static_assert(sizeof(long unsigned int) == sizeof(long long unsigned int)); + +// we make this assumption frequently +static_assert(sizeof(1ull) == 8); + +#ifndef __SIZEOF_INT128__ +#error "Compiler does not support __int128" +#endif + +namespace udb { + + // helper to find the max of N unsigned values at compile time + // example: + // static_assert(constmax<5, 43, 1>::value == 43); + template + struct constmax { + template + consteval static unsigned Max() { + constexpr unsigned AorB = (A > B) ? A : B; + if constexpr (sizeof...(Nums) > 0) { + return Max(); + } else { + return AorB; + } + } + constexpr static unsigned value = Max<_N...>(); + }; + template + static constexpr unsigned constmax_v = constmax<_N...>::value; + + static_assert(constmax::max(), + std::numeric_limits::max()>::value == + std::numeric_limits::max()); + + // given the Bits vector length, get the type of the underlying storage for + // unsigned values + // clang-format off + template + struct BitsStorageType { + using type = std::conditional_t< + (N > 128), mpz_class, // > 128 bits --> GMP + std::conditional_t< + (N > 64), unsigned __int128, // between 65-128 bits --> uint128_t (g++/clang builtin) + std::conditional_t< + (N > 32), uint64_t, // between 33-64 bits --> uint64_t + std::conditional_t< + (N > 16), uint32_t, // between 17-32 bits --> uint32_t + std::conditional_t<(N > 8), + uint16_t, // between 9-16 bits --> uint16_t + uint8_t>>>>>; // <= 8 bits --> uint8_t + }; + // clang-format on + + // given the Bits vector length, get the type of the underlying storage for + // signed values + template + struct BitsSignedStorageType { + using type = std::conditional_t< + (N > 128), mpz_class, + std::conditional_t< + (N > 64), __int128, + std::conditional_t< + (N > 32), int64_t, + std::conditional_t< + (N > 16), int32_t, + std::conditional_t<(N > 8), int16_t, int8_t>>>>>; + }; + + // need to define the conversion since GMP doesn (int128 isn't standard) + static inline auto to_mpz(const unsigned __int128 &rhs) { + mpz_class i = static_cast(rhs >> 64); + i <<= 64; + i |= static_cast(rhs); + return i; + } + + // need to define the conversion since GMP doesn (int128 isn't standard) + static inline mpz_class to_mpz(const __int128 &rhs) { + if (rhs < 0) { + if (rhs == std::numeric_limits<__int128>::min()) { + // special case, since we can't represent -rhs + return (-to_mpz(static_cast(-(rhs + 1)))) - 1; + } else { + return -to_mpz(static_cast(-rhs)); + } + } else { + return to_mpz(static_cast(rhs)); + } + } + + template + requires(sizeof(IntType) < 16) + mpz_class to_mpz(const IntType &rhs) { + return {rhs}; + } + + // N that actually means infinite + constexpr static unsigned BitsInfinitePrecision = + std::numeric_limits::max(); + + // max N value where storage is using a native integer type + // above this, the storage is GMP, and the Bits type can't be constexpr + constexpr static unsigned BitsMaxNativePrecision = 128; + + template + class _RuntimeBits; + + template + class _PossiblyUnknownBits; + + // saturating add + template + struct addsat { + static constexpr unsigned value = (((A + B) < A) || ((A + B) < B)) + ? std::numeric_limits::max() + : A + B; + }; + template + static constexpr unsigned addsat_v = addsat::value; + + // The _Bits class represents an arbitrary-width integer. + // N is the width of the integer, known at compile time. + // If N == BitsInfinitePrecision, then the width is infinite (i.e., not known) + // + // The underlying storage is optimized for N. + // When N <= BitsMaxNativePrecision, The underlying storage is native. + // When N > BitsMaxNativePrecison, the underlying storage is a libgmp + // multiprecision object. + // + // The operators are implemented to match the semantics of the Bits type in + // IDL. + template + class _Bits { + static_assert(N > 0); + + public: + // used for template concept resolution + constexpr static bool IsABits = true; + + // value of N that represents unknown precision (happens when there is a + // left shift by unknown value) + constexpr static unsigned InfinitePrecision = BitsInfinitePrecision; + + // largest value of N that still uses a native underlying type + // beyond this, the number of bits is still tracked, but the storage is + // using gmp + constexpr static unsigned MaxNativePrecision = BitsMaxNativePrecision; + + // advertise the width + constexpr static unsigned Width = N; + constexpr static unsigned width() { return N; } + + using StorageType = typename BitsStorageType::type; + using SignedStorageType = typename BitsSignedStorageType::type; + + // returns true if this Bits width requires storage masking (i.e., N != # + // bits in the underlying type) + template + static consteval bool needs_mask() { + using _StorageType = typename BitsStorageType<_N>::type; + + if constexpr (_N == InfinitePrecision) { + return false; + } else if constexpr (_N > MaxNativePrecision) { + // gmp (infinite) storage, so everything needs masked + return true; + } else if constexpr (_N == (sizeof(_StorageType) * 8)) { + // we fit exactly in our native storage + return false; + } else { + // using native storage, but there are unused bits + return true; + } + } + + public: + // mask of all ones for the Bits type + template + requires(N <= MaxNativePrecision) + static consteval StorageType mask() { + if constexpr (std::integral && + (N == (sizeof(StorageType) * 8))) { + return ~StorageType{0}; + } else { + return ((StorageType{1} << N) - 1); + } + } + template + requires(N > MaxNativePrecision) + static StorageType mask() { + static_assert(N != InfinitePrecision); // this isn't a good idea ;) + return ((StorageType{1} << N) - 1); + } + static_assert(needs_mask<4>() == true); + static_assert(needs_mask<8>() == false); + static_assert(needs_mask<16>() == false); + static_assert(needs_mask<32>() == false); + static_assert(needs_mask<64>() == false); + static_assert(needs_mask<65>() == true); + static_assert(needs_mask<128>() == false); + static_assert(needs_mask<129>() == true); + static_assert(needs_mask<256>() == true); + static_assert(needs_mask<512>() == true); + static_assert(needs_mask() == false); + + public: + // given storage for a Bits type, return a signed version of it in the + // storage class + template + requires(_N <= MaxNativePrecision) + static constexpr SignedStorageType cast_to_signed( + const StorageType &unsigned_value) { + if constexpr (N == (sizeof(StorageType) * 8)) { + // exactly fits in a native type, so just cast it + return static_cast(unsigned_value); + } else { + // we have a native type, but some bits are unsed. need to sign extend + // the storage to the native width + return static_cast(sign_extend(unsigned_value)); + } + } + + template + requires(_N > MaxNativePrecision) + static SignedStorageType cast_to_signed(const StorageType &unsigned_value) { + // this is gmp storage. We can't just sign extend, so we'll need to do the + // compliment + if constexpr (N == InfinitePrecision) { + // our 'unsigned' value is actually signed + return unsigned_value; + } else { + auto v = unsigned_value; + if (((v >> (N - 1)) & 1) == 1) { + // the number is now negative! + // The two's compliment value is 2^N - value + v = -((StorageType{1} << N) - v); + } + return v; + } + } + + // return a signed version of self + constexpr SignedStorageType cast_to_signed() const { + return cast_to_signed(m_val); + } + + template + requires(_Signed == false) + constexpr _Bits make_signed() const { + return _Bits{m_val}; + } + template + requires(_Signed == true) + constexpr _Bits &make_signed() const { + return *this; + } + + // given a Bits storage type, sign extend it to the full width of + // StorageType + static constexpr StorageType sign_extend(const StorageType &value) { + static_assert(N <= MaxNativePrecision); // doesn't make sense with gmp + if constexpr (N == sizeof(StorageType) * 8) { + // exact fit, no extension needed + return value; // no extension needed + } else { + if (value & (StorageType{1} << (N - 1))) { + // fill with ones + return value | ~mask(); + } else { + // no extension needed + return value; + } + } + } + + static constexpr std::conditional_t + apply_mask(const StorageType &value) { + if constexpr (needs_mask()) { + return value & mask(); + } else { + return value; + } + } + + static _Bits from_string(const std::string &str) { + if constexpr (std::is_same_v) { + mpz_class gmp_int(str.c_str()); + return _Bits{gmp_int}; + } else if constexpr (N <= 64 && N > 32) { + static_assert(sizeof(long long) == sizeof(uint64_t), + "Unexpected long long type"); + if constexpr (Signed) { + return _Bits(std::stoll(str, nullptr, 0)); + } else { + return _Bits(std::stoull(str, nullptr, 0)); + } + } else if constexpr (N <= 32 && N > 16) { + // static_assert(sizeof(long) == sizeof(uint32_t), "Unexpected long + // type"); + if constexpr (Signed) { + return _Bits(std::stol(str, nullptr, 0)); + } else { + return _Bits(std::stoul(str, nullptr, 0)); + } + } else if constexpr (N <= 16 && N > 8) { + // static_assert(sizeof(long) == sizeof(uint32_t), "Unexpected long + // type"); + if constexpr (Signed) { + int32_t tmp = std::stol(str, nullptr, 0); + // assert(tmp <= std::numeric_limits::max() && tmp >= + // std::numeric_limits::min()); + return _Bits(tmp); + } else { + uint32_t tmp = std::stoul(str, nullptr, 0); + // assert(tmp <= std::numeric_limits::max()); + return _Bits(tmp); + } + } else if constexpr (N <= 8) { + // static_assert(sizeof(long) == sizeof(uint32_t), "Unexpected long + // type"); + if constexpr (Signed) { + int32_t tmp = std::stol(str, nullptr, 0); + // assert(tmp <= std::numeric_limits::max() && tmp >= + // std::numeric_limits::min()); + return _Bits(tmp); + } else { + uint32_t tmp = std::stoul(str, nullptr, 0); + // assert(tmp <= std::numeric_limits::max()); + return _Bits(tmp); + } + } + } + + constexpr _Bits() : m_val(0) {} + constexpr _Bits(const _Bits &) = default; + _Bits(_Bits &&) noexcept = default; + + constexpr _Bits(const _Bits &other) : m_val(other.m_val) {} + + constexpr _Bits(const StorageType &val) : m_val(val) {} + + // also make a gmp constructor for native widths + template + requires(N <= MaxNativePrecision) + constexpr _Bits(const mpz_class &val) { + m_val = val.get_ui(); + } + + template + _Bits(const _RuntimeBits &); + + // other is smaller N + // everything fits, so just copy the storage + template + requires(M < N) + constexpr _Bits(const _Bits &o) { + if constexpr (N <= MaxNativePrecision && M > MaxNativePrecision) { + static_assert(false, "this can never happen??"); + m_val = o.m_val.get_ui(); + } else if constexpr ((N > MaxNativePrecision) && + (M <= MaxNativePrecision) && + (sizeof(typename _Bits::StorageType) == + 16)) { + if constexpr (!_Signed) { + static_assert(std::same_as::StorageType, + unsigned __int128>); + // libgmp can't convert __int128, so we have to do it manually + m_val = static_cast(o.get() & 0xffffffff'ffffffffull); + m_val <<= 64; + m_val |= static_cast(o.get() >> 64); + } else { + static_assert( + std::same_as::StorageType, __int128>); + // libgmp can't convert __int128, so we have to do it manually + if (o.get() == std::numeric_limits<__int128>::min()) { + // can't just negate this, so it's a special case + __int128 neg = -(o.get() + 1); + m_val = static_cast(neg & 0xffffffff'ffffffffull); + m_val <<= 64; + m_val |= static_cast(neg >> 64); + m_val = (-m_val) - 1; + + } else { + __int128 abs = (o.get() < 0) ? -o.get() : o.get(); + m_val = static_cast(abs & 0xffffffff'ffffffffull); + m_val <<= 64; + m_val |= static_cast(abs >> 64); + if (o.get() < 0) { + m_val = -m_val; + } + } + } + } else { + m_val = o.get(); + } + } + + // other is bigger N + // the other value will be truncated + template + requires(M > N) + constexpr _Bits(const _Bits &o) { + if constexpr (N <= MaxNativePrecision && M > MaxNativePrecision) { + m_val = apply_mask(o.m_val.get_ui()); + } else { + m_val = apply_mask(o.m_val); + } + } + + // built-in integer type, mask needed + template + requires(!std::is_same_v && + std::integral && needs_mask() && + _N <= BitsMaxNativePrecision) + constexpr _Bits(const IntType &val) : m_val(apply_mask(val)) {} + + // built-in integer type, mask needed, integral conversion to mpz + template + requires(!std::is_same_v && + std::integral && needs_mask() && + _N > BitsMaxNativePrecision) + constexpr _Bits(const IntType &val) : m_val(apply_mask(to_mpz(val))) {} + + // built-in integer type, no mask needed + template + requires(!std::is_same_v && + std::integral && !needs_mask()) + constexpr _Bits(const IntType &val) { + if constexpr (N == InfinitePrecision && std::is_signed_v) { + if (val < 0) { + abort(); // Can't mask off a negative value with infinite precision! + } + } + if constexpr (N <= MaxNativePrecision) { + m_val = val; + } else { + m_val = to_mpz(val); + } + } + + constexpr ~_Bits() noexcept = default; + + template + requires(std::integral && std::is_unsigned_v) + constexpr operator T() const noexcept { + if constexpr (N > MaxNativePrecision) { + return m_val.get_ui(); + } else { + return m_val; + } + } + + template + requires(std::integral && std::is_signed_v) + constexpr operator T() const noexcept { + if constexpr (N > MaxNativePrecision) { + return cast_to_signed().get_si(); + } else { + return cast_to_signed(); + } + } + + // cast to any other Bits type + template + requires((M != N) || (Signed != _Signed)) + constexpr explicit operator _Bits() const noexcept { + if constexpr (Signed) { + return cast_to_signed(); + } else { + return m_val; + } + } + + template < + typename T = std::conditional_t> + constexpr T get() const { + if constexpr (std::integral && std::is_signed_v) { + if constexpr (N > MaxNativePrecision) { + return cast_to_signed().get_si(); + } else { + return cast_to_signed(); + } + } else { + return m_val; + } + } + + // assignment + _Bits &operator=(const _Bits &o) = default; + _Bits &operator=(_Bits &&o) noexcept = default; + + template + requires std::integral + _Bits &operator=(const IntType &o) { + m_val = apply_mask(o); + return *this; + } + + template + requires((M != N) || (Signed != _Signed)) + _Bits &operator=(const _Bits &o) { + if constexpr ((N <= MaxNativePrecision) && (M > MaxNativePrecision)) { + m_val = apply_mask(o.m_val.get_ui()); + } else { + m_val = apply_mask(o.m_val); + } + + return *this; + } + + // negate operator + constexpr _Bits operator-() const { + _Bits negated_value; + negated_value.m_val = apply_mask(-m_val); + return negated_value; + } + + // invert operator + constexpr _Bits operator~() const & { return apply_mask(~m_val); } + +#define BITS_COMPARISON_OPERATOR(op) \ + constexpr bool operator op(const _Bits &o) const noexcept { \ + if constexpr (Signed) { \ + return cast_to_signed() op o.cast_to_signed(); \ + } else { \ + return m_val op o.m_val; \ + } \ + } \ + \ + template \ + requires(std::integral) \ + constexpr bool operator op(const IntType &o) const noexcept { \ + if constexpr (Signed) { \ + return cast_to_signed() op o; \ + } else { \ + return m_val op o; \ + } \ + } \ + \ + constexpr bool operator op(const mpz_class &o) const noexcept { \ + if constexpr (Signed) { \ + return cast_to_signed() op SignedStorageType{o}; \ + } else { \ + return m_val op o; \ + } \ + } \ + \ + constexpr friend bool operator op(const mpz_class &lhs, const _Bits &rhs) { \ + if constexpr (Signed) { \ + return SignedStorageType{lhs} op rhs.cast_to_signed(); \ + } else { \ + return lhs op rhs.m_val; \ + } \ + } \ + \ + template \ + requires(std::integral) \ + constexpr friend bool operator op(const IntType &lhs, const _Bits &rhs) { \ + if constexpr (Signed) { \ + return lhs op rhs.cast_to_signed(); \ + } else { \ + return lhs op rhs.m_val; \ + } \ + } \ + \ + template \ + requires((N != M) || (Signed != _Signed)) \ + constexpr bool operator op(const _Bits &o) const noexcept { \ + if constexpr (Signed && _Signed) { \ + return cast_to_signed() op o.cast_to_signed(); \ + } else if constexpr (Signed && !_Signed) { \ + return cast_to_signed() op o.m_val; \ + } else if constexpr (!Signed && _Signed) { \ + return m_val op o.cast_to_signed(); \ + } else { \ + return m_val op o.m_val; \ + } \ + } + + BITS_COMPARISON_OPERATOR(==) + BITS_COMPARISON_OPERATOR(!=) + BITS_COMPARISON_OPERATOR(<) + BITS_COMPARISON_OPERATOR(>) + BITS_COMPARISON_OPERATOR(<=) + BITS_COMPARISON_OPERATOR(>=) + +#undef BITS_COMPARISON_OPERATOR + +#define BITS_ARITHMETIC_OPERATOR(op) \ + constexpr _Bits operator op(const _Bits &o) const { \ + return _Bits{get() op o.get()}; \ + } \ + \ + template \ + requires((M != N) || (Signed != _Signed)) \ + constexpr _Bits::value, Signed && _Signed> operator op( \ + const _Bits &o) const { \ + if constexpr (M > N) { \ + return _Bits < constmax::value, \ + Signed && _Signed > {_Bits{get()}.get() op o.get()}; \ + } else { \ + return _Bits < constmax::value, \ + Signed && _Signed > {get() op _Bits{o.get()}.get()}; \ + } \ + } \ + \ + constexpr _Bits operator op(const mpz_class &o) const { \ + return _Bits{get() op o}; \ + } \ + \ + template \ + constexpr _Bits operator op(const IntType &_rhs) const { \ + if constexpr (std::is_signed_v) { \ + SignedStorageType rhs = _rhs; \ + return _Bits{get() op rhs}; \ + } else { \ + StorageType rhs = _rhs; \ + return _Bits{get() op rhs}; \ + } \ + } \ + \ + template \ + constexpr friend _Bits operator op(const IntType &_lhs, const _Bits &rhs) { \ + if constexpr (std::is_signed_v) { \ + SignedStorageType lhs = _lhs; \ + return _Bits{lhs op rhs.get()}; \ + } else { \ + StorageType lhs = _lhs; \ + return _Bits{lhs op rhs.get()}; \ + } \ + } + + BITS_ARITHMETIC_OPERATOR(+) + BITS_ARITHMETIC_OPERATOR(-) + BITS_ARITHMETIC_OPERATOR(*) + BITS_ARITHMETIC_OPERATOR(/) + BITS_ARITHMETIC_OPERATOR(%) + BITS_ARITHMETIC_OPERATOR(&) + BITS_ARITHMETIC_OPERATOR(|) + BITS_ARITHMETIC_OPERATOR(^) + +#undef BITS_ARITHMETIC_OPERATOR + + constexpr _Bits operator<<(const _Bits &shamt) const { + return _Bits{m_val << shamt.get()}; + } + + template + requires((M != N) || (Signed != _Signed)) + constexpr _Bits operator<<(const _Bits &shamt) const { + return _Bits{m_val << shamt.get()}; + } + + template + constexpr _Bits operator<<(const IntType &shamt) const { + return _Bits{m_val << shamt}; + } + + constexpr _Bits operator<<(const mpz_t &shamt) const { + return _Bits{m_val << shamt}; + } + + template + constexpr friend IntType operator<<(const IntType &val, + const _Bits &shamt) { + if constexpr (N > MaxNativePrecision) { + if constexpr (std::is_signed_v) { + return (val << shamt.m_val).get_si(); + } else { + return (val << shamt.m_val).get_ui(); + } + } else { + return val << shamt.m_val; + } + } + + // left shift when the shift amount is not known at compile time. + // MSB shifted bits are discarded + constexpr friend mpz_class operator<<(const mpz_class &val, + const _Bits &shamt) { + return val << shamt.m_val; + } + + // widening left shift when the shift amount is known at compile time + template + constexpr _Bits, Signed> sll() const { + using ReturnType = _Bits, Signed>; + if constexpr (N >= MaxNativePrecision) { + return ReturnType{ReturnType{m_val}.m_val << SHAMT}; + } else { + return ReturnType{m_val}.m_val << SHAMT; + } + } + + // widening left shift when the shift amount is not known at compile time + constexpr _Bits widening_sll( + unsigned shamt) const { + using ReturnType = _Bits; + return ReturnType{m_val} << shamt; + } + + constexpr _Bits operator>>(const _Bits &shamt) const { + return _Bits{m_val >> shamt.m_val}; + } + + template + requires((M != N) || (Signed != _Signed)) + constexpr _Bits operator>>(const _Bits &shamt) const { + return _Bits{m_val >> shamt.m_val}; + } + + template + constexpr _Bits operator>>(const IntType &shamt) const { + return _Bits{m_val >> shamt}; + } + + _Bits operator>>(const mpz_class &shamt) const { + return _Bits{m_val >> shamt}; + } + + template + constexpr friend IntType operator>>(const IntType &val, + const _Bits &shamt) { + return val >> shamt.get(); + } + + friend mpz_class operator>>(const mpz_class &val, const _Bits &shamt) { + return val >> shamt.get(); + } + + constexpr _Bits sra(const _Bits &shamt) const { + return apply_mask(cast_to_signed() >> shamt.get()); + } + + template + requires((M != N) || (Signed != _Signed)) + constexpr _Bits sra(const _Bits &shamt) const { + return apply_mask(cast_to_signed() >> shamt.get()); + } + + template + constexpr _Bits sra(const IntType &shamt) const { + return apply_mask(cast_to_signed() >> shamt); + } + + template + constexpr friend IntType sra(const IntType &val, const _Bits &shamt) { + if constexpr (N > MaxNativePrecision) { + if (Signed) { + return (val >> shamt.get()).get_si(); + } else { + return (val >> shamt.get()).get_ui(); + } + } else { + return val >> shamt.get(); + } + } + + _Bits sra(const mpz_class &shamt) const { + return apply_mask(cast_to_signed() >> shamt); + } + + friend mpz_class sra(const mpz_class &val, const _Bits &shamt) { + return val >> shamt.get(); + } + +#define BITS_OP_ASSIGN(op) \ + template \ + constexpr _Bits &operator op##=(const T & o) { \ + m_val = (*this op o).m_val; \ + return *this; \ + } + + BITS_OP_ASSIGN(+) + BITS_OP_ASSIGN(-) + BITS_OP_ASSIGN(/) + BITS_OP_ASSIGN(*) + BITS_OP_ASSIGN(%) + BITS_OP_ASSIGN(&) + BITS_OP_ASSIGN(|) + BITS_OP_ASSIGN(^) + +#undef BITS_OP_ASSIGN + + // pre-increment. Can't add a bit here, so use carefully if overflow could + // happen + _Bits &operator++() { + m_val = apply_mask(m_val + 1); + return *this; + } + + // post-increment + _Bits operator++(int) { + _Bits tmp{m_val}; + m_val = apply_mask(m_val + 1); + return tmp; + } + + // pre-decrement. Can't add a bit here, so use carefully if overflow could + // happen + _Bits &operator--() { + m_val = apply_mask(m_val - 1); + return *this; + } + + // post-increment + _Bits operator--(int) { + _Bits tmp{m_val}; + m_val = apply_mask(m_val - 1); + return tmp; + } + + friend std::ostream &operator<<(std::ostream &stream, const _Bits &val) { + if constexpr (std::same_as || + std::same_as) { + stream << fmt::format("{}", val.m_val); + } else { + stream << val.m_val; + } + return stream; + } + + template + constexpr _Bits extract() const { + static_assert(msb >= lsb); + return _Bits{ + m_val >> lsb}; // masking will happen in the constructor + } + + template + constexpr _Bits &setBit(const IndexType &idx, const ValueType &value) { + StorageType pos_mask = static_cast(1) << idx; + m_val = + (m_val & ~pos_mask) | ((static_cast<_Bits>(value) << idx) & pos_mask); + return *this; + } + + // private: + // If m_val is private, then _Bits cannot be used as a template parameter + // (Would not be 'structural'): + // https://en.cppreference.com/w/cpp/language/template_parameters + // We want to be able to use it as a template parameter because templated + // IDL functions will do so. + StorageType m_val; + }; + + struct BitsStrHelpers { + template + static consteval unsigned get_width(const char *str) { + uint64_t val = 0; + unsigned width = 0; + auto len = strlen(str); + unsigned base = 10; + const char *ptr = str; + if (len >= 3) { + if (strncmp(str, "0x", 2) == 0) { + base = 16; + ptr = &str[2]; + len -= 2; + } + } + if (base == 10) { + uint64_t pow = 1; + uint64_t last_val = val; + for (int i = len - 1; i >= 0; i--) { + val += (ptr[i] - '0') * pow; + if (val < last_val) { + // we overflowed; can't represent this value in 64 bits. + // For now, we'll just return an overappoximation because + // trying to find the width from the decimal string at compile time + // is tricky + return 1 + (10 * len) / (3); // 2^3 fits in one decimal digit + } + last_val = val; + pow *= 10; + } + width = val == 0 ? 1 : 64 - std::countl_zero(val); + } + if (base == 16) { + // the msb only needs enough bits to hold itself + if (*ptr >= '0' && *ptr <= '9') { + width += 32 - std::countl_zero(((uint32_t)*ptr) - (uint32_t)'0'); + } else if (*ptr >= 'A' && *ptr <= 'F') { + width += 32 - std::countl_zero(((uint32_t)*ptr) - (uint32_t)'A' + + (uint32_t)10); + } else if (*ptr >= 'a' && *ptr <= 'f') { + width += 32 - std::countl_zero(((uint32_t)*ptr) - (uint32_t)'a' + + (uint32_t)10); + } + if constexpr (AllowUnknown) { + if (*ptr == 'x' || *ptr == 'X') { + // unknown is full width + width += 4; + } + } + + // the lsbs need full bits + width += (len - 1) * 4; + + // special case: if zero, need one bit + width = width == 0 ? 1 : width; + } + return width; + } + + static consteval unsigned __int128 get_val(const char *str) { + unsigned __int128 val = 0; + auto len = strlen(str); + unsigned base = 10; + const char *ptr = str; + if (len >= 3) { + if (strncmp(str, "0x", 2) == 0) { + base = 16; + ptr = &str[2]; + len -= 2; + } + } + if (base == 10) { + // we only support base-10 literals up to 64 bits. (otherwise, it's hard + // to know bitwidth at compile time) + unsigned __int128 pow = 1; + for (int i = len - 1; i >= 0; i--) { + val += ((unsigned __int128)(ptr[i] - '0')) * pow; + pow *= 10; + } + } + if (base == 16) { + unsigned __int128 pow = 1; + for (int i = len - 1; i >= 0; i--) { + if (ptr[i] >= '0' && ptr[i] <= '9') { + val += ((unsigned __int128)(ptr[i] - '0')) * pow; + } else if (ptr[i] >= 'A' && ptr[i] <= 'F') { + val += ((unsigned __int128)(ptr[i] - 'A' + 10)) * pow; + } else if (ptr[i] >= 'a' && ptr[i] <= 'f') { + val += ((unsigned __int128)(ptr[i] - 'a' + 10)) * pow; + } + pow *= 16; + } + } + return val; + } + + static consteval unsigned __int128 get_unknown_mask(const char *str) { + unsigned __int128 mask = 0; + auto len = strlen(str); + unsigned base = 10; + const char *ptr = str; + if (len >= 3) { + if (strncmp(str, "0x", 2) == 0) { + base = 16; + ptr = &str[2]; + len -= 2; + } + } + if (base == 10) { + return 0; // there are no unknowns in a base-10 literal + } + if (base == 16) { + unsigned __int128 shamt = 0; + for (int i = len - 1; i >= 0; i--) { + if (ptr[i] == 'x' || ptr[i] == 'X') { + mask |= static_cast<__int128>(0xf) << shamt; + } + shamt += 4; + } + } + return mask; + } + }; + + // used to translate literal strings into a Bits type + template + struct BitsStr { + static constexpr char str[sizeof...(Str)] = {Str...}; + + static constexpr unsigned width = + BitsStrHelpers::get_width(str); + static_assert( + width <= 128, + "Cannot create bits literal of width >= 128 (use \"\"_mpz instead)"); + static constexpr unsigned __int128 val = BitsStrHelpers::get_val(str); + static constexpr unsigned __int128 unknown_mask = + BitsStrHelpers::get_unknown_mask(str); + }; + + template + struct BitsTemplateStr { + static constexpr unsigned width = + BitsStrHelpers::get_width(Str.cstr_value); + static_assert( + width <= 128, + "Cannot create bits literal of width >= 128 (use \"\"_mpz instead)"); + static constexpr unsigned __int128 val = + BitsStrHelpers::get_val(Str.cstr_value); + static constexpr unsigned __int128 unknown_mask = + BitsStrHelpers::get_unknown_mask(Str.cstr_value); + }; + + static_assert(BitsStr::width == 1); + static_assert(BitsStr::width == 2); + static_assert(BitsStr::width == 4); + static_assert(BitsStr::width == 5); + + // be careful with negative numbers here, since literals are always unsigned + // + // auto b = -15_b; // b will be +1, because 15_b is only four bits, and + // negation loses the sign bit + template + constexpr _Bits::width, false> operator""_b() { + if constexpr (BitsStr::width <= + _Bits::width, + false>::MaxNativePrecision) { + return BitsStr::val; + } else { + return mpz_class{BitsStr::str}; + } + } + + // signed bits + template + constexpr _Bits::width + 1, false> + operator""_sb() { + if constexpr ((BitsStr::width + 1) <= + _Bits<(BitsStr::width + 1), + false>::MaxNativePrecision) { + return BitsStr::val; + } else { + return mpz_class{BitsStr::str}; + } + } + + static_assert((0x0_b).Width == 1); + static_assert((0x1_b).Width == 1); + static_assert((0_b).Width == 1); + static_assert((1_b).Width == 1); + static_assert((0x2_b).Width == 2); + static_assert((0x7_b).Width == 3); + static_assert((0x8_b).Width == 4); + static_assert((0xf_b).Width == 4); + static_assert((0x1f_b).Width == 5); + static_assert((0xffffffffffffffff_b).Width == 64); + + static_assert((0x1_b).get() == 1); + static_assert((0x2_b).get() == 2); + static_assert((0x7_b).get() == 7); + static_assert((0x8_b).get() == 8); + static_assert((0xf_b).get() == 15); + static_assert((0x1f_b).get() == 0x1f); + static_assert((0xff_b).get() == 0xff); + static_assert((0xffffffff_b).get() == 0xfffffffful); + static_assert((0xfffffffff_b).get() == 0xffffffffful); + static_assert((0xffffffff1_b).get() == 0xffffffff1ul); + static_assert((0xfffffffffffffff_b).get() == 0xffffffffffffffful); + static_assert((0xffffffffffffffff_b).get() == 0xfffffffffffffffful); + + static_assert((1_b).get() == 1); + static_assert((2_b).get() == 2); + static_assert((7_b).get() == 7); + static_assert((8_b).get() == 8); + static_assert((15_b).get() == 15); + static_assert((31_b).get() == 31); + static_assert((1152921504606846975_b).get() == 0xfffffffffffffff); + static_assert((18446744073709551615_b).get() == 0xffffffffffffffff); +} // namespace udb + +// format Bits as their underlying type when using format() +template + requires(N <= udb::BitsMaxNativePrecision) +struct fmt::formatter> + : formatter::StorageType> { + template + auto format(udb::_Bits value, CONTEXT_TYPE &ctx) const { + return fmt::formatter::StorageType>::format( + value.get(), ctx); + } +}; + +template + requires(N > udb::BitsMaxNativePrecision) +struct fmt::formatter> { + private: + fmt::detail::dynamic_format_specs specs_; + + public: + constexpr auto parse(fmt::format_parse_context &ctx) + -> decltype(ctx.begin()) { + auto end = parse_format_specs(ctx.begin(), ctx.end(), specs_, ctx, + fmt::detail::type::int_type); + return end; + } + + template + auto format(const udb::_Bits &c, + FormatContext &ctx) -> decltype(ctx.out()) { + fmt::detail::handle_dynamic_spec( + specs_.width, specs_.width_ref, ctx); + int base = 10; + std::string gmp_fmt_string = "%"; + if (specs_.fill.data()[0] == '0') gmp_fmt_string += "0"; + if (specs_.alt) gmp_fmt_string += "#"; + if (specs_.sign == fmt::sign_t::plus) gmp_fmt_string += "+"; + if (specs_.sign == fmt::sign_t::minus) gmp_fmt_string += "-"; + if (specs_.sign == fmt::sign_t::space) gmp_fmt_string += " "; + if (specs_.width != 0) gmp_fmt_string += std::to_string(specs_.width); + gmp_fmt_string += "Z"; + if (specs_.type == fmt::presentation_type::hex_lower) { + base = 16; + gmp_fmt_string += "x"; + } else if (specs_.type == fmt::presentation_type::hex_upper) { + base = 16; + gmp_fmt_string += "X"; + } else if (specs_.type == fmt::presentation_type::oct) { + base = 8; + gmp_fmt_string += "o"; + } else { + base = 10; + gmp_fmt_string += "d"; + } + size_t strwidth = std::max((size_t)specs_.width, + mpz_sizeinbase(c.get().get_mpz_t(), base)); + char *str = new char[strwidth + 100]; + gmp_snprintf(str, strwidth + 100, gmp_fmt_string.c_str(), + c.get().get_mpz_t()); + auto ret_val = fmt::format_to(ctx.out(), "{}", str); + delete[] str; + return ret_val; + } +}; + +namespace std { + template + std::string to_string(const udb::_Bits &i) { + if constexpr (N > udb::_Bits<64, false>::MaxNativePrecision) { + return i.get_str(); + } else { + return to_string(i.get()); + } + } +} // namespace std + +namespace std { + + template + struct hash> { + std::size_t operator()(const udb::_Bits &s) const noexcept { + return std::hash::StorageType>{}(s.get()); + } + }; + + template + class numeric_limits> { + public: + static constexpr bool is_specialized = true; + static constexpr bool is_signed = Signed; + static constexpr bool is_integer = true; + static constexpr bool is_exact = true; + static constexpr bool has_infinity = false; + static constexpr bool has_quiet_NaN = false; + static constexpr bool has_signaling_NaN = false; + static constexpr bool has_dermon_loss = false; + static constexpr std::float_round_style round_style = + std::round_toward_zero; + static constexpr bool is_iec559 = false; + static constexpr bool is_bounded = + (N != udb::_Bits::InfinitePrecision); + static constexpr int digits = Signed ? N - 1 : N; + static constexpr int digits10 = N < udb::_Bits::InfinitePrecision + ? digits * std::log10(2) + : 0; + static constexpr int max_digits10 = 0; + static constexpr int radix = 2; + static constexpr int min_exponent = 0; + static constexpr int min_exponent10 = 0; + static constexpr int max_exponent = 0; + static constexpr int max_exponent10 = 0; + static constexpr bool trap = true; + static constexpr bool tinyness_before = false; + + static consteval udb::_Bits min() noexcept { + if constexpr (Signed) { + return -typename udb::_Bits{ + typename udb::_Bits::SignedStorageType{1} << (N - 1)}; + } else { + return 0; + } + } + static consteval udb::_Bits lowest() noexcept { return min(); } + static consteval udb::_Bits max() noexcept { + if constexpr (N <= udb::_Bits::MaxNativePrecision) { + if (N == sizeof(typename udb::_Bits::StorageType) * 8) { + if (Signed) { + return std::numeric_limits< + typename udb::_Bits::SignedStorageType>::max(); + } else { + return std::numeric_limits< + typename udb::_Bits::StorageType>::max(); + } + } else { + if (Signed) { + return (typename udb::_Bits::StorageType{1} << (N - 1)) - + 1; + } else { + return (typename udb::_Bits::StorageType{1} << (N)) - 1; + } + } + } else { + if (Signed) { + return (typename udb::_Bits::StorageType{1} << (N - 1)) - + 1; + } else { + return (typename udb::_Bits::StorageType{1} << N) - 1; + } + } + } + static consteval udb::_Bits epsilon() noexcept { return 0; } + static consteval udb::_Bits round_error() noexcept { return 0; } + static consteval udb::_Bits infinity() noexcept { return 0; } + static consteval udb::_Bits quiet_NaN() noexcept { return 0; } + static consteval udb::_Bits denorm_min() noexcept { return 0; } + }; +} // namespace std + +static_assert(std::numeric_limits>::min() == 0); +static_assert(std::numeric_limits>::min() == -128); +static_assert(std::numeric_limits>::max() == 255); +static_assert(std::numeric_limits>::max() == 127); +static_assert(std::numeric_limits>::min() == 0); +static_assert(std::numeric_limits>::min() == -256); +static_assert(std::numeric_limits>::max() == 511); +static_assert(std::numeric_limits>::max() == 255); + +namespace udb { + // Bits where the width is only known at runtime (usually because the width is + // parameter-dependent) + template + class _RuntimeBits { + struct UnknownWidthType {}; + UnknownWidthType UnknownWidth; + + template + _RuntimeBits(const T &initial_value, const UnknownWidthType &) + : m_value(initial_value), m_width_known(false) {} + + template + friend class _RuntimeBits; + + using StorageType = _Bits::StorageType; + + StorageType mask() const { + if (m_width == MaxN) { + return ~StorageType{0}; + } else { + return (StorageType{1} << m_width) - 1; + } + } + + void apply_mask() { + if (m_width_known) { + m_value = m_value & mask(); + } + } + + public: + static constexpr bool IsABits = true; + + _RuntimeBits() : m_width_known(false) {} + + template + _RuntimeBits(const IntType &initial_value) + : m_value(initial_value), m_width_known(false) {} + + template + _RuntimeBits(const _Bits &initial_value) + : m_value(initial_value), m_width(N), m_width_known(true) {} + + template + _RuntimeBits(const T &initial_value, unsigned width) + : m_value(initial_value), m_width(width), m_width_known(true) { + apply_mask(); + } + + _RuntimeBits(const _RuntimeBits &initial_value) + : m_value(initial_value.value()), + m_width(initial_value.m_width), + m_width_known(initial_value.m_width_known) {} + + template + _RuntimeBits(const _RuntimeBits &initial_value) + : m_value(initial_value.value()), + m_width(initial_value.m_width), + m_width_known(initial_value.m_width_known) { + apply_mask(); + } + + template + requires(_Signed == false) + _RuntimeBits make_signed() const { + return _RuntimeBits{sign_extend(m_value.get()), m_width}; + } + template + requires(_Signed == true) + const _RuntimeBits &make_signed() const { + return *this; + } + + StorageType sign_extend(const StorageType &value) const { + if (m_width_known) { + udb_assert(m_width <= BitsMaxNativePrecision, + "Can't sign extend a GMP number"); + if (m_width == sizeof(StorageType) * 8) { + // exact fit, no extension needed + return value; // no extension needed + } else { + if ((value & (StorageType{1} << (m_width - 1))) != 0) { + // fill with ones + return value | ~mask(); + } else { + // no extension needed + return value; + } + } + } else { + throw UndefinedValueError( + "Can't sign extend when the width is unknown"); + } + } + + unsigned width() const { + if (!m_width_known) { + throw UndefinedValueError("RuntimeBits width is not known"); + } + return m_width; + } + bool width_known() const { return m_width_known; } + auto value() const { return m_value; } + auto get() const { return m_value.get(); } + + template + requires(std::integral) + operator IntType() const noexcept { + return static_cast(m_value); + } + + template + _RuntimeBits operator<<(const T &shamt) const { + if (m_width_known) { + return {m_value << shamt, m_width + shamt}; + } else { + return {m_value << shamt, UnknownWidth}; + } + } + + template + _RuntimeBits operator>>(const T &shamt) const { + if (m_width_known) { + return {m_value >> shamt, m_width}; + } else { + return {m_value >> shamt, UnknownWidth}; + } + } + +#define RUNTIME_BITS_BINARY_OP(op) \ + template \ + requires(MaxN >= N) \ + _RuntimeBits operator op(const _Bits &other) const { \ + if (m_width_known) { \ + return {m_value op _Bits{other}.get(), \ + std::max(N, m_width)}; \ + } else { \ + return {m_value op _Bits{other}.get(), UnknownWidth}; \ + } \ + } \ + \ + template \ + requires(MaxN < N) \ + _RuntimeBits operator op(const _Bits &other) const { \ + if (m_width_known) { \ + return {m_value op other.get(), std::max(N, m_width)}; \ + } else { \ + return {m_value op other.get(), UnknownWidth}; \ + } \ + } \ + \ + _RuntimeBits operator op(const _RuntimeBits &other) const { \ + if (m_width_known && other.m_width_known) { \ + return {m_value op other.m_value, std::max(other.m_width, m_width)}; \ + } else { \ + return {m_value op other.m_value, UnknownWidth}; \ + } \ + } + + RUNTIME_BITS_BINARY_OP(|) + RUNTIME_BITS_BINARY_OP(&) + RUNTIME_BITS_BINARY_OP(^) + +#undef RUNTIME_BITS_BINARY_OP + +#define RUNTIME_BITS_BINARY_OP(op) \ + _RuntimeBits operator op(const _RuntimeBits &other) const { \ + if (m_width_known && other.m_width_known) { \ + if (other.m_width != m_width) { \ + if (other.m_width > m_width) { \ + return { \ + _RuntimeBits{m_value, other.m_width}.m_value op other.m_value, \ + std::max(other.m_width, m_width)}; \ + } else { \ + return {m_value op _RuntimeBits{other.m_value, m_width}.m_value, \ + std::max(other.m_width, m_width)}; \ + } \ + } else { \ + return {m_value op other.m_value, std::max(other.m_width, m_width)}; \ + } \ + } else { \ + return {m_value op other.m_value, UnknownWidth}; \ + } \ + } \ + \ + template \ + _RuntimeBits::value, Signed && _Signed> operator op( \ + const _Bits &other) const { \ + using ReturnType = \ + _RuntimeBits::value, Signed && _Signed>; \ + if (m_width_known) { \ + if (N != m_width) { \ + if (N > m_width) { \ + return {ReturnType{m_value, N}.m_value op other.get(), N}; \ + } else { \ + return {m_value op ReturnType{other.get(), m_width}.m_value, \ + m_width}; \ + } \ + } else { \ + return {m_value op other.m_val, N}; \ + } \ + } else { \ + constexpr unsigned BigN = constmax::value; \ + return {_RuntimeBits{m_value, UnknownWidth} \ + .m_value op _Bits{other.m_val} \ + .get(), \ + UnknownWidth}; \ + } \ + } + + RUNTIME_BITS_BINARY_OP(+) + RUNTIME_BITS_BINARY_OP(-) + RUNTIME_BITS_BINARY_OP(*) + RUNTIME_BITS_BINARY_OP(/) + RUNTIME_BITS_BINARY_OP(%) + +#undef RUNTIME_BITS_BINARY_OP + +#define RUNTIME_BITS_BINARY_OP(op) \ + bool operator op(const _RuntimeBits &other) const { \ + return m_value op other.m_value; \ + } + + RUNTIME_BITS_BINARY_OP(==) + RUNTIME_BITS_BINARY_OP(!=) + RUNTIME_BITS_BINARY_OP(>) + RUNTIME_BITS_BINARY_OP(>=) + RUNTIME_BITS_BINARY_OP(<) + RUNTIME_BITS_BINARY_OP(<=) + +#undef RUNTIME_BITS_BINARY_OP + + _RuntimeBits operator~() { return {~m_value, m_width}; } + + _RuntimeBits operator-() { return {-m_value, m_width}; } + +#define RUNTIME_BITS_ASSIGN_OP(op) \ + template \ + _RuntimeBits &operator op(const _Bits &other) { \ + m_value op other; \ + return *this; \ + } \ + \ + _RuntimeBits &operator op(const _RuntimeBits & other) { \ + m_value op other.m_value; \ + return *this; \ + } \ + \ + template \ + _RuntimeBits &operator op(const IntType & other) { \ + m_value op other; \ + return *this; \ + } + + RUNTIME_BITS_ASSIGN_OP(|=) + RUNTIME_BITS_ASSIGN_OP(&=) + RUNTIME_BITS_ASSIGN_OP(^=) + RUNTIME_BITS_ASSIGN_OP(+=) + RUNTIME_BITS_ASSIGN_OP(-=) + RUNTIME_BITS_ASSIGN_OP(*=) + RUNTIME_BITS_ASSIGN_OP(/=) + RUNTIME_BITS_ASSIGN_OP(%=) + +#undef RUNTIME_BITS_ASSIGN_OP + + // left shift when the shift amount is known at compile time + template + _RuntimeBits, Signed> sll() const { + if (m_width_known) { + auto result_width = m_width + SHAMT; + return {_Bits, Signed>{m_value} << SHAMT, + result_width}; + } else { + return {_Bits, Signed>{m_value} << SHAMT}; + } + } + + // widening left shift when the shift amount is not known at compile time + _RuntimeBits widening_sll( + unsigned shamt) const { + if (m_width_known) { + return {_Bits{m_value} << shamt, + m_width + shamt}; + } else { + return _Bits{m_value} << shamt; + } + } + + private: + _Bits m_value; + unsigned m_width; + bool m_width_known; + }; + + template + bool operator==(const _Bits &a, + const _RuntimeBits &b) { + return a == b.value(); + } + + // construct Bits from RuntimeBits + template + template + _Bits::_Bits(const _RuntimeBits &val) { + if constexpr (RuntimeSigned) { + m_val = _Bits{val.make_signed().value().get()}.m_val; + } else { + m_val = _Bits{val.value().get()}.m_val; + } + } + + using RuntimeBits = _RuntimeBits; + + template + class _PossiblyUnknownBits { + public: + // used for template concept resolution + constexpr static bool IsABits = true; + + // advertise the width + constexpr static unsigned Width = N; + + constexpr static unsigned width() { return N; } + + using StorageType = typename _Bits::StorageType; + using SignedStorageType = typename _Bits::SignedStorageType; + + constexpr _PossiblyUnknownBits() + : m_unknown_mask(~static_cast(0)) {} + + template + constexpr _PossiblyUnknownBits( + const _PossiblyUnknownBits &other) + : m_value(other.m_value), m_unknown_mask(other.m_unknown_mask) {} + + template + constexpr _PossiblyUnknownBits(const _Bits &other) + : m_value(other), m_unknown_mask(0) {} + + template + constexpr _PossiblyUnknownBits(const _Bits &other, + const _Bits &other_mask) + : m_value(other), m_unknown_mask(other_mask) {} + + template + _PossiblyUnknownBits(_PossiblyUnknownBits &&other) noexcept + : m_value(std::move(other.m_value)), + m_unknown_mask(std::move(other.m_value)) {} + + template + _PossiblyUnknownBits(_Bits &&other) noexcept + : m_value(std::move(other)), m_unknown_mask(0) {} + + template + constexpr _PossiblyUnknownBits(const IntType &val) + : m_value(val), m_unknown_mask(0) {} + + template + constexpr _PossiblyUnknownBits(const IntType &val, const MaskIntType &mask) + : m_value(val), m_unknown_mask(mask) {} + + constexpr ~_PossiblyUnknownBits() noexcept = default; + + constexpr _Bits unknown_mask() const { return m_unknown_mask; } + + template + constexpr operator T() const { + if (m_unknown_mask == 0) { + return static_cast(m_value); + } else { + throw UndefinedValueError( + "Cannot convert value with unknowns to a native C++ type"); + } + } + + template + constexpr operator _Bits() const { + if (m_unknown_mask == 0) { + return m_value; + } else { + throw UndefinedValueError( + "Cannot convert value with unknowns to Bits type"); + } + } + + template < + typename T = std::conditional_t> + constexpr T get() const { + if (m_unknown_mask == 0) { + return m_value.get(); + } else { + throw UndefinedValueError( + "Cannot convert value with unknowns to a native C++ type"); + } + } + + // assignment + template + constexpr _PossiblyUnknownBits &operator=( + const _PossiblyUnknownBits &o) { + m_value = o.m_value; + m_unknown_mask = o.m_unknown_mask; + return *this; + } + + template + _PossiblyUnknownBits &operator=( + const _PossiblyUnknownBits &&o) noexcept { + m_value = std::move(o.m_value); + m_unknown_mask = std::move(o.m_unknown_mask); + return *this; + } + + template + constexpr _PossiblyUnknownBits &operator=(const _Bits &o) { + m_value = o; + m_unknown_mask = 0; + return *this; + } + + template + _PossiblyUnknownBits &operator=(const _Bits &&o) noexcept { + m_value = std::move(o); + m_unknown_mask = 0; + return *this; + } + + template + constexpr _PossiblyUnknownBits &operator=(const IntType &o) { + m_value = o; + m_unknown_mask = 0; + return *this; + } + + // negate operator + constexpr _PossiblyUnknownBits operator-() const { + return {-m_value, m_unknown_mask}; + } + + // invert operator + constexpr _PossiblyUnknownBits operator~() const & { + return {~m_value, m_unknown_mask}; + } + +#define BITS_COMPARISON_OPERATOR(op) \ + template \ + constexpr bool operator op(const _PossiblyUnknownBits &o) \ + const { \ + if (m_unknown_mask != 0 || o.m_unknown_mask != 0) { \ + throw UndefinedValueError("Cannot compare unknown value"); \ + } \ + return m_value op o; \ + } \ + \ + template \ + constexpr bool operator op(const _Bits &o) const { \ + if (m_unknown_mask != 0) { \ + throw UndefinedValueError("Cannot compare unknown value"); \ + } \ + return m_value op o; \ + } \ + \ + template \ + constexpr bool operator op(const IntType &o) const { \ + if (m_unknown_mask != 0) { \ + throw UndefinedValueError("Cannot compare unknown value"); \ + } \ + return m_value op o; \ + } \ + \ + constexpr bool operator op(const mpz_class &o) const { \ + if (m_unknown_mask != 0) { \ + throw UndefinedValueError("Cannot compare unknown value"); \ + } \ + return m_value op o; \ + } \ + \ + constexpr friend bool operator op(const mpz_class &lhs, \ + const _PossiblyUnknownBits &rhs) { \ + if (rhs.m_unknown_mask != 0) { \ + throw UndefinedValueError("Cannot compare unknown value"); \ + } \ + return lhs op rhs.m_value; \ + } \ + \ + template \ + constexpr friend bool operator op(const IntType &lhs, \ + const _PossiblyUnknownBits &rhs) { \ + if (rhs.m_unknown_mask != 0) { \ + throw UndefinedValueError("Cannot compare unknown value"); \ + } \ + return lhs op rhs.m_value; \ + } + + BITS_COMPARISON_OPERATOR(==) + BITS_COMPARISON_OPERATOR(!=) + BITS_COMPARISON_OPERATOR(<) + BITS_COMPARISON_OPERATOR(>) + BITS_COMPARISON_OPERATOR(<=) + BITS_COMPARISON_OPERATOR(>=) + +#undef BITS_COMPARISON_OPERATOR + +#define BITS_ARITHMETIC_OPERATOR(op) \ + template \ + constexpr _PossiblyUnknownBits operator op( \ + const _PossiblyUnknownBits &o) const { \ + if (m_unknown_mask != 0 || o.m_unknown_mask != 0) { \ + throw UndefinedValueError("Operator undefined with unknown values"); \ + } \ + return {m_value op o.m_value}; \ + } \ + \ + template \ + constexpr _PossiblyUnknownBits operator op(const _Bits &o) \ + const { \ + if (m_unknown_mask != 0) { \ + throw UndefinedValueError("Operator undefined with unknown values"); \ + } \ + return {m_value op o}; \ + } \ + \ + constexpr _PossiblyUnknownBits operator op(const mpz_class &o) const { \ + if (m_unknown_mask != 0) { \ + throw UndefinedValueError("Operator undefined with unknown values"); \ + } \ + return {m_value op o}; \ + } \ + \ + template \ + constexpr _PossiblyUnknownBits operator op(const IntType &_rhs) const { \ + if (m_unknown_mask != 0) { \ + throw UndefinedValueError("Operator undefined with unknown values"); \ + } \ + return {m_value op _rhs}; \ + } \ + \ + template \ + constexpr friend _PossiblyUnknownBits operator op( \ + const IntType &_lhs, const _PossiblyUnknownBits &rhs) { \ + if (rhs.m_unknown_mask != 0) { \ + throw UndefinedValueError("Operator undefined with unknown values"); \ + } \ + return {_lhs op rhs.m_value}; \ + } + + BITS_ARITHMETIC_OPERATOR(+) + BITS_ARITHMETIC_OPERATOR(-) + BITS_ARITHMETIC_OPERATOR(*) + BITS_ARITHMETIC_OPERATOR(/) + BITS_ARITHMETIC_OPERATOR(%) + +#undef BITS_ARITHMETIC_OPERATOR + +#define BITS_BITWISE_OPERATOR(op) \ + template \ + constexpr _PossiblyUnknownBits operator op( \ + const _PossiblyUnknownBits &o) const { \ + return {m_value op o.m_value, m_unknown_mask & o.m_unknown_mask}; \ + } \ + template \ + constexpr _PossiblyUnknownBits operator op(const _Bits &o) \ + const { \ + return {m_value op o, m_unknown_mask}; \ + } \ + template \ + constexpr _PossiblyUnknownBits operator op( \ + const _RuntimeBits &o) const { \ + return {m_value op o, m_unknown_mask}; \ + } \ + \ + constexpr _PossiblyUnknownBits operator op(const mpz_class &o) const { \ + return {m_value op o, m_unknown_mask}; \ + } \ + template \ + constexpr _PossiblyUnknownBits operator op(const IntType &_rhs) const { \ + return {m_value op _rhs, m_unknown_mask}; \ + } \ + template \ + constexpr friend _PossiblyUnknownBits operator op( \ + const _Bits &_lhs, const _PossiblyUnknownBits &rhs) { \ + return {_lhs op rhs.m_value, rhs.m_unknown_mask}; \ + } \ + template \ + constexpr friend _PossiblyUnknownBits operator op( \ + const IntType &_lhs, const _PossiblyUnknownBits &rhs) { \ + return {_lhs op rhs.m_value, rhs.m_unknown_mask}; \ + } + + BITS_BITWISE_OPERATOR(&) + BITS_BITWISE_OPERATOR(|) + BITS_BITWISE_OPERATOR(^) + +#undef BITS_BITWISE_OPERATOR + + template + constexpr _PossiblyUnknownBits operator<<( + const _PossiblyUnknownBits &shamt) const { + if (shamt.m_unknown_mask != 0) { + throw UndefinedValueError("Cannot shift an unknown amount"); + } + return {m_value << shamt.m_value, m_unknown_mask << shamt.m_value}; + } + + template + constexpr _PossiblyUnknownBits operator<<( + const _Bits &shamt) const { + return {m_value << shamt, m_unknown_mask << shamt}; + } + + template + constexpr _PossiblyUnknownBits operator<<(const IntType &shamt) const { + return {m_value << shamt, m_unknown_mask << shamt}; + } + + constexpr _PossiblyUnknownBits operator<<(const mpz_t &shamt) const { + return {m_value << shamt, m_unknown_mask << shamt}; + } + + // the result has to be known + template + constexpr friend IntType operator<<(const IntType &val, + const _PossiblyUnknownBits &shamt) { + if (shamt.m_unknown_mask != 0) { + throw UndefinedValueError("Cannot shift an unknown amount"); + } + return {val << shamt.m_value}; + } + + template + constexpr friend _Bits operator<<( + const _Bits &lhs, const _PossiblyUnknownBits &shamt) { + if (shamt.m_unknown_mask != 0) { + throw UndefinedValueError("Cannot shift an unknown amount"); + } + return {lhs << shamt.m_value}; + } + + template + constexpr _PossiblyUnknownBits operator>>( + const _PossiblyUnknownBits &shamt) const { + if (shamt.m_unknown_mask != 0) { + throw UndefinedValueError("Cannot shift an unknown amount"); + } + return {m_value >> shamt.m_value, m_unknown_mask >> shamt.m_value}; + } + + template + constexpr _PossiblyUnknownBits operator>>( + const _Bits &shamt) const { + return {m_value >> shamt, m_unknown_mask >> shamt}; + } + + template + constexpr _PossiblyUnknownBits operator>>(const IntType &shamt) const { + return {m_value >> shamt, m_unknown_mask >> shamt}; + } + + _PossiblyUnknownBits operator>>(const mpz_class &shamt) const { + return {m_value >> shamt, m_unknown_mask >> shamt}; + } + + template + constexpr friend _Bits operator>>( + const _Bits &val, const _PossiblyUnknownBits &shamt) { + if (shamt.m_unknown_mask != 0) { + throw UndefinedValueError("Cannot shift an unknown amount"); + } + return val >> shamt.m_value; + } + + template + constexpr friend IntType operator>>(const IntType &val, + const _PossiblyUnknownBits &shamt) { + if (shamt.m_unknown_mask != 0) { + throw UndefinedValueError("Cannot shift an unknown amount"); + } + return val >> shamt.m_value; + } + + friend mpz_class operator>>(const mpz_class &val, + const _PossiblyUnknownBits &shamt) { + if (shamt.m_unknown_mask != 0) { + throw UndefinedValueError("Cannot shift an unknown amount"); + } + return val >> shamt.m_value; + } + + // widening left shift when the shift amount is known at compile time + template + constexpr _PossiblyUnknownBits, Signed> sll() const { + using ReturnType = _PossiblyUnknownBits, Signed>; + if constexpr (addsat_v >= BitsMaxNativePrecision) { + return ReturnType{ReturnType{m_value}.m_value << SHAMT, + ReturnType{m_value}.m_unknown_mask << SHAMT}; + } else { + return ReturnType{ReturnType{m_value}.m_value << SHAMT, + ReturnType{m_value}.m_unknown_mask << SHAMT}; + } + } + +#define BITS_OP_ASSIGN(op) \ + template \ + constexpr _PossiblyUnknownBits &operator op##=(const T & o) { \ + *this = (*this op o); \ + return *this; \ + } + + BITS_OP_ASSIGN(+) + BITS_OP_ASSIGN(-) + BITS_OP_ASSIGN(/) + BITS_OP_ASSIGN(*) + BITS_OP_ASSIGN(%) + BITS_OP_ASSIGN(&) + BITS_OP_ASSIGN(|) + BITS_OP_ASSIGN(^) + +#undef BITS_OP_ASSIGN + + friend std::ostream &operator<<(std::ostream &stream, + const _PossiblyUnknownBits &val) { + if (val.m_unknown_mask == 0) { + stream << val.m_value; + } else { + stream << fmt::format("{} (unknown mask: {})", val.m_value, + val.m_unknown_mask); + } + return stream; + } + + template + constexpr _PossiblyUnknownBits extract() const { + static_assert(msb >= lsb); + return _PossiblyUnknownBits{ + m_value >> lsb, + m_unknown_mask >> lsb}; // masking will happen in the constructor + } + + template + constexpr _PossiblyUnknownBits &setBit(const IndexType &idx, + const ValueType &value) { + StorageType pos_mask = static_cast(1) << idx; + m_value = (m_value & ~pos_mask) | + ((static_cast(value) << idx) & pos_mask); + m_unknown_mask &= ~pos_mask; + return *this; + } + + // private: + _Bits m_value; + _Bits m_unknown_mask; + }; + + template + constexpr _PossiblyUnknownBits::width, false> + operator""_xb() { + if constexpr (BitsStr::width <= + BitsMaxNativePrecision) { + return {BitsStr::val, + BitsStr::unknown_mask}; + } else { + return mpz_class{BitsStr::str}; + } + } + + template + constexpr _PossiblyUnknownBits::width, false> + operator""_xb() { + if constexpr (BitsTemplateStr::width <= BitsMaxNativePrecision) { + return {BitsTemplateStr::val, + BitsTemplateStr::unknown_mask}; + } else { + return mpz_class{BitsTemplateStr::str}; + } + } + + template + constexpr _PossiblyUnknownBits::width, true> + operator""_xsb() { + if constexpr (BitsStr::width <= + BitsMaxNativePrecision) { + return {BitsStr::val, + BitsStr::unknown_mask}; + } else { + return mpz_class{BitsStr::str}; + } + } + + template + constexpr _PossiblyUnknownBits::width, true> + operator""_xsb() { + if constexpr (BitsTemplateStr::width <= BitsMaxNativePrecision) { + return {BitsTemplateStr::val, + BitsTemplateStr::unknown_mask}; + } else { + return mpz_class{BitsTemplateStr::str}; + } + } + + static_assert((0x0_xb).Width == 1); + static_assert((0x1_xb).Width == 1); + static_assert((0_xb).Width == 1); + static_assert((1_xb).Width == 1); + static_assert((0x2_xb).Width == 2); + static_assert((0x7_xb).Width == 3); + static_assert((0x8_xb).Width == 4); + static_assert((0xf_xb).Width == 4); + static_assert((0x1f_xb).Width == 5); + static_assert((0xffffffffffffffff_xb).Width == 64); + + static_assert((0x1_xb).get() == 1); + static_assert((0x2_xb).get() == 2); + static_assert((0x7_xb).get() == 7); + static_assert((0x8_xb).get() == 8); + static_assert((0xf_xb).get() == 15); + static_assert((0x1f_xb).get() == 0x1f); + static_assert((0xff_xb).get() == 0xff); + static_assert((0xffffffff_xb).get() == 0xfffffffful); + static_assert((0xfffffffff_xb).get() == 0xffffffffful); + static_assert((0xffffffff1_xb).get() == 0xffffffff1ul); + static_assert((0xfffffffffffffff_xb).get() == 0xffffffffffffffful); + static_assert((0xffffffffffffffff_xb).get() == 0xfffffffffffffffful); + + static_assert((1_xb).get() == 1); + static_assert((2_xb).get() == 2); + static_assert((7_xb).get() == 7); + static_assert((8_xb).get() == 8); + static_assert((15_xb).get() == 15); + static_assert((31_xb).get() == 31); + static_assert((1152921504606846975_xb).get() == 0xfffffffffffffff); + static_assert((18446744073709551615_xb).get() == 0xffffffffffffffff); + + static_assert(("0x1x"_xb).Width == 5); + static_assert(("0x1x"_xb).unknown_mask() == 0xf); + + template + using Bits = _Bits; + + template + using SignedBits = _Bits; + + // special values + static constexpr Bits<65> UNDEFINED_LEGAL = 0x10000000000000000_b; + static constexpr Bits<66> UNDEFINED_LEGAL_DETERMINISTIC = + 0x20000000000000000_b; + + template + using PossiblyUnknownBits = _PossiblyUnknownBits; +} // namespace udb diff --git a/backends/cpp_hart_gen/cpp/include/udb/config_validator.hpp b/backends/cpp_hart_gen/cpp/include/udb/config_validator.hpp new file mode 100644 index 0000000000..1c8e1acc7c --- /dev/null +++ b/backends/cpp_hart_gen/cpp/include/udb/config_validator.hpp @@ -0,0 +1,93 @@ +#pragma once + +#include + +#include +#include + +#include "udb/db_data.hxx" + +namespace udb { + class ConfigValidator { + ConfigValidator() = delete; + + public: + static nlohmann::json validate(const YAML::Node& config) { + auto json = yaml_to_json(config); + if (!json.contains("$schema")) { + throw std::runtime_error("No $schema in config file"); + } + fmt::print("{}\n", json.dump()); + std::smatch m; + std::regex re("^https://riscv.org/udb/schemas/(.*\\.json)"); + std::string schema_path = json["$schema"].template get(); + if (!std::regex_match(schema_path, m, re)) { + throw std::runtime_error("Invalid $schema in config file"); + } + auto schema = m[1]; + + if (schema == "config-0.1.0.json") { + auto loader = [](const nlohmann::json_uri& uri, nlohmann::json& value) { + value = nlohmann::json::parse(DbData::SCHEMAS[uri.path().substr(1)]); + }; + nlohmann::json_schema::json_validator validator(loader); + try { + validator.set_root_schema( + nlohmann::json::parse(DbData::SCHEMAS[schema])); + } catch (const std::exception* e) { + throw std::runtime_error( + "Validation of schema config-0.1.0 failed: " + + std::string(e->what())); + } + + try { + auto default_patch = validator.validate(json); + return json.patch(default_patch); + } catch (const std::exception& e) { + throw std::runtime_error("Config validation failed: " + + std::string(e.what())); + } + } + return json; + } + + private: + static nlohmann::json yaml_to_json(const YAML::Node& node) { + if (node.IsScalar()) { + union { + int64_t i; + double d; + bool b; + } scalar; + std::string s; + if (YAML::convert::decode(node, scalar.i)) { + return scalar.i; + // } else if (YAML::convert::decode(node, scalar.d)) { + // return scalar.d; + } else if (YAML::convert::decode(node, scalar.b)) { + return scalar.b; + } else if (YAML::convert::decode(node, s)) { + return s; + } else { + throw std::runtime_error("Unknown scalar type in YAML conversion"); + } + } else if (node.IsSequence()) { + nlohmann::json json = nlohmann::json::array(); + for (auto it = node.begin(); it != node.end(); ++it) { + json.push_back(yaml_to_json(*it)); + } + return json; + } else if (node.IsMap()) { + nlohmann::json json = nlohmann::json::object(); + for (auto&& it : node) { + json[it.first.as()] = yaml_to_json(it.second); + } + return json; + } else if (node.IsNull()) { + return {nullptr}; + } else { + throw std::runtime_error("Unknown YAML type in conversion"); + } + } + }; +} // namespace udb diff --git a/backends/cpp_hart_gen/cpp/include/udb/cpp_exceptions.hpp b/backends/cpp_hart_gen/cpp/include/udb/cpp_exceptions.hpp new file mode 100644 index 0000000000..5a61a81ca1 --- /dev/null +++ b/backends/cpp_hart_gen/cpp/include/udb/cpp_exceptions.hpp @@ -0,0 +1,63 @@ +#pragma once + +#include + +namespace udb { + // thrown when there is an issue querying the db (e.g., asking for an enum + // member that doesn't exist) + class DbError : public std::runtime_error { + public: + DbError(const std::string& why) : std::runtime_error(why) {} + }; + + // thrown when a input to a calculation is undefined + class UndefinedValueError : public std::runtime_error { + public: + UndefinedValueError(const std::string& why) : std::runtime_error(why) {} + }; + + // thrown when the source or destination registers of an instruction + // is dependent on the value of a register and cannot be determined statically + class ComplexRegDetermination : public std::runtime_error { + public: + ComplexRegDetermination() + : std::runtime_error( + "Register set cannot be determined at compile time") {} + }; + + // thrown when a running program exits (only occurs with certain tracers) + class ExitEvent : public std::exception { + public: + ExitEvent(int exit_code) : std::exception(), m_exit_code(exit_code) {} + virtual ~ExitEvent() = default; + + int code() const { return m_exit_code; } + + private: + int m_exit_code; + }; + + // object that is thrown when an instruction (or fetch) encounters an + // exception + class AbortInstruction : public std::exception { + public: + const char* what() const noexcept override { return "Instruction Abort"; } + }; + + class WfiException : public std::exception { + public: + const char* what() const noexcept override { return "WFI instruction"; } + }; + + class PauseException : public std::exception { + public: + const char* what() const noexcept override { return "PAUSE instruction"; } + }; + + class UnpredictableBehaviorException : public std::exception { + public: + const char* what() const noexcept override { + return "Encountered unpredictable behavior"; + } + }; +} // namespace udb diff --git a/backends/cpp_hart_gen/cpp/include/udb/csr.hpp b/backends/cpp_hart_gen/cpp/include/udb/csr.hpp new file mode 100644 index 0000000000..668d582cbe --- /dev/null +++ b/backends/cpp_hart_gen/cpp/include/udb/csr.hpp @@ -0,0 +1,119 @@ +#pragma once + +#include +#include + +#include "udb/enum.hxx" +#include "udb/soc_model.hpp" + +namespace udb { + // represents the location of a field within a CSR + struct CsrFieldLocation { + unsigned msb; + unsigned lsb; + + constexpr CsrFieldLocation(unsigned _msb, unsigned _lsb) + : msb(_msb), lsb(_lsb) {} + unsigned size() const { return msb - lsb + 1; } + }; + + class CsrFieldBase { + public: + CsrFieldBase() {} + + virtual const CsrFieldLocation location(const unsigned& xlen) const = 0; + + virtual void reset() = 0; + + // read field out of parent CSR, given the effective xlen + virtual uint64_t hw_read(const unsigned& xlen) const = 0; + + // read field out of full csr_value (field is located at offset in + // csr_value) given the effective xlen + virtual uint64_t hw_read(const uint64_t& csr_value, + const unsigned& xlen) const = 0; + + // virtual uint64_t sw_read(const unsigned& xlen) const = 0; + + // write the field, without performing any checks + // given the effective xlen + virtual void hw_write(const uint64_t& field_write_value, + const unsigned& xlen) = 0; + + // write the field, applying any restrictions first + // given teh effective xlen + // virtual void sw_write(const uint64_t& field_write_value, + // const unsigned& xlen) = 0; + + virtual CsrFieldType type(const unsigned& xlen) const = 0; + + bool readOnly(const unsigned& xlen) const { + return type(xlen) == CsrFieldType::RO || type(xlen) == CsrFieldType::ROH; + } + bool writeable(const unsigned& xlen) const { return !readOnly(xlen); } + bool immutable(const unsigned& xlen) const { + return type(xlen) == CsrFieldType::RO; + } + + // true when this field is updated by hardware without an explicit software + // write + bool hardwareUpdates(const unsigned& xlen) const { + return (type(xlen) == CsrFieldType::ROH || + type(xlen) == CsrFieldType::RWH || + type(xlen) == CsrFieldType::RWRH); + } + + // true when only a subset of values are legal for the field + bool restrictedValues(const unsigned& xlen) const { + return (type(xlen) == CsrFieldType::RWR || + type(xlen) == CsrFieldType::RWRH); + } + }; + + template + class HartBase; + + class CsrBase { + friend class CsrFieldBase; + + public: + CsrBase() {} + + virtual unsigned address() const = 0; + virtual const std::string name() const = 0; + + virtual void reset() = 0; + + // read the raw bits of a CSR value + // + // some CSRs are shorter than XLEN bits, but none are longer + // therefore, we can safely use XReg as a value placeholder + virtual uint64_t hw_read(const unsigned& xlen) const = 0; + + // read the overall CSR value, as software would see it through a Zicsr + // instruction + // + // if the CSR presents a different value to software, + // the CSR can override sw_read() accordingly + // + // some CSRs are shorter than XLEN bits, but none are longer + // therefore, we can safely use XReg as a value placeholder + virtual uint64_t sw_read(const unsigned& xlen) const = 0; + + // tries to write 'value' into the CSR. Checks/conversions will be applied, + // so the value written may be different than 'value' + // + // If the write is illegal, then the function returns false. + // If the write was accepted (possibly with adjustments), then the function + // returns true + virtual bool sw_write(const uint64_t& value, const unsigned& xlen) = 0; + + // write all fields as given in 'value' + // + // no checks or transformations are applied + virtual void hw_write(const uint64_t& value, const unsigned& xlen) = 0; + + // cant this CSR be implemented when ext is not? + virtual bool implemented_without_Q_(const ExtensionName&) const = 0; + }; +} // namespace udb diff --git a/backends/cpp_hart_gen/cpp/include/udb/defines.hpp b/backends/cpp_hart_gen/cpp/include/udb/defines.hpp new file mode 100644 index 0000000000..a7ecc06a9b --- /dev/null +++ b/backends/cpp_hart_gen/cpp/include/udb/defines.hpp @@ -0,0 +1,73 @@ +#pragma once + +#include +#include +#include +#include + +// type to be used when you want to pass a string literal as a template +// arugment +template +struct TemplateString { + constexpr TemplateString(const char (&str)[N]) : size(N) { + std::copy_n(str, N, cstr_value); + } + constexpr char *value() const { return cstr_value; } + constexpr std::string_view sv() const { return cstr_value; } + const size_t size; + char cstr_value[N == 0 ? 1 : N]; +}; + +#if defined(__cpp_lib_source_location) && __cpp_lib_source_location >= 201907L +#include +#endif + +#if defined(__cpp_lib_unreachable) && __cpp_lib_unreachable >= 202202L +#include +#define udb_unreachable() std::unreachable() +#else +#define udb_unreachable() __builtin_unreachable() +#endif + +#include +#include + +#if defined(__cpp_lib_source_location) && __cpp_lib_source_location >= 201907L +#define __udb_assert(cond, msg) \ + do { \ + if (!(cond)) { \ + fmt::print(stderr, "At {} :\n Assertion failed: {}", \ + std::source_location::current(), (msg)); \ + std::abort(); \ + } \ + } while (false) +#else +#define __udb_assert(cond, msg) \ + do { \ + if (!(cond)) { \ + fmt::print(stderr, "At {}:{} :\n Assertion failed: {}", __FILE__, \ + __LINE__, msg); \ + std::abort(); \ + } \ + } while (false) +#endif + +#if __has_cpp_attribute(assume) >= 202207L + +#if defined(NDEBUG) +#define udb_assert(cond, msg) [[assume(cond)]] +#else +#define udb_assert(cond, msg) \ + __udb_assert(cond, msg); \ + [[assume(cond)]] +#endif + +#else // !__has_cpp_attribute( assume ) + +#if defined(NDEBUG) +#define udb_assert(cond, msg) // do nothing +#else +#define udb_assert(cond, msg) __udb_assert(cond, msg) +#endif + +#endif // __has_cpp_attribute( assume ) diff --git a/backends/cpp_hart_gen/cpp/include/udb/elf_reader.hpp b/backends/cpp_hart_gen/cpp/include/udb/elf_reader.hpp new file mode 100644 index 0000000000..e015b4c45e --- /dev/null +++ b/backends/cpp_hart_gen/cpp/include/udb/elf_reader.hpp @@ -0,0 +1,135 @@ +#pragma once + +#include + +#include +#include +#include + +#include "udb/soc_model.hpp" + +namespace udb { + // Class to read data out of an ELF file + class Memory; + class ElfReader { + // ElfException is thrown when something goes wrong when reading an ELF file + class ElfException : public std::exception { + public: + ElfException() = default; + ElfException(const std::string& what) : std::exception(), m_what(what) {} + ElfException(std::string&& what) + : std::exception(), m_what(std::move(what)) {} + + const char* what() const noexcept override { return m_what.c_str(); } + + private: + const std::string m_what; + }; + + public: + ElfReader() = delete; + ElfReader(const std::string& path); + ~ElfReader(); + + // return the smallest and largest address from any LOADable segment + std::pair mem_range(); + + // return starting address + uint64_t entry(); + + // get the address of a symbol named 'name', and put it in 'result' + // + // returns false if the symbol is not found, true otherwise + bool getSym(const std::string& name, Elf64_Addr* result); + + // Loads all LOADable sections from an ELF into 'm' + // + // returns the start address + template + uint64_t loadLoadableSegments(SocType& soc); + + private: + template + std::pair _mem_range(); + + template + uint64_t _loadLoadableSegments(SocType& soc); + + private: + int m_fd; + Elf* m_elf; + unsigned char m_class; + uint64_t m_entry; + }; + + template + std::pair ElfReader::_mem_range() { + std::conditional_t* phdr; + size_t n; + uint64_t smallest_addr = std::numeric_limits::max(); + uint64_t largest_addr = std::numeric_limits::min(); + + if (elf_getphdrnum(m_elf, &n) != 0) { + throw ElfException("Could not find number of Program Headers"); + } + + if constexpr (CLASS == ELFCLASS32) { + phdr = elf32_getphdr(m_elf); + } else { + phdr = elf64_getphdr(m_elf); + } + for (size_t i = 0; i < n; i++) { + if (phdr[i].p_type == PT_LOAD) { + if (phdr[i].p_vaddr < smallest_addr) { + smallest_addr = phdr[i].p_vaddr; + } + if (phdr[i].p_vaddr + phdr[i].p_memsz > largest_addr) { + largest_addr = phdr[i].p_vaddr + phdr[i].p_memsz; + } + } + } + + return std::make_pair(smallest_addr, largest_addr); + } + + template + uint64_t ElfReader::_loadLoadableSegments(SocType& soc) { + std::conditional_t* phdr; + size_t n; + + if (elf_getphdrnum(m_elf, &n) != 0) { + throw ElfException("Could not find number of Program Headers"); + } + + if constexpr (CLASS == ELFCLASS32) { + phdr = elf32_getphdr(m_elf); + } else { + phdr = elf64_getphdr(m_elf); + } + for (size_t i = 0; i < n; i++) { + if (phdr[i].p_type == PT_LOAD) { + Elf_Data* d = elf_getdata_rawchunk(m_elf, phdr[i].p_offset, + phdr[i].p_filesz, ELF_T_BYTE); + soc.memcpy_from_host(phdr[i].p_vaddr, + reinterpret_cast(d->d_buf), + d->d_size); + } + } + + if constexpr (CLASS == ELFCLASS32) { + return elf32_getehdr(m_elf)->e_entry; + } else { + return elf64_getehdr(m_elf)->e_entry; + } + } + + // returns start address + template + uint64_t udb::ElfReader::loadLoadableSegments(SocType& soc) { + if (m_class == ELFCLASS32) { + return _loadLoadableSegments(soc); + } else { + return _loadLoadableSegments(soc); + } + } +} // namespace udb diff --git a/backends/cpp_hart_gen/cpp/include/udb/hart.hpp b/backends/cpp_hart_gen/cpp/include/udb/hart.hpp new file mode 100644 index 0000000000..fd5d55d9c2 --- /dev/null +++ b/backends/cpp_hart_gen/cpp/include/udb/hart.hpp @@ -0,0 +1,364 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "udb/csr.hpp" +#include "udb/enum.hxx" +#include "udb/soc_model.hpp" +#include "udb/stop_reason.h" +#include "udb/version.hpp" +#include "udb/xregister.hpp" + +#if !defined(JSON_ASSERT) +#define JSON_ASSERT(cond) udb_assert(cond, "JSON assert"); +#endif +#include + +#ifdef assert +#undef assert +#endif + +namespace udb { + // base class for tracers; defines the tracepoints + class AbstractTracer { + public: + AbstractTracer() = default; + virtual ~AbstractTracer() = default; + + virtual void trace_exception() {} + + virtual void trace_mem_read_phys(uint64_t paddr, unsigned len) {} + virtual void trace_mem_write_phys(uint64_t paddr, unsigned len, + uint64_t data) {} + }; + + class InstBase; + + template + class HartBase { + public: + HartBase(unsigned hart_id, SocType& soc, const nlohmann::json& cfg) + : m_hart_id(hart_id), + m_soc(soc), + m_tracer(nullptr), + m_current_priv_mode(PrivilegeMode::M), + m_exit_requested(false), + m_num_inst_exec(0) {} + + virtual void reset(uint64_t reset_pc) { + m_exit_requested = 0; + m_num_inst_exec = 0; + } + + void attach_tracer(AbstractTracer* t) { + udb_assert(m_tracer == nullptr, "m_tracer NULL ptr"); + m_tracer = t; + } + + virtual void set_pc(uint64_t new_pc) = 0; + virtual void set_next_pc(uint64_t next_pc) = 0; + virtual uint64_t pc() const = 0; + virtual void advance_pc() = 0; + + // get the next instruction encoding + virtual uint64_t fetch() = 0; + + PrivilegeMode mode() const { return m_current_priv_mode; } + void set_mode(const PrivilegeMode& next_mode) { + m_current_priv_mode = next_mode; + } + + // access a physical address. All translations and physical checks + // should have already occurred + // template + // Bits read_physical_memory(AddrBitsType paddr) { + // if (m_tracer != nullptr) { + // m_tracer->trace_mem_read_phys(paddr.get(), Len); + // } + // if constexpr (Len == 8) { + // return m_soc.read_physical_memory_8(paddr.get()); + // } else if constexpr (Len == 16) { + // return m_soc.read_physical_memory_16(paddr.get()); + // } else if constexpr (Len == 32) { + // return m_soc.read_physical_memory_32(paddr.get()); + // } else if constexpr (Len == 64) { + // return m_soc.read_physical_memory_64(paddr.get()); + // } else { + // udb_assert(false, "TODO"); + // return 0; + // } + // } + + void assert(bool arg, const char* str) { udb_assert(arg, str); } + void assert(bool arg, const std::string_view& str) { udb_assert(arg, str); } + + // write a physical address. All translations and physical checks + // should have already occurred + // template + // void write_physical_memory(uint64_t paddr, const Bits &value) { + // if (m_tracer != nullptr) { + // m_tracer->trace_mem_write_phys(paddr, Len, value.get()); + // } + // if constexpr (Len == 8) { + // m_soc.write_physical_memory_8(paddr, value); + // } else if constexpr (Len == 16) { + // m_soc.write_physical_memory_16(paddr, value); + // } else if constexpr (Len == 32) { + // m_soc.write_physical_memory_32(paddr, value); + // } else if constexpr (Len == 64) { + // m_soc.write_physical_memory_64(paddr, value); + // } else { + // udb_assert(false, "TODO"); + // } + // } + + [[noreturn]] void abort_current_instruction() { + if (m_tracer != nullptr) { + m_tracer->trace_exception(); + } + throw AbortInstruction(); + } + + void wfi() { throw WfiException(); } + + void pause() { throw PauseException(); } + + // SoC functions + uint64_t read_hpm_counter(uint64_t counternum) { + return m_soc.read_hpm_counter(counternum); + } + uint64_t read_mcycle() { return m_soc.read_mcycle(); } + uint64_t read_mtime() { return m_soc.read_mtime(); } + Bits<64> sw_write_mcycle(const Bits<64>& value) { + return m_soc.sw_write_mcycle(value); + } + void cache_block_zero(uint64_t paddr) { m_soc.cache_block_zero(paddr); } + void eei_ecall_from_m() { m_soc.eei_ecall_from_m(); } + void eei_ecall_from_s() { m_soc.eei_ecall_from_s(); } + void eei_ecall_from_u() { m_soc.eei_ecall_from_u(); } + void eei_ecall_from_vs() { m_soc.eei_ecall_from_vs(); } + void eei_ebreak() { m_soc.eei_ebreak(); } + void memory_model_acquire() { m_soc.memory_model_acquire(); } + void memory_model_release() { m_soc.memory_model_release(); } + void notify_mode_change(const PrivilegeMode& from, + const PrivilegeMode& to) { + m_soc.notify_mode_change(from, to); + } + void ebreak() { m_soc.ebreak(); } + void prefetch_instruction(uint64_t paddr) { + m_soc.prefetch_instruction(paddr); + } + void prefetch_read(uint64_t paddr) { m_soc.prefetch_read(paddr); } + void prefetch_write(uint64_t paddr) { m_soc.prefetch_write(paddr); } + void fence(bool pi, bool pr, bool po, bool pw, bool si, bool sr, bool so, + bool sw) { + m_soc.fence(pi, pr, po, pw, si, sr, so, sw); + } + void fence_tso() { m_soc.fence_tso(); } + void ifence() { m_soc.ifence(); } + template + void order_pgtbl_writes_before_vmafence(Args...) { + // TODO: pass along order info (not easy now because VmaOrderType is + // cfg-dependent) + m_soc.order_pgtbl_writes_before_vmafence(); + } + template + void order_pgtbl_reads_after_vmafence(Args...) { + // TODO: pass along order info + m_soc.order_pgtbl_reads_after_vmafence(); + } + Bits<8> read_physical_memory_8(const Bits<64>& paddr) { + return m_soc.read_physical_memory_8(paddr); + } + Bits<16> read_physical_memory_16(const Bits<64>& paddr) { + return m_soc.read_physical_memory_16(paddr); + } + Bits<32> read_physical_memory_32(const Bits<64>& paddr) { + return m_soc.read_physical_memory_32(paddr); + } + Bits<64> read_physical_memory_64(const Bits<64>& paddr) { + return m_soc.read_physical_memory_64(paddr); + } + void write_physical_memory_8(const Bits<64>& paddr, const Bits<8>& value) { + m_soc.write_physical_memory_8(paddr, value); + } + void write_physical_memory_16(const Bits<64>& paddr, + const Bits<16>& value) { + m_soc.write_physical_memory_16(paddr, value); + } + void write_physical_memory_32(const Bits<64>& paddr, + const Bits<32>& value) { + m_soc.write_physical_memory_32(paddr, value); + } + void write_physical_memory_64(const Bits<64>& paddr, + const Bits<64>& value) { + m_soc.write_physical_memory_64(paddr, value); + } + bool atomic_check_then_write_32(uint64_t paddr, uint32_t compare_value, + uint32_t write_value) { + return m_soc.atomic_check_then_write_32(paddr, compare_value, + write_value); + } + bool atomic_check_then_write_64(uint64_t paddr, uint64_t compare_value, + uint64_t write_value) { + return m_soc.atomic_check_then_write_64(paddr, compare_value, + write_value); + } + bool atomically_set_pte_a(uint64_t pte_paddr, uint64_t pte_value, + uint32_t pte_len) { + return atomically_set_pte_a(pte_paddr, pte_value, pte_len); + } + bool atomically_set_pte_a_d(uint64_t pte_paddr, uint64_t pte_value, + uint32_t pte_len) { + return atomically_set_pte_a_d(pte_paddr, pte_value, pte_len); + } + uint64_t atomic_read_modify_write_32(uint64_t paddr, uint32_t value, + AmoOperation op) { + return m_soc.atomic_read_modify_write_32(paddr, value, op); + } + uint64_t atomic_read_modify_write_64(uint64_t paddr, uint64_t value, + AmoOperation op) { + return m_soc.atomic_read_modify_write_64(paddr, value, op); + } + bool pma_applies_Q_(const PmaAttribute& attr, uint64_t start_paddr, + uint32_t len) { + return m_soc.pma_applies_Q_(attr, start_paddr, len); + } + + // external interrupt interface + virtual void set_mmode_ext_int() = 0; + virtual void clear_mmode_ext_int() = 0; + virtual void set_smode_ext_int() = 0; + virtual void clear_smode_ext_int() = 0; + // virtual void set_vsmode_ext_int() = 0; + // virtual void clear_vsmode_ext_int() = 0; + + // + // virtual memory caching builtins + // + + struct SoftTlbEntry { + bool valid; + bool global; + bool smode; // was translation satp-based? + bool vsmode; // was translation vsatp-based? + bool gstage; // was translation hgatp-based? + + Bits<16> asid; + Bits<16> vmid; + + uint64_t vpn; // virtual page number + uint64_t ppn; // physical page number + uintptr_t vaddr; // offset to the page in *host* memory; ~0 = not valid + uintptr_t paddr; // offset to the page in *host* memory; ~0 = not valid + }; + + constexpr static unsigned SOFT_TLB_SIZE = 1024; + + SoftTlbEntry m_va_smode_read_tlb[SOFT_TLB_SIZE]; + SoftTlbEntry m_va_smode_write_tlb[SOFT_TLB_SIZE]; + SoftTlbEntry m_va_smode_exe_tlb[SOFT_TLB_SIZE]; + + SoftTlbEntry m_va_vsmode_read_tlb[SOFT_TLB_SIZE]; + SoftTlbEntry m_va_vsmode_write_tlb[SOFT_TLB_SIZE]; + SoftTlbEntry m_va_vsmode_exe_tlb[SOFT_TLB_SIZE]; + + SoftTlbEntry m_va_gstage_read_tlb[SOFT_TLB_SIZE]; + SoftTlbEntry m_va_gstage_write_tlb[SOFT_TLB_SIZE]; + SoftTlbEntry m_va_gstage_exe_tlb[SOFT_TLB_SIZE]; + + template + void invalidate_translations(const VmaOrderType&) {} + + void invalidate_all_translations() {} + void invalidate_asid_translations(Bits<16> asid) {} + void invalidate_vaddr_translations(uint64_t vaddr) {} + void invalidate_asid_vaddr_translations(Bits<16> asid, uint64_t vaddr) {} + + template + void maybe_cache_translation(Bits<64> vaddr, MemoryOperation op, + TranslationResult result) {} + + void sfence_all() {} + void sfence_asid(Bits<16> asid) {} + void sfence_vaddr(uint64_t vaddr) {} + void sfence_asid_vaddr(Bits<16> asid, uint64_t vaddr) {} + + // Return true if the address at paddr has the PMA attribute 'attr' + bool check_pma(const uint64_t& paddr, const PmaAttribute& attr) const { + return true; + } + + // xlen of M-mode, i.e., MXLEN + virtual unsigned mxlen() = 0; + + virtual uint64_t xreg(unsigned num) const = 0; + virtual void set_xreg(unsigned num, uint64_t value) = 0; + + virtual CsrBase* csr(unsigned address) = 0; + virtual const CsrBase* csr(unsigned address) const = 0; + + virtual CsrBase* csr(const std::string& name) = 0; + virtual const CsrBase* csr(const std::string& name) const = 0; + + virtual void printState(FILE* out = stdout) const = 0; + + virtual bool implemented_Q_(const ExtensionName& ext) const = 0; + virtual bool implemented_version_Q_( + const ExtensionName& ext, const VersionRequirement& req) const = 0; + + [[noreturn]] void unpredictable(const char* why) { + fmt::print(stderr, "Encountered unpredictable behavior: {}\n", why); + throw UnpredictableBehaviorException(); + } + [[noreturn]] void unpredictable(const std::string_view& why) { + fmt::print(stderr, "Encountered unpredictable behavior: {}\n", why); + throw UnpredictableBehaviorException(); + } + + unsigned hartid() const { return m_hart_id; } + + virtual int run_one() = 0; + virtual int run_bb() = 0; + virtual int run_n(uint64_t n) = 0; + + // called by the ISS; ask the hart to exit (from run_*) immediately + void request_exit() { m_exit_requested = true; } + + // after run*() returns ExitSuccess or ExitFailure, this will return the + // exit code from the running program (if run in a way that produces an + // exit) + int exit_code() { return m_exit_code; } + + // after run*() returns ExitSuccess or ExitFailure, this will return the + // exit message from the running program (if run in a way that produces an + // exit) + const std::string& exit_reason() { return m_exit_reason; } + + uint64_t num_insts_exec() const { return m_num_inst_exec; } + + protected: + const unsigned m_hart_id; + SocType& m_soc; + AbstractTracer* m_tracer; + PrivilegeMode m_current_priv_mode; + + int m_exit_code; + std::string m_exit_reason; + + bool m_exit_requested; + + // the number of instruction *executed* + // THIS IS NOT minstret (some executed instructions do not retire) + uint64_t m_num_inst_exec; + }; + +} // namespace udb diff --git a/backends/cpp_hart_gen/cpp/include/udb/inst.hpp b/backends/cpp_hart_gen/cpp/include/udb/inst.hpp new file mode 100644 index 0000000000..bb6d3c583c --- /dev/null +++ b/backends/cpp_hart_gen/cpp/include/udb/inst.hpp @@ -0,0 +1,137 @@ +#pragma once + +#include "udb/bitfield.hpp" +#include "udb/pool_alloc.hpp" + +namespace udb { + + class Reg { + public: + enum Enum { + X0 = 0, + X1 = 1, + X2 = 2, + X3 = 3, + X4 = 4, + X5 = 5, + X6 = 6, + X7 = 7, + X8 = 8, + X9 = 9, + X10 = 10, + X11 = 11, + X12 = 12, + X13 = 13, + X14 = 14, + X15 = 15, + X16 = 16, + X17 = 17, + X18 = 18, + X19 = 19, + X20 = 20, + X21 = 21, + X22 = 22, + X23 = 23, + X24 = 24, + X25 = 25, + X26 = 26, + X27 = 27, + X28 = 28, + X29 = 29, + X30 = 30, + X31 = 31, + F0 = 32, + F1 = 33, + F2 = 34, + F3 = 35, + F4 = 36, + F5 = 37, + F6 = 38, + F7 = 39, + F8 = 40, + F9 = 41, + F10 = 42, + F11 = 43, + F12 = 44, + F13 = 45, + F14 = 46, + F15 = 47, + F16 = 48, + F17 = 49, + F18 = 50, + F19 = 51, + F20 = 52, + F21 = 53, + F22 = 54, + F23 = 55, + F24 = 56, + F25 = 57, + F26 = 58, + F27 = 59, + F28 = 60, + F29 = 61, + F30 = 62, + F31 = 63, + INVALID = 64 + }; + + Reg(Enum r) : m_reg(r) {} + Reg(uint64_t r, bool is_fp = false) : m_reg(Enum(is_fp ? r + 32 : r)) {} + operator Enum() const { return m_reg; } + bool operator==(const Reg &other) const { return m_reg == other.m_reg; } + bool operator==(Enum other) const { return m_reg == other; } + std::ostream &operator<<(std::ostream &o) const { + o << to_string(); + return o; + } + + bool is_int() const { return m_reg <= X31; } + bool is_fp() const { return m_reg >= F0 && m_reg <= F31; } + + uint64_t get_num() const { + uint64_t num = static_cast(m_reg); + return (m_reg <= X31) ? num : num - 32; // NOLINT + } + + std::string to_string(uint64_t size = 64) const { + if (is_fp()) { + return "f" + std::to_string(get_num()); + } + return "x" + std::to_string(get_num()); + } + + private: + Enum m_reg = INVALID; + }; + + class InstBase { + public: + InstBase(uint64_t pc, uint64_t encoding) : m_pc(pc), m_encoding(encoding) {} + + uint64_t pc() { return m_pc; } + uint64_t encoding() { return m_encoding; } + // return encoding length, in bytes + virtual size_t enc_len() const = 0; + + // execute the instruction, updating the hart state + // if the instruction causes a synchronous exception, a + // HartBase::SynchronousException will be raised in C++ + virtual void execute() = 0; + + virtual const std::string_view &name() = 0; + virtual std::string disassemble(bool use_abi_reg_names = false) const = 0; + + // true if the instruction could change the pc without causing an exception + // i.e., is a branch + virtual bool control_flow() const = 0; + + // return the + virtual std::vector srcRegs() const = 0; + virtual std::vector dstRegs() const = 0; + + protected: + uint64_t m_pc; + uint64_t m_encoding; + }; + +} // namespace udb diff --git a/backends/cpp_hart_gen/cpp/include/udb/iss_soc_model.hpp b/backends/cpp_hart_gen/cpp/include/udb/iss_soc_model.hpp new file mode 100644 index 0000000000..887808e727 --- /dev/null +++ b/backends/cpp_hart_gen/cpp/include/udb/iss_soc_model.hpp @@ -0,0 +1,308 @@ +#pragma once + +#include + +#include +#include +#include + +#include "udb/soc_model.hpp" + +namespace udb { + class IssSocModel { + class DenseMemory { + public: + DenseMemory(uint64_t size, uint64_t base_addr) : m_offset(base_addr) { + m_data.resize(size); + m_addend = &m_data[0] - base_addr; + } + ~DenseMemory() = default; + + // subclasses only need to override these functions: + virtual uint64_t read(uint64_t addr, size_t bytes) { + switch (bytes) { + case 1: + return m_data[addr - m_offset]; + case 2: + return *(uint16_t *)(addr + m_addend); + case 4: + return *(uint32_t *)(addr + m_addend); + case 8: + return *(uint64_t *)(addr + m_addend); + default: + __builtin_unreachable(); + } + } + + void write(uint64_t addr, uint64_t data, size_t bytes) { + switch (bytes) { + case 1: + m_data[addr - m_offset] = data; + break; + case 2: + *(uint16_t *)(addr + m_addend) = data; + break; + case 4: + *(uint32_t *)(addr + m_addend) = data; + break; + case 8: + *(uint64_t *)(addr + m_addend) = data; + break; + default: + __builtin_unreachable(); + } + } + + int memcpy_from_host(uint64_t guest_paddr, const uint8_t *host_ptr, + std::size_t size) { + const size_t SZ_64 = sizeof(uint64_t); + auto host_ptr64 = (const uint64_t *)host_ptr; // NOLINT + while (size >= SZ_64) { + write((guest_paddr += SZ_64) - SZ_64, *host_ptr64++, 8); + size -= SZ_64; + } + + auto host_ptr8 = (const uint8_t *)host_ptr64; // NOLINT + while (size > 0) { + write(guest_paddr++, *host_ptr8++, 1); + size--; + } + return size; + } + + int memcpy_to_host(uint8_t *host_ptr, uint64_t guest_paddr, + std::size_t size) { + const size_t SZ_64 = sizeof(uint64_t); + auto host_ptr64 = (uint64_t *)host_ptr; // NOLINT + while (size >= SZ_64) { + *(host_ptr64++) = read(guest_paddr += SZ_64, 8); + size -= SZ_64; + } + + auto host_ptr8 = (uint8_t *)host_ptr64; // NOLINT + while (size > 0) { + *(host_ptr8++) = read(guest_paddr += SZ_64, 1); + size--; + } + return size; + } + + private: + std::vector m_data; + uint64_t m_offset; + uint8_t *m_addend = nullptr; + }; + + public: + IssSocModel(uint64_t size, uint64_t base_addr) + : m_memory(size, base_addr) {} + IssSocModel() = delete; + ~IssSocModel() = default; + + uint64_t read_hpm_counter(uint64_t n) { return 0; } + uint64_t read_mcycle() { return 0; } + uint64_t read_mtime() { return 0; } + uint64_t sw_write_mcycle(uint64_t value) { return value; } + void cache_block_zero(uint64_t cache_block_physical_address) {} + void eei_ecall_from_m() {} + void eei_ecall_from_s() {} + void eei_ecall_from_u() {} + void eei_ecall_from_vs() {} + void eei_ebreak() {} + void memory_model_acquire() {} + void memory_model_release() {} + void assert(uint8_t test, const char *message) {} + void notify_mode_change(PrivilegeMode new_mode, PrivilegeMode old_mode) {} + void prefetch_instruction(uint64_t virtual_address) {} + void prefetch_read(uint64_t virtual_address) {} + void prefetch_write(uint64_t virtual_address) {} + void fence(uint8_t pi, uint8_t pr, uint8_t po, uint8_t pw, uint8_t si, + uint8_t sr, uint8_t so, uint8_t sw) {} + void fence_tso() {} + void ifence() {} + void order_pgtbl_writes_before_vmafence() {} + void order_pgtbl_reads_after_vmafence() {} + + uint64_t read_physical_memory_8(uint64_t paddr) { + return m_memory.read(paddr, 1); + } + uint64_t read_physical_memory_16(uint64_t paddr) { + return m_memory.read(paddr, 2); + } + uint64_t read_physical_memory_32(uint64_t paddr) { + return m_memory.read(paddr, 4); + } + uint64_t read_physical_memory_64(uint64_t paddr) { + return m_memory.read(paddr, 8); + } + void write_physical_memory_8(uint64_t paddr, uint64_t value) { + m_memory.write(paddr, value, 1); + } + void write_physical_memory_16(uint64_t paddr, uint64_t value) { + m_memory.write(paddr, value, 2); + } + void write_physical_memory_32(uint64_t paddr, uint64_t value) { + m_memory.write(paddr, value, 4); + } + void write_physical_memory_64(uint64_t paddr, uint64_t value) { + m_memory.write(paddr, value, 8); + } + + int memcpy_from_host(uint64_t guest_paddr, const uint8_t *host_ptr, + uint64_t size) { + return m_memory.memcpy_from_host(guest_paddr, host_ptr, size); + } + int memcpy_to_host(uint8_t *host_ptr, uint64_t guest_paddr, uint64_t size) { + return m_memory.memcpy_to_host(host_ptr, guest_paddr, size); + } + + uint8_t atomic_check_then_write_32(uint64_t paddr, uint64_t compare_value, + uint64_t write_value) { + m_memory.write(paddr, write_value, 4); + return true; + } + uint8_t atomic_check_then_write_64(uint64_t paddr, uint64_t compare_value, + uint64_t write_value) { + m_memory.write(paddr, write_value, 8); + return true; + } + uint8_t atomically_set_pte_a(uint64_t pte_addr, uint64_t pte_value, + uint32_t pte_len) { + return true; + } + uint8_t atomically_set_pte_a_d(uint64_t pte_addr, uint64_t pte_value, + uint32_t pte_len) { + return true; + } + uint64_t atomic_read_modify_write_32(uint64_t phys_addr, uint64_t value, + AmoOperation op) { + switch (op.value()) { + case AmoOperation::Swap: { + uint32_t orig = m_memory.read(phys_addr, 4); + m_memory.write(phys_addr, value, 4); + return orig; + } + case AmoOperation::Add: { + uint32_t orig = m_memory.read(phys_addr, 4); + m_memory.write(phys_addr, orig + value, 4); + return orig; + } + case AmoOperation::And: { + uint32_t orig = m_memory.read(phys_addr, 4); + m_memory.write(phys_addr, orig & value, 4); + return orig; + } + case AmoOperation::Or: { + uint32_t orig = m_memory.read(phys_addr, 4); + m_memory.write(phys_addr, orig | value, 4); + return orig; + } + case AmoOperation::Xor: { + uint32_t orig = m_memory.read(phys_addr, 4); + m_memory.write(phys_addr, orig ^ value, 4); + return orig; + } + case AmoOperation::Max: { + uint32_t orig = m_memory.read(phys_addr, 4); + m_memory.write(phys_addr, + std::max(static_cast(orig), + static_cast(value & 0xffffffff)), + 4); + return orig; + } + case AmoOperation::Maxu: { + uint32_t orig = m_memory.read(phys_addr, 4); + m_memory.write( + phys_addr, + std::max(orig, static_cast(value & 0xffffffff)), 4); + return orig; + } + case AmoOperation::Min: { + uint32_t orig = m_memory.read(phys_addr, 4); + m_memory.write(phys_addr, + std::min(static_cast(orig), + static_cast(value & 0xffffffff)), + 4); + return orig; + } + case AmoOperation::Minu: { + uint32_t orig = m_memory.read(phys_addr, 4); + m_memory.write( + phys_addr, + std::min(orig, static_cast(value & 0xffffffff)), 4); + return orig; + } + default: + __builtin_unreachable(); + } + } + uint64_t atomic_read_modify_write_64(uint64_t phys_addr, uint64_t value, + AmoOperation op) { + switch (op.value()) { + case AmoOperation::Swap: { + uint64_t orig = m_memory.read(phys_addr, 8); + m_memory.write(phys_addr, value, 8); + return orig; + } + case AmoOperation::Add: { + uint64_t orig = m_memory.read(phys_addr, 8); + m_memory.write(phys_addr, orig + value, 8); + return orig; + } + case AmoOperation::And: { + uint64_t orig = m_memory.read(phys_addr, 8); + m_memory.write(phys_addr, orig & value, 8); + return orig; + } + case AmoOperation::Or: { + uint64_t orig = m_memory.read(phys_addr, 8); + m_memory.write(phys_addr, orig | value, 8); + return orig; + } + case AmoOperation::Xor: { + uint64_t orig = m_memory.read(phys_addr, 8); + m_memory.write(phys_addr, orig ^ value, 8); + return orig; + } + case AmoOperation::Max: { + uint64_t orig = m_memory.read(phys_addr, 8); + m_memory.write( + phys_addr, + std::max(static_cast(orig), static_cast(value)), + 4); + return orig; + } + case AmoOperation::Maxu: { + uint64_t orig = m_memory.read(phys_addr, 8); + m_memory.write(phys_addr, std::max(orig, value & 0xffffffff), 4); + return orig; + } + case AmoOperation::Min: { + uint64_t orig = m_memory.read(phys_addr, 8); + m_memory.write( + phys_addr, + std::min(static_cast(orig), static_cast(value)), + 4); + return orig; + } + case AmoOperation::Minu: { + uint64_t orig = m_memory.read(phys_addr, 8); + m_memory.write(phys_addr, std::min(orig, value), 4); + return orig; + } + default: + __builtin_unreachable(); + } + } + + uint8_t pma_applies_Q_(PmaAttribute attr, uint64_t paddr, uint32_t len) { + return true; + } + + private: + DenseMemory m_memory; + }; + + static_assert(SocModel, + "IssSocModel does not obey SocModel interface"); +} // namespace udb diff --git a/backends/cpp_hart_gen/cpp/include/udb/memory.hpp b/backends/cpp_hart_gen/cpp/include/udb/memory.hpp new file mode 100644 index 0000000000..d9a224c958 --- /dev/null +++ b/backends/cpp_hart_gen/cpp/include/udb/memory.hpp @@ -0,0 +1,170 @@ +#pragma once + +#include "udb/defines.hpp" + +namespace udb { + class Memory { + public: + Memory() = default; + virtual ~Memory() = default; + + template + T read(uint64_t paddr); + template + void write(uint64_t paddr, T data); + void memcpy_from_host(uint64_t guest_paddr, const void* host_ptr, + size_t size); + void memcpy_to_host(void* host_ptr, uint64_t guest_paddr, size_t size); + virtual uint8_t* get_host_region_ptr(uint64_t paddr) { return nullptr; } + virtual void reset() {} + + protected: + // subclasses only need to override these functions: + virtual uint64_t read(uint64_t addr, size_t bytes) { + udb_assert(false, "Memory::read() not implemented"); + return 0; + } + virtual void write(uint64_t addr, uint64_t data, size_t bytes) { + udb_assert(false, "Memory::write() not implemented"); + } + + // but may optionally override these instead: + virtual uint8_t read1(uint64_t addr) { return read(addr, 1); } + virtual uint16_t read2(uint64_t addr) { return read(addr, 2); } + virtual uint32_t read4(uint64_t addr) { return read(addr, 4); } + virtual uint64_t read8(uint64_t addr) { return read(addr, 8); } + virtual void write1(uint64_t addr, uint8_t data) { write(addr, data, 1); } + virtual void write2(uint64_t addr, uint16_t data) { write(addr, data, 2); } + virtual void write4(uint64_t addr, uint32_t data) { write(addr, data, 4); } + virtual void write8(uint64_t addr, uint64_t data) { write(addr, data, 8); } + }; + template <> + inline uint8_t Memory::read(uint64_t addr) { + return read1(addr); + } + template <> + inline uint16_t Memory::read(uint64_t addr) { + return read2(addr); + } + template <> + inline uint32_t Memory::read(uint64_t addr) { + return read4(addr); + } + template <> + inline uint64_t Memory::read(uint64_t addr) { + return read8(addr); + } + template <> + inline unsigned __int128 Memory::read(uint64_t addr) { + return read8(addr) | + (static_cast(read8(addr + 8)) << 64); + } + template <> + inline void Memory::write(uint64_t addr, uint8_t data) { + write1(addr, data); + } + template <> + inline void Memory::write(uint64_t addr, uint16_t data) { + write2(addr, data); + } + template <> + inline void Memory::write(uint64_t addr, uint32_t data) { + write4(addr, data); + } + template <> + inline void Memory::write(uint64_t addr, uint64_t data) { + write8(addr, data); + } + template <> + inline void Memory::write(uint64_t addr, unsigned __int128 data) { + write8(addr, uint64_t(data)); + write8(addr + 8, uint64_t(data >> 64)); + } + + class MemObject { + public: + MemObject(uint64_t base_addr, uint64_t size) + : m_base_addr(base_addr), m_end_addr(base_addr + size), m_size(size) {} + virtual ~MemObject() = default; + + template + T read(uint64_t addr); + + virtual uint8_t read1(uint64_t addr) = 0; + virtual uint16_t read2(uint64_t addr) = 0; + virtual uint32_t read4(uint64_t addr) = 0; + virtual uint64_t read8(uint64_t addr) = 0; + virtual void write(uint64_t addr, uint8_t data) = 0; + virtual void write(uint64_t addr, uint16_t data) = 0; + virtual void write(uint64_t addr, uint32_t data) = 0; + virtual void write(uint64_t addr, uint64_t data) = 0; + + uint64_t base_addr() const { return m_base_addr; } + uint64_t size() const { return m_size; } + bool contains_addr(uint64_t addr) const { + return addr >= m_base_addr && addr < m_end_addr; + } + virtual uint8_t* host_pointer() { return nullptr; } + + private: + uint64_t m_base_addr = 0; + uint64_t m_end_addr = 0; + uint64_t m_size = 0; + }; + template <> + inline uint8_t MemObject::read(uint64_t addr) { + return read1(addr); + } + template <> + inline uint16_t MemObject::read(uint64_t addr) { + return read2(addr); + } + template <> + inline uint32_t MemObject::read(uint64_t addr) { + return read4(addr); + } + template <> + inline uint64_t MemObject::read(uint64_t addr) { + return read8(addr); + } + + class MemRegion : public MemObject { + public: + MemRegion(uint64_t base_addr, uint64_t size) : MemObject(base_addr, size) { + m_data.resize(size); + m_addend = &m_data[0] - base_addr; + } + + uint8_t* host_pointer() override { return &m_data[0]; } + + // the caller must guarantee that the access falls within the region + uint8_t read1(uint64_t addr) override { + return *(uint8_t*)(addr + m_addend); + } + uint16_t read2(uint64_t addr) override { + return *(uint16_t*)(addr + m_addend); + } + uint32_t read4(uint64_t addr) override { + return *(uint32_t*)(addr + m_addend); + } + uint64_t read8(uint64_t addr) override { + return *(uint64_t*)(addr + m_addend); + } + void write(uint64_t addr, uint8_t data) override { + *(uint8_t*)(addr + m_addend) = data; + } + void write(uint64_t addr, uint16_t data) override { + *(uint16_t*)(addr + m_addend) = data; + } + void write(uint64_t addr, uint32_t data) override { + *(uint32_t*)(addr + m_addend) = data; + } + void write(uint64_t addr, uint64_t data) override { + *(uint64_t*)(addr + m_addend) = data; + } + + private: + std::vector m_data; + uint8_t* m_addend = nullptr; + }; +} // namespace udb diff --git a/backends/cpp_hart_gen/cpp/include/udb/pool_alloc.hpp b/backends/cpp_hart_gen/cpp/include/udb/pool_alloc.hpp new file mode 100644 index 0000000000..1d313e6139 --- /dev/null +++ b/backends/cpp_hart_gen/cpp/include/udb/pool_alloc.hpp @@ -0,0 +1,86 @@ +#pragma once + +#include +#include +#include + +#include "udb/defines.hpp" + +namespace udb { + // simple memory pool allocator + + template + concept PoolAllocatable = (sizeof(Klass) >= sizeof(Klass*)); + + struct PoolObj { + void* m_next; + }; + + template + class PoolAllocator { + public: + PoolAllocator() + : m_freelist_head(nullptr) +#ifndef NDEBUG + , + m_total_obj_created(0), + m_allocated_objs(0) +#endif + { + } + + ~PoolAllocator() { + // fprintf(stderr, + // "Warning: The pool allocator does not free memory " + // "on deconstruction. You probably shouldn't delete it.\n"); + } + + // allocate a single object with a custom size + BaseObjType* allocate() noexcept { + if (m_freelist_head == nullptr) { + // need more objects + + m_freelist_head = (BaseObjType*)new char[PoolSize * ObjSize]; + + for (unsigned i = 0; i < PoolSize; i++) { + if (i == PoolSize - 1) { + reinterpret_cast(&m_freelist_head[i])->m_next = nullptr; + } else { + reinterpret_cast(&m_freelist_head[i])->m_next = + reinterpret_cast(&m_freelist_head[i + 1]); + } + } +#ifndef NDEBUG + m_total_obj_created += PoolSize; +#endif + } + BaseObjType* ret = m_freelist_head; + m_freelist_head = reinterpret_cast( + reinterpret_cast(ret)->m_next); +#ifndef NDEBUG + m_allocated_objs++; +#endif + return ret; + } + + void free(BaseObjType* del) noexcept { + reinterpret_cast(del)->m_next = m_freelist_head; + m_freelist_head = del; +#ifndef NDEBUG + // check against double free + udb_assert(m_allocated_objs > 0, "double free"); + m_allocated_objs--; +#endif + } + + private: + BaseObjType* m_freelist_head; + +#ifndef NDEBUG + uint64_t m_total_obj_created; + uint64_t m_allocated_objs; +#endif + }; +} // namespace udb diff --git a/backends/cpp_hart_gen/cpp/include/udb/soc_model.hpp b/backends/cpp_hart_gen/cpp/include/udb/soc_model.hpp new file mode 100644 index 0000000000..80a2699782 --- /dev/null +++ b/backends/cpp_hart_gen/cpp/include/udb/soc_model.hpp @@ -0,0 +1,118 @@ +#pragma once + +#include + +#include "udb/enum.hxx" + +#ifdef assert +#undef assert +#endif + +namespace udb { + template + concept SocModel = requires(SocType s) { + { s.read_hpm_counter(static_cast(0)) } -> std::same_as; + { s.read_mcycle() } -> std::same_as; + { s.read_mtime() } -> std::same_as; + { s.sw_write_mcycle(static_cast(0)) } -> std::same_as; + { s.cache_block_zero(static_cast(0)) }; + { s.eei_ecall_from_m() }; + { s.eei_ecall_from_s() }; + { s.eei_ecall_from_u() }; + { s.eei_ecall_from_vs() }; + { s.eei_ebreak() }; + { s.memory_model_acquire() }; + { s.memory_model_release() }; + { + s.notify_mode_change(PrivilegeMode::ValueType{}, + PrivilegeMode::ValueType{}) + }; + { s.prefetch_instruction(static_cast(0)) }; + { s.prefetch_read(static_cast(0)) }; + { s.prefetch_write(static_cast(0)) }; + { + s.fence(static_cast(0), static_cast(0), + static_cast(0), static_cast(0), + static_cast(0), static_cast(0), + static_cast(0), static_cast(0)) + }; + { s.fence_tso() }; + { s.ifence() }; + { s.order_pgtbl_writes_before_vmafence() }; + { s.order_pgtbl_reads_after_vmafence() }; + + { + s.read_physical_memory_8(static_cast(0)) + } -> std::same_as; + { + s.read_physical_memory_16(static_cast(0)) + } -> std::same_as; + { + s.read_physical_memory_32(static_cast(0)) + } -> std::same_as; + { + s.read_physical_memory_64(static_cast(0)) + } -> std::same_as; + { + s.write_physical_memory_8(static_cast(0), + static_cast(0)) + }; + { + s.write_physical_memory_16(static_cast(0), + static_cast(0)) + }; + { + s.write_physical_memory_32(static_cast(0), + static_cast(0)) + }; + { + s.write_physical_memory_64(static_cast(0), + static_cast(0)) + }; + + { + s.memcpy_from_host(static_cast(0), + static_cast(0), + static_cast(0)) + } -> std::same_as; + { + s.memcpy_to_host(static_cast(0), static_cast(0), + static_cast(0)) + } -> std::same_as; + + { + s.atomic_check_then_write_32(static_cast(0), + static_cast(0), + static_cast(0)) + } -> std::same_as; + { + s.atomic_check_then_write_64(static_cast(0), + static_cast(0), + static_cast(0)) + } -> std::same_as; + { + s.atomically_set_pte_a(static_cast(0), static_cast(0), + static_cast(0)) + } -> std::same_as; + { + s.atomically_set_pte_a_d(static_cast(0), + static_cast(0), + static_cast(0)) + } -> std::same_as; + { + s.atomic_read_modify_write_32(static_cast(0), + static_cast(0), + AmoOperation::ValueType{}) + } -> std::same_as; + { + s.atomic_read_modify_write_64(static_cast(0), + static_cast(0), + AmoOperation::ValueType{}) + } -> std::same_as; + + { + s.pma_applies_Q_(PmaAttribute::ValueType{}, static_cast(0), + static_cast(0)) + } -> std::same_as; + }; +} // namespace udb diff --git a/backends/cpp_hart_gen/cpp/include/udb/util.hpp b/backends/cpp_hart_gen/cpp/include/udb/util.hpp new file mode 100644 index 0000000000..5a42524274 --- /dev/null +++ b/backends/cpp_hart_gen/cpp/include/udb/util.hpp @@ -0,0 +1,356 @@ +#pragma once + +#include + +#include "udb/bits.hpp" +#include "udb/defines.hpp" +#include "udb/xregister.hpp" + +namespace udb { + + // extract bits from an integral type + template + constexpr Bits extract(Type value) { + static_assert(size > 0, "Must extract at least one bit"); + static_assert((start + size) <= sizeof(Type) * 8, + "Cannot extract more bits than type contains"); + + if constexpr (size == sizeof(Type) * 8) { + return value; + } else { + constexpr Type mask = (static_cast(1) << size) - 1; + return (value >> start) & mask; + } + } + static_assert(extract<0, 8>(0xeeff) == 0xff, "Did not extract a byte"); + static_assert(extract<8, 8>(0xeeff) == 0xee, "Did not extract a byte"); + static_assert(extract<24, 8>(0xccddeeffu) == 0xcc, "Did not extract a byte"); + static_assert(extract<0, 32>(0xccddeeffu) == 0xccddeeff, + "Did not extract a byte"); + static_assert(extract<0, 1>(0xeeff) == 0x1, "Did not extract a bit"); + static_assert(extract<8, 1>(0xeeff) == 0x0, "Did not extract a bit"); + + // extract bits from a Bits type + template + requires(BITS_LEN <= BitsMaxNativePrecision) + constexpr Bits extract(const Bits &value) { + static_assert((START + SIZE) <= BITS_LEN, + "Cannot extract more bits than type contains"); + + if constexpr (SIZE == BITS_LEN) { + return value; + } else { + constexpr Bits mask = + (static_cast>(1).template sll()) - 1; + return (value >> START) & mask; + } + } + + // extract bits from an integral type + template + requires(BITS_LEN <= BitsMaxNativePrecision) + constexpr Bits extract(const IntType &value) { + static_assert((START + SIZE) <= BITS_LEN, + "Cannot extract more bits than type contains"); + + if constexpr (SIZE == BITS_LEN) { + return value; + } else { + constexpr Bits mask = + (static_cast>(1).template sll()) - 1; + return (value >> START) & mask; + } + } + + // extract bits from a PossiblyUnknownBits type + template + requires(BITS_LEN <= BitsMaxNativePrecision) + constexpr PossiblyUnknownBits extract( + const PossiblyUnknownBits &value) { + static_assert((START + SIZE) <= BITS_LEN, + "Cannot extract more bits than type contains"); + + if constexpr (SIZE == BITS_LEN) { + return value; + } else { + constexpr Bits mask = + (static_cast>(1).template sll()) - 1; + return (value >> START) & mask; + } + } + + template + requires(BITS_LEN > BitsMaxNativePrecision) + Bits extract(const Bits &value) { + static_assert((START + SIZE) <= BITS_LEN, + "Cannot extract more bits than type contains"); + + if constexpr (SIZE == BITS_LEN) { + return value; + } else { + Bits mask = + (static_cast>(1).template sll()) - 1; + return (value >> START) & mask; + } + } + + template + Bits extract(const _RuntimeBits &value) { + static_assert((START + SIZE) <= MAX_BITS_LEN, + "Cannot extract more bits than type contains"); + if (value.width_known()) { + udb_assert((START + SIZE) <= value.width(), + "Cannot extract more bits than type contains"); + } + + if (value.width_known() && (SIZE == value.width())) { + return value; + } else { + Bits mask = + (static_cast>(1).template sll()) - 1; + return (value >> START) & mask; + } + } + + // extract bits from an XRegister type + template + constexpr Bits extract(const XRegister &value) { + static_assert((start + size) <= XLEN, + "Cannot extract more bits than type contains"); + + if constexpr (size == XLEN) { + return value; + } else { + constexpr Bits mask = + (static_cast>(1).template sll()) - 1; + return (value >> start) & mask; + } + } + + // extract from a bitfield member + template + constexpr Bits extract( + const BitfieldMember &value) { + static_assert((start + size) <= (BitfieldMemberSize), + "Cannot extract more bits than type contains"); + + if constexpr (size == BitfieldMemberSize) { + return value; + } else { + constexpr Bits mask = + (static_cast>(1).template sll()) - 1; + return (value >> start) & mask; + } + } + + // extract bits, where the extraction is not known at compile time + template + requires((std::integral || ValueType::IsABits) && + (std::integral || StartType::IsABits) && + (std::integral || SizeType::IsABits)) + RuntimeBits extract(const ValueType &value, const StartType &start, + const SizeType &size) { + udb_assert((start + size) <= sizeof(ValueType) * 8, + "extraction out of bound"); + + if (size == sizeof(ValueType) * 8) { + return {value, size}; + } else { + ValueType mask = (static_cast(1) << size) - 1; + return {(value >> start) & mask, size}; + } + } + + template + Bits::Width> extract( + const BitfieldMember &value, const StartType &start, + const SizeType &size) { + udb_assert((start + size) <= (BitfieldMember::Width), + "extraction out of bound"); + + if (size == BitfieldMember::Width) { + return static_cast::Width>>(value); + } else { + if constexpr (BitfieldMember::Width < 64) { + uint64_t mask = (1ull << size) - 1; + return (value >> start) & mask; + } else { + Bits::Width> mask = + (static_cast::Width>>(1) << size) - 1; + return (value >> start) & mask; + } + } + } + + template + constexpr Bits bit_insert(const Bits &target, + const Bits &value) { + static_assert(MSB < T, "MSB is outside target range"); + static_assert(LSB <= MSB, "LSB is greater than MSB"); + static_assert(T <= Bits::MaxNativePrecision, + "Multi-precision Bits is not constexpr"); + Bits mask = + ((Bits<1>{1}.template sll()) - 1).template sll(); + return (target & ~mask) | ((value.template sll()) & mask); + } + + static_assert(bit_insert<0, 0, 32>(0, 1).get() == 0x1, "Did not insert bit"); + static_assert(bit_insert<1, 1, 32>(0, 1) == 0x2, "Did not insert bit"); + static_assert(bit_insert<8, 8, 32>(0, 1) == 0x100, "Did not insert bit"); + static_assert(bit_insert<15, 15, 32>(0, 1) == 0x8000, "Did not insert bit"); + static_assert(bit_insert<31, 31, 32>(0, 1).get() == 0x80000000, + "Did not insert bit"); + static_assert(bit_insert<3, 0, 32>(0, 0xa) == 0xa, "Did not insert nibble"); + static_assert(bit_insert<7, 4, 32>(0, 0xa) == 0xa0, "Did not insert nibble"); + static_assert(bit_insert<7, 4, 32>(0xf, 0xa) == 0xaf, + "Did not insert nibble"); + static_assert(bit_insert<7, 4, 32>(0xff, 0xa) == 0xaf, + "Did not insert nibble"); + + template + void bit_insert(Bits &target, const MsbType &msb, const LsbType &lsb, + const ValueType &value) { + Bits mask = ((Bits{1} << msb) - 1) << lsb; + target = (target & ~mask) | ((Bits{value} << lsb) & mask); + } + + template + constexpr Bits replicate(const Bits &_value) { + static_assert(N > 0, "Must replicate at least once"); + static_assert(M < BitsMaxNativePrecision, + "Please don't replicate multiprecision numbers ;("); + + Bits value = _value; + Bits result = value; + for (unsigned i = 1; i < N; i++) { + result |= value << (i * M); + } + return result; + } + + template + constexpr RuntimeBits replicate(const Bits &_value, const T &N) { + udb_assert(N > 0, "Must replicate at least once"); + static_assert(M < BitsMaxNativePrecision, + "Please don't replicate multiprecision numbers ;("); + + Bits value = _value; + Bits result = value; + for (unsigned i = 1; i < N; i++) { + result |= value << (i * M); + } + return {result, M * N}; + } + + template + constexpr RuntimeBits replicate(const _RuntimeBits &_value, + const T &N) { + udb_assert(N > 0, "Must replicate at least once"); + + RuntimeBits value{_value.value(), _value.width() * N}; + RuntimeBits result{value.value(), value.width() * N}; + for (unsigned i = 1; i < N; i++) { + result |= value.value() << (i * value.width()); + } + return result; + } + + template + constexpr Bits sign_extend(const Bits &value) { + bool zero = (value & (static_cast>(1) + .template sll())) == 0; + constexpr Bits zero_mask = + static_cast>(1).template sll() - 1; + if (zero) { + return Bits(value) & zero_mask; + } else { + return Bits(value) | ~zero_mask; + } + } + + static_assert(sign_extend<5, 8, 8>(0x10).get() == 0xf0); + static_assert(sign_extend<5, 16, 8>(0x10).get() == 0xfff0); + static_assert(sign_extend<6, 8, 8>(0x10).get() == 0x10); + static_assert(sign_extend<6, 16, 8>(0x10).get() == 0x10); + + template + struct ConcatWidth; + + template + struct ConcatWidth { + static constexpr unsigned Width = + BitsType::Width + ConcatWidth::Width; + }; + + template <> + struct ConcatWidth<> { + static constexpr unsigned Width = 0; + }; + + template + constexpr Bits::Width> __concat( + const BitsType &a, const BitsTypes &...bits) { + if constexpr (sizeof...(BitsTypes) == 0) { + return a; + } else { + return (Bits::Width>{a} + << ConcatWidth::Width) | + __concat(bits...); + } + } + + template + requires((BitsTypes::Width != BitsInfinitePrecision) && ...) + constexpr Bits::Width> concat(BitsTypes... bits) { + return __concat(bits...); + } + + template + RuntimeBits __runtime_concat(const BitsType &a, const BitsTypes &...bits) { + if constexpr (sizeof...(BitsTypes) == 0) { + return a; + } else { + auto shamt = (bits.width() + ...); + return (RuntimeBits{a} << shamt) | __runtime_concat(bits...); + } + } + + template + requires((std::same_as || ...)) + RuntimeBits concat(BitsTypes... bits) { + return __runtime_concat(bits...); + } + + static_assert( + std::is_same_v{1}, Bits<4>(2), Bits<4>(3))), + Bits<12>>); + static_assert(concat(Bits<4>{1}, Bits<4>(2), Bits<4>(3)) == Bits<12>(0x123)); + + template + static consteval bool is_power_of_2() { + if constexpr (N == 0) { + return true; + } else { + unsigned M = N; + while ((M & 1) == 0) { + M = M >> 1; + } + return M == 1; + } + } + static_assert(is_power_of_2<128>()); + static_assert(is_power_of_2<64>()); + static_assert(is_power_of_2<32>()); + static_assert(is_power_of_2<16>()); + static_assert(is_power_of_2<32>()); + static_assert(is_power_of_2<16>()); + static_assert(is_power_of_2<8>()); + static_assert(is_power_of_2<4>()); + static_assert(is_power_of_2<2>()); + static_assert(is_power_of_2<1>()); +} // namespace udb diff --git a/backends/cpp_hart_gen/cpp/include/udb/version.hpp b/backends/cpp_hart_gen/cpp/include/udb/version.hpp new file mode 100644 index 0000000000..f49c1d0199 --- /dev/null +++ b/backends/cpp_hart_gen/cpp/include/udb/version.hpp @@ -0,0 +1,215 @@ +#pragma once + +#include + +#include +#include +#include +#include +#include + +#include "udb/defines.hpp" + +#if !defined(__cpp_lib_constexpr_charconv) || \ + (__cpp_lib_constexpr_charconv < 202207L) +#error "No conexpr from_char" +#endif + +using namespace std::literals; + +namespace udb { + class Version { + public: + Version() = default; + constexpr Version(const std::string_view& ver_str) { set(ver_str); } + Version(const std::string& ver_str) { set(ver_str); } + constexpr Version(unsigned major, unsigned minor, unsigned patch, bool pre) + : m_major(major), m_minor(minor), m_patch(patch), m_pre(pre) {} + + constexpr void set(const std::string_view& ver_str) { + auto result = + ctre::match<"(([0-9]+)(?:\\.([0-9]+)(?:\\.([0-9]+)(?:-(pre))?)?)?)">( + ver_str); + if (!result) { + throw std::runtime_error( + fmt::format("Bad version string '{}'", ver_str)); + } + m_major = result.get<1>().to_number(); + m_minor = 0; + m_patch = 0; + m_pre = false; + if (!result.get<3>().str().empty()) { + m_minor = result.get<3>().to_number(); + } + if (!result.get<4>().str().empty()) { + m_patch = result.get<4>().to_number(); + } + if (!result.get<5>().str().empty()) { + m_pre = true; + } + } + constexpr void set(unsigned major, unsigned minor, unsigned patch, + bool pre) { + m_major = major; + m_minor = minor; + m_patch = patch; + m_pre = pre; + } + + constexpr std::strong_ordering operator<=>(const Version& other) const { + if (m_major != other.m_major) { + return m_major <=> other.m_major; + } else if (m_minor != other.m_minor) { + return m_minor <=> other.m_minor; + } else if (m_patch != other.m_patch) { + return m_patch <=> other.m_patch; + } else { + return m_pre == other.m_pre ? std::strong_ordering::equivalent + : (m_pre ? std::strong_ordering::less + : std::strong_ordering::greater); + } + } + constexpr bool operator==(const Version& other) const { + return m_major == other.m_major && m_minor == other.m_minor && + m_patch == other.m_patch && m_pre == other.m_pre; + } + + constexpr unsigned major() const { return m_major; } + constexpr unsigned minor() const { return m_minor; } + constexpr unsigned patch() const { return m_patch; } + constexpr bool pre() const { return m_pre; } + + private: + unsigned m_major; + unsigned m_minor; + unsigned m_patch; + bool m_pre; + }; + + static_assert(Version{"1.2.3"sv}.major() == 1, "Bad major"); + static_assert(Version{"1.2.3"sv}.minor() == 2, "Bad minor"); + static_assert(Version{"1.2.3"sv}.patch() == 3, "Bad patch"); + static_assert(Version{"1.2.3"sv}.pre() == false, "Bad pre"); + static_assert(Version{"1.2.3-pre"sv}.pre() == true, "Bad pre"); + static_assert(Version{"1"sv}.major() == 1, "Bad major"); + static_assert(Version{"1"sv}.minor() == 0, "Bad minor"); + static_assert(Version{"1"sv}.patch() == 0, "Bad patch"); + static_assert(Version{"1"sv}.pre() == false, "Bad pre"); + + class VersionRequirement { + public: + enum class OpKind : unsigned { INVALID, GTE, LTE, GT, LT, EQ, NE, COMPAT }; + + class Op { + public: + Op() : m_kind(OpKind::INVALID) {} + constexpr Op(const std::string_view& op) { set(op); } + constexpr Op(const OpKind& kind) : m_kind(kind) {} + + constexpr void set(const std::string_view& op) { + if (op == ">=") { + m_kind = OpKind::GTE; + } else if (op == ">") { + m_kind = OpKind::GT; + } else if (op == "<=") { + m_kind = OpKind::LTE; + } else if (op == "<") { + m_kind = OpKind::LT; + } else if (op == "=") { + m_kind = OpKind::EQ; + } else if (op == "!=") { + m_kind = OpKind::NE; + } else if (op == "~>") { + m_kind = OpKind::COMPAT; + } else { + throw std::runtime_error("Invalid operator"); + } + } + + constexpr bool operator==(const Op& other) const { + return m_kind == other.m_kind; + } + constexpr bool operator==(const OpKind& other) const { + return m_kind == other; + } + constexpr bool operator!=(const Op& other) const { + return m_kind != other.m_kind; + } + constexpr bool operator!=(const OpKind& other) const { + return m_kind != other; + } + + constexpr OpKind kind() const { return m_kind; } + + private: + OpKind m_kind; + }; + + // default requiremnt is >= 0 + VersionRequirement() : m_op(OpKind::GTE), m_version(0, 0, 0, false) {} + + constexpr VersionRequirement(const std::string_view& req) + : m_op(op_from_str(req)) { + set(req); + } + constexpr VersionRequirement(const OpKind& op_kind, unsigned major, + unsigned minor, unsigned patch, bool pre) + : m_op(op_kind), m_version(major, minor, patch, pre) {} + + constexpr Op op_from_str(const std::string_view& req) { + auto result = ctre::match< + "((?:>=)|(?:>)|(?:~>)|(?:<)|(?:<=)|(?:!=)|(?:=))\\s*(([0-9]+)(?:\\.([" + "0-9]" + "+)(?:\\.([0-9]+)(?:-(pre))?)?)?)">(req); + + if (!result) { + throw std::runtime_error( + fmt::format("Bad version requirement string '{}'", req)); + } + + return static_cast(result.get<1>().to_number()); + } + constexpr void set(const std::string_view& req) { + auto result = ctre::match< + "((?:>=)|(?:>)|(?:~>)|(?:<)|(?:<=)|(?:!=)|(?:=))\\s*(([0-9]+)(?:\\.([" + "0-9]" + "+)(?:\\.([0-9]+)(?:-(pre))?)?)?)">(req); + + if (!result) { + throw std::runtime_error( + fmt::format("Bad version requirement string '{}'", req)); + } + + m_op.set(result.get<1>()); + m_version.set(result.get<2>()); + } + + constexpr Op op() const { return m_op; } + constexpr unsigned major() const { return m_version.major(); } + constexpr unsigned minor() const { return m_version.minor(); } + constexpr unsigned patch() const { return m_version.patch(); } + constexpr bool pre() const { return m_version.pre(); } + + constexpr bool satisfied_by(const Version& version) const { + switch (m_op.kind()) { + case OpKind::GTE: + return version >= m_version; + case OpKind::LTE: + return version <= m_version; + case OpKind::GT: + return version > m_version; + case OpKind::LT: + return version < m_version; + case OpKind::EQ: + return version == m_version; + case OpKind::NE: + return version != m_version; + } + udb_unreachable(); + } + + private: + Op m_op; + Version m_version; + }; +} // namespace udb diff --git a/backends/cpp_hart_gen/cpp/include/udb/xregister.hpp b/backends/cpp_hart_gen/cpp/include/udb/xregister.hpp new file mode 100644 index 0000000000..7e229c4a40 --- /dev/null +++ b/backends/cpp_hart_gen/cpp/include/udb/xregister.hpp @@ -0,0 +1,235 @@ +#pragma once + +#include + +#include + +#include "udb/bitfield.hpp" +#include "udb/bits.hpp" +#include "udb/defines.hpp" + +namespace udb { + // XRegister class represents a general purpose X register in a hart + // + // The class just wraps an XReg (primitive integral) type, with the caveat + // that an XRegister can be declared a "ZeroReg," in which case all + // assignments are ignored + // + // This class just lets us pass around X register references without having + // to do explicit zero index checks all over the place + template + class XRegister { + public: + using ValueType = Bits; + using ValueTypeCRef = + std::add_const_t>; + + XRegister() : m_value(0), m_zero_reg(false) {} + XRegister(const XRegister &other) + : m_value(other.m_value), m_zero_reg(other.m_zero_reg) {} + XRegister(const ValueType &value) : m_value(value), m_zero_reg(false) {} + + void makeZeroReg() { m_zero_reg = true; } + + ValueType &get() { return m_value; } + ValueType get() const { return m_value; } + + template + operator Bits() const { + return m_value; + } + + XRegister &operator=(const XRegister &other) { + if (!m_zero_reg) { + m_value = other.m_value; + } + return *this; + } + template + requires(std::integral || T::IsABits) + XRegister &operator=(const T &other) { + if (!m_zero_reg) { + m_value = other; + } + return *this; + } + + template + XRegister &operator=(const _RuntimeBits &other) { + if (!m_zero_reg) { + m_value = other.value(); + } + return *this; + } + + template + decltype(std::declval().sra(std::declval())) sra( + const T &shamt) const { + return m_value.sra(shamt); + } + +#define UNARY_ARITH_OP(op) \ + ValueType operator op() const { return op(m_value); } + + UNARY_ARITH_OP(-) + UNARY_ARITH_OP(~) + UNARY_ARITH_OP(!) + +#undef UNARY_ARITH_OP + + // prefix + XRegister &operator++() { + m_value++; + return *this; + } + XRegister &operator--() { + m_value--; + return *this; + } + + // postfix + ValueType operator++(int) { + ValueType old = m_value; + m_value++; + return old; + } + + ValueType operator--(int) { + ValueType old = m_value; + m_value--; + return old; + } + +#define BINARY_ARITH_ASSIGN_OP(op) \ + XRegister &operator op(const XRegister & other) { \ + if (!m_zero_reg) { \ + m_value op other.m_value; \ + } \ + return *this; \ + } \ + XRegister &operator op(const ValueType & other) { \ + if (!m_zero_reg) { \ + m_value op other; \ + } \ + return *this; \ + } + + BINARY_ARITH_ASSIGN_OP(+=) + BINARY_ARITH_ASSIGN_OP(-=) + BINARY_ARITH_ASSIGN_OP(*=) + BINARY_ARITH_ASSIGN_OP(/=) + BINARY_ARITH_ASSIGN_OP(%=) + BINARY_ARITH_ASSIGN_OP(^=) + BINARY_ARITH_ASSIGN_OP(&=) + BINARY_ARITH_ASSIGN_OP(|=) + BINARY_ARITH_ASSIGN_OP(>>=) + BINARY_ARITH_ASSIGN_OP(<<=) + + auto operator<=>(const XRegister &other) const { + return m_value <=> other.m_value; + } + + private: + ValueType m_value; + bool m_zero_reg; + }; +} // namespace udb + +#define BINARY_ARITH_OP(op) \ + template \ + inline decltype(std::declval< \ + const typename udb::XRegister::ValueType &>() \ + op std::declval< \ + const typename udb::XRegister::ValueType &>()) \ + operator op(const udb::XRegister &lhs, \ + const udb::XRegister &rhs) { \ + return lhs.get() op rhs.get(); \ + } \ + template \ + inline decltype(std::declval::ValueTypeCRef>() \ + op std::declval>()) \ + operator op(const udb::XRegister &lhs, \ + const udb::_Bits &rhs) { \ + return lhs.get() op rhs; \ + } \ + template \ + inline decltype(std::declval &>() \ + op std::declval< \ + typename udb::XRegister::ValueTypeCRef>()) \ + operator op(const udb::_Bits &lhs, \ + const udb::XRegister &rhs) { \ + return lhs op rhs.get(); \ + } \ + template \ + requires(std::integral) \ + inline decltype(std::declval< \ + const typename udb::XRegister::ValueType &>() \ + op std::declval()) \ + operator op(const udb::XRegister &lhs, const T &rhs) { \ + return lhs.get() op rhs; \ + } \ + template \ + requires(std::integral) \ + inline decltype(std::declval() op std::declval< \ + const typename udb::XRegister::ValueType &>()) \ + operator op(const T &lhs, const udb::XRegister &rhs) { \ + return lhs op rhs.get(); \ + } + +BINARY_ARITH_OP(+) +BINARY_ARITH_OP(-) +BINARY_ARITH_OP(*) +BINARY_ARITH_OP(/) +BINARY_ARITH_OP(%) +BINARY_ARITH_OP(^) +BINARY_ARITH_OP(&) +BINARY_ARITH_OP(|) +BINARY_ARITH_OP(>>) +BINARY_ARITH_OP(<<) + +#undef BINARY_ARITH_OP + +#define BINARY_LOGICAL_OP(op) \ + template \ + inline bool operator op(const udb::XRegister &lhs, \ + const udb::XRegister &rhs) { \ + return lhs.get() op rhs.get(); \ + } \ + template \ + inline bool operator op(udb::XRegister &lhs, \ + udb::XRegister &rhs) { \ + return lhs.get() op rhs.get(); \ + } \ + template \ + inline bool operator op( \ + const udb::XRegister &lhs, \ + const typename udb::XRegister::ValueType &rhs) { \ + return lhs.get() op rhs; \ + } \ + template \ + inline bool operator op(const typename udb::XRegister::ValueType &lhs, \ + const udb::XRegister &rhs) { \ + return lhs op rhs.get(); \ + } + +BINARY_LOGICAL_OP(<) +BINARY_LOGICAL_OP(>) +BINARY_LOGICAL_OP(==) +BINARY_LOGICAL_OP(!=) +BINARY_LOGICAL_OP(<=) +BINARY_LOGICAL_OP(>=) +// BINARY_LOGICAL_OP(&&) // will be automatically generated +// BINARY_LOGICAL_OP(||) // will be automatically generated + +#undef BINARY_LOGICAL_OP + +// format XRegister as an XReg (integral) when using format() +template +struct fmt::formatter> + : formatter::ValueType> { + template + auto format(udb::XRegister value, CONTEXT_TYPE &ctx) const { + return fmt::formatter::ValueType>::format( + (typename udb::XRegister::ValueType)value, ctx); + } +}; diff --git a/backends/cpp_hart_gen/cpp/src/elf_reader.cpp b/backends/cpp_hart_gen/cpp/src/elf_reader.cpp new file mode 100644 index 0000000000..03e630d05c --- /dev/null +++ b/backends/cpp_hart_gen/cpp/src/elf_reader.cpp @@ -0,0 +1,104 @@ + +#include +#include +#include +#include + +#include +#include + +udb::ElfReader::ElfReader(const std::string& path) { + if (elf_version(EV_CURRENT) == EV_NONE) { + throw ElfException("Bad Elf version"); + } + + m_fd = open(path.c_str(), O_RDONLY, 0); + if (m_fd < 0) { + throw ElfException("Could not open ELF file"); + } + + m_elf = elf_begin(m_fd, ELF_C_READ, NULL); + if (m_elf == nullptr) { + throw ElfException("Could not begin reading ELF"); + } + + if (elf_kind(m_elf) != ELF_K_ELF) { + throw ElfException("Not an ELF file"); + } + + auto hdr = elf32_getehdr(m_elf); + m_class = ELFCLASS32; + if (hdr == nullptr) { + m_class = ELFCLASS64; + } + if (m_class == ELFCLASS32) { + m_entry = elf32_getehdr(m_elf)->e_entry; + } else { + m_entry = elf64_getehdr(m_elf)->e_entry; + } +} + +udb::ElfReader::~ElfReader() { + if (m_elf != nullptr) { + elf_end(m_elf); + close(m_fd); + } +} + +std::pair udb::ElfReader::mem_range() { + if (m_class == ELFCLASS32) { + return _mem_range(); + } else { + return _mem_range(); + } +} + +uint64_t udb::ElfReader::entry() { return m_entry; } + +bool udb::ElfReader::getSym(const std::string& name, Elf64_Addr* result) { + size_t num_sections; + if (elf_getshdrnum(m_elf, &num_sections) != 0) { + throw ElfException("Could not determine number of sections"); + } + size_t shstrtab_index; + if (elf_getshdrstrndx(m_elf, &shstrtab_index) != 0) { + throw ElfException("Could not get Section Header String Table"); + } + // first, find the strtab + int strtab_index; + for (size_t i = 0; i < num_sections; i++) { + auto* strtab_section = elf_getscn(m_elf, i); + Elf64_Shdr* header = elf64_getshdr(strtab_section); + if (strcmp(elf_strptr(m_elf, shstrtab_index, header->sh_name), ".strtab") == + 0) { + strtab_index = i; + break; + } + } + // now, get the symtab + for (size_t i = 0; i < num_sections; i++) { + Elf_Scn* section; + section = elf_getscn(m_elf, i); + Elf64_Shdr* section_header = elf64_getshdr(section); + if (strcmp(elf_strptr(m_elf, shstrtab_index, section_header->sh_name), + ".symtab") == 0) { + unsigned num_syms = section_header->sh_size / section_header->sh_entsize; + Elf64_Sym* symtab; + Elf_Data* data; + if ((data = elf_getdata(section, nullptr)) == nullptr) { + throw ElfException(fmt::format("Could not get symtab data. {}", + elf_errmsg(elf_errno())) + .c_str()); + } + symtab = (Elf64_Sym*)data->d_buf; + for (unsigned j = 0; j < num_syms; j++) { + if (strcmp(elf_strptr(m_elf, strtab_index, symtab[j].st_name), + name.c_str()) == 0) { + *result = symtab[j].st_value; + return true; + } + } + } + } + return false; +} diff --git a/backends/cpp_hart_gen/cpp/src/hart.cpp b/backends/cpp_hart_gen/cpp/src/hart.cpp new file mode 100644 index 0000000000..426eeaaf2c --- /dev/null +++ b/backends/cpp_hart_gen/cpp/src/hart.cpp @@ -0,0 +1,54 @@ +#include "iss/hart.hpp" + +void riscv::Memory::memcpy_from_host(uint64_t guest_paddr, const void* host_ptr, + size_t size) { + const size_t SZ_64 = sizeof(uint64_t); + auto host_ptr64 = (const uint64_t*)host_ptr; // NOLINT + while (size >= SZ_64) { + write((guest_paddr += SZ_64) - SZ_64, *host_ptr64++); + size -= SZ_64; + } + + auto host_ptr8 = (const uint8_t*)host_ptr64; // NOLINT + while (size > 0) { + write(guest_paddr++, *host_ptr8++); + size--; + } +} + +void riscv::Memory::memcpy_to_host(void* host_ptr, uint64_t guest_paddr, + size_t size) { + const size_t SZ_64 = sizeof(uint64_t); + auto host_ptr64 = (uint64_t*)host_ptr; // NOLINT + while (size >= SZ_64) { + *(host_ptr64++) = read(guest_paddr += SZ_64); + size -= SZ_64; + } + + auto host_ptr8 = (uint8_t*)host_ptr64; // NOLINT + while (size > 0) { + *(host_ptr8++) = read(guest_paddr += SZ_64); + size--; + } +} + +void riscv::HartBase::printState(FILE* out) const { + fprintf(out, "Hart %u:\n", m_hart_id); + if (sizeof(XReg) == 8) { + fprintf(out, ISS_FORMAT("PC: {:#18x}\n", m_pc).c_str()); + for (int i = 0; i < 16; i++) { + fprintf(out, ISS_FORMAT("x{:2}: {:#18x}\tx{:2}: {:#18x}\n", i, m_xregs[i], + i + 16, m_xregs[16 + 1]) + .c_str()); + } + } else if (sizeof(XReg) == 4) { + fprintf(out, ISS_FORMAT("PC: {:#10x}\n", m_pc).c_str()); + for (int i = 0; i < 16; i++) { + fprintf(out, ISS_FORMAT("x{:2}: {:#10x}\tx{:2}: {:#10x}\n", i, m_xregs[i], + i + 16, m_xregs[16 + 1]) + .c_str()); + } + } else { + assert(!"unsupported xlen"); + } +} diff --git a/backends/cpp_hart_gen/cpp/src/iss.cpp b/backends/cpp_hart_gen/cpp/src/iss.cpp new file mode 100644 index 0000000000..a7ce02f8fd --- /dev/null +++ b/backends/cpp_hart_gen/cpp/src/iss.cpp @@ -0,0 +1,92 @@ + +#include + +#include +#include + +#include "udb/defines.hpp" +#include "udb/elf_reader.hpp" +#include "udb/hart_factory.hxx" +#include "udb/inst.hpp" +#include "udb/iss_soc_model.hpp" + +struct Options { + std::string config_name; + std::filesystem::path config_path; + bool show_configs; + std::string elf_file_path; + + Options() : show_configs(false) {} +}; + +static const int PARSE_OK = 1234; +int parse_cmdline(int argc, char **argv, Options &options) { + CLI::App app("Bare-bones ISS"); + app.add_option("-m,--model", options.config_name, "Hart model"); + app.add_option("-c,--cfg", options.config_path, "Hart configuration file"); + app.add_flag("-l,--list-configs", options.show_configs, + "List available configurations"); + + app.add_option("elf_file", options.elf_file_path, "File to run"); + + CLI11_PARSE(app, argc, argv); + return PARSE_OK; +} + +int main(int argc, char **argv) { + Options opts; + int ret; + if ((ret = parse_cmdline(argc, argv, opts)) != PARSE_OK) { + return ret; + } + + if (opts.show_configs) { + for (auto &config : udb::HartFactory::configs()) { + fmt::print("{}\n", config); + } + return 0; + } + + if (opts.config_path.empty()) { + fmt::print("No configuration file provided\n"); + return 1; + } + + udb::ElfReader elf_reader(opts.elf_file_path.c_str()); + + // how much memory do we need? + auto range = elf_reader.mem_range(); + uint64_t memsz = range.second - range.first + 1; + + // round up to a page for good measure + memsz = (memsz & ~0xfffull) + 0x1000; + + udb::IssSocModel soc(memsz, range.first); + + auto hart = udb::HartFactory::create(opts.config_name, 0, + opts.config_path, soc); + auto tracer = udb::HartFactory::create_tracer( + "riscv-tests", opts.config_name, hart); + hart->attach_tracer(tracer); + auto entry_pc = elf_reader.loadLoadableSegments(soc); + hart->reset(entry_pc); + + // get the first instruction + while (true) { + auto stop_reason = hart->run_n(100); + if (stop_reason != StopReason::InstLimitReached && + stop_reason != StopReason::Exception) { + if (stop_reason == StopReason::ExitSuccess) { + fmt::print("SUCCESS"); + return 0; + } else if (stop_reason == StopReason::ExitFailure) { + fmt::print(stderr, "{}", hart->exit_reason()); + return hart->exit_code(); + } else { + udb_assert(false, "TODO: stop_reason"); + } + } + } + + return 0; +} diff --git a/backends/cpp_hart_gen/cpp/src/libhart_renode.cpp b/backends/cpp_hart_gen/cpp/src/libhart_renode.cpp new file mode 100644 index 0000000000..b3929ba5b3 --- /dev/null +++ b/backends/cpp_hart_gen/cpp/src/libhart_renode.cpp @@ -0,0 +1,158 @@ + + +#include "udb/enum.hxx" +#include "udb/hart.hpp" +#include "udb/hart_factory.hxx" + +#define UDB_EXPORT __attribute__((visibility("default"))) + +#include "udb/renode_imports.h" + +EXTERNAL_AS(uint64_t, ReadByteFromBus, renode_read_byte, uint64_t) +EXTERNAL_AS(uint64_t, ReadWordFromBus, renode_read_word, uint64_t) +EXTERNAL_AS(uint64_t, ReadDoubleWordFromBus, renode_read_double, uint64_t) +EXTERNAL_AS(uint64_t, ReadQuadWordFromBus, renode_read_quad, uint64_t) + +EXTERNAL_AS(void, WriteByteToBus, renode_write_byte, uint64_t, uint64_t) +EXTERNAL_AS(void, WriteWordToBus, renode_write_word, uint64_t, uint64_t) +EXTERNAL_AS(void, WriteDoubleWordToBus, renode_write_double, uint64_t, uint64_t) +EXTERNAL_AS(void, WriteQuadWordToBus, renode_write_quad, uint64_t, uint64_t) + +struct RenodeSocModel { + uint64_t read_hpm_counter(uint64_t counternum) { return 0; } + + uint64_t read_mcycle() { return 0; } + uint64_t read_mtime() { return 0; } + + // returns new value of mcycle (coudl be different than new_value) + uint64_t sw_write_mcycle(uint64_t new_value) { return 0; } + + void cache_block_zero(uint64_t paddr) {} + + // eei_* occur when the configuration indicates that ecall/ebreak don't cause + // exceptions + void eei_ecall_from_m() {} + void eei_ecall_from_s() {} + void eei_ecall_from_u() {} + void eei_ecall_from_vs() {} + void eei_ebreak() {} + + void memory_model_acquire() {} + void memory_model_release() {} + void notify_mode_change(udb::PrivilegeMode::ValueType from, + udb::PrivilegeMode::ValueType to) {} + void prefetch_instruction(uint64_t paddr) {} + void prefetch_read(uint64_t paddr) {} + void prefetch_write(uint64_t paddr) {} + void fence(uint8_t pi, uint8_t pr, uint8_t po, uint8_t pw, uint8_t si, + uint8_t sr, uint8_t so, uint8_t sw) {} + void fence_tso() {} + void ifence() {} + void order_pgtbl_writes_before_vmafence() {} + void order_pgtbl_reads_after_vmafence() {} + + uint64_t read_physical_memory_8(uint64_t paddr) { + return renode_read_byte(paddr); + } + uint64_t read_physical_memory_16(uint64_t paddr) { + return renode_read_word(paddr); + } + uint64_t read_physical_memory_32(uint64_t paddr) { + return renode_read_double(paddr); + } + uint64_t read_physical_memory_64(uint64_t paddr) { + return renode_read_quad(paddr); + } + void write_physical_memory_8(uint64_t paddr, uint64_t value) { + renode_write_byte(paddr, value); + } + void write_physical_memory_16(uint64_t paddr, uint64_t value) { + renode_write_word(paddr, value); + } + void write_physical_memory_32(uint64_t paddr, uint64_t value) { + renode_write_double(paddr, value); + } + void write_physical_memory_64(uint64_t paddr, uint64_t value) { + renode_write_quad(paddr, value); + } + + int memcpy_from_host(uint64_t guest_paddr, const uint8_t* host_ptr, + uint64_t size) { + return -1; + } + int memcpy_to_host(uint8_t* host_ptr, uint64_t guest_paddr, uint64_t size) { + return -1; + } + + uint8_t atomic_check_then_write_32(uint64_t, uint32_t, uint32_t) { return 0; } + uint8_t atomic_check_then_write_64(uint64_t, uint64_t, uint64_t) { return 0; } + uint8_t atomically_set_pte_a(uint64_t, uint64_t, uint32_t) { return 0; } + uint8_t atomically_set_pte_a_d(uint64_t, uint64_t, uint32_t) { return 0; } + uint64_t atomic_read_modify_write_32(uint64_t, uint64_t, + udb::AmoOperation::ValueType) { + return 0; + } + uint64_t atomic_read_modify_write_64(uint64_t, uint64_t, + udb::AmoOperation::ValueType) { + return 0; + } + + // returns 1 if pma applies to the *entire* region [paddr, paddr + len) + // returns 0 otherwise + uint8_t pma_applies_Q_(udb::PmaAttribute::ValueType pma, uint64_t paddr, + uint32_t len) { + return 0; + } +}; + +static RenodeSocModel callbacks; +static udb::HartBase* hart = nullptr; + +extern "C" UDB_EXPORT int32_t renode_init_ex(uint32_t hart_id, + const char* model_name, + const char* cfg_path) { + if (hart != nullptr) { + return -1; + } + + hart = udb::HartFactory::create( + model_name, hart_id, std::filesystem::path{cfg_path}, callbacks); + + return 0; +} + +extern "C" UDB_EXPORT const char* renode_exit_reason_ex() { + return hart->exit_reason().c_str(); +} + +extern "C" UDB_EXPORT void renode_destruct_ex() { + if (hart != nullptr) { + delete hart; + hart = nullptr; + } +} + +extern "C" UDB_EXPORT int64_t renode_execute_ex(int64_t n) { + return hart->run_n(n); +} + +extern "C" UDB_EXPORT void renode_set_register_value64_ex(int32_t reg, + uint64_t value) { + if (reg == 32) { + hart->set_pc(value); + } else { + udb_assert(false, "TODO: reg num"); + } +} + +extern "C" UDB_EXPORT uint32_t renode_get_register_value64_ex(int32_t reg) { + if (reg == 32) { + return hart->pc(); + } else { + udb_assert(false, "TODO: reg num"); + } +} + +extern "C" UDB_EXPORT uint64_t renode_get_icount_ex() { + return hart->num_insts_exec(); +} diff --git a/backends/cpp_hart_gen/cpp/src/memory.cpp b/backends/cpp_hart_gen/cpp/src/memory.cpp new file mode 100644 index 0000000000..205c0c3cf1 --- /dev/null +++ b/backends/cpp_hart_gen/cpp/src/memory.cpp @@ -0,0 +1,34 @@ + +#include "udb/memory.hpp" + +void udb::Memory::memcpy_from_host(uint64_t guest_paddr, const void* host_ptr, + size_t size) { + const size_t SZ_64 = sizeof(uint64_t); + auto host_ptr64 = (const uint64_t*)host_ptr; // NOLINT + while (size >= SZ_64) { + write((guest_paddr += SZ_64) - SZ_64, *host_ptr64++); + size -= SZ_64; + } + + auto host_ptr8 = (const uint8_t*)host_ptr64; // NOLINT + while (size > 0) { + write(guest_paddr++, *host_ptr8++); + size--; + } +} + +void udb::Memory::memcpy_to_host(void* host_ptr, uint64_t guest_paddr, + size_t size) { + const size_t SZ_64 = sizeof(uint64_t); + auto host_ptr64 = (uint64_t*)host_ptr; // NOLINT + while (size >= SZ_64) { + *(host_ptr64++) = read(guest_paddr += SZ_64); + size -= SZ_64; + } + + auto host_ptr8 = (uint8_t*)host_ptr64; // NOLINT + while (size > 0) { + *(host_ptr8++) = read(guest_paddr += SZ_64); + size--; + } +} diff --git a/backends/cpp_hart_gen/cpp/test/test_bits.cpp b/backends/cpp_hart_gen/cpp/test/test_bits.cpp new file mode 100644 index 0000000000..178c106045 --- /dev/null +++ b/backends/cpp_hart_gen/cpp/test/test_bits.cpp @@ -0,0 +1,292 @@ + +#include + +#include +#include +#include + +constexpr __uint128_t operator""_u128(const char* x) { + __uint128_t y = 0; + if (x[0] == '0' && (x[1] == 'x' || x[1] == 'X')) { + for (int i = 2; x[i] != '\0'; ++i) { + if (x[i] == '\'') { + continue; + } + y *= 16ull; + if ('0' <= x[i] && x[i] <= '9') + y += x[i] - '0'; + else if ('A' <= x[i] && x[i] <= 'F') + y += x[i] - 'A' + 10; + else if ('a' <= x[i] && x[i] <= 'f') + y += x[i] - 'a' + 10; + } + } else if (x[0] == '0' && (x[1] == 'o' || x[1] == 'O')) { + for (int i = 2; x[i] != '\0'; ++i) { + if (x[i] == '\'') { + continue; + } + y *= 8ull; + if ('0' <= x[i] && x[i] <= '8') y += x[i] - '0'; + } + } else if (x[0] == '0' && (x[1] == 'b' || x[1] == 'B')) { + for (int i = 2; x[i] != '\0'; ++i) { + if (x[i] == '\'') { + continue; + } + y *= 2ull; + if ('0' <= x[i] && x[i] <= '1') y += x[i] - '0'; + } + } else { + for (int i = 0; x[i] != '\0'; ++i) { + if (x[i] == '\'') { + continue; + } + y *= 10ull; + if ('0' <= x[i] && x[i] <= '8') y += x[i] - '0'; + } + } + return y; +} + +using namespace udb; + +constexpr unsigned InfinitePrecision = Bits<64>::InfinitePrecision; +constexpr unsigned GmpPrecision = Bits<64>::MaxNativePrecision + 1; + +static_assert(Bits<32>{0xffffffffffffffffull}.get() == 0xffffffffu); +static_assert(Bits<32>{0xffffffffffffffffull}.get() == -1); +static_assert(Bits<32>{0xffffffffffffffffull}.get() < 0); +static_assert(Bits<31>{0x7fffffffu}.get() == -1); +static_assert(Bits<31>{0x7fffffffu}.get() < 0); + +static_assert(Bits<31>{Bits<31>{0x7fff'ffffu}}.get() == 2147483647); +static_assert(Bits<31>{Bits<31>{-1}}.get() == 2147483647); +static_assert(Bits<31>{Bits<31>{1234}}.get() == 1234); +static_assert(Bits<32>{Bits<32>{-1}}.get() == 4294967295); +static_assert(Bits<32>{Bits<32>{1234}}.get() == 1234); + +static_assert(Bits<31>{Bits<31>{0x7fff'ffffu}}.get() == -1); +static_assert(Bits<31>{Bits<31>{-1}}.get() == -1); +static_assert(Bits<31>{Bits<31>{1234}}.get() == 1234); +static_assert(Bits<32>{Bits<32>{-1}}.get() == -1); +static_assert(Bits<32>{Bits<32>{1234}}.get() == 1234); + +TEST_CASE("InifitePrecision works with int", "[bits]") { + Bits a{Bits{0x7fff'ffffu}}; + REQUIRE(a.get() == 2147483647); +} + +// negation +static_assert((-(-Bits<64>(5))).get() == Bits<64>(5).get()); +static_assert((-(-Bits<64>(5))).get() == 5); +static_assert((-Bits<64>(5)).get() == -5); +static_assert((-Bits<64>(5)).get() == 18446744073709551611ull); +static_assert((-Bits<64>(5)).get() < 0); + +TEST_CASE("Negation", "[bits]") { + REQUIRE((-(-Bits<65>(5))).get() == Bits<64>(5).get()); + REQUIRE((-(-Bits<65>(5))).get() == 5); + REQUIRE((-Bits<65>(5)).get<__int128_t>() == -5); + REQUIRE((-Bits<65>(5)).get<__int128_t>() < 0); + + REQUIRE((-(-Bits<129>(5))).get() == Bits<64>(5).get()); + REQUIRE((-(-Bits<129>(5))).get() == 5); + REQUIRE((-Bits<129>(5)).get() == -5); + REQUIRE((-Bits<129>(5)).get() < 0); +} + +// inversion +static_assert((~(~Bits<64>(5))).get() == Bits<64>(5).get()); +static_assert((~(~Bits<64>(5))).get() == 5); +static_assert((~Bits<64>(5)).get() == -6); +static_assert((~Bits<64>(5)).get() == 18446744073709551610ull); +static_assert((~Bits<64>(5)).get() < 0); + +TEST_CASE("Inversion", "[bits]") { + REQUIRE((~(~Bits<65>(5))).get() == Bits<64>(5).get()); + REQUIRE((~(~Bits<65>(5))).get() == 5); + REQUIRE((~Bits<65>(5)).get<__int128_t>() == -6); + REQUIRE((~Bits<65>(5)).get() == 0x1fffffffffffffff_u128); + REQUIRE((~Bits<65>(5)).get() < 0); + + REQUIRE((~(~Bits<129>(5))).get() == Bits<129>(5).get()); + REQUIRE((~(~Bits<129>(5))).get() == 5); + REQUIRE((~Bits<129>(5)).get<__int128_t>() == -6); + REQUIRE((~Bits<129>(5)).get() == 0x1fffffffffffffffffffffffffffffffa_mpz); + REQUIRE((~Bits<129>(5)).get() < 0); +} +TEST_CASE("64-bit Assignement", "[bits]") { + Bits<64> a{5}; + Bits<64> b; + b = a; + REQUIRE(a.get() == b.get()); + REQUIRE(a.get() == 5); +} +TEST_CASE("65-bit Assignement", "[bits]") { + Bits<65> a{5}; + Bits<65> b; + b = a; + REQUIRE(a.get() == b.get()); + REQUIRE(a.get() == 5); +} + +TEST_CASE("129-bit Assignement", "[bits]") { + Bits<129> a{5}; + Bits<129> b; + b = a; + REQUIRE(a.get() == b.get()); + REQUIRE(a.get() == 5); +} + +TEST_CASE("64-bit unsigned negation", "[bits]") { + Bits<64> a; + a = 5; + REQUIRE(a.get() == 5); + a = -5; + REQUIRE(a.get() == 18446744073709551611ull); +} + +TEST_CASE("65-bit unsigned negation", "[bits]") { + Bits<65> a; + a = 5; + REQUIRE(a.get() == 5); + a = -5; + REQUIRE(a.get() == 0x1fffffffffffffffb_u128); +} + +TEST_CASE("129-bit unsigned negation", "[bits]") { + Bits<129> a; + a = 5; + REQUIRE(a.get() == 5); + a = -5; + REQUIRE(a.get() == 0x1fffffffffffffffffffffffffffffffb_mpz); +} + +TEST_CASE("mixed-bit assignment", "[bits]") { + Bits<129> a; + Bits<64> b = 5; + a = b; + REQUIRE(a.get() == 5); +} + +TEST_CASE("mixed-bit assignment, reversed", "[bits]") { + Bits<64> a; + Bits<129> b = 5; + a = b; + REQUIRE(a.get() == 5); +} + +TEST_CASE("mixed-bit assignment, negated", "[bits]") { + Bits<64> a; + Bits<129> b = -5; + a = b; + REQUIRE(a.get() == 0xfffffffffffffffbull); +} + +TEST_CASE("mixed-bit assignment, negated, reversed", "[bits]") { + Bits<64> a{Bits<129>{-5}}; + REQUIRE(a.get() == 0xfffffffffffffffbull); +} + +TEST_CASE("mixed-bit assignment, negated, constructor", "[bits]") { + Bits<129> a{Bits<65>{-5}}; + REQUIRE(a.get() == 0x1fffffffffffffffb_mpz); +} + +TEST_CASE("mixed-bit assignment, bits comparison", "[bits]") { + Bits<129> a; + Bits<64> b = 5; + a = b; + REQUIRE(a == b); +} + +TEST_CASE("mixed-bit assignment, bits comparison, reversed", "[bits]") { + Bits<64> a; + Bits<129> b = 5; + a = b; + REQUIRE(a == b); +} + +TEST_CASE("mixed-bit multiplication", "[bits]") { + Bits<64> a = 5; + Bits<129> b = 5; + REQUIRE(a * b == 25); +} + +TEST_CASE("mixed-bit multiplication, reversed", "[bits]") { + Bits<129> a = 5; + Bits<64> b = 5; + REQUIRE(a * b == 25); +} + +TEST_CASE("129-bit multiplication, reversed", "[bits]") { + Bits<129> a; + Bits<129> b = 5; + a = b; + REQUIRE(a == b); +} + +TEST_CASE("129-bit multiplication, literal", "[bits]") { + Bits<129> a = 5; + REQUIRE(a * 5 == 25); +} + +TEST_CASE("129-bit multiplication, literal, reversed", "[bits]") { + Bits<129> a = 5; + REQUIRE(5 * a == 25); +} + +TEST_CASE("8-bit multiplication, literal, reversed", "[bits]") { + Bits<8> a = 5; + REQUIRE(a * 255 == 0xfb); +} + +TEST_CASE("8-bit SRA", "[bits]") { + Bits<8> a = 0x80; + REQUIRE(a.sra(3) == 0xf0); +} + +TEST_CASE("9-bit SRA", "[bits]") { + Bits<9> a = 0x100; + REQUIRE(a.sra(3) == 0x1e0); +} + +TEST_CASE("65-bit SRA", "[bits]") { + Bits<65> a = 0x10000000000000000_mpz; + REQUIRE(a.sra(3) == 0x1e000000000000000_u128); +} + +TEST_CASE("Printing", "[bits]") { + fmt::print("{}\n", Bits<129>{16}); + fmt::print("{:x}\n", Bits<129>{16}); + fmt::print("{:#x}\n", Bits<129>{16}); + fmt::print("{:#10x}\n", Bits<129>{16}); + fmt::print("{:#010x}\n", Bits<129>{16}); + // fmt::print("{+:#10x}\n", Bits<129>{16}); +} + +TEST_CASE("Runtime", "[bits]") { + _RuntimeBits<64, false> a(0, 8); + a = 255; + REQUIRE(a == 255); + a = a + 1; + REQUIRE(a == 0); + + _RuntimeBits<64, true> b(128, 8); + REQUIRE(b == 0); + b = -128; + REQUIRE(b == -128); + b = b - 1; + REQUIRE(b == 0); + b = -128; + b = b + 1; + REQUIRE(b == -127); + b = 127; + REQUIRE(b == 127); + b = b + 1; + REQUIRE(b == 0); + b = 128; + REQUIRE(b == 0); + b = 129; + REQUIRE(b == 1); +} diff --git a/backends/cpp_hart_gen/cpp/test/test_csr.cpp b/backends/cpp_hart_gen/cpp/test/test_csr.cpp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/backends/cpp_hart_gen/cpp/test/test_decode.cpp b/backends/cpp_hart_gen/cpp/test/test_decode.cpp new file mode 100644 index 0000000000..bccaa05161 --- /dev/null +++ b/backends/cpp_hart_gen/cpp/test/test_decode.cpp @@ -0,0 +1,65 @@ +#include +#include +#include +#include + +using namespace udb; + +const std::string cfg_yaml = R"( + +$schema: config_schema.json# +kind: architecture configuration +type: fully configured +name: test_cfg +description: For testing + +implemented_extensions: + - [I, "2.1.0"] + - [Sm, "1.12.0"] + +params: + XLEN: 64 + NAME: test + ARCH_ID: 0x1000000000000000 + IMP_ID: 0x0 + VENDOR_ID_BANK: 0x0 + VENDOR_ID_OFFSET: 0x0 + MISALIGNED_LDST: true + MISALIGNED_LDST_EXCEPTION_PRIORITY: high + MISALIGNED_MAX_ATOMICITY_GRANULE_SIZE: 0 + MISALIGNED_SPLIT_STRATEGY: by_byte + PRECISE_SYNCHRONOUS_EXCEPTIONS: true + TRAP_ON_ECAL_FROM_M: true + TRAP_ON_EBREAK: true + TRAP_ON_ILLEGAL_WLRL: true + TRAP_ON_UNIMPLEMENTED_INSTRUCTION: true + TRAP_ON_RESERVED_INSTRUCTION: true + TRAP_ON_UNIMPLEMENTED_CSR: true + REPORT_VA_IN_MTVAL_ON_BREAKPOINT: true + REPORT_VA_IN_MTVAL_ON_STORE_AMO_MISALIGNED: true + REPORT_VA_IN_MTVAL_ON_INSTRUCTION_MISALIGNED: true + REPORT_VA_IN_MTVAL_ON_LOAD_ACCESS_FAULT: true + REPORT_VA_IN_MTVAL_ON_STORE_AMO_ACCESS_FAULT: true + REPORT_VA_IN_MTVAL_ON_INSTRUCTION_ACCESS_FAULT: true + REPORT_VA_IN_MTVAL_ON_LOAD_PAGE_FAULT: true + REPORT_VA_IN_MTVAL_ON_STORE_AMO_PAGE_FAULT: true + REPORT_VA_IN_MTVAL_ON_INSTRUCTION_PAGE_FAULT: true + REPORT_ENCODING_IN_MTVAL_ON_ILLEGAL_INSTRUCTION: true + MTVAL_WIDTH: 64 + CONFIG_PTR_ADDRESS: 0 + PMA_GRANULARITY: 12 + PHYS_ADDR_WIDTH: 54 + M_MODE_ENDIANESS: little + MISA_CSR_IMPLEMENTED: true + MTVEC_MODES: [0, 1] + MTVEC_BASE_ALIGNMENT_DIRECT: 4 + MTVEC_BASE_ALIGNMENT_VECTORED: 4 +)"; + +udb::Memory mem; +auto hart = udb::HartFactory::create("_", 0, cfg_yaml, mem); + +TEST_CASE("Hints", "[version]") { + hart->decode( + 0, 0b00000000000000000000000000010111ull); // auipc, or lpad if Zicfilp +} diff --git a/backends/cpp_hart_gen/cpp/test/test_util.cpp b/backends/cpp_hart_gen/cpp/test/test_util.cpp new file mode 100644 index 0000000000..3b049139e0 --- /dev/null +++ b/backends/cpp_hart_gen/cpp/test/test_util.cpp @@ -0,0 +1,13 @@ + +#include +#include +#include + +using namespace udb; + +TEST_CASE("concat", "[util]") { + Bits<4> a{0x1}; + Bits<4> b{0x2}; + Bits<4> c{0x3}; + REQUIRE(concat(a, b, c) == 0x123); +} diff --git a/backends/cpp_hart_gen/cpp/test/test_version.cpp b/backends/cpp_hart_gen/cpp/test/test_version.cpp new file mode 100644 index 0000000000..87a80374f5 --- /dev/null +++ b/backends/cpp_hart_gen/cpp/test/test_version.cpp @@ -0,0 +1,75 @@ +#include +#include + +using namespace std::literals; +using namespace udb; + +TEST_CASE("major only", "[version]") { + VersionRequirement r{">= 2"sv}; + REQUIRE(r.major() == 2); + REQUIRE(r.minor() == 0); + REQUIRE(r.patch() == 0); + REQUIRE(r.pre() == false); +} + +TEST_CASE("major and minor", "[version]") { + VersionRequirement r{">= 2.1"sv}; + REQUIRE(r.major() == 2); + REQUIRE(r.minor() == 1); + REQUIRE(r.patch() == 0); + REQUIRE(r.pre() == false); +} + +TEST_CASE("major, minor, and patch", "[version]") { + VersionRequirement r{">= 2.1.3"sv}; + REQUIRE(r.major() == 2); + REQUIRE(r.minor() == 1); + REQUIRE(r.patch() == 3); + REQUIRE(r.pre() == false); +} + +TEST_CASE("major, minor, patch, and pre", "[version]") { + VersionRequirement r{">= 2.1.3-pre"sv}; + REQUIRE(r.major() == 2); + REQUIRE(r.minor() == 1); + REQUIRE(r.patch() == 3); + REQUIRE(r.pre() == true); +} + +TEST_CASE("version ordering", "[version]") { + Version v1("2.1.3"sv); + Version v2("3.4"sv); + + REQUIRE(v1 < v2); + REQUIRE(v1 <= v2); + REQUIRE(v1 != v2); + REQUIRE(v2 > v1); + REQUIRE(v2 >= v1); +} + +TEST_CASE("version ordering iwth pre", "[version]") { + Version v1("2.1.3-pre"sv); + Version v2("2.1.3"sv); + + REQUIRE(v1 < v2); + REQUIRE(v1 <= v2); + REQUIRE(v1 != v2); + REQUIRE(v2 > v1); + REQUIRE(v2 >= v1); +} + +TEST_CASE("version requirement satisfaction", "[version]") { + VersionRequirement req(">= 2.1.3"sv); + Version v1("2.1.3"sv); + Version v2("2.1.2"sv); + Version v3("2.1.3-pre"sv); + + REQUIRE(req.satisfied_by(v1)); + REQUIRE(!req.satisfied_by(v2)); + REQUIRE(!req.satisfied_by(v3)); + + req.set("< 2.1.3"sv); + REQUIRE(!req.satisfied_by(v1)); + REQUIRE(req.satisfied_by(v2)); + REQUIRE(req.satisfied_by(v3)); +} diff --git a/backends/cpp_hart_gen/lib/constexpr_pass.rb b/backends/cpp_hart_gen/lib/constexpr_pass.rb new file mode 100644 index 0000000000..45abce38eb --- /dev/null +++ b/backends/cpp_hart_gen/lib/constexpr_pass.rb @@ -0,0 +1,59 @@ +# frozen_string_literal: true + +module Idl + class AstNode + def constexpr?(symtab) + if children.empty? + true + else + children.all? { |child| child.constexpr?(symtab) } + end + end + end + class IdAst + def constexpr?(symtab) + sym = symtab.get(name) + return true if sym.nil? + return true if sym.is_a?(Type) + return false if sym.value.nil? # assuming undefined syms are local (be sure to type check first!!) + + if sym.param? + symtab.cfg_arch.params_with_value.any? { |p| p.name == text_value } + elsif sym.template_value? + true + else + !sym.type.global? + end + end + end + class PcAssignmentAst + def constexpr?(symtab) = false + end + class FunctionCallExpressionAst + def constexpr?(symtab) = false # conservative, can do better... + end + class CsrFieldReadExpressionAst + def constexpr?(symtab) = false + end + class CsrReadExpressionAst + def constexpr?(symtab) = false + end + class CsrSoftwareWriteAst + def constexpr?(symtab) = false + end + class CsrFunctionCallAst + def constexpr?(symtab) = function_name == "address" + end + class CsrWriteAst + def constexpr?(symtab) = false + end + class FunctionDefAst + # @return [Boolean] If the function is possibly C++ constexpr (does not access CSRs or registers) + def constexpr?(symtab) + return false if builtin? + return false if generated? # might actually know this in some cases... + + body.constexpr?(symtab) + end + end +end diff --git a/backends/cpp_hart_gen/lib/control_flow_pass.rb b/backends/cpp_hart_gen/lib/control_flow_pass.rb new file mode 100644 index 0000000000..e53230dd6e --- /dev/null +++ b/backends/cpp_hart_gen/lib/control_flow_pass.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +# pass to see if node sets the PC +# does _NOT_ count exceptions + +module Idl + class AstNode + def control_flow?(symtab) + if children.empty? + false + else + children.any? { |child| child.control_flow?(symtab) } + end + end + end + + class PcAssignmentAst + def control_flow?(symtab) = true + end + + class FunctionCallExpressionAst + def control_flow?(symtab) + return true if children.any? { |child| child.control_flow?(symtab) } + + return false if name =~ /^raise.*$/ # we don't count exceptions + + func_def_type = func_type(symtab) + + return false if func_def_type.builtin? || func_def_type.generated? + + func_def_type.body.control_flow?(symtab) + end + end +end diff --git a/backends/cpp_hart_gen/lib/csr_template_helpers.rb b/backends/cpp_hart_gen/lib/csr_template_helpers.rb new file mode 100644 index 0000000000..db58fa7aa1 --- /dev/null +++ b/backends/cpp_hart_gen/lib/csr_template_helpers.rb @@ -0,0 +1,36 @@ + +class CsrField + TYPE_VALUE_TO_CPP_NAME = { + 0 => "RO", + 1 => "ROH", + 2 => "RW", + 3 => "RWR", + 4 => "RWH", + 5 => "RWRH" + }.freeze + + # @return [String] C++ implementation of type() body + def type_to_cpp(xlen) + raise "#{csr.name}.#{field.name} is not defined in RV#{xlen}" unless defined_in_base?(xlen) + + pruned_ast = pruned_type_ast(xlen) + if pruned_ast.nil? + # type is const + udb_type = type(xlen) + return "return CsrFieldType::#{udb_type.sub('-', '')};" + end + + cpp = nil + symtab = fill_symtab_for_type(xlen, pruned_ast) + value_result = pruned_ast.value_try do + type_value = pruned_ast.return_value(symtab) + cpp = "return CsrFieldType::#{TYPE_VALUE_TO_CPP_NAME[type_value]};" + end + pruned_ast.value_else(value_result) do + cpp = pruned_ast.gen_cpp(symtab) + end + symtab.release + + cpp + end +end diff --git a/backends/cpp_hart_gen/lib/decode_tree.rb b/backends/cpp_hart_gen/lib/decode_tree.rb new file mode 100644 index 0000000000..ee95824c0b --- /dev/null +++ b/backends/cpp_hart_gen/lib/decode_tree.rb @@ -0,0 +1,368 @@ +# frozen_string_literal: true + +class DecodeGen + class DecodeTreeNode + # insts are a group of instructions that share opcode bit positions + # up to the most significant bit considered along this path + # + # range: the encoding range considered at this point + # value: the value of the encoding at this range that will match insts + # type: either endpoint (instructions are fully decoded) or in_progress + attr_accessor :parent, :children, :insts, :range, :value, :type + + # types + ENDPOINT_TYPE = 1 # holding instructions that are fully defined at this point + SELECT_TYPE = 2 # holding instructions that need to be selected on the range + + def initialize(parent, insts, range, value, type) + @parent = parent + @insts = insts + @range = range + @value = value + @type = type + @children = [] + end + + def print(indent = 0) + $stdout.print(" " * indent) + $stdout.print @type == ENDPOINT_TYPE ? "ENDPOINT" : "SELECT" + @children.each do |child| + child.print(indent + 2) + end + end + + def range_overlap?(a, b) + (b.begin <= a.end) && (a.begin <= b.end) + end + private :range_overlap? + + def mask_overlap?(test_range, extra_range = nil) + return true if !@range.nil? && range_overlap?(@range, test_range) + return true if !extra_range.nil? && range_overlap?(extra_range, test_range) + return true if !@parent.nil? && @parent.mask_overlap?(test_range) + + false + end + + # return mask of opcode positions at this node + def mask(extra_range = nil) + this_mask = + if @range.nil? + 0 + else + ((1 << @range.size) - 1) << @range.first + end + extra_mask = + if extra_range.nil? + 0 + else + ((1 << extra_range.size) - 1) << extra_range.first + end + parent_mask = + if @parent.nil? + 0 + else + parent.mask + end + this_mask | parent_mask | extra_mask + end + + # returns lowest non opcode bit either: + # gte == nil : overall + # gte is_a? Integer : greater than or equal to gte + def lowest_non_opcode_bit(gte=nil) + if gte.nil? + "0#{mask.to_s(2)}".reverse.index('0') + else + if gte >= mask.to_s(2).size + gte + else + "0#{mask.to_s(2)}".reverse[gte..].index('0') + gte + end + end + end + + def opcode_bit?(index) + if index >= mask.to_s(2).size + false + else + mask.to_s(2).reverse[index] == '1' + end + end + + def <<(child) + @children << child + end + end + + + def initialize(cfg_arch) + @cfg_arch = cfg_arch + end + + def construct_decode_tree(tree, xlen, cur_range, test: false) + # list of instructions that are completely decoded with this (and all previous) range + done_insts = {} + + # hash of instructions that still being decoded, grouped by value over the current range + in_progress_groups = {} + + # list of instructions that have a variable bit in cur_range + variable_insts = [] + + tree.insts.each do |inst| + inst_format = inst.encoding(xlen).format + if inst_format.reverse[cur_range].match?(/^[01]+$/) + # puts "#{inst.name} has opcode bit(s) in #{cur_range} (#{inst_format.reverse[cur_range].reverse})" + # whole range is opcode bits + if inst_format.gsub('0', '1') == tree.mask(cur_range).to_s(2).gsub('0', '-').rjust(inst.encoding(xlen).size, '-') + done_insts[inst_format.reverse[cur_range]] = inst + else + in_progress_groups[inst_format.reverse[cur_range]] ||= [] + in_progress_groups[inst_format.reverse[cur_range]] << inst + end + else + # puts "#{inst.name} has variable bit(s) in #{cur_range} (#{inst_format.reverse[cur_range].reverse} #{inst_format.reverse})" + variable_insts << inst + end + end + if test + # puts "test result for #{cur_range}: #{variable_insts.empty?} (#{tree.insts.map(&:name)})" + return variable_insts.empty? + end + if !variable_insts.empty? && (!done_insts.empty? || !in_progress_groups.empty?) + raise 'Problem: variable/opcode mix when range size > 1' unless cur_range.size == 1 + + # some instructions have an opcode, while some have hit a variable + # that means that the next test needs to come from a higher bit position + # we'll have to search for that next position, and then circle back to the current spot + next_range = nil + loop do + next_range = (cur_range.last+1..cur_range.last+1) + break unless tree.mask_overlap?(next_range) + end + # puts "testing range #{next_range} on #{tree.insts.map(&:name)}" + while construct_decode_tree(tree, xlen, next_range, test: true) == false + loop do + next_range = (next_range.last+1..next_range.last+1) + break unless tree.mask_overlap?(next_range) + end + end + # puts "found range that works (#{next_range}) on #{tree.insts.map(&:name)}...constructing" + construct_decode_tree(tree, xlen, next_range) + return + end + + if done_insts.empty? && !in_progress_groups.empty? + # everything is still in an opcode, so grow the range and try again + next_range = (cur_range.first..cur_range.last+1) + next_range_out_of_bounds = in_progress_groups.any? { |val, group| group.size > 1 && group.any? { |inst| inst.max_encoding_width <= next_range.last } } + # puts "All insts have opcode at #{cur_range}, trying #{next_range}..." + if next_range_out_of_bounds || tree.opcode_bit?(cur_range.last+1) || (construct_decode_tree(tree, xlen, next_range, test: true) == false) + # next bit goes too far, so this is the endpoint + in_progress_groups.each do |val, insts| + child = DecodeTreeNode.new(tree, insts, cur_range, val, DecodeTreeNode::SELECT_TYPE) + tree << child + # puts "starting child for selector #{cur_range}, starting search again at bit #{child.lowest_non_opcode_bit} for #{insts.map{ |i| i.name}}" + construct_decode_tree(child, xlen, (child.lowest_non_opcode_bit..child.lowest_non_opcode_bit)) + end + done_insts.each do |val, inst| + # puts "Found endpoint for #{inst.name}: #{tree.mask}" + child = DecodeTreeNode.new(tree, [inst], cur_range, val, DecodeTreeNode::ENDPOINT_TYPE) + tree << child + end + else + # go to the next range + construct_decode_tree(tree, xlen, next_range) + end + elsif !done_insts.empty? + # must end + done_insts.each do |val, inst| + # puts "Completed #{inst.name} at #{cur_range} -- #{tree.mask(cur_range).to_s(2).ljust(32, '0')}" + child = DecodeTreeNode.new(tree, [inst], cur_range, val, DecodeTreeNode::ENDPOINT_TYPE) + tree << child + end + in_progress_groups.each do |val, insts| + child = DecodeTreeNode.new(tree, insts, cur_range, val, DecodeTreeNode::SELECT_TYPE) + tree << child + # puts "Starting child at #{child.lowest_non_opcode_bit} for #{insts.map{|i| i.name}}" + construct_decode_tree(child, xlen, (child.lowest_non_opcode_bit..child.lowest_non_opcode_bit)) + end + else + raise 'unexpected' if variable_insts.empty? + + raise "unexpected: variable when range size > 1 #{cur_range} #{cur_range.size} #{variable_insts.map{ |i| i.name}}" unless cur_range.size == 1 + + # puts "Moving to next variable at #{tree.lowest_non_opcode_bit(cur_range.last+1)}" + construct_decode_tree(tree, xlen, (tree.lowest_non_opcode_bit(cur_range.last+1)..tree.lowest_non_opcode_bit(cur_range.last+1))) + end + end + private :construct_decode_tree + + def comment_tree(tree, indent) + str = tree.parent.nil? ? "" : comment_tree(tree.parent, indent) + if !tree.value.nil? + str + "#{' ' * indent}// encoding[#{tree.range}] == #{tree.value.reverse}\n" + else + str + end + end + + def extract_dv(dv, encoding_var_name) + idx = 0 + efs = [] + dv.encoding_fields.reverse.each do |ef| + bits = "extract<#{ef.range.first}, #{ef.range.size}>(#{encoding_var_name}.get())" + efs << + if idx.zero? + bits + else + "(#{bits}.template sll<#{idx}>())" + end + idx += ef.size + end + + "(#{efs.join(' | ')})" + end + + # @return [Boolean] whether or not the instruction in node is a base of HINTs + def has_hints?(node, inst_list, xlen) + return false unless node.type == DecodeTreeNode::ENDPOINT_TYPE + + return false unless node.insts.size == 1 + + !node.insts[0].hints.select { |hint_inst| hint_inst.defined_in_base?(xlen) && inst_list.include?(hint_inst) }.empty? + end + + def needs_to_check_implemented?(inst) + if @cfg_arch.unconfigured? + !inst.defined_by_condition.satisfied_by? { |ext_req| @cfg_arch.extension("I").versions.all? { |i_ver| ext_req.satisfied_by?(i_ver) } } + elsif @cfg_arch.partially_configured? + !inst.defined_by_condition.satisfied_by? { |ext_req| ext_req.satisfied_by?(@cfg_arch.mandatory_extension_reqs) } + else + false # fully configured, inst_list is already prunned for the config + end + end + + # can this be handled with a simple case clause at the endpoint? + # Reasons that it can't: + # - Need to check if an extension is implemented + # - There is a hint with a higher decode priority + # - Only certain values of a decode variable are valid + def needs_long_form?(node, inst_list, xlen) + node.children.any? do |child| + needs_to_check_dv = child.type == DecodeTreeNode::ENDPOINT_TYPE \ + && child.insts[0].encoding(xlen).decode_variables.any? { |dv| !dv.excludes.empty? } + needs_to_check_hint = has_hints?(child, inst_list, xlen) + + needs_to_check_implemented?(child.insts[0]) || needs_to_check_dv || needs_to_check_hint + end + end + # @return [String] C++ decoder switch + def decode_c(encoding_var_name, xlen, inst_list, node = nil, indent = 0) + # frst, sanity check that all the children have the same range + raise "Bad tree" unless (node.children.empty? || node.children.all? { |child| child.range == node.children.first.range }) + + tenv = CppHartGen::TemplateEnv.new(@cfg_arch) + + code = "" + if node.type == DecodeTreeNode::SELECT_TYPE + if needs_long_form?(node, inst_list, xlen) + # there is at least one child with a not statement or a conflict, can't use a simple switch + els = "" + node.children.each do |child| + code += comment_tree(child, indent + 2) + has_not = child.type == DecodeTreeNode::ENDPOINT_TYPE \ + && child.insts[0].encoding(xlen).decode_variables.any? { |dv| !dv.excludes.empty? } + has_hints = has_hints?(child, inst_list, xlen) + conds = [] + if has_not + # some field(s) in the instruction have prohibited values ('not:' in the yaml) + child.insts[0].encoding(xlen).decode_variables.each do |dv| + next if dv.excludes.empty? + + dv_val = extract_dv(dv, encoding_var_name) + conds.concat(dv.excludes.map { |val| "(#{dv_val} != #{val})" }) + end + end + if has_hints + impl_hints = child.insts[0].hints.select { |hint_inst| hint_inst.defined_in_base?(xlen) && inst_list.include?(hint_inst) } + impl_hints.each do |hint_inst| + mask = hint_inst.encoding(xlen).format.gsub("0", "1").gsub("-", "0") + value = hint_inst.encoding(xlen).format.gsub("-", "0") + conds << ("((#{encoding_var_name} & 0b#{mask}ull) != 0b#{value}ull)") + end + end + + if child.type == DecodeTreeNode::ENDPOINT_TYPE && needs_to_check_implemented?(child.insts[0]) + conds << child.insts[0].defined_by_condition.to_cxx do |ext_name, ext_version_req| + if ext_version_req.nil? + "implemented_Q_(ExtensionName::#{ext_name})" + else + "implemented_version_Q_(ExtensionName::#{ext_name}, \"#{ext_version_req}\"sv)" + end + end + end + if !conds.empty? + code += "#{' '*indent}#{els}if ((extract<#{child.range.first}, #{child.range.size}>(#{encoding_var_name}).get() == 0b#{child.value.reverse}) && #{conds.join(' && ')}) {\n" + else + code += "#{' '*indent}#{els}if (extract<#{child.range.first}, #{child.range.size}>(#{encoding_var_name}).get() == 0b#{child.value.reverse}) {\n" + end + code += decode_c(encoding_var_name, xlen, inst_list, child, indent + 2) + code += "#{' '*indent}}\n" + els = "else " + end + else + code += "#{' ' * indent}switch(extract<#{node.children.first.range.first}, #{node.children.first.range.size}>(#{encoding_var_name}).get()) {\n" + node.children.each do |child| + code += comment_tree(child, indent + 2) + code += "#{' ' * (indent + 2)}case 0b#{child.value.reverse}:\n" + code += decode_c(encoding_var_name, xlen, inst_list, child, indent + 2) + code += "#{' ' * (indent + 4)}break;\n" + end + code += "#{' ' * indent}}\n" + end + else + raise 'unexpected' unless node.type == DecodeTreeNode::ENDPOINT_TYPE + + code += <<~NEW_INST + { + std::construct_at( + reinterpret_cast<#{tenv.name_of(:inst, @cfg_arch, node.insts[0].name)}<#{xlen}, SocType>*>(inst), + this, pc, #{encoding_var_name} + ); + return true; + } + NEW_INST + # code += "#{' ' * (indent + 2)}return new #{tenv.name_of(:inst, @cfg_arch, node.insts[0].name)}<#{xlen}, SocType>(this, pc, #{encoding_var_name});\n" + end + code + end + private :decode_c + + def annotate_identical(tree, xlen) + tree.children.each do |child| + if child.type == DecodeTreeNode::ENDPOINT_TYPE + matches = tree.children.select do |other_child| + other_child.type == DecodeTreeNode::ENDPOINT_TYPE \ + && child != other_child \ + && child.insts[0].encoding(xlen).format == other_child.insts[0].encoding(xlen).format + end + unless matches.empty? + # puts "#{child.insts[0].name} identical to #{ matches.map { |n| n.insts[0].name }.join(', ')}" + end + end + annotate_identical(child, xlen) if child.type == DecodeTreeNode::SELECT_TYPE + end + end + + # @param instructions [Array] Set of instructions to generate a decode for + # @param xlen [Integer] Effective xlen + # @return [String] Decoder function for the given set of instructions and the effective xlen + def generate(instructions, xlen, indent: 2) + root = DecodeTreeNode.new(nil, instructions, nil, nil, DecodeTreeNode::SELECT_TYPE) + construct_decode_tree(root, xlen, 0..0) + annotate_identical(root, xlen) + decode_c('encoding', xlen, instructions, root, indent) + end +end diff --git a/backends/cpp_hart_gen/lib/gen_cpp.rb b/backends/cpp_hart_gen/lib/gen_cpp.rb new file mode 100644 index 0000000000..56b305eb5c --- /dev/null +++ b/backends/cpp_hart_gen/lib/gen_cpp.rb @@ -0,0 +1,796 @@ + +require_relative "constexpr_pass" +require_relative "control_flow_pass" +require_relative "written_pass" + +class TrueClass + def to_cxx = "true" +end + +class FalseClass + def to_cxx = "false" +end + +class Integer + def to_cxx + if negative? + "-#{-self}_sb" + else + "#{self}_b" + end + end +end + +class String + def to_cxx + "\"#{self}\"sv"; + end +end + + +module Idl + class AstNode + def gen_cpp(symtab, indent = 0, indent_spaces: 2) + internal_error "Need to implemente #gen_cpp for #{self.class.name}" + end + end + + class NoopAst + def gen_cpp(symtab, indent = 0, indent_spaces: 2) = ";" + end + + class AryRangeAssignmentAst + def gen_cpp(symtab, indent = 0, indent_spaces: 2) + expression = nil + value_result = value_try do + # see if msb, lsb is compile-time-known + _ = msb.value(symtab) + _ = lsb.value(symtab) + expression = "bit_insert<#{msb.gen_cpp(symtab)}, #{lsb.gen_cpp(symtab)}>(#{variable.gen_cpp(symtab)}, #{write_value.gen_cpp(symtab)})" + end + value_else(value_result) do + expression = "bit_insert(#{variable.gen_cpp(symtab)}, #{msb.gen_cpp(symtab)}, #{lsb.gen_cpp(symtab)}, #{write_value.gen_cpp(symtab)})" + end + + "#{' ' * indent}#{expression}" + end + end + + class ConditionalReturnStatementAst + def gen_cpp(symtab, indent = 0, indent_spaces: 2) + cpp = <<~CPP + if (#{condition.gen_cpp(symtab, 0, indent_spaces:)}) { + #{return_expression.gen_cpp(symtab, indent_spaces, indent_spaces:)}; + } + CPP + "#{' ' * indent}#{cpp.gsub("\n", "\n#{' ' * indent}")}" + end + end + + class ReturnExpressionAst + def gen_cpp(symtab, indent = 0, indent_spaces: 2) + return_expressions = return_value_nodes.map { |ast| ast.gen_cpp(symtab) } + if return_expressions.size == 1 + "#{' ' * indent}return #{return_expressions[0]}" + elsif return_expressions.size > 1 + "#{' ' * indent}return std::make_tuple<#{return_types(symtab).map(&:to_cxx_no_qualifiers).join(', ')}>(#{return_expressions.join(', ')})" + else + "#{' ' * indent}return" + end + end + end + + class IfBodyAst + def gen_cpp(symtab, indent = 0, indent_spaces: 2) + cpp = [] + children.each do |child| + cpp << child.gen_cpp(symtab, indent, indent_spaces:) + end + cpp.join("\n") + end + end + + class PostIncrementExpressionAst + def gen_cpp(symtab, indent = 0, indent_spaces: 2) + "#{' ' * indent}#{rval.gen_cpp(symtab, indent, indent_spaces:)}++" + end + end + + class PostDecrementExpressionAst + def gen_cpp(symtab, indent = 0, indent_spaces: 2) + "#{' ' * indent}#{rval.gen_cpp(symtab, indent, indent_spaces:)}--" + end + end + + class StringLiteralAst + def gen_cpp(symtab, indent = 0, indent_spaces: 2) + # text_value will include leading and trailing quotes + "#{' ' * indent}#{text_value}sv" + end + end + + class DontCareReturnAst + def gen_cpp(symtab, indent = 0, indent_spaces: 2) + "#{' ' * indent}{}" + end + end + + class UserTypeNameAst + def gen_c(symtab) + type = symtab.get(text_value) + if type.kind == :struct + "struct #{text_value}" + else + text_value + end + end + def gen_cpp(symtab, indent = 0, indent_spaces: 2) + type = symtab.get(text_value) + if type.kind == :struct + "#{' ' * indent}__UDB_STRUCT(#{text_value})" + else + "#{' ' * indent}#{text_value}" + end + end + end + + class MultiVariableAssignmentAst + def gen_cpp(symtab, indent, indent_spaces: 2) + lhs = "std::tie(#{variables.map { |v| v.gen_cpp(symtab, 0, indent_spaces: )}.join(', ')})" + rhs = function_call.gen_cpp(symtab, 0, indent_spaces:) + "#{' ' * indent}#{lhs} = #{rhs}" + end + end + + class CsrFunctionCallAst + def gen_cpp(symtab, indent, indent_spaces: 2) + args_cpp = args.map { |a| a.gen_cpp(symtab, 0, indent_spaces:) } + + csr_obj = csr.csr_def(symtab) + if csr_obj.nil? + if function_name == "sw_read" + "#{' '*indent}__UDB_CSR_BY_ADDR(#{csr.idx_expr.gen_cpp(symtab, 0, indent_spaces:)}).#{function_name}(__UDB_XLEN)" + else + "#{' '*indent}__UDB_CSR_BY_ADDR(#{csr.idx_expr.gen_cpp(symtab, 0, indent_spaces:)}).#{function_name.gsub('?', '_Q_')}(#{args_cpp.join(', ')})" + end + else + if function_name == "sw_read" + if symtab.cfg_arch.multi_xlen? && csr_def(symtab).format_changes_with_xlen? + "#{' '*indent}__UDB_CSR_BY_NAME(#{csr_obj.name})._#{function_name}(__UDB_XLEN)" + else + "#{' '*indent}__UDB_CSR_BY_NAME(#{csr_obj.name})._#{function_name}()" + end + else + "#{' '*indent}__UDB_CSR_BY_NAME(#{csr_obj.name}).#{function_name.gsub('?', '_Q_')}(#{args_cpp.join(', ')})" + end + end + end + end + + class FunctionDefAst + def gen_return_type(symtab) + if templated? + template_names.each_with_index do |tname, idx| + symtab.add!(tname, Var.new(tname, template_types(symtab)[idx])) + end + end + + cpp = + if @return_type_nodes.empty? + "void" + elsif @return_type_nodes.size == 1 + @return_type_nodes[0].gen_cpp(symtab, 0) + else + rts = @return_type_nodes.map { |rt| rt.gen_cpp(symtab, 0) } + "std::tuple<#{rts.join(', ')}>" + end + + if templated? + template_names.each do |tname| + symtab.del(tname) + end + end + + cpp + end + + def gen_c_return_type(symtab) + if @return_type_nodes.empty? + "void" + elsif @return_type_nodes.size == 1 + @return_type_nodes[0].gen_c(symtab) + else + raise "Can't have multiple return types for C #{name}" + end + end + + def gen_cpp_argument_list(symtab) + symtab.push(self) + apply_template_and_arg_syms(symtab) + + list = @argument_nodes.map do |arg| + written = (builtin? || generated?) || body.written?(symtab, arg.name) + "#{written ? '' : 'const'} #{arg.gen_cpp(symtab, 0, ref: !written)}" + end.join(", ") + + symtab.pop + + list + end + + def gen_c_argument_list(symtab) + symtab.push(self) + apply_template_and_arg_syms(symtab) + + list = @argument_nodes.map do |arg| + arg.gen_c(symtab) + end.join(", ") + + symtab.pop + + list + end + + def gen_cpp_template(symtab) + if !templated? + "" + else + list = [] + ttypes = template_types(symtab) + ttypes.each_index { |i| + list << "#{ttypes[i].to_cxx_no_qualifiers} #{template_names[i]}" + } + "template <#{list.join(', ')}>" + end + end + + def gen_cpp_prototype(symtab, indent, indent_spaces: 2, qualifiers: "", include_semi: true, cpp_class: nil) + scope = cpp_class.nil? ? "" : "#{cpp_class}::" + <<~PROTOTYPE + #{' ' * indent}#{gen_cpp_template(symtab)} + #{' ' * indent}#{name =~ /^raise.*/ ? '[[noreturn]] ' : ''} #{qualifiers} #{gen_return_type(symtab)} #{scope}#{name.gsub('?', '_Q_')}(#{gen_cpp_argument_list(symtab)})#{include_semi ? ';' : ''} + PROTOTYPE + end + end + + class CsrSoftwareWriteAst + def gen_cpp(symtab, indent, indent_spaces: 2) + # csr isn't known at runtime for sw_write... + csr_obj = csr.csr_def(symtab) + if csr_obj.nil? + "#{' '*indent}__UDB_CSR_BY_ADDR(#{csr.idx_expr.gen_cpp(symtab, 0, indent_spaces:)}).sw_write(#{expression.gen_cpp(symtab, 0, indent_spaces:)}, __UDB_XLEN)" + else + "#{' '*indent}__UDB_CSR_BY_NAME(#{csr_obj.name}).sw_write(#{expression.gen_cpp(symtab, 0, indent_spaces:)}, __UDB_XLEN)" + end + end + end + + class FieldAccessExpressionAst + def gen_cpp(symtab, indent, indent_spaces: 2) + "#{' '*indent}#{obj.gen_cpp(symtab, 0, indent_spaces: )}.#{@field_name}" + end + end + + class FieldAssignmentAst + def gen_cpp(symtab, indent, indent_spaces: 2) + "#{field_access.gen_cpp(symtab, 0, indent_spaces:)} = #{write_value.gen_cpp(symtab, 0, indent_spaces:)}" + end + end + + class ConcatenationExpressionAst + def gen_cpp(symtab, indent, indent_spaces: 2) + "#{' '*indent}concat(#{expressions.map { |e| e.gen_cpp(symtab, 0, indent_spaces: )}.join(', ')})" + end + end + + class BitsCastAst + def gen_cpp(symtab, indent, indent_spaces: 2) + t = expr.type(symtab) + width = + if t.kind == :enum_ref + t.enum_class.width + else + t.width + end + + if width == :unknown + "#{' '*indent}Bits(#{expr.gen_cpp(symtab, 0, indent_spaces: )})" + else + raise "nil" if width.nil? + "#{' '*indent}Bits<#{width}>(#{expr.gen_cpp(symtab, 0, indent_spaces: )})" + end + end + end + + class EnumCastAst + def gen_cpp(symtab, indent, indent_spaces: 2) + "#{' '*indent}#{enum_name.gen_cpp(symtab, 0, indent_spaces:)}{#{expression.gen_cpp(symtab, 0, indent_spaces: )}}" + end + end + + class CsrFieldAssignmentAst + def gen_cpp(symtab, indent, indent_spaces: 2) + + field = csr_field.field_def(symtab) + if symtab.cfg_arch.multi_xlen? && field.dynamic_location? + "#{' '*indent}__UDB_CSR_BY_NAME(#{csr_field.csr_name(symtab)}).#{field.name}()._hw_write(#{write_value.gen_cpp(symtab, 0, indent_spaces:)}, __UDB_XLEN)" + else + "#{' '*indent}__UDB_CSR_BY_NAME(#{csr_field.csr_name(symtab)}).#{field.name}()._hw_write(#{write_value.gen_cpp(symtab, 0, indent_spaces:)})" + end + end + end + + class EnumRefAst + def gen_cpp(symtab, indent, indent_spaces: 2) + "#{' '*indent}#{class_name}::#{member_name}" + end + end + + class EnumSizeAst + def gen_cpp(symtab, indent, indent_spaces: 2) + "#{' '*indent}#{value(symtab)}" + end + end + + class EnumElementSizeAst + def gen_cpp(symtab, indent, indent_spaces: 2) + "#{' '*indent}#{value(symtab)}" + end + end + + class EnumArrayCastAst + def gen_cpp(symtab, indent, indent_spaces: 2) + "#{' '*indent}std::array, #{enum_class.type(symtab).element_names.size}> {#{enum_class.type(symtab).element_values.map(&:to_s).join(', ')}}" + end + end + + class ParenExpressionAst + def gen_cpp(symtab, indent = 0, indent_spaces: 2) + "#{' '*indent}(#{expression.gen_cpp(symtab, 0, indent_spaces:)})" + end + end + + class IntLiteralAst + def gen_cpp(symtab, indent = 0, indent_spaces: 2) + v = value(symtab) + w = width(symtab) + t = type(symtab) + + if w == :unknown + "#{' ' * indent}_RuntimeBits<#{symtab.cfg_arch.possible_xlens.max}, #{t.signed?}>{#{v}_b, __UDB_XLEN}" + else + "#{' ' * indent}_Bits<#{w}, #{t.signed?}>{#{v}_b}" + end + end + end + + class IdAst + def gen_c(symtab) + text_value + end + def gen_cpp(symtab, indent = 0, indent_spaces: 2) + var = symtab.get(text_value) + + if !var.nil? && var.param? + if constexpr?(symtab) + "#{' ' * indent}__UDB_STATIC_PARAM(#{text_value}) /* #{var.value} */" + else + "#{' ' * indent}__UDB_RUNTIME_PARAM(#{text_value})" + end + elsif !var.nil? && var.type.global? + if var.type.const? + "#{' ' * indent}__UDB_CONST_GLOBAL(#{text_value})" + else + "#{' ' * indent}__UDB_MUTABLE_GLOBAL(#{text_value})" + end + elsif !var.nil? && var.decode_var? + "#{' ' * indent}#{text_value}()" + else + "#{' ' * indent}#{text_value}" + end + end + end + + class SignCastAst + def gen_cpp(symtab, indent = 0, indent_spaces: 2) + "#{' '*indent}(#{expression.gen_cpp(symtab, 0, indent_spaces:)}).make_signed()" + end + end + + class AryRangeAccessAst + def gen_cpp(symtab, indent = 0, indent_spaces: 2) + value_result = value_try do + return "#{' '*indent}extract<#{lsb.value(symtab)}, #{msb.value(symtab) - lsb.value(symtab) + 1}>(#{var.gen_cpp(symtab, 0, indent_spaces:)})" + end + value_else(value_result) do + # we don't know the value of something (probably a param), so we need the slow extract + return "#{' '*indent}extract(#{var.gen_cpp(symtab, 0, indent_spaces:)}, #{lsb.gen_cpp(symtab, 0, indent_spaces:)}, #{msb.gen_cpp(symtab, 0, indent_spaces:)} - #{lsb.gen_cpp(symtab, 0, indent_spaces:)} + 1)" + end + end + end + + class VariableDeclarationAst + def gen_c(symtab) + add_symbol(symtab) + if ary_size.nil? + "#{type_name.gen_c(symtab)} #{id.gen_c(symtab)}" + else + raise "TODO" + cpp = nil + value_result = value_try do + cpp = "#{' ' * indent}std::array<#{type_name.gen_cpp(symtab)},#{ary_size.value(symtab)}>#{ref ? '&' : ''} #{id.gen_cpp(symtab)}" + end + value_else(value_result) do + cpp = "#{' ' * indent}std::array<#{type_name.gen_cpp(symtab)}, #{ary_size.gen_cpp(symtab)}>#{ref ? '&' : ''} #{id.gen_cpp(symtab)}" + end + cpp + end + end + def gen_cpp(symtab, indent = 0, indent_spaces: 2, ref: false) + add_symbol(symtab) + if ary_size.nil? + "#{' ' * indent}#{type_name.gen_cpp(symtab, 0, indent_spaces:)}#{ref ? '&' : ''} #{id.gen_cpp(symtab, 0, indent_spaces:)}" + else + cpp = nil + value_result = value_try do + cpp = "#{' ' * indent}std::array<#{type_name.gen_cpp(symtab)},#{ary_size.value(symtab)}>#{ref ? '&' : ''} #{id.gen_cpp(symtab)}" + end + value_else(value_result) do + cpp = "#{' ' * indent}std::array<#{type_name.gen_cpp(symtab)}, #{ary_size.gen_cpp(symtab)}>#{ref ? '&' : ''} #{id.gen_cpp(symtab)}" + end + cpp + end + end + end + + class MultiVariableDeclarationAst + def gen_cpp(symtab, indent = 0, indent_spaces: 2) + add_symbol(symtab) + "#{' ' * indent}#{type_name.gen_cpp(symtab, 0, indent_spaces:)} #{var_name_nodes.map { |var| var.gen_cpp(symtab, 0, indent_spaces:) }.join(', ')}" + end + end + + class TernaryOperatorExpressionAst + def gen_cpp(symtab, indent = 0, indent_spaces: 2) + "#{' ' * indent}(#{condition.gen_cpp(symtab, 0, indent_spaces:)}) ? static_cast<#{type(symtab).to_cxx}>(#{true_expression.gen_cpp(symtab, 0, indent_spaces:)}) : static_cast<#{type(symtab).to_cxx}>(#{false_expression.gen_cpp(symtab, 0, indent_spaces:)})" + end + end + + class BuiltinTypeNameAst + def gen_c(symtab) + if @type_name == "Bits" + result = "" + value_result = value_try do + val = bits_expression.value(symtab) + result = val <= 64 ? "uint64_t" : "unsigned __int128" + end + value_else(value_result) do + # do we know the max? + max_val = bits_expression.max_value(symtab) + if max_val.nil? + result = "unsigned __int128" + elsif max_val <= 64 + result = "uint64_t" + else + result = "unsigned __int128" + end + end + result + elsif @type_name == "XReg" + "uint64_t" + elsif @type_name == "Boolean" + "uint8_t" + elsif @type_name == "U32" + "uint32_t" + elsif @type_name == "U64" + "uint64_t" + elsif @type_name == "String" + "const char*" + else + raise "TODO: #{@type_name}" + end + end + + def gen_cpp(symtab, indent = 0, indent_spaces: 2) + if @type_name == "Bits" + result = "" + value_result = value_try do + bits_expression.value(symtab) + result = "#{' '*indent}Bits<#{bits_expression.gen_cpp(symtab, 0, indent_spaces:)}>" + end + value_else(value_result) do + if bits_expression.constexpr?(symtab) + result = "#{' '*indent}Bits<#{bits_expression.gen_cpp(symtab)}>" + elsif bits_expression.max_value(symtab).nil? + result = "#{' '*indent}Bits" + else + max = bits_expression.max_value(symtab) + max = "BitsInfinitePrecision" if max == :unknown + result = "#{' '*indent}_RuntimeBits<#{max}, false>" + end + end + result + elsif @type_name == "XReg" + "#{' '*indent}Bits<#{symtab.cfg_arch.possible_xlens.max}>" + elsif @type_name == "Boolean" + "#{' '*indent}bool" + elsif @type_name == "U32" + "#{' '*indent}Bits<32>" + elsif @type_name == "U64" + "#{' '*indent}Bits<64>" + elsif @type_name == "String" + "#{' '*indent}std::string" + else + raise "TODO: #{@type_name}" + end + end + end + + class ForLoopAst + def gen_cpp(symtab, indent = 0, indent_spaces: 2) + # lines = ["#{' '*indent}for pass:[(]#{init.gen_cpp(0, indent_spaces:)}; #{condition.gen_cpp(0, indent_spaces:)}; #{update.gen_cpp(0, indent_spaces:)}) {"] + lines = [] + symtab.push(nil) + init.add_symbol(symtab) + symtab.get(init.lhs.text_value).value = nil + + stmts.each do |s| + lines << s.gen_cpp(symtab, indent_spaces, indent_spaces:) + end + cpp = <<~LOOP + for (#{init.gen_cpp(symtab, 0, indent_spaces:)}; #{condition.gen_cpp(symtab, 0, indent_spaces:)}; #{update.gen_cpp(symtab, 0, indent_spaces:)}) { + #{lines.join("\n ")} + } + LOOP + symtab.pop() + cpp.lines.map { |l| "#{' ' * indent}#{l}" }.join('') + end + end + + class BuiltinVariableAst + def gen_cpp(symtab, indent = 0, indent_spaces: 2) + case name + when "$encoding" + "#{' ' * indent}__UDB_ENCODING" + when "$pc" + "#{' ' * indent}__UDB_PC" + else + raise "TODO: #{name}" + end + end + end + + class VariableDeclarationWithInitializationAst + def gen_cpp(symtab, indent = 0, indent_spaces: 2) + add_symbol(symtab) + if ary_size.nil? + t = lhs_type(symtab) + if t.kind == :bits && t.width == :unknown + "#{' ' * indent}#{type_name.gen_cpp(symtab, 0, indent_spaces:)} #{lhs.gen_cpp(symtab, 0, indent_spaces:)}(#{rhs.gen_cpp(symtab, 0, indent_spaces:)}, #{type_name.bits_expression.gen_cpp(symtab)})" + else + "#{' ' * indent}#{type_name.gen_cpp(symtab, 0, indent_spaces:)} #{lhs.gen_cpp(symtab, 0, indent_spaces:)}(#{rhs.gen_cpp(symtab, 0, indent_spaces:)})" + end + else + "#{' ' * indent}std::array<#{type_name.gen_cpp(symtab, 0, indent_spaces:)}, #{ary_size.gen_cpp(symtab, 0, indent_spaces:)}> #{lhs.gen_cpp(symtab, 0, indent_spaces:)} = #{rhs.gen_cpp(symtab, 0, indent_spaces:)}" + end + end + end + + class AryElementAccessAst + def gen_cpp(symtab, indent = 0, indent_spaces: 2) + if var.type(symtab).integral? + if index.constexpr?(symtab) && var.type(symtab).width != :unknown + "#{' '*indent}extract<#{index.gen_cpp(symtab, 0)}, 1, #{var.type(symtab).width}>(#{var.gen_cpp(symtab, 0, indent_spaces:)})" + else + "#{' '*indent}extract( #{var.gen_cpp(symtab, 0, indent_spaces:)}, #{index.gen_cpp(symtab, 0)}, 1_b)" + end + else + if var.text_value.start_with?("X") + #"#{' '*indent}#{var.gen_cpp(symtab, 0, indent_spaces:)}[#{index.gen_cpp(symtab, 0, indent_spaces:)}]" + "#{' '*indent} __UDB_HART->_xreg(#{index.gen_cpp(symtab, 0, indent_spaces:)})" + else + "#{' '*indent}#{var.gen_cpp(symtab, 0, indent_spaces:)}[#{index.gen_cpp(symtab, 0, indent_spaces:)}]" + end + end + end + end + + class BinaryExpressionAst + def gen_cpp(symtab, indent = 0, indent_spaces: 2) + if op == ">>>" + "#{' '*indent}(#{lhs.gen_cpp(symtab, 0, indent_spaces:)}.sra(#{rhs.gen_cpp(symtab, 0, indent_spaces:)}))" + elsif op == "<<" + if rhs.constexpr?(symtab) + # use template form of shift + "#{' '*indent}(#{lhs.gen_cpp(symtab, 0, indent_spaces:)}.template sll<#{rhs.value(symtab)}>())" + elsif rhs.type(symtab).const? + # use widening shift + "#{' '*indent}(#{lhs.gen_cpp(symtab, 0, indent_spaces:)}.widening_sll(#{rhs.gen_cpp(symtab, 0, indent_spaces:)}))" + else + "#{' '*indent}(#{lhs.gen_cpp(symtab, 0, indent_spaces:)} << #{rhs.gen_cpp(symtab, 0, indent_spaces:)})" + end + else + "#{' '*indent}(#{lhs.gen_cpp(symtab, 0, indent_spaces:)} #{op} #{rhs.gen_cpp(symtab, 0, indent_spaces:)})" + end + end + end + + class VariableAssignmentAst + def gen_cpp(symtab, indent = 0, indent_spaces: 2) + "#{' '*indent}#{lhs.gen_cpp(symtab, 0, indent_spaces:)} = #{rhs.gen_cpp(symtab, 0, indent_spaces:)}" + end + end + + class PcAssignmentAst + def gen_cpp(symtab, indent = 0, indent_spaces: 2) + "#{' '*indent}__UDB_SET_PC(#{rhs.gen_cpp(symtab, 0, indent_spaces:)})" + end + end + + class AryElementAssignmentAst + def gen_cpp(symtab, indent = 0, indent_spaces: 2) + if lhs.text_value.start_with?("X") + #"#{' '*indent} #{lhs.gen_cpp(symtab, 0, indent_spaces:)}[#{idx.gen_cpp(symtab, 0, indent_spaces:)}] = #{rhs.gen_cpp(symtab, 0, indent_spaces:)}" + "#{' '*indent}__UDB_HART->_set_xreg( #{idx.gen_cpp(symtab, 0, indent_spaces:)}, #{rhs.gen_cpp(symtab, 0, indent_spaces:)})" + elsif lhs.type(symtab).kind == :bits + "#{' '*indent}#{lhs.gen_cpp(symtab, 0, indent_spaces:)}.setBit(#{idx.gen_cpp(symtab, 0, indent_spaces:)}, #{rhs.gen_cpp(symtab, 0, indent_spaces:)})" + else + # actually an array + "#{' '*indent}#{lhs.gen_cpp(symtab, 0, indent_spaces:)}[#{idx.gen_cpp(symtab, 0, indent_spaces:)}] = #{rhs.gen_cpp(symtab, 0, indent_spaces:)}" + end + end + end + + class StatementAst + def gen_cpp(symtab, indent = 0, indent_spaces: 2) + "#{' ' * indent}#{action.gen_cpp(symtab, 0, indent_spaces:)};" + end + end + + class UnaryOperatorExpressionAst + def gen_cpp(symtab, indent = 0, indent_spaces: 2) + "#{' '*indent}#{op}(#{exp.gen_cpp(symtab, 0, indent_spaces:)})" + end + end + + class ReturnStatementAst + def gen_cpp(symtab, indent = 0, indent_spaces: 2) + expression = + if return_value_nodes.empty? + "return;" + elsif return_value_nodes.size == 1 + "#{' ' * indent}return #{return_value_nodes[0].gen_cpp(symtab, 0, indent_spaces:)};" + else + func_def = find_ancestor(FunctionDefAst) + internal_error "Can't find function of return" if func_def.nil? + return_values = return_value_nodes.map { |rv| rv.gen_cpp(symtab, 0, indent_spaces:) } + "#{' ' * indent}return std::tuple<#{func_def.return_type_nodes.map { |rt| rt.gen_cpp(symtab)}.join(', ')}>{#{return_values.join(', ')}};" + end + "#{' ' * indent}#{expression}" + end + end + + class ReplicationExpressionAst + def gen_cpp(symtab, indent = 0, indent_spaces: 2) + result = "" + value_result = value_try do + result = "#{' '*indent}replicate<#{n.value(symtab)}>(#{v.gen_cpp(symtab, 0, indent_spaces:)})" + end + value_else(value_result) do + result = "#{' '*indent}replicate(#{v.gen_cpp(symtab, 0, indent_spaces:)}, #{n.gen_cpp(symtab, 0, indent_spaces:)})" + end + result + end + end + + class ConditionalStatementAst + def gen_cpp(symtab, indent = 0, indent_spaces: 2) + cpp = <<~IF + if (#{condition.gen_cpp(symtab, 0, indent_spaces:)}) { + #{action.gen_cpp(symtab, indent_spaces, indent_spaces:)}; + } + IF + cpp.lines.map { |l| "#{' ' * indent}#{l}" }.join("") + end + end + + class ArrayLiteralAst + def gen_cpp(symtab, indent = 0, indent_spaces: 2) + "{#{element_nodes.map { |e| e.gen_cpp(symtab, 0) }.join(', ')}}" + end + end + + class FunctionCallExpressionAst + def gen_cpp(symtab, indent = 0, indent_spaces: 2) + if name == "ary_includes?" + # special case + if arg_nodes[0].type(symtab).width == :unknown + # vector + "__UDB_FUNC_CALL ary_includes_Q_(#{arg_nodes[0].gen_cpp(symtab, 0)}, #{arg_nodes[1].gen_cpp(symtab, 0)})" + else + # array + "__UDB_CONSTEXPR_FUNC_CALL template ary_includes_Q_<#{arg_nodes[0].type(symtab).width}>(#{arg_nodes[0].gen_cpp(symtab, 0)}, #{arg_nodes[1].gen_cpp(symtab, 0)})" + end + elsif name == "implemented?" + "__UDB_FUNC_CALL template _implemented_Q_<#{arg_nodes[0].gen_cpp(symtab, 0)}>()" + elsif name == "implemented_version?" + "__UDB_FUNC_CALL template _implemented_version_Q_<#{arg_nodes[0].gen_cpp(symtab, 0)}, #{arg_nodes[1].text_value}>()" + else + targs_cpp = template_arg_nodes.map { |t| t.gen_cpp(symtab, 0, indent_spaces:) } + args_cpp = arg_nodes.map { |a| a.gen_cpp(symtab, 0, indent_spaces:) } + ftype = func_type(symtab) + if ftype.func_def_ast.constexpr?(symtab) + if targs_cpp.empty? + "__UDB_CONSTEXPR_FUNC_CALL #{name.gsub("?", "_Q_")}(#{args_cpp.join(', ')})" + else + "__UDB_CONSTEXPR_FUNC_CALL template #{name.gsub("?", "_Q_")}<#{targs_cpp.join(', ')}>(#{args_cpp.join(', ')})" + end + else + if targs_cpp.empty? + "__UDB_FUNC_CALL #{name.gsub("?", "_Q_")}(#{args_cpp.join(', ')})" + else + "__UDB_FUNC_CALL template #{name.gsub("?", "_Q_")}<#{targs_cpp.join(', ')}>(#{args_cpp.join(', ')})" + end + end + end + end + end + + class ArraySizeAst + def gen_cpp(symtab, indent = 0, indent_spaces: 2) + "#{' '*indent}(#{expression.gen_cpp(symtab, 0, indent_spaces:)}).size()" + end + end + + class FunctionBodyAst + def gen_cpp(symtab, indent = 0, indent_spaces: 2) + statements.map{ |s| "#{' ' * indent}#{s.gen_cpp(symtab, 0, indent_spaces:)}" }.join("\n") + end + end + + class CsrFieldReadExpressionAst + def gen_cpp(symtab, indent = 0, indent_spaces: 2) + "#{' '*indent}__UDB_CSR_BY_NAME(#{csr_def(symtab).name}).#{@field_name}()._hw_read()" + end + end + + class CsrReadExpressionAst + def gen_cpp(symtab, indent = 0, indent_spaces: 2) + csr = csr_def(symtab) + if csr.nil? + # csr isn't known at runtime... + "#{' '*indent}__UDB_CSR_BY_ADDR(#{idx_expr.gen_cpp(symtab, 0, indent_spaces:)}).hw_read(__UDB_XLEN)" + else + if symtab.cfg_arch.multi_xlen? && csr.format_changes_with_xlen? + "#{' '*indent}__UDB_CSR_BY_NAME(#{csr.name})._hw_read(__UDB_XLEN)" + else + "#{' '*indent}__UDB_CSR_BY_NAME(#{csr.name})._hw_read()" + end + end + end + end + + class IfAst + def gen_cpp(symtab, indent = 0, indent_spaces: 2) + cpp = [] + cpp << "if (#{if_cond.gen_cpp(symtab, 0, indent_spaces:)}) {" + if_body.stmts.each do |stmt| + cpp << stmt.gen_cpp(symtab, indent_spaces, indent_spaces:) + end + elseifs.each do |eif| + cpp << "} else if (#{eif.cond.gen_cpp(symtab, 0, indent_spaces:)}) {" + eif.body.stmts.each do |s| + cpp << s.gen_cpp(symtab, indent_spaces, indent_spaces:) + end + end + unless final_else_body.stmts.empty? + cpp << "} else {" + final_else_body.stmts.each do |s| + cpp << s.gen_cpp(symtab, indent_spaces, indent_spaces:) + end + end + cpp << "}" + cpp.map { |l| "#{' ' * indent}#{l}" }.join("\n") + end + end +end diff --git a/backends/cpp_hart_gen/lib/template_helpers.rb b/backends/cpp_hart_gen/lib/template_helpers.rb new file mode 100644 index 0000000000..3288aa478c --- /dev/null +++ b/backends/cpp_hart_gen/lib/template_helpers.rb @@ -0,0 +1,121 @@ + + +class Array + def to_cxx + "{#{map(&:to_cxx).join(', ')}}" + end +end + +class Instruction + def assembly_fmt(xlen) + fmt = assembly.dup + dvs = encoding(xlen).decode_variables + dvs.each do |dv| + fmt.gsub!(dv.name, "{}") + end + fmt + end + + def assembly_fmt_args(xlen) + args = [] + dvs = encoding(xlen).decode_variables + dvs.each do |dv| + if dv.name[0] == "x" || dv.name[0] == "r" + args << "Reg(#{dv.name}()).to_string()" + elsif dv.name[0] == "f" + args << "Reg(#{dv.name}(), true).to_string()" + else + args << "#{dv.name}()" + end + end + if args.empty? + "" + else + ", #{args.reverse.join(', ')}" + end + end +end + +class ExtensionRequirementExpression + class LogicNode + def to_cxx(&block) + if type == :term + yield @children[0].name, @children[0].requirement_specs_to_s + elsif type == :not + "!(#{@children[0].to_cxx(&block)})" + elsif type == :and + "(#{@children[0].to_cxx(&block)} && #{@children[1].to_cxx(&block)})" + elsif type == :or + "(#{@children[0].to_cxx(&block)} || #{@children[1].to_cxx(&block)})" + end + end + end + + def to_cxx(&block) + raise ArgumentError, "Missing block" unless block_given? + raise ArgumentError, "Blcok expects two arguments" unless block.arity == 2 + + to_logic_tree(expand: false).to_cxx(&block) + end +end + +module CppHartGen + module TemplateHelpers + + # get the name of a c++ class + # + # e.g.: + # + # name_of(:hart, cfg_arch) + # name_of(:params, "rv64") + def name_of(kind, cfg_arch_or_config_name, *extras) + config_name = cfg_arch_or_config_name.is_a?(ConfiguredArchitecture) ? cfg_arch.name : cfg_arch_or_config_name + config_name = config_name.gsub("-", "_") + case kind + when :cfg + config_name.camelize + when :hart + "#{config_name.camelize}_Hart" + when :params + "#{config_name.camelize}_Params" + when :csr + raise "Missing csr name" unless extras.size == 1 + + "#{config_name.camelize}_#{extras[0].gsub(".", "_").capitalize}_Csr" + when :csr_field + raise "Missing csr name" unless extras.size == 2 + + "#{config_name.camelize}_#{extras[0].gsub(".", "_").capitalize}_#{extras[1].capitalize}_Field" + when :csr_container + "#{config_name.camelize}_CsrContainer" + when :csr_view + raise "Missing csr name" unless extras.size == 1 + + "#{config_name.camelize}_#{extras[0].gsub(".", "_").capitalize}_CsrView" + when :inst + raise "Missing Instruction name" unless extras.size == 1 + + "#{config_name.camelize}_#{extras[0].gsub(".", "_").capitalize}_Inst" + when :struct + raise "Missing struct name" unless extras.size == 1 + + "#{config_name.camelize}_#{extras[0]}_Struct" + else + raise "TODO: #{kind}" + end + end + end + + class TemplateEnv + attr_reader :cfg_arch + def initialize(cfg_arch) + @cfg_arch = cfg_arch + end + + include TemplateHelpers + + def get_binding + binding + end + end +end diff --git a/backends/cpp_hart_gen/lib/written_pass.rb b/backends/cpp_hart_gen/lib/written_pass.rb new file mode 100644 index 0000000000..656e27f084 --- /dev/null +++ b/backends/cpp_hart_gen/lib/written_pass.rb @@ -0,0 +1,53 @@ +# frozen_string_literal: true + +module Idl + class AstNode + def written?(symtab, varname, in_assignment: false) + add_symbol(symtab) if is_a?(Declaration) + + if children.empty? + false + else + children.any? { |child| child.written?(symtab, varname, in_assignment:) } + end + end + end + class IdAst + def written?(symtab, varname, in_assignment: false) + in_assignment && (text_value == varname) + end + end + class VariableAssignmentAst + def written?(symtab, varname, in_assignment: false) + lhs.written?(symtab, varname, in_assignment: true) || \ + rhs.written?(symtab, varname, in_assignment:) + end + end + class AryElementAssignmentAst + def written?(symtab, varname, in_assignment: false) + lhs.written?(symtab, varname, in_assignment: true) || \ + idx.written?(symtab, varname, in_assignment:) || \ + rhs.written?(symtab, varname, in_assignment:) + end + end + class AryRangeAssignmentAst + def written?(symtab, varname, in_assignment: false) + variable.written?(symtab, varname, in_assignment: true) || \ + msb.written?(symtab, varname, in_assignment:) || \ + lsb.written?(symtab, varname, in_assignment:) || \ + write_value.written?(symtab, varname, in_assignment:) + end + end + class FieldAssignmentAst + def written?(symtab, varname, in_assignment: false) + field_access.written?(symtab, varname, in_assignment: true) || \ + write_value.written?(symtab, varname, in_assignment:) + end + end + class MultiVariableAssignmentAst + def written?(symtab, varname, in_assignment: false) + variables.any? { |variable| variable.written?(symtab, varname, in_assignment: true) } || \ + function_call.written?(symtab, varname, in_assignment:) + end + end +end diff --git a/backends/cpp_hart_gen/renode/UdbCpu.cs b/backends/cpp_hart_gen/renode/UdbCpu.cs new file mode 100644 index 0000000000..5e16c4dba5 --- /dev/null +++ b/backends/cpp_hart_gen/renode/UdbCpu.cs @@ -0,0 +1,258 @@ +// +// Copyright (c) 2010-2024 Antmicro +// +// This file is licensed under the MIT License. +// Full license text is available in 'licenses/MIT.txt'. +// +using System; +using System.Linq; +using System.Threading; +using System.Collections.Generic; +using System.Collections.Concurrent; +using Antmicro.Renode.Core; +using Antmicro.Renode.Exceptions; +using Antmicro.Renode.Logging; +using Antmicro.Renode.Peripherals.Bus; +using Antmicro.Renode.Peripherals.CPU; +using Antmicro.Renode.Peripherals.Timers; +using Antmicro.Renode.Peripherals.CPU.Disassembler; +using Antmicro.Renode.Peripherals.CPU.Registers; +using Antmicro.Renode.Utilities; +using Antmicro.Renode.Utilities.Binding; +using Antmicro.Renode.Time; +using ELFSharp.ELF; +using ELFSharp.UImage; +using Range = Antmicro.Renode.Core.Range; +using Machine = Antmicro.Renode.Core.Machine; + +namespace Antmicro.Renode.Peripherals +{ + public class UdbCpu : BaseCPU, IGPIOReceiver, ITimeSink, IDisposable + { + public UdbCpu(string cpuType, string sharedLibrary, + string modelType, string configFile, + Machine machine, Endianess endianness = Endianess.LittleEndian, + CpuBitness bitness = CpuBitness.Bits32, uint id = 0) + : base(id, cpuType, machine, endianness, bitness) + { + binder = new NativeBinder(this, sharedLibrary); + if (renodeInit(id, modelType, configFile) < 0) { + this.Log(LogLevel.Error, "UDB CPU initialization failed"); + } + } + + public override void Reset() + { + base.Reset(); + // clear all fields defined in this class + // clear the shared library state + } + + public override void Dispose() + { + base.Dispose(); + // call any cleanup function in the shared library + renodeDestruct(); + //after that: + binder.Dispose(); + } + + public override string Architecture { get { return "riscv"; } } + + public void OnGPIO(int number, bool value) + { + // deliver the interrupt to the core + + } + + public override RegisterValue PC + { + get + { + return GetRegisterValue64(32); + } + set + { + SetRegisterValue64(32, value); + } + } + + public virtual void SetRegisterValue64(int register, ulong value) + { + // call API to set + renodeSetRegisterValue64(register, value); + } + + public virtual ulong GetRegisterValue64(int register) + { + // call API to get + return renodeGetRegisterValue64(register); + } + + [Export] + public void SetTestResult(int result) + { + //... + if(result == 0) + this.Log(LogLevel.Info, "test passed"); + else + this.Log(LogLevel.Info, "test failed"); + } + + [Export] + public void LogCurrentPC(ulong pc) + { + machine.SystemBus.TryFindSymbolAt(pc, out var name, out var symbol, this); + + this.Log(LogLevel.Info, $"Entering function {name ?? "without name"} at 0x{pc.ToString("X")}"); + } + + public override ExecutionResult ExecuteInstructions(ulong numberOfInstructionsToExecute, out ulong numberOfExecutedInstructions) + { + instructionsExecutedThisRound = 0UL; + ulong instructionsBefore = renodeGetIcount(); + ExecutionResult result = ExecutionResult.Ok; + + try + { + // call API to execute N instructions + int udb_result = renodeExecute(numberOfInstructionsToExecute); + if (udb_result == 1) { + // inst limit reached + result = ExecutionResult.Ok; + } else if (udb_result == 0) { + // exit success + this.Log(LogLevel.Info, "test passed"); + InvokeHalted(new HaltArguments(HaltReason.Abort, this)); + return ExecutionResult.Aborted; + } else if (udb_result == 2) { + // wfi + result = ExecutionResult.WaitingForInterrupt; + } else if (udb_result == 3) { + // Pause + this.Log(LogLevel.Warning, "TODO: Pause"); + InvokeHalted(new HaltArguments(HaltReason.Abort, this)); + return ExecutionResult.Aborted; +// result = ExecutionResult.Pause; + } else if (udb_result == 4) { + // Ebreak + result = ExecutionResult.StoppedAtBreakpoint; + } else if (udb_result == -1) { + // Exit failure + this.Log(LogLevel.Warning, "test failed"); + this.Log(LogLevel.Warning, renodeExitReason()); + InvokeHalted(new HaltArguments(HaltReason.Abort, this)); + return ExecutionResult.Aborted; + } else if (udb_result == -2) { + // exception + result = ExecutionResult.Ok; + } else if (udb_result == -3) { + // unpredictable behavior + this.Log(LogLevel.Warning, "CPU hit unpredictable behavior"); + InvokeHalted(new HaltArguments(HaltReason.Abort, this)); + return ExecutionResult.Aborted; + } + } + catch(Exception) + { + this.NoisyLog("CPU exception detected, halting."); + InvokeHalted(new HaltArguments(HaltReason.Abort, this)); + return ExecutionResult.Aborted; + } + finally + { + instructionsExecutedThisRound = instructionsBefore - renodeGetIcount(); + numberOfExecutedInstructions = instructionsExecutedThisRound; + totalExecutedInstructions += instructionsExecutedThisRound; + } + + this.Log(LogLevel.Info, $"Executed, {result}"); + + return result; + } + + public override ulong ExecutedInstructions => totalExecutedInstructions; + + private NativeBinder binder; + + [Import] + private Func renodeInit; + + [Import] + private Action renodeDestruct; + + [Import] + private Func renodeExitReason; + + [Import] + private Func renodeExecute; + + [Import] + private Action renodeSetRegisterValue64; + + [Import] + private Func renodeGetRegisterValue64; + + [Import] + private Func renodeGetIcount; + + [Export] + protected virtual ulong ReadByteFromBus(ulong offset) + { + return (ulong)machine.SystemBus.ReadByte(offset, this); + } + + [Export] + protected virtual ulong ReadWordFromBus(ulong offset) + { + return (ulong)machine.SystemBus.ReadWord(offset, this); + } + + [Export] + protected virtual ulong ReadDoubleWordFromBus(ulong offset) + { + return machine.SystemBus.ReadDoubleWord(offset, this); + } + + [Export] + protected virtual ulong ReadQuadWordFromBus(ulong offset) + { + return machine.SystemBus.ReadQuadWord(offset, this); + } + + [Export] + protected virtual void WriteByteToBus(ulong offset, ulong value) + { + machine.SystemBus.WriteByte(offset, unchecked((byte)value), this); + } + + [Export] + protected virtual void WriteWordToBus(ulong offset, ulong value) + { + machine.SystemBus.WriteWord(offset, unchecked((ushort)value), this); + } + + [Export] + protected virtual void WriteDoubleWordToBus(ulong offset, ulong value) + { + machine.SystemBus.WriteDoubleWord(offset, (uint)value, this); + } + + [Export] + protected void WriteQuadWordToBus(ulong offset, ulong value) + { + machine.SystemBus.WriteQuadWord(offset, value, this); + } + + // list of fields below is random and not verified + private ulong registerValue; + + private bool gotRegisterValue; + private bool setRegisterValue; + private bool gotSingleStepMode; + private bool gotStep; + private ulong instructionsExecutedThisRound; + private ulong totalExecutedInstructions; + private bool ticksProcessed; + } +} diff --git a/backends/cpp_hart_gen/renode/udb.repl b/backends/cpp_hart_gen/renode/udb.repl new file mode 100644 index 0000000000..8e81e759c4 --- /dev/null +++ b/backends/cpp_hart_gen/renode/udb.repl @@ -0,0 +1,26 @@ +rom: Memory.MappedMemory @ { + sysbus 0x80000000; + sysbus 0x0 + } + size: 0x2000000 + +cpu: UdbCpu @ sysbus + cpuType: "rv64imacfd_zicsr_zifencei" + modelType: "_" + configFile: "/local/mnt/workspace/dhower/riscv-unified-db/worktrees/cpp_hart/cfgs/mc100-32-full-example.yaml" + sharedLibrary: "/local/mnt/workspace/dhower/riscv-unified-db/worktrees/cpp_hart/gen/cpp_hart_gen/__Debug/build/libhart_renode.so" + bitness: CpuBitness.Bits64 + + +uart: UART.SiFive_UART @ sysbus 0x38000000 + -> plic@33 + +clint: IRQControllers.CoreLevelInterruptor @ sysbus 0x02000000 + [0,1] -> cpu@[3,7] + frequency: 62000000 + numberOfTargets: 2 + +plic: IRQControllers.PlatformLevelInterruptController @ sysbus 0x0C000000 + [0,1] -> cpu@[11,9] + numberOfSources: 65 + numberOfContexts: 4 diff --git a/backends/cpp_hart_gen/renode/udb.resc b/backends/cpp_hart_gen/renode/udb.resc new file mode 100644 index 0000000000..8fba4953b0 --- /dev/null +++ b/backends/cpp_hart_gen/renode/udb.resc @@ -0,0 +1,17 @@ +using sysbus + +include @backends/cpp_hart_gen/renode/UdbCpu.cs +mach create +machine LoadPlatformDescription @backends/cpp_hart_gen/renode/udb.repl + +# $bin?=@https://dl.antmicro.com/projects/renode/kendryte-k210--vmlinux-s_2206416-2c1f2b2c2f2fc0c48a7b12a3f3c65809b81f452e +$bin?=$ORIGIN/../../../ext/riscv-tests/isa/rv32ui-p-add + +macro reset +""" + sysbus LoadELF $bin +""" + +runMacro $reset + +start diff --git a/backends/cpp_hart_gen/tasks.rake b/backends/cpp_hart_gen/tasks.rake new file mode 100644 index 0000000000..1b8e44a816 --- /dev/null +++ b/backends/cpp_hart_gen/tasks.rake @@ -0,0 +1,392 @@ + +require "active_support" +require "active_support/core_ext/string/inflections" + +require_relative "lib/template_helpers" +require_relative "lib/csr_template_helpers" +require_relative "lib/gen_cpp" +require_relative "lib/decode_tree" +require_relative "../../lib/idl/passes/find_src_registers" + +CPP_HART_GEN_SRC = $root / "backends" / "cpp_hart_gen" +CPP_HART_GEN_DST = $root / "gen" / "cpp_hart_gen" + +# copy the includes to dst +rule %r{#{CPP_HART_GEN_DST}/.*/include/udb/.*\.hpp$} => proc { |tname| + [(CPP_HART_GEN_SRC / "cpp" / "include" / "udb" / File.basename(tname)).to_s] +} do |t| + src_path = CPP_HART_GEN_SRC / "cpp" / "include" / "udb" / File.basename(t.name) + FileUtils.mkdir_p File.dirname(t.name) + FileUtils.ln_s src_path, t.name +end + +# copy the includes to dst +rule %r{#{CPP_HART_GEN_DST}/.*/include/udb/.*\.h$} => proc { |tname| + [(CPP_HART_GEN_SRC / "c" / "include" / "udb" / File.basename(tname)).to_s] +} do |t| + src_path = CPP_HART_GEN_SRC / "c" / "include" / "udb" / File.basename(t.name) + FileUtils.mkdir_p File.dirname(t.name) + FileUtils.ln_s src_path, t.name +end + +# copy the srcs to dst +rule %r{#{CPP_HART_GEN_DST}/.*/src/.*\.cpp$} => proc { |tname| + [(CPP_HART_GEN_SRC / "cpp" / "src" / File.basename(tname)).to_s] +} do |t| + src_path = CPP_HART_GEN_SRC / "cpp" / "src" / File.basename(t.name) + FileUtils.mkdir_p File.dirname(t.name) + FileUtils.ln_s src_path, t.name +end + +# copy the tests to dst +rule %r{#{CPP_HART_GEN_DST}/.*/test/.*\.cpp$} => proc { |tname| + [(CPP_HART_GEN_SRC / "cpp" / "test" / File.basename(tname)).to_s] +} do |t| + src_path = CPP_HART_GEN_SRC / "cpp" / "test" / File.basename(t.name) + FileUtils.mkdir_p File.dirname(t.name) + FileUtils.ln_s src_path, t.name +end + +# rule for generating when the thing being generated is not config-specific +rule %r{#{CPP_HART_GEN_DST}/[^/]+/include/udb/[^/]+\.h(xx)?\.unformatted$} => proc { |tname| + parts = tname.split("/") + fname = parts[-1].sub(/\.unformatted$/, "") + [ + "#{CPP_HART_GEN_SRC}/templates/#{fname}.erb", + __FILE__ + ] + Dir.glob(CPP_HART_GEN_SRC / 'lib' / '**' / '*') +} do |t| + configs, = configs_build_name + config_name = configs[0] + parts = t.name.split("/") + fname = parts[-1].sub(/\.unformatted$/, "") + + cfg_arch = cfg_arch_for(config_name) + + template_path = CPP_HART_GEN_SRC / "templates" / "#{fname}.erb" + erb = ERB.new(template_path.read, trim_mode: "-") + erb.filename = template_path.to_s + + File.write(t.name, erb.result(CppHartGen::TemplateEnv.new(cfg_arch).get_binding)) +end + +# rule for generating when the thing being generated is not config-specific +rule %r{#{CPP_HART_GEN_DST}/[^/]+/src/[^/]+\.cxx\.unformatted$} => proc { |tname| + # we just need one config for this, doesn't matter which one (enums are config-independent) + parts = tname.split("/") + fname = parts[-1].sub(/\.unformatted$/, "") + [ + "#{CPP_HART_GEN_SRC}/templates/#{fname}.erb", + __FILE__ + ] +} do |t| + configs, = configs_build_name + config_name = configs[0] + parts = t.name.split("/") + fname = parts[-1].sub(/\.unformatted$/, "") + + cfg_arch = cfg_arch_for(config_name) + + template_path = CPP_HART_GEN_SRC / "templates" / "#{fname}.erb" + erb = ERB.new(template_path.read, trim_mode: "-") + erb.filename = template_path.to_s + + File.write(t.name, erb.result(CppHartGen::TemplateEnv.new(cfg_arch).get_binding)) +end + +# a config-specifc generated header +rule %r{#{CPP_HART_GEN_DST}/.*/include/udb/cfgs/[^/]+/[^/]+\.h(xx)?\.unformatted$} => proc { |tname| + parts = tname.split("/") + filename = parts[-1].sub(/\.unformatted$/, "") + config_name = parts[-2] + [ + "#{$root}/.stamps/resolve-#{config_name}.stamp", + "#{CPP_HART_GEN_SRC}/templates/#{filename}.erb", + "#{CPP_HART_GEN_SRC}/lib/gen_cpp.rb", + "#{$root}/lib/idl/passes/prune.rb", + "#{CPP_HART_GEN_SRC}/lib/template_helpers.rb", + "#{CPP_HART_GEN_SRC}/lib/csr_template_helpers.rb", + __FILE__ + ] +} do |t| + parts = t.name.split("/") + filename = parts[-1].sub(/\.unformatted$/, "") + config_name = parts[-2] + + cfg_arch = cfg_arch_for(config_name) + + template_path = CPP_HART_GEN_SRC / "templates" / "#{filename}.erb" + erb = ERB.new(template_path.read, trim_mode: "-") + erb.filename = template_path.to_s + + FileUtils.mkdir_p File.dirname(t.name) + File.write(t.name, erb.result(CppHartGen::TemplateEnv.new(cfg_arch).get_binding)) +end + +rule %r{#{CPP_HART_GEN_DST}/.*\.[ch](xx)?$} => proc { |tname| + ["#{tname}.unformatted"] +} do |t| + sh "clang-format #{t.name}.unformatted > #{t.name}" +end + +rule %r{#{CPP_HART_GEN_DST}/.*/src/cfgs/[^/]+/[^/]+\.cxx\.unformatted$} => proc { |tname| + parts = tname.split("/") + filename = parts[-1].sub(/\.unformatted$/, "") + config_name = parts[-2] + [ + "#{$root}/.stamps/resolve-#{config_name}.stamp", + "#{CPP_HART_GEN_SRC}/templates/#{filename}.erb", + "#{CPP_HART_GEN_SRC}/lib/gen_cpp.rb", + "#{$root}/lib/idl/passes/prune.rb", + "#{CPP_HART_GEN_SRC}/lib/template_helpers.rb", + "#{CPP_HART_GEN_SRC}/lib/csr_template_helpers.rb", + __FILE__ + ] +} do |t| + parts = t.name.split("/") + filename = parts[-1].sub(/\.unformatted$/, "") + config_name = parts[-2] + + cfg_arch = cfg_arch_for(config_name) + + template_path = CPP_HART_GEN_SRC / "templates" / "#{filename}.erb" + erb = ERB.new(template_path.read, trim_mode: "-") + erb.filename = template_path.to_s + + FileUtils.mkdir_p File.dirname(t.name) + File.write(t.name, erb.result(CppHartGen::TemplateEnv.new(cfg_arch).get_binding)) +end + +rule %r{#{CPP_HART_GEN_DST}/[^/]+/CMakeLists\.txt} => [ + "#{CPP_HART_GEN_SRC}/CMakeLists.txt", + __FILE__ +] do |t| + FileUtils.mkdir_p File.dirname(t.name) + FileUtils.cp t.prerequisites.first, t.name +end + +rule %r{#{CPP_HART_GEN_DST}/[^/]+/build/Makefile} => [ + "#{CPP_HART_GEN_SRC}/CMakeLists.txt" +] do |t| + build_name = t.name.split("/")[-3] + cmd = [ + "cmake", + "-S#{CPP_HART_GEN_DST}/#{build_name}", + "-B#{CPP_HART_GEN_DST}/#{build_name}/build", + "-DCONFIG_LIST=\"#{ENV['CONFIG'].gsub(',', ';')}\"", + "-DCMAKE_EXPORT_COMPILE_COMMANDS=ON", + "-DCMAKE_BUILD_TYPE=#{cmake_build_type}" + ].join(" ") + + sh cmd +end + +rule %r{#{CPP_HART_GEN_DST}/[^/]+/include/udb/[^/]+\.hpp} do |t| + FileUtils.mkdir_p File.dirname(t.name) + fname = File.basename(t.name) + FileUtils.ln_s "#{CPP_HART_GEN_SRC}/cpp/include/udb/#{fname}", t.name +end + +def configs_build_name + raise ArgumentError, "Missing required option CONFIG:\n#{help}" if ENV["CONFIG"].nil? + + configs = ENV["CONFIG"].split(",") + build_type = cmake_build_type + + if configs.include?("all") + raise ArgumentError, "'all' was specified with another config name" unless configs.size == 1 + + configs = Dir.glob("#{$root}/cfgs/*").map { |path| File.basename(path, ".yaml") } + end + + configs.each do |config| + unless File.file?("#{$root}/cfgs/#{config}.yaml") || File.file?(config) + raise ArgumentError, "No config named '#{config}'" + end + end + + config_names = configs.map do |config| + cfg_arch_for(config).name + end + + build_name = + if ENV["BUILD_NAME"].nil? + if configs.size != 1 + raise ArgumentError, "BUILD_NAME is required when there are multiple configs" + end + config_names[0] + else + ENV["BUILD_NAME"] + end + + build_name += "_#{build_type}" + + [config_names, build_name] +end + +OPTION_STR = <<~DESC_OPTIONS +Options: + + * CONFIG: Comma-separated list of configurations to generate. + "rv32" is the generic RV32 architecture (i.e., no config). + "rv64" is the generic RV64 architecture (i.e., no config). + "all" will generate all configurations and the generic architecture. + + CONFIG can either be the name, excluding extension '.yaml', of a file under cfgs/ + or the (absolute or relative) path to a config file. + * BUILD_NAME: Name of the build. Required if CONFIG is a list. Otherwise, BUILD_NAME will equal CONFIG. +DESC_OPTIONS + +namespace :gen do + help = <<~DESC + Generate a C++ model of a hart(s) for configurations (./do --desc for more options) + + #{OPTION_STR} + + Examples: + + ./do gen:cpp_hart CONFIG=rv64 # generate generic hart model + ./do gen:cpp_hart CONFIG=example_rv64_with_overlay # generate hart model for example_rv64_with_overlay config + + # generate hart model for example_rv64_with_overlay and custom_cfg + ./do gen:cpp_hart CONFIG=example_rv64_with_overlay,custom_cfg BUILD_NAME=custom + + DESC + desc help + task :cpp_hart do + configs, build_name = configs_build_name + + Dir.glob("#{CPP_HART_GEN_SRC}/cpp/include/udb/*.hpp").each do |inc| + dst_path = CPP_HART_GEN_DST / build_name / "include" / "udb" / File.basename(inc) + Rake::Task[dst_path].invoke + end + Dir.glob("#{CPP_HART_GEN_SRC}/c/include/udb/*.h").each do |inc| + dst_path = CPP_HART_GEN_DST / build_name / "include" / "udb" / File.basename(inc) + Rake::Task[dst_path].invoke + end + Dir.glob("#{CPP_HART_GEN_SRC}/cpp/src/*.cpp").each do |src| + dst_path = CPP_HART_GEN_DST / build_name / "src" / File.basename(src) + Rake::Task[dst_path].invoke + end + Dir.glob("#{CPP_HART_GEN_SRC}/cpp/test/*.cpp").each do |src| + dst_path = CPP_HART_GEN_DST / build_name / "test" / File.basename(src) + Rake::Task[dst_path].invoke + end + + Rake::Task["#{CPP_HART_GEN_DST}/#{build_name}/CMakeLists.txt"].invoke + + generated_files = [] + generated_files << "#{CPP_HART_GEN_DST}/#{build_name}/include/udb/hart_factory.hxx" + generated_files << "#{CPP_HART_GEN_DST}/#{build_name}/include/udb/db_data.hxx" + generated_files << "#{CPP_HART_GEN_DST}/#{build_name}/src/db_data.cxx" + generated_files << "#{CPP_HART_GEN_DST}/#{build_name}/include/udb/enum.hxx" + generated_files << "#{CPP_HART_GEN_DST}/#{build_name}/src/enum.cxx" + generated_files << "#{CPP_HART_GEN_DST}/#{build_name}/include/udb/bitfield.hxx" + generated_files << "#{CPP_HART_GEN_DST}/#{build_name}/include/udb/libhart.h" + + configs.each do |config| + generated_files << "#{CPP_HART_GEN_DST}/#{build_name}/include/udb/cfgs/#{config}/inst.hxx" + generated_files << "#{CPP_HART_GEN_DST}/#{build_name}/include/udb/cfgs/#{config}/inst_impl.hxx" + generated_files << "#{CPP_HART_GEN_DST}/#{build_name}/include/udb/cfgs/#{config}/params.hxx" + generated_files << "#{CPP_HART_GEN_DST}/#{build_name}/src/cfgs/#{config}/params.cxx" + generated_files << "#{CPP_HART_GEN_DST}/#{build_name}/include/udb/cfgs/#{config}/hart.hxx" + generated_files << "#{CPP_HART_GEN_DST}/#{build_name}/include/udb/cfgs/#{config}/hart_impl.hxx" + generated_files << "#{CPP_HART_GEN_DST}/#{build_name}/include/udb/cfgs/#{config}/csrs.hxx" + generated_files << "#{CPP_HART_GEN_DST}/#{build_name}/include/udb/cfgs/#{config}/csrs_impl.hxx" + generated_files << "#{CPP_HART_GEN_DST}/#{build_name}/include/udb/cfgs/#{config}/csr_container.hxx" + generated_files << "#{CPP_HART_GEN_DST}/#{build_name}/include/udb/cfgs/#{config}/structs.hxx" + generated_files << "#{CPP_HART_GEN_DST}/#{build_name}/include/udb/cfgs/#{config}/func_prototypes.hxx" + generated_files << "#{CPP_HART_GEN_DST}/#{build_name}/include/udb/cfgs/#{config}/idl_funcs_impl.hxx" + + Dir.glob("#{CPP_HART_GEN_SRC}/cpp/include/udb/*.hpp") do |f| + Rake::Task["#{CPP_HART_GEN_DST}/#{build_name}/include/udb/#{File.basename(f)}"].invoke + end + end + + generated_files.each { |fn| Rake::Task["#{fn}.unformatted"].invoke } + + multitask "__generate_formatted_cpp_#{build_name}" => generated_files + Rake::MultiTask["__generate_formatted_cpp_#{build_name}"].invoke + end +end + +def cmake_build_type + return "RelWithDebInfo" unless ENV.key?("BUILD_TYPE") + + case ENV["BUILD_TYPE"].upcase + when "DEBUG" + "Debug" + when "FAST_DEBUG" + "RelWithDebInfo" + when "RELEASE", "", nil + "Release" + when "ASAN" + "Asan" + else + raise "Bad BUILD_TYPE; must be DEBUG, FAST_DEBUG, ASAN, or RELEASE" + end +end + +namespace :build do + help = <<~DESC + Build a C++ model of a hart(s) for configurations (./do --desc for more options) + + #{OPTION_STR} + + Examples: + + ./do build:cpp_hart CONFIG=rv64 # generate generic hart model + ./do build:cpp_hart CONFIG=example_rv64_with_overlay # generate hart model for example_rv64_with_overlay config + + # generate hart model for example_rv64_with_overlay and custom_cfg + ./do gen:cpp_hart CONFIG=example_rv64_with_overlay,custom_cfg BUILD_NAME=custom + + DESC + desc help + task cpp_hart: ["gen:cpp_hart"] do + _, build_name = configs_build_name + + Rake::Task["#{CPP_HART_GEN_DST}/#{build_name}/build/Makefile"].invoke + Dir.chdir("#{CPP_HART_GEN_DST}/#{build_name}/build") do + sh "make -j #{$jobs}" + end + end + + task renode_hart: ["gen:cpp_hart"] do + _, build_name = configs_build_name + + Rake::Task["#{CPP_HART_GEN_DST}/#{build_name}/build/Makefile"].invoke + Dir.chdir("#{CPP_HART_GEN_DST}/#{build_name}/build") do + sh "make -j #{$jobs} hart_renode" + end + end +end + +file "#{$root}/ext/riscv-tests/LICENSE" do + sh "git submodule update --init ext/riscv-tests" +end + +file "#{$root}/ext/riscv-tests/env/LICENSE" => ["#{$root}/ext/riscv-tests/LICENSE"] do + Dir.chdir "#{$root}/ext/riscv-tests" do + sh "git submodule update --init --recursive" + end +end + +task "checkout-riscv-tests" => "#{$root}/ext/riscv-tests/env/LICENSE" + +file "#{CPP_HART_GEN_DST}/riscv-tests-build-64/Makefile" => "#{$root}/ext/riscv-tests/env/LICENSE" do |t| + FileUtils.mkdir_p File.dirname(t.name) + Dir.chdir File.dirname(t.name) do + sh "#{$root}/ext/riscv-tests/configure --with-xlen=64" + end +end + +namespace :test do + task cpp_hart: ["build:cpp_hart"] do + _, build_name = configs_build_name + + Dir.chdir "#{CPP_HART_GEN_DST}/#{build_name}/build" do + sh "ctest" + end + end +end diff --git a/backends/cpp_hart_gen/templates/bitfield.hxx.erb b/backends/cpp_hart_gen/templates/bitfield.hxx.erb new file mode 100644 index 0000000000..c08e3a564f --- /dev/null +++ b/backends/cpp_hart_gen/templates/bitfield.hxx.erb @@ -0,0 +1,38 @@ +// THIS FILE IS AUTOGENERATED + +#pragma once + +#include +#include "udb/bits.hpp" +#include "udb/bitfield.hpp" + +namespace udb { + + <%- cfg_arch.global_ast.bitfields.each do |bitfield| -%> + class <%= bitfield.name %> : public Bitfield<<%= bitfield.size(cfg_arch.symtab) %>> { + public: + + // constructors + <%= bitfield.name %>() + : <%= bitfield.element_names.map { |name| "#{name}(*this)" }.join(",\n#{' '*8}") %> + {} + <%= bitfield.name %>(const Bits<<%= bitfield.size(cfg_arch.symtab) %>>& value) + : Bitfield<<%= bitfield.size(cfg_arch.symtab) %>>(value), + <%= bitfield.element_names.map { |name| "#{name}(*this)" }.join(",\n#{' '*8}") %> + {} + + <%= bitfield.name %>& operator=(const <%= bitfield.name %>& other) + { + <%- bitfield.element_names.each do |element_name| -%> + <%= element_name %> = other.<%= element_name %>; + <%- end -%> + + return *this; + } + + <%- bitfield.element_names.size.times do |idx| -%> + BitfieldMember<<%= bitfield.size(cfg_arch.symtab) %>, <%= bitfield.element_ranges(cfg_arch.symtab)[idx].begin %>, <%= bitfield.element_ranges(cfg_arch.symtab)[idx].size %>> <%= bitfield.element_names[idx] %>; + <%- end -%> + }; + <%- end -%> +} diff --git a/backends/cpp_hart_gen/templates/csr_container.hxx.erb b/backends/cpp_hart_gen/templates/csr_container.hxx.erb new file mode 100644 index 0000000000..b109a6c44d --- /dev/null +++ b/backends/cpp_hart_gen/templates/csr_container.hxx.erb @@ -0,0 +1,28 @@ +#pragma once + +#include "udb/cfgs/<%= cfg_arch.name %>/csrs.hxx" + +<%- csrs = cfg_arch.possible_csrs -%> + +namespace udb { + template + class <%= name_of(:hart, cfg_arch) %>; + + // just holds the a Hart's Csrs + template + struct <%= name_of(:csr_container, cfg_arch) %> { + <%- csrs.each do |csr| -%> + <%= name_of(:csr, cfg_arch, csr.name) %> <%= csr.name %>; + <%- end -%> + + <%= name_of(:csr_container, cfg_arch) %> (<%= name_of(:hart, cfg_arch) %>* parent) : + <%= csrs.map { |csr| "#{csr.name}(parent)" }.join(",\n ") -%> + {} + + void reset() { + <%- csrs.each do |csr| -%> + <%= csr.name %>.reset(); + <%- end -%> + } + }; +} diff --git a/backends/cpp_hart_gen/templates/csrs.hxx.erb b/backends/cpp_hart_gen/templates/csrs.hxx.erb new file mode 100644 index 0000000000..7bae01031f --- /dev/null +++ b/backends/cpp_hart_gen/templates/csrs.hxx.erb @@ -0,0 +1,351 @@ +#pragma once + +#include + +#include +#include + +#include "udb/util.hpp" +#include "udb/bits.hpp" +#include "udb/csr.hpp" +#include "udb/cpp_exceptions.hpp" + +<%- csrs = cfg_arch.not_prohibited_csrs -%> + +namespace udb { + template + class <%= name_of(:hart, cfg_arch) %>; + + <%- csrs.each do |csr| -%> + <%- next unless cfg_arch.possible_xlens.any? { |xlen| csr.defined_in_base?(xlen) } -%> + <%- fields = cfg_arch.fully_configured? ? csr.possible_fields(cfg_arch) : csr.fields.select { |field| field.exists_in_cfg?(cfg_arch) } -%> + + // Field classes for <%= csr.name %> + + #define __UDB_RUNTIME_PARAM(param_name) m_hart->m_params.param_name + + <%- fields.each do |field| -%> + <%- next unless cfg_arch.possible_xlens.any? { |xlen| field.defined_in_base?(xlen) } -%> + <%- field_base = field.defined_in_all_bases? ? cfg_arch.possible_xlens[0] : (field.defined_in_base32? ? 32 : 64) -%> + template + class <%= name_of(:csr_field, cfg_arch, csr.name, field.name) %> : public CsrFieldBase { + public: + <%- max_width = cfg_arch.possible_xlens.map { |xlen| field.defined_in_base?(xlen) ? field.location(xlen).size : 0 }.max -%> + using ValueType = PossiblyUnknownBits<<%= max_width %>>; + + // constructor + <%= name_of(:csr_field, cfg_arch, csr.name, field.name) %>(<%= name_of(:hart, cfg_arch) %>* hart) + : m_undefined(<%= field.reset_value.is_a?(String) ? true : false %>), + m_hart(hart), + <%- if cfg_arch.multi_xlen? && field.dynamic_location? -%> + m_location_32(<%= "#{field.location(32).end}, #{field.location(32).begin}" %>), + m_location_64(<%= "#{field.location(64).end}, #{field.location(64).begin}" %>) + <%- else -%> + m_location(<%= "#{field.location(field_base).end}, #{field.location(field_base).begin}" %>) + <%- end -%> + {} + + const CsrFieldLocation location(const unsigned& xlen) const override { + <%- if cfg_arch.multi_xlen? && field.dynamic_location? -%> + return xlen == 32 ? m_location_32 : m_location_64; + <%- else -%> + return m_location; + <%- end -%> + } + <%- if cfg_arch.multi_xlen? && field.dynamic_location? -%> + const CsrFieldLocation _location(const unsigned& xlen) const { + return xlen == 32 ? m_location_32 : m_location_64; + } + <%- else -%> + const CsrFieldLocation _location() const { return m_location; } + <%- end -%> + + void reset() override; + + uint64_t hw_read(const unsigned& xlen) const override { + if (m_undefined) { + return 0; // TODO!! + // throw UndefinedValueError("<%= field.csr.name %>.<%= field.name %> has an undefined value"); + } + return hw_read(m_value, xlen); + } + ValueType _hw_read() const { + if (m_undefined) { + return 0; // TODO!! + // throw UndefinedValueError("<%= field.csr.name %>.<%= field.name %> has an undefined value"); + } + return m_value; + } + uint64_t hw_read(const uint64_t& csr_value, const unsigned& xlen) const override { + <%- if cfg_arch.multi_xlen? && field.defined_in_all_bases? -%> + static constexpr uint64_t mask32 = 0x<%= (((1 << field.location(32).size) - 1) << field.location(32).begin).to_s(16) %>ull; + static constexpr unsigned shift32 = <%= field.location(32).begin %>; + static constexpr uint64_t mask64 = 0x<%= (((1 << field.location(64).size) - 1) << field.location(64).begin).to_s(16) %>ull; + static constexpr unsigned shift64 = <%= field.location(64).begin %>; + return (xlen == 32) ? (csr_value & mask32) >> shift32 : (csr_value & mask64) >> shift64; + <%- else -%> + static constexpr uint64_t mask = 0x<%= (((1 << field.location(field_base).size) - 1) << field.location(field_base).begin).to_s(16) %>ull; + static constexpr unsigned shift = <%= field.location(field_base).begin %>; + return (csr_value & mask) >> shift; + <%- end -%> + } + + // uint64_t sw_read(const unsigned& xlen) const override { + // <%- if cfg_arch.multi_xlen? && field.dynamic_location? -%> + // return _sw_read(xlen); + // <%- else -%> + // return _sw_read(); + // <%- end -%> + // } + // <%- if cfg_arch.multi_xlen? && field.dynamic_location? -%> + // uint64_t _sw_read(const unsigned& xlen) const { + // if (m_undefined) { + // throw UndefinedValueError("<%= field.csr.name %>.<%= field.name %> has an undefined value"); + // } + // <%- if cfg_arch.multi_xlen? && field.dynamic_location? -%> + // if (xlen == 32) { + // constexpr static ValueType mask = 0x<%= ((1 << field.location(32).size) - 1).to_s(16) %>ull; + // return m_value & mask; + // } else { + // constexpr static ValueType mask = 0x<%= ((1 << field.location(64).size) - 1).to_s(16) %>ull; + // return m_value & mask; + // } + // <%- else -%> + // return m_value; + // <%- end -%> + // } + // <%- else -%> + // uint64_t _sw_read() const { return m_value; } + // <%- end -%> + + void hw_write(const uint64_t &field_write_value, const unsigned& xlen) override { + <%- if cfg_arch.multi_xlen? && field.dynamic_location? -%> + return _hw_write(field_write_value, xlen); + <%- else -%> + return _hw_write(field_write_value); + <%- end -%> + } + <%- if cfg_arch.multi_xlen? && field.dynamic_location? -%> + void _hw_write(const ValueType &field_write_value, const unsigned& xlen) { + if (xlen == 32) { + static constexpr ValueType mask = 0x<%= ((1 << field.location(32).size) - 1).to_s(16) %>ull; + m_value = field_write_value & mask; + m_undefined = false; + } else { + static constexpr ValueType mask = 0x<%= ((1 << field.location(64).size) - 1).to_s(16) %>ull; + m_value = field_write_value & mask; + m_undefined = false; + } + } + <%- else -%> + void _hw_write(const ValueType &field_write_value) { + m_value = field_write_value; + m_undefined = false; + } + <%- end -%> + + // void sw_write(const uint64_t &field_write_value, const unsigned& xlen) override { + // <%- if cfg_arch.multi_xlen? && field.dynamic_location? -%> + // return _sw_write(field_write_value, xlen); + // <%- else -%> + // return _sw_write(field_write_value); + // <%- end -%> + // } + // <%- if cfg_arch.multi_xlen? && field.dynamic_location? -%> + // void _sw_write(const ValueType &field_write_value, const unsigned& xlen); + // <%- else -%> + // void _sw_write(const ValueType &field_write_value); + // <%- end -%> + + void makeUndefined() { m_undefined = true; } + + CsrFieldType type(const unsigned& xlen) const override; + + private: + <%- if cfg_arch.multi_xlen? && field.dynamic_location? -%> + const CsrFieldLocation m_location_32; + const CsrFieldLocation m_location_64; + <%- else -%> + const CsrFieldLocation m_location; + <%- end -%> + ValueType m_value; + bool m_undefined; + <%= name_of(:hart, cfg_arch) %>* m_hart; + }; + <%- end -%> + + // Csr view + template + class <%= name_of(:csr, cfg_arch, csr.name) %>View; + + <%- cfg_arch.possible_xlens.each do |xlen| -%> + <%- next unless csr.defined_in_base?(xlen) -%> + template<> + class <%= name_of(:csr, cfg_arch, csr.name) %>View<<%= xlen %>> : public Bitfield<<%= csr.length(xlen) %>> + { + public: + <%= name_of(:csr, cfg_arch, csr.name) %>View() = default; + <%= name_of(:csr, cfg_arch, csr.name) %>View(const Bits<<%= csr.length(xlen) %>>& value) + : Bitfield<<%= csr.length(xlen) %>>(value) + <%= csr.fields.select { |field| field.defined_in_base?(xlen) }.map { |field| ", #{field.name}(*this)" }.join("\n#{' ' * 8}") %> + {} + + <%- csr.fields.each do |field| -%> + <%- next unless field.defined_in_base?(xlen) -%> + BitfieldMember<<%= csr.length(xlen) %>, <%= field.location(xlen).begin %>, <%= field.location(xlen).size %>> <%= field.name %>; + <%- end -%> + }; + <%- end -%> + + // Csr class + template + class <%= name_of(:csr, cfg_arch, csr.name) %> : public CsrBase { + <%- fields_for_xlen = fields.select { |f| cfg_arch.possible_xlens.any? { |xlen| f.defined_in_base?(xlen) } } -%> + <%- fields_for_xlen32 = fields.select { |f| f.defined_in_base32? } -%> + <%- fields_for_xlen64 = fields.select { |f| f.defined_in_base64? } -%> + <%- fields_for_xlen.each do |field| -%> + friend class <%= name_of(:csr_field, cfg_arch, csr.name, field.name ) -%>; + <%- end -%> + + public: + template + using View = <%= name_of(:csr, cfg_arch, csr.name) %>View; + + using ValueType = PossiblyUnknownBits<<%= csr.max_length %>>; + + public: + // constructor + <%= name_of(:csr, cfg_arch, csr.name) %>(<%= name_of(:hart, cfg_arch) %>* parent); + + unsigned address() const override { return <%= csr.address %>; } + static constexpr unsigned _address() { return <%= csr.address %>; } + const std::string name() const override { return "<%= csr.name %>"; } + + void reset() override { + <%- fields_for_xlen.each do |field| -%> + m_<%= field.name %>.reset(); + <%- end -%> + } + + uint64_t hw_read(const unsigned& xlen) const override { + <%- if cfg_arch.multi_xlen? && csr.format_changes_with_xlen? -%> + return _hw_read(xlen); + <%- else -%> + return _hw_read(); + <%- end -%> + } + + <%- if cfg_arch.multi_xlen? && csr.format_changes_with_xlen? -%> + ValueType _hw_read(const unsigned& xlen) const { + if (xlen == 32) { + <%- + field_cpp = fields_for_xlen32.map do |field| + if field.dynamic_location? + "((m_#{field.name}._hw_read() & 0x#{((1 << field.location(32).size) - 1).to_s(16)}).template sll<#{field.location(32).begin}>())" + else + "(m_#{field.name}._hw_read().template sll<#{field.location(32).begin}>())" + end + end + -%> + return <%= field_cpp.empty? ? 0 : field_cpp.join(" | ") %>; + } else { + udb_assert(xlen == 64, "Bad xlen"); + <%- + field_cpp = fields_for_xlen32.map do |field| + if field.dynamic_location? + "((m_#{field.name}._hw_read() & 0x#{((1 << field.location(64).size) - 1).to_s(16)}).template sll<#{field.location(64).begin}>())" + else + "(m_#{field.name}._hw_read().template sll<#{field.location(64).begin}>())" + end + end + -%> + return <%= field_cpp.empty? ? 0 : field_cpp.join(" | ") %>; + } + } + <%- else -%> + ValueType _hw_read() const { + <%- + xlen = cfg_arch.possible_xlens[0] # any possible xlen will do + field_cpp = fields_for_xlen.map do |field| + if field.dynamic_location? + "((m_#{field.name}._hw_read() & 0x#{((1 << field.location(xlen).size) - 1).to_s(16)}).template sll<#{field.location(xlen).begin}>())" + else + "(m_#{field.name}._hw_read().template sll<#{field.location(xlen).begin}>())" + end + end + -%> + return <%= field_cpp.empty? ? 0 : field_cpp.join(" | ") %>; + } + <%- end -%> + + // Generic sw_read that always returns 64 bits + // Users that only know this is a CsrBase must use this function + uint64_t sw_read(const unsigned& xlen) const override { + <%- if cfg_arch.multi_xlen? && csr.format_changes_with_xlen? -%> + return _sw_read(xlen); + <%- else -%> + return _sw_read(); + <%- end -%> + } + + // Specific sw_read that returns the correct Bits<<%= csr.max_length %>> type. + // This isn't accessible to CsrBase + <%- if cfg_arch.multi_xlen? && csr.format_changes_with_xlen? -%> + ValueType _sw_read(const unsigned& xlen) const; + <%- else -%> + ValueType _sw_read() const; + <%- end -%> + + void hw_write(const uint64_t& value, const unsigned& xlen) override { + <%- if cfg_arch.multi_xlen? && csr.format_changes_with_xlen? -%> + _hw_write(value, xlen); + <%- else -%> + _hw_write(value); + <%- end -%> + } + + <%- if cfg_arch.multi_xlen? && csr.format_changes_with_xlen? -%> + void _hw_write(const Bits<<%= csr.max_length %>>& value, const unsigned& xlen); + <%- else -%> + void _hw_write(const Bits<<%= csr.max_length %>>& value); + <%- end -%> + + + // Generic sw_write that takes a 64 bit value + // Users that only know this is a CsrBase must use this function + bool sw_write(const uint64_t& value, const unsigned& xlen) override { + <%- if cfg_arch.multi_xlen? && csr.format_changes_with_xlen? -%> + return _sw_write(value, xlen); + <%- else -%> + return _sw_write(value); + <%- end -%> + } + + // Specific sw_write that takes the correct Bits<<%= csr.max_length %>> type. + // This isn't accessible to CsrBase + <%- if cfg_arch.multi_xlen? && csr.format_changes_with_xlen? -%> + bool _sw_write(const Bits<<%= csr.max_length %>>& value, const unsigned& xlen); + <%- else -%> + bool _sw_write(const Bits<<%= csr.max_length %>>& value); + <%- end -%> + + bool implemented_without_Q_(const ExtensionName&) const override; + + <%- fields_for_xlen.each do |field| %> + <%= name_of(:csr_field, cfg_arch, csr.name, field.name) %>& <%= field.name %>() { return m_<%= field.name %>; } + <%- end -%> + + private: + <%= name_of(:hart, cfg_arch) %>* m_parent; + <%- fields_for_xlen.each do |field| %> + <%= name_of(:csr_field, cfg_arch, csr.name, field.name) %> m_<%= field.name %>; + <%- end -%> + }; + #undef __UDB_RUNTIME_PARAM + <%- end -%> +} + +#include "udb/cfgs/<%= cfg_arch.name %>/hart.hxx" +#include "udb/inst.hpp" + +#include "udb/cfgs/<%= cfg_arch.name %>/csrs_impl.hxx" diff --git a/backends/cpp_hart_gen/templates/csrs_impl.hxx.erb b/backends/cpp_hart_gen/templates/csrs_impl.hxx.erb new file mode 100644 index 0000000000..7f086f3ebc --- /dev/null +++ b/backends/cpp_hart_gen/templates/csrs_impl.hxx.erb @@ -0,0 +1,299 @@ +#pragma once + +#include "udb/cfgs/<%= cfg_arch.name %>/structs.hxx" +#include "udb/cfgs/<%= cfg_arch.name %>/params.hxx" + +using namespace std::literals; + +namespace udb { + +<%- csrs = cfg_arch.possible_csrs -%> + +<%- csrs.each do |csr| -%> +<%- fields = cfg_arch.fully_configured? ? csr.possible_fields(cfg_arch) : csr.fields.select { |field| field.exists_in_cfg?(cfg_arch) } -%> + +#define __UDB_RUNTIME_PARAM(param_name) m_hart->m_params.param_name +#define __UDB_STATIC_PARAM(param_name) <%= name_of(:params, cfg_arch) %>::param_name ## _VALUE +#define __UDB_FUNC_CALL m_hart-> +#define __UDB_CONSTEXPR_FUNC_CALL <%= name_of(:hart, cfg_arch) %>:: +#define __UDB_CSR_BY_NAME(csr_name) m_hart->m_csrs.csr_name +#define __UDB_CONST_GLOBAL(global_name) <%= name_of(:hart, cfg_arch) %>::global_name +#define __UDB_MUTABLE_GLOBAL(global_name) m_hart->global_name +#define __UDB_STRUCT(struct_name) <%= cfg_arch.name.camelize %>_ ## struct_name ## _Struct + + +<%- fields.each do |field| -%> +<%- next unless cfg_arch.possible_xlens.any? { |xlen| field.defined_in_base?(xlen) } -%> +<%- max_width = cfg_arch.possible_xlens.map { |xlen| field.defined_in_base?(xlen) ? field.location(xlen).size : 0 }.max -%> +<%- field_base = field.defined_in_all_bases? ? cfg_arch.possible_xlens[0] : (field.defined_in_base32? ? 32 : 64) -%> +template +CsrFieldType <%= name_of(:csr_field, cfg_arch, csr.name, field.name) %>::type(const unsigned& xlen) const { + <%- if field.defined_in_all_bases? && cfg_arch.multi_xlen? -%> + if (xlen == 32) { + <%= field.type_to_cpp(32) %> + } else { // if (xlen == 64) + <%= field.type_to_cpp(64) %> + } + <%- else -%> + <%= field.type_to_cpp(field_base) %> + <%- end -%> +} + +template +void <%= name_of(:csr_field, cfg_arch, csr.name, field.name) %>::reset() { + + <%- ast = field.pruned_reset_value_ast -%> + <%- if ast.nil? -%> + <%- v = field.reset_value -%> + <%- if v == "UNDEFINED_LEGAL" -%> + m_undefined = true; + <%- else -%> + <%- if cfg_arch.multi_xlen? && field.dynamic_location? -%> + _hw_write(<%= v %>ULL, m_hart->xlen()); + <%- else -%> + _hw_write(<%= v %>ULL); + <%- end -%> + <%- end -%> + <%- else -%> + auto val_fn = [this]() -> + <%- if field.could_be_undefined? -%> + PossiblyUnknownBits<<%= max_width + 1 %>> + <%- else -%> + Bits<<%= max_width + 1 %>> + <%- end -%> + { + <%= ast.gen_cpp(cfg_arch.symtab, 8) %> + }; + auto val = val_fn(); + if (val == UNDEFINED_LEGAL) { + m_undefined = true; + } else { + <%- if cfg_arch.multi_xlen? && field.dynamic_location? -%> + _hw_write(val, m_hart->xlen()); + <%- else -%> + _hw_write(val); + <%- end -%> + } + <%- end -%> +} + +<%- end -%> + +#undef __UDB_RUNTIME_PARAM +#undef __UDB_STATIC_PARAM +#undef __UDB_CONSTEXPR_FUNC_CALL +#undef __UDB_FUNC_CALL +#undef __UDB_CSR_BY_NAME +#undef __UDB_CONST_GLOBAL +#undef __UDB_MUTABLE_GLOBAL +#undef __UDB_STRUCT + +#define __UDB_RUNTIME_PARAM(param_name) m_parent->m_params.param_name +#define __UDB_STATIC_PARAM(param_name) <%= name_of(:params, cfg_arch) %>::param_name +#define __UDB_CSR_BY_ADDR(addr) m_parent->csr(addr) +#define __UDB_CSR_BY_NAME(csr_name) m_parent->m_csrs.csr_name +#define __UDB_CSR_FIELD_READ(field_name) m_##field_name +#define __UDB_FUNC_CALL m_parent-> +#define __UDB_CONSTEXPR_FUNC_CALL <%= name_of(:hart, cfg_arch) %>:: +#define __UDB_XLEN m_parent->xlen() +#define __UDB_ENCODING m_parent->m_cur_inst->encoding() +#define __UDB_CONST_GLOBAL(global_name) <%= name_of(:hart, cfg_arch) %>::global_name +#define __UDB_MUTABLE_GLOBAL(global_name) m_parent->global_name +#define __UDB_STRUCT(struct_name) <%= cfg_arch.name.camelize %>_ ## struct_name ## _Struct + +// constructor +<%- fields_for_xlen = fields.select { |f| cfg_arch.possible_xlens.any? { |xlen| f.defined_in_base?(xlen) } } -%> +template +<%= name_of(:csr, cfg_arch, csr.name) %>::<%= name_of(:csr, cfg_arch, csr.name) %>(<%= name_of(:hart, cfg_arch) %>* parent) + : CsrBase(), + m_parent(parent) + <%- unless fields_for_xlen.empty? -%>,<%- end -%> + <%= fields_for_xlen.map { |field| "m_#{field.name}(parent)" }.join(", ") %> +{ +} + +<%- if cfg_arch.multi_xlen? && csr.format_changes_with_xlen? -%> +template +<%= name_of(:csr, cfg_arch, csr.name) %>::ValueType <%= name_of(:csr, cfg_arch, csr.name) %>::_sw_read(const unsigned& xlen) const +{ + <%- if csr.has_custom_sw_read? -%> + if (xlen == 32) { + <%- pruned_ast = csr.pruned_sw_read_ast(32) -%> + <%- symtab = csr.fill_symtab(pruned_ast, 32) -%> + <%= pruned_ast.gen_cpp(symtab) %> + <%- symtab.release -%> + } else { + udb_assert(xlen == 64, "Bad xlen"); + <%- pruned_ast = csr.pruned_sw_read_ast(64) -%> + <%- symtab = csr.fill_symtab(pruned_ast, 64) -%> + <%= pruned_ast.gen_cpp(symtab) %> + <%- symtab.release -%> + } + <%- else -%> + <%- if cfg_arch.multi_xlen? && csr.format_changes_with_xlen? -%> + return _hw_read(xlen); + <%- else -%> + return _hw_read(); + <%- end -%> + <%- end -%> +} +<%- else -%> +template +<%= name_of(:csr, cfg_arch, csr.name) %>::ValueType <%= name_of(:csr, cfg_arch, csr.name) %>::_sw_read() const +{ + <%- if csr.has_custom_sw_read? -%> + <%- xlen = cfg_arch.possible_xlens[0] # any xlen will do -%> + <%- pruned_ast = csr.pruned_sw_read_ast(xlen) -%> + <%- symtab = csr.fill_symtab(pruned_ast, xlen) -%> + <%= pruned_ast.gen_cpp(symtab) %> + <%- symtab.release -%> + <%- else -%> + return _hw_read(); + <%- end -%> +} +<%- end -%> + +<%- if cfg_arch.multi_xlen? && csr.format_changes_with_xlen? -%> +template +void <%= name_of(:csr, cfg_arch, csr.name) %>::_hw_write(const Bits<<%= csr.max_length %>>& value, const unsigned& xlen) +{ + if (xlen == 32) { + <%- fields_for_xlen.each do |field| -%> + <%- next unless field.defined_in_base?(32) -%> + <%- if cfg_arch.multi_xlen? && field.dynamic_location? -%> + m_<%= field.name %>._hw_write(extract<<%= field.location(32).begin %>, <%= field.location(32).size %>>(value), xlen); + <%- else -%> + m_<%= field.name %>._hw_write(extract<<%= field.location(32).begin %>, <%= field.location(32).size %>>(value)); + <%- end -%> + <%- end -%> + } else { + udb_assert(xlen == 64, "Bad xlen"); + <%- fields_for_xlen.each do |field| -%> + <%- next unless field.defined_in_base?(64) -%> + <%- if cfg_arch.multi_xlen? && field.dynamic_location? -%> + m_<%= field.name %>._hw_write(extract<<%= field.location(64).begin %>, <%= field.location(64).size %>>(value), xlen); + <%- else -%> + m_<%= field.name %>._hw_write(extract<<%= field.location(64).begin %>, <%= field.location(64).size %>>(value)); + <%- end -%> + <%- end -%> + } +} +<%- else -%> +template +void <%= name_of(:csr, cfg_arch, csr.name) %>::_hw_write(const Bits<<%= csr.max_length %>>& value) +{ + <%- xlen = cfg_arch.possible_xlens[0] # any valid xlen will work -%> + <%- fields_for_xlen.each do |field| -%> + m_<%= field.name %>._hw_write(extract<<%= field.location(xlen).begin %>, <%= field.location(xlen).size %>>(value)); + <%- end -%> +} +<%- end -%> + +<%- if cfg_arch.multi_xlen? && csr.format_changes_with_xlen? -%> +template +bool <%= name_of(:csr, cfg_arch, csr.name) %>::_sw_write(const Bits<<%= csr.max_length %>>& value, const unsigned& xlen) { + <%- fields = cfg_arch.fully_configured? ? csr.possible_fields(cfg_arch) : csr.fields.select { |field| field.exists_in_cfg?(cfg_arch) } -%> + <%- fields.each do |field| -%> + <%- next unless cfg_arch.possible_xlens.any? { |xlen| field.defined_in_base?(xlen) } -%> + <%- cfg_arch.possible_xlens.each do |xlen| -%> + <%- next unless field.defined_in_base?(xlen) -%> + if (xlen == <%= xlen %>) { + View<<%= xlen %>> csr_value(value); + + <%- if field.has_custom_sw_write? -%> + auto wr_val_fn = [this, &csr_value = std::as_const(csr_value)]() -> + <%- if field.could_be_undefined? -%> + PossiblyUnknownBits<<%= field.width(xlen) %>> + <%- else -%> + Bits<<%= field.width(xlen) %>> + <%- end -%> + { + <%- pruned_ast = field.pruned_sw_write_ast(xlen) -%> + <%- symtab = field.fill_symtab_for_sw_write(xlen, pruned_ast) -%> + <%= pruned_ast.gen_cpp(symtab) %> + <%- symtab.release -%> + }; + auto wr_val = wr_val_fn(); + if (wr_val == UNDEFINED_LEGAL_DETERMINISTIC) { + m_<%= field.name %>.makeUndefined(); + } else { + <%- if cfg_arch.multi_xlen? && field.dynamic_location? -%> + m_<%= field.name %>._hw_write(wr_val, xlen); + <%- else -%> + m_<%= field.name %>._hw_write(wr_val); + <%- end -%> + } + <%- else -%> + <%- if cfg_arch.multi_xlen? && field.dynamic_location? -%> + m_<%= field.name %>._hw_write(csr_value.<%= field.name %>, xlen); + <%- else -%> + m_<%= field.name %>._hw_write(csr_value.<%= field.name %>); + <%- end -%> + <%- end -%> + } + <%- end -%> + <%- end -%> + return true; +} +<%- else -%> +template +bool <%= name_of(:csr, cfg_arch, csr.name) %>::_sw_write(const Bits<<%= csr.max_length %>>& value) { + View<<%= cfg_arch.possible_xlens[0] %>> csr_value(value); + + <%- fields = cfg_arch.fully_configured? ? csr.possible_fields(cfg_arch) : csr.fields.select { |field| field.exists_in_cfg?(cfg_arch) } -%> + <%- fields.each do |field| -%> + <%- next unless cfg_arch.possible_xlens.any? { |xlen| field.defined_in_base?(xlen) } -%> + <%- if field.has_custom_sw_write? -%> + { + auto wr_val_fn = [this, &csr_value = std::as_const(csr_value)]() -> + <%- if field.could_be_undefined? -%> + PossiblyUnknownBits<<%= field.width(cfg_arch.possible_xlens[0]) %>> + <%- else -%> + Bits<<%= field.width(cfg_arch.possible_xlens[0]) %>> + <%- end -%> + { + <%- pruned_ast = field.pruned_sw_write_ast(cfg_arch.possible_xlens[0]) -%> + <%- symtab = field.fill_symtab_for_sw_write(cfg_arch.possible_xlens[0], pruned_ast) -%> + <%= pruned_ast.gen_cpp(symtab) %> + <%- symtab.release -%> + }; + auto wr_val = wr_val_fn(); + if (wr_val == UNDEFINED_LEGAL_DETERMINISTIC) { + m_<%= field.name %>.makeUndefined(); + } else { + m_<%= field.name %>._hw_write(wr_val); + } + } + <%- else -%> + m_<%= field.name %>._hw_write(csr_value.<%= field.name %>); + <%- end -%> + <%- end -%> + return true; +} + +<%- end -%> + +template +bool <%= name_of(:csr, cfg_arch, csr.name) %>::implemented_without_Q_(const ExtensionName& ext) const +{ + return <%= csr.defined_by_condition.to_cxx { |ext_name, ext_ver| "(ExtensionName::#{ext_name} != ext) && (m_parent->m_implemented_exts.find(ext) != m_parent->m_implemented_exts.end())" } %>; +} + +#undef __UDB_RUNTIME_PARAM +#undef __UDB_STATIC_PARAM +#undef __UDB_CSR_BY_ADDR +#undef __UDB_CSR_BY_NAME +#undef __UDB_CSR_FIELD_READ +#undef __UDB_FUNC_CALL +#undef __UDB_CONSTEXPR_FUNC_CALL +#undef __UDB_XLEN +#undef __UDB_CONST_GLOBAL +#undef __UDB_MUTABLE_GLOBAL +#undef __UDB_STRUCT +#undef __UDB_ENCODING + +<%- end -%> + +#undef __UDB__FUNC_OBJ + +} diff --git a/backends/cpp_hart_gen/templates/db_data.cxx.erb b/backends/cpp_hart_gen/templates/db_data.cxx.erb new file mode 100644 index 0000000000..168949cfc6 --- /dev/null +++ b/backends/cpp_hart_gen/templates/db_data.cxx.erb @@ -0,0 +1,16 @@ +#include "udb/db_data.hxx" + +std::map udb::DbData::SCHEMAS = { + { "schema_defs.json", + R"SCHEMA( + <%= File.read("#{$root}/schemas/schema_defs.json") %> + )SCHEMA" + }, + <%- Dir.glob("#{$root}/schemas/config*").map do |f| -%> + { "<%= File.basename(f) %>", + R"SCHEMA( + <%= File.read(f) %> + )SCHEMA" + } + <%- end.join(", ") -%> + }; diff --git a/backends/cpp_hart_gen/templates/db_data.hxx.erb b/backends/cpp_hart_gen/templates/db_data.hxx.erb new file mode 100644 index 0000000000..96883d8441 --- /dev/null +++ b/backends/cpp_hart_gen/templates/db_data.hxx.erb @@ -0,0 +1,41 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "udb/version.hpp" + +using namespace std::literals; + +namespace udb { + class DbData { + DbData() = delete; + + public: + static std::map SCHEMAS; + + static std::vector params_for(const std::string& ext_name, const udb::Version& ext_ver) + { + <%- i = 0 -%> + <%- cfg_arch.extensions.each do |ext| -%> + <%= i.zero? ? "" : "else " %>if (ext_name == "<%= ext.name %>") { + <%- ext.versions.each do |ext_ver| -%> + if (ext_ver == udb::Version{"<%= ext_ver.version_str %>"sv}) { + return { + <%= ext_ver.params.map { |param| " \"#{param.name}\"" }.join(",\n") %> + }; + } + <%- end -%> + } + <%- i += 1 -%> + <%- end -%> + else { + throw std::runtime_error("Bad extension version"); + return {}; + } + } + }; +} diff --git a/backends/cpp_hart_gen/templates/enum.cxx.erb b/backends/cpp_hart_gen/templates/enum.cxx.erb new file mode 100644 index 0000000000..cc6f0686d8 --- /dev/null +++ b/backends/cpp_hart_gen/templates/enum.cxx.erb @@ -0,0 +1,39 @@ +// THIS FILE IS AUTOGENERATED + +#include + +#include "udb/cpp_exceptions.hpp" +#include "udb/enum.hxx" + +<%- enums = cfg_arch.global_ast.enums -%> +<%- symtab = cfg_arch.symtab -%> + +namespace udb { + <%- enums.each do |enum| -%> + <%- element_names = enum.type(symtab).element_names -%> + <%- element_values = enum.type(symtab).element_values -%> + const std::string to_s(const <%= enum.name %>& value) { + switch(value.value()) { + <%- seen = [] -%> + <%- element_values.each_index do |idx| -%> + <%- next if seen.include?(element_values[idx]) -%> + case <%= element_values[idx] %>: return "<%= element_names[idx] %>"; + <%- seen << element_values[idx] -%> + <%- end -%> + default: return "Uknown"; + } + } + + const <%= enum.name %> <%= enum.name %>::from_s(const std::string& value) { + <%- element_values.each_index do |idx| -%> + <%= idx.zero? ? "" : "else" %> if (value == "<%= element_names[idx] %>") { + return <%= element_values[idx] %>; + } + <%- end -%> + else { + throw DbError("Bad enum value"); + } + } + + <%- end -%> +} diff --git a/backends/cpp_hart_gen/templates/enum.hxx.erb b/backends/cpp_hart_gen/templates/enum.hxx.erb new file mode 100644 index 0000000000..395b036934 --- /dev/null +++ b/backends/cpp_hart_gen/templates/enum.hxx.erb @@ -0,0 +1,76 @@ +// THIS FILE IS AUTOGENERATED + +<%- enums = cfg_arch.global_ast.enums -%> +<%- symtab = cfg_arch.symtab -%> + +#pragma once + +#include +#include +#include + +namespace udb { + <%- enums.each do |enum| -%> + <%- raise "TODO: handle enum with width > 64" if enum.type(symtab).width > 64 -%> + <%- underlying_type = enum.type(symtab).width <= 32 ? "uint32_t" : "uint64_t" -%> + struct <%= enum.name %> { + using ValueType = <%= underlying_type %>; + + <%- element_names = enum.type(symtab).element_names -%> + <%- element_values = enum.type(symtab).element_values -%> + <%- element_names.each_index do |idx| -%> + static constexpr <%= underlying_type %> <%= element_names[idx] %> = <%= element_values[idx] %>; + <%- end -%> + + // constructors + constexpr <%= enum.name %>() = default; + constexpr <%= enum.name %>(const <%= underlying_type %>& value) : m_value(value) {} + + static const <%= enum.name %> from_s(const std::string& value); + + <%= underlying_type %> m_value; + <%= underlying_type %> value() const { return m_value; } + + constexpr uint32_t size() const { return <%= element_names.size %>; } + + template + constexpr operator Type() const { return m_value; } + + template + constexpr <%= enum.name %>& operator<<(const Type& rhs) { + m_value << rhs; + return *this; + } + + template + constexpr bool operator==(const Type& rhs) const { + return m_value == rhs; + } + + constexpr bool operator==(const <%= enum.name %>& rhs) const { + return m_value == rhs.m_value; + } + + constexpr bool operator!=(const <%= enum.name %>& rhs) const { + return m_value == rhs.m_value; + } + + constexpr bool operator<(const <%= enum.name %>& rhs) const { + return m_value < rhs.m_value; + } + + }; + + const std::string to_s(const <%= enum.name %>& value); + + template + constexpr Type operator<<(const Type& lhs, const <%= enum.name %>& rhs) { + return lhs << rhs.value(); + } + template + constexpr bool operator==(const Type& lhs, const <%= enum.name %>& rhs) { + return lhs == rhs.value(); + } + + <%- end -%> +} diff --git a/backends/cpp_hart_gen/templates/func_prototypes.hxx.erb b/backends/cpp_hart_gen/templates/func_prototypes.hxx.erb new file mode 100644 index 0000000000..a67a637bfe --- /dev/null +++ b/backends/cpp_hart_gen/templates/func_prototypes.hxx.erb @@ -0,0 +1,36 @@ +// THIS FILE IS AUTOGENERATED +// IT IS NOT STANDALONE. IT IS A FUNCTION LIST FOR THE Hart CLASS + +#define __UDB_CONSTEXPR_FUNC_CALL +#define __UDB_CONST_GLOBAL(global_name) <%= name_of(:hart, cfg_arch) %>::global_name +#define __UDB_MUTABLE_GLOBAL(global_name) global_name +#define __UDB_STRUCT(struct_name) <%= name_of(:cfg, cfg_arch) %>_ ## struct_name ## _Struct +#define __UDB_STATIC_PARAM(param_name) <%= name_of(:params, cfg_arch) %>::param_name ## _VALUE + +<%# need to get symtab at function scope -%> +<%- cfg_arch.reachable_functions.each do |func| -%> +<%- next if func.builtin? || func.generated? -%> +<%- symtab = cfg_arch.symtab.global_clone.push(nil) -%> + +<%- if func.name == "ary_includes?" -%> +template +static constexpr bool ary_includes_Q_(const std::array& ary, const ValueType& value); + +template +bool ary_includes_Q_(const std::vector& ary, const ValueType& value); + +<%- else -%> +<%- qualifiers = func.constexpr?(cfg_arch.symtab) ? "static constexpr" : "" -%> +// +// <%= func.description.gsub("\n", "\n// ") %> +<%= func.gen_cpp_prototype(symtab, 0, qualifiers:) %> + +<%- end -%> +<%- symtab.release -%> +<%- end -%> + +#undef __UDB_CONSTEXPR_FUNC_CALL +#undef __UDB_CONST_GLOBAL +#undef __UDB_MUTABLE_GLOBAL +#undef __UDB_STRUCT +#undef __UDB_STATIC_PARAM diff --git a/backends/cpp_hart_gen/templates/hart.hxx.erb b/backends/cpp_hart_gen/templates/hart.hxx.erb new file mode 100644 index 0000000000..c9452a580c --- /dev/null +++ b/backends/cpp_hart_gen/templates/hart.hxx.erb @@ -0,0 +1,395 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "udb/defines.hpp" + +#if !defined(JSON_ASSERT) +#define JSON_ASSERT(cond) udb_assert(cond, "JSON assert"); +#endif +#include + +#include "udb/hart.hpp" +#include "udb/cfgs/<%= cfg_arch.name %>/params.hxx" +#include "udb/cfgs/<%= cfg_arch.name %>/csr_container.hxx" +#include "udb/cfgs/<%= cfg_arch.name %>/structs.hxx" +#include "udb/enum.hxx" +#include "udb/bitfield.hxx" +#include "udb/util.hpp" +#include "udb/inst.hpp" +#include "udb/bb_cache.hpp" + +<%- hart_name = name_of(:hart, cfg_arch) -%> + +namespace udb { + template + class <%= hart_name -%>; +} + +#include "udb/cfgs/<%= cfg_arch.name %>/inst.hxx" + +namespace udb { + template + class <%= hart_name -%> : public HartBase { + <%- csrs = cfg_arch.not_prohibited_csrs -%> + <%- csrs.each do |csr| -%> + friend class <%= name_of(:csr, cfg_arch, csr.name) %>; + <%- fields = cfg_arch.fully_configured? ? csr.possible_fields(cfg_arch) : csr.fields.select { |field| field.exists_in_cfg?(cfg_arch) } -%> + <%- fields.each do |field| -%> + <%- next unless cfg_arch.possible_xlens.any? { |xlen| field.defined_in_base?(xlen) } -%> + + friend class <%= name_of(:csr_field, cfg_arch, csr.name, field.name) %>; + <%- end -%> + <%- end -%> + friend class InstBase; + <%- ilist = cfg_arch.possible_instructions -%> + <%- ilist.each do |inst| -%> + template + friend class <%= name_of(:inst, cfg_arch, inst.name) %>; + <%- end -%> + + // size (in C++ storage) of the largest instruction + static constexpr size_t __MAX_INST_CPP_SIZE = + std::max({ + <%= ilist.map do |i| + cfg_arch.possible_xlens.select do |xlen| + i.defined_in_base?(xlen) + end.map do |xlen| + "sizeof(#{name_of(:inst, cfg_arch, i.name)}<#{xlen}, SocType>)" + end + end.flatten.join(', ') %> }); + + static inline PoolAllocator::__MAX_INST_CPP_SIZE> inst_allocator; + + public: + <%- cfg_arch.globals.each do |global| -%> + <%- if global.is_a?(Idl::GlobalWithInitializationAst) -%> + <%- if global.type(cfg_arch.symtab).const? -%> + static <%= global.type(cfg_arch.symtab).const? ? 'constexpr ' : '' %><%= global.type(cfg_arch.symtab).to_cxx %> <%= global.id %> = <%= global.rhs.gen_cpp(cfg_arch.symtab, 0) %>; + <%- else -%> + static <%= global.type(cfg_arch.symtab).to_cxx %> <%= global.id %>; + <%- end -%> + <%- else -%> + static <%= global.type(cfg_arch.symtab).to_cxx %> <%= global.id %>; + <%- end -%> + <%- end -%> + + #include "udb/cfgs/<%= cfg_arch.name %>/func_prototypes.hxx" + + static constexpr unsigned MXLEN = <%= cfg_arch.mxlen.nil? ? 64 : cfg_arch.mxlen %>; + using XReg = Bits; + + <%= hart_name -%>(uint64_t hart_id, SocType& soc, const nlohmann::json& cfg) + : HartBase(hart_id, soc, cfg), + m_params(<%= name_of(:params, cfg_arch) %>(cfg)), + m_csrs(this), + m_implemented_exts(expand_implemented_extensions(cfg["implemented_extensions"])) + { + m_xregs[0].makeZeroReg(); + init_csr_map(); + } + + void reset(uint64_t reset_pc) override { + this->HartBase::reset(reset_pc); + m_csrs.reset(); + + m_pc = reset_pc; + for (auto i = 1; i < m_xregs_unknown.size(); i++) { + m_xregs_unknown.set(i); + } + + <%- cfg_arch.globals.each do |global| -%> + <%- if global.is_a?(Idl::GlobalWithInitializationAst) -%> + <%- next if global.type(cfg_arch.symtab).const? -%> + <%= global.id %> = <%= global.rhs.gen_cpp(cfg_arch.symtab, 0) %>; + <%- else -%> + // <%= global.id %> = unknown; + <%- end -%> + <%- end -%> + } + + void init_csr_map(); + + bool implemented_version_Q_(const ExtensionName& ext, const VersionRequirement& req) const override + { + auto ext_ver = this->m_implemented_exts.find(ext); + if (ext_ver == this->m_implemented_exts.end()) { + return false; + } + return req.satisfied_by(ext_ver->second); + + } + + template + bool _implemented_version_Q_() const + { + constexpr VersionRequirement req(ver_req_str.sv()); + <%- cfg_arch.transitive_prohibited_extension_versions.each do |ext_ver| -%> + if constexpr ((ext == ExtensionName::<%= ext_ver.name %>) && (req.satisfied_by({"<%= ext_ver.version_str %>"sv}))) { + return false; + } + <%- end -%> + + auto ext_ver = this->m_implemented_exts.find(ext); + if (ext_ver == this->m_implemented_exts.end()) { + return false; + } + return req.satisfied_by(ext_ver->second); + } + + bool implemented_Q_(const ExtensionName& ext) const override { + return this->m_implemented_exts.find(ext) != this->m_implemented_exts.end(); + } + + template + bool _implemented_Q_() const + { + <%- cfg_arch.extensions.each do |ext| -%> + <%- if ext.versions.all? { |ext_ver| cfg_arch.transitive_prohibited_extension_versions.include?(ext_ver) } -%> + if constexpr (ext == ExtensionName::<%= ext.name %>) { + return false; + } + <%- end -%> + <%- end -%> + + <%- if cfg_arch.fully_configured? -%> + return true; + <%- else -%> + return this->m_implemented_exts.find(ext) != this->m_implemented_exts.end(); + <%- end -%> + } + + // go through the list of implemented extensions, + // and add implications + std::map expand_implemented_extensions(const nlohmann::json& ext_ver_list) + { + std::deque> implied_exts; + std::map implemented_exts; + + for (auto ext : ext_ver_list) { + implemented_exts.emplace(ExtensionName::from_s(ext[0]), ext[1]); + } + for (auto ext : ext_ver_list) { + <%- cfg_arch.extensions.each do |ext| -%> + <%- ext.versions.each do |ext_ver| -%> + <%- unless ext_ver.implications.empty? -%> + if ((ext[0] == "<%= ext_ver.name %>") && (Version{ext[1]} == Version{"<%= ext_ver.version_str %>"sv})) { + <%- ext_ver.implications.each do |implied_ext_ver_and_cond| -%> + <%- unless implied_ext_ver_and_cond[:cond].empty? -%> + auto implemented = [this](const std::string_view& ext_name, const std::string_view& req) -> bool { + for (const auto& pair : implemented_ext) { + if ((pair.first == ExtensionName::from_s(ext_name)) && (VersionRequirement(req).satisfied_by(Version(pair.second)))) { + return true; + } + } + return false; + }; + if (<%= implied_ext_ver_and_cond[:cond].to_cxx { |ext_name, req| "implemented(\"#{ext_name}\"sv, \"#{req}\"sv)" } %>) { + implied_exts.push_back({ExtensionName::<%= implied_ext_ver_and_cond[:ext_ver].name %>, Version{"<%= implied_ext_ver_and_cond[:ext_ver].version_str %>"sv}}); + } + <%- else -%> + implied_exts.push_back({ExtensionName::<%= implied_ext_ver_and_cond[:ext_ver].name %>, Version{"<%= implied_ext_ver_and_cond[:ext_ver].version_str %>"sv}}); + <%- end -%> + <%- end -%> + } + <%- end -%> + <%- end -%> + <%- end -%> + } + + while (!implied_exts.empty()) { + auto implied_ext = implied_exts.front(); + implemented_exts.emplace(std::get<0>(implied_ext), std::get<1>(implied_ext)); + <%- cfg_arch.extensions.each do |ext| -%> + <%- ext.versions.each do |ext_ver| -%> + <%- unless ext_ver.implications.empty? -%> + if ((std::get<0>(implied_ext) == ExtensionName::<%= ext_ver.name %>) && (std::get<1>(implied_ext) == Version{"<%= ext_ver.version_str %>"sv})) { + <%- ext_ver.implications.each do |implied_ext_ver| -%> + implied_exts.push_back({ExtensionName::<%= ext_ver.name %>, Version{"<%= ext_ver.version_str %>"sv}}); + <%- end -%> + } + <%- end -%> + <%- end -%> + <%- end -%> + implied_exts.pop_front(); + } + + return implemented_exts; + } + + bool implemented_csr_Q_(const Bits<12>& csr_addr) { + return m_csr_addr_map.count(csr_addr) == 1; + } + + + + void set_pc(uint64_t new_pc) override { + m_pc = new_pc; + } + + void set_next_pc(uint64_t next_pc) override { + m_next_pc = next_pc; + } + + uint64_t pc() const override { return m_pc; } + + void advance_pc() override { + m_pc = m_next_pc; + } + + unsigned mxlen() override { return MXLEN; } + + uint64_t xreg(unsigned num) const override { + if (num >= 32) { + throw std::out_of_range("X register indicies are 0 - 31, inclusive"); + } + return _xreg(num); + } + + Bits _xreg(unsigned num) const { + if (m_xregs_unknown[num] == true) { + throw UndefinedValueError("X register value is uninitialized"); + } + return m_xregs[num].get(); + } + + // XRegister& xregRef(unsigned num) { return m_xregs[num]; } + + void set_xreg(unsigned num, uint64_t value) override { + if (num >= 32) { + throw std::out_of_range("X register indicies are 0 - 31, inclusive"); + } + _set_xreg(num, value); + } + + template + void _set_xreg(const IdxType& num, const Bits& value) { + if (num != 0) { + m_xregs_unknown[num] = false; + m_xregs[static_cast(num)] = value; + } + } + + void printState(FILE* out = stdout) const override; + + CsrBase* csr(unsigned address) override { + auto it = m_csr_addr_map.find(address); + if (it == m_csr_addr_map.end()) { + return nullptr; + } + return it->second; + } + + const CsrBase* csr(unsigned address) const override { + auto it = m_csr_addr_map.find(address); + if (it == m_csr_addr_map.end()) { + return nullptr; + } + return it->second; + } + + CsrBase* csr(const std::string& name) override { + auto it = m_csr_name_map.find(name); + if (it == m_csr_name_map.end()) { + return nullptr; + } + return it->second; + } + + const CsrBase* csr(const std::string& name) const override { + auto it = m_csr_name_map.find(name); + if (it == m_csr_name_map.end()) { + return nullptr; + } + return it->second; + } + + const <%= name_of(:params, cfg_arch) %>& params() const { + return m_params; + } + + InstBase* _decode(const XReg& pc, const Bits<<%= cfg_arch.largest_encoding %>>& encoding) { + InstBase* inst = inst_allocator.allocate(); + if (_decode(pc, encoding, inst) == false) { + inst_allocator.free(inst); + return nullptr; + } else { + return inst; + } + } + bool _decode(const XReg& pc, const Bits<<%= cfg_arch.largest_encoding %>>& encoding, InstBase* obj); + + uint64_t fetch() override { return _fetch(); } + Bits _fetch(); + + <%= name_of(:struct, cfg_arch, "CachedTranslationResult") %> cached_translation(const XReg& vaddr, const MemoryOperation& op) const { + return <%= name_of(:struct, cfg_arch, "CachedTranslationResult") %>{}; + } + + <%= name_of(:csr_container, cfg_arch) %>& _csrContainer() { return m_csrs; } + + int run_one() override { return _run_one(); } + int _run_one(); + + int run_bb() override { return _run_bb(); } + int _run_bb(); + + int run_n(uint64_t n) override { return _run_n(n); } + int _run_n(uint64_t n); + + // external interrupt interface + void set_mmode_ext_int() { + m_csrs.mip.MEIP()._hw_write(1); + refresh_pending_interrupts(); + } + void clear_mmode_ext_int() { + m_csrs.mip.MEIP()._hw_write(0); + refresh_pending_interrupts(); + } + void set_smode_ext_int() { + pending_smode_external_interrupt = true; + refresh_pending_interrupts(); + } + void clear_smode_ext_int() { + pending_smode_external_interrupt = false; + refresh_pending_interrupts(); + } + // void set_vsmode_ext_int() { + // m_csrs.hvip.MEIP = 0; + // pending_vsmode_external_interrupt = true; + // refresh_pending_interrupts(); + // } + // void clear_vsmode_ext_int() { + // m_csrs.mip.MEIP = 0; + // pending_vsmode_external_interrupt = false; + // refresh_pending_interrupts(); + // } + + private: + XReg m_pc; + XReg m_next_pc; + InstBase* m_cur_inst; + std::array, 32> m_xregs; + std::bitset<32> m_xregs_unknown; + + <%= name_of(:params, cfg_arch) %> m_params; + <%= name_of(:csr_container, cfg_arch) %> m_csrs; + std::unordered_map, CsrBase*> m_csr_addr_map; + std::map m_csr_name_map; + + std::array m_run_one_inst_storage; + BasicBlockCache<__MAX_INST_CPP_SIZE> m_bb_cache; + + const std::map m_implemented_exts; + }; +} + +#include "udb/cfgs/<%= cfg_arch.name %>/hart_impl.hxx" + +#include "udb/cfgs/<%= cfg_arch.name %>/idl_funcs_impl.hxx" + +#include "udb/cfgs/<%= cfg_arch.name %>/inst_impl.hxx" diff --git a/backends/cpp_hart_gen/templates/hart_factory.hxx.erb b/backends/cpp_hart_gen/templates/hart_factory.hxx.erb new file mode 100644 index 0000000000..fd2e416e1a --- /dev/null +++ b/backends/cpp_hart_gen/templates/hart_factory.hxx.erb @@ -0,0 +1,125 @@ + +#include "udb/defines.hpp" + +#include "udb/config_validator.hpp" + +#include + +<%- cfg_list = ENV["CONFIG"].split(",").map(&:strip) -%> + +<%- cfg_list.each do |cfg| -%> +#include "udb/cfgs/<%= cfg %>/hart.hxx" +<%- end -%> + +namespace udb { + template + class RiscvTestsTracer : public udb::AbstractTracer + { + public: + class Pass : public udb::ExitEvent + { + public: + Pass() : udb::ExitEvent(0) {} + + const char* what() const noexcept override { + return "Pass"; + } + }; + + class Fail : public udb::ExitEvent + { + public: + Fail(uint64_t testnum) : udb::ExitEvent(-1), m_testnum(testnum) {} + + const char* what() const noexcept override { + return strdup(fmt::format("Test #{} failed", m_testnum).c_str()); + } + + private: + uint64_t m_testnum; + }; + + public: + RiscvTestsTracer(HartBase* hart) + : udb::AbstractTracer(), + m_hart(dynamic_cast(hart)) + {} + + void trace_exception() override { + <%- if cfg_arch.multi_xlen? && cfg_arch.csr("mcause").format_changes_with_xlen? -%> + auto cause = m_hart->_csrContainer().mcause._hw_read(m_hart->xlen()); + <%- else -%> + auto cause = m_hart->_csrContainer().mcause._hw_read(); + <%- end -%> + if (cause == udb::ExceptionCode::Mcall || cause == udb::ExceptionCode::Scall || cause == udb::ExceptionCode::Ucall) { + auto a7 = m_hart->_xreg(17); + if (a7 == 93) { + auto gp = m_hart->_xreg(3); + if (gp == 1) { + throw Pass(); + } else { + auto testnum = m_hart->_xreg(10) >> 1; + throw Fail(testnum); + } + } + } + } + + private: + HART_TYPE* m_hart; + }; + + class HartFactory { + HartFactory() = delete; + + public: + static constexpr std::array> configs() { + return { <%= cfg_list.map { |c| "\"#{c}\"" }.join(", ") %> }; + } + + template + static HartBase* create(const std::string& config_name, uint64_t hart_id, const std::filesystem::path& cfg_path, SocType& soc) + { + auto yaml = YAML::LoadFile(cfg_path.string()); + nlohmann::json json = ConfigValidator::validate(yaml); + + <%- cfg_list.each do |config| -%> + if (config_name == "<%= config %>") { + return new <%= name_of(:hart, config) %>(hart_id, soc, json); + } + <%- end %> + + // bad config name + fmt::print("'{}' is not a valid config name\n", config_name); + exit(1); + } + + template + static HartBase* create(const std::string& config_name, uint64_t hart_id, const std::string& cfg_yaml, SocType& soc) + { + auto yaml = YAML::Load(cfg_yaml); + nlohmann::json json = ConfigValidator::validate(yaml); + + <%- cfg_list.each do |config| -%> + if (config_name == "<%= config %>") { + return new <%= name_of(:hart, config) %>(hart_id, soc, json); + } + <%- end %> + + // bad config name + fmt::print("'{}' is not a valid config name\n", config_name); + exit(1); + } + + template + static AbstractTracer* create_tracer(const std::string& tracer_name, const std::string& config_name, HartBase* hart) + { + <%- cfg_list.each do |config| -%> + if (config_name == "<%= config %>") { + return new RiscvTestsTracer<<%= name_of(:hart, config) %>, SocType>(hart); + } + <%- end %> + + } + }; +} diff --git a/backends/cpp_hart_gen/templates/hart_impl.hxx.erb b/backends/cpp_hart_gen/templates/hart_impl.hxx.erb new file mode 100644 index 0000000000..72207eb8dc --- /dev/null +++ b/backends/cpp_hart_gen/templates/hart_impl.hxx.erb @@ -0,0 +1,257 @@ +#pragma once + +#include + +#include + +#include "udb/inst.hpp" +#include "udb/cfgs/<%= cfg_arch.name %>/inst.hxx" +#include "udb/hart.hpp" + +using namespace std::literals; + +namespace udb { + + <%- cfg_arch.globals.each do |global| -%> + <%- next if global.type(cfg_arch.symtab).const? -%> + template + <%= global.type(cfg_arch.symtab).to_cxx %> <%= name_of(:hart, cfg_arch) %>::<%= global.id %>; + <%- end -%> + + template + void udb::<%= name_of(:hart, cfg_arch) %>::printState(FILE* out) const { + fmt::print(out, "Hart %u:\n", this->m_hart_id); + if constexpr (sizeof(XReg) == 8) { + fmt::print(out, "PC: {:#18x}\n", m_pc); + for (int i=0; i<16; i++) { + fmt::print(out, "x{:2}: {:#18x}\tx{:2}: {:#18x}\n", i, m_xregs[i], i + 16, m_xregs[16 + 1]); + } + } else if constexpr (sizeof(XReg) == 4) { + fmt::print(out, "PC: {:#10x}\n", m_pc); + for (int i=0; i<16; i++) { + fmt::print(out, "x{:2}: {:#10x}\tx{:2}: {:#10x}\n", i, m_xregs[i], i + 16, m_xregs[16 + 1]); + } + } else { + udb_assert(false, "unsupported xlen"); + } + } + + template + void udb::<%= name_of(:hart, cfg_arch) %>::init_csr_map() + { + <%- cfg_arch.not_prohibited_csrs.each do |csr| -%> + <%- unless csr.address.nil? -%> + m_csr_addr_map[<%= csr.address %>] = &m_csrs.<%= csr.name %>; + <%- end -%> + m_csr_name_map["<%= csr.name %>"] = &m_csrs.<%= csr.name %>; + <%- end -%> + } + +#define __UDB_CONST_GLOBAL(global_name) <%= name_of(:hart, cfg_arch) %>::global_name +#define __UDB_MUTABLE_GLOBAL(global_name) this->global_name +#define __UDB_FUNC_CALL this-> +#define __UDB_PC this->m_pc + + + template + Bits<<%= name_of(:hart, cfg_arch) %>::INSTR_ENC_SIZE> <%= name_of(:hart, cfg_arch) %>::_fetch() + { + <%- symtab = cfg_arch.symtab.global_clone -%> + <%- symtab.push(cfg_arch.fetch) -%> + <%= cfg_arch.fetch.body.prune(symtab).gen_cpp(symtab, 4) %> + <%- symtab.release -%> + } + + template + bool <%= name_of(:hart, cfg_arch) %>::_decode( + const XReg& pc, + const Bits<<%= cfg_arch.largest_encoding %>>& encoding, + InstBase* inst + ) { + <%- if cfg_arch.multi_xlen? -%> + if (xlen() == 32) { + <%= DecodeGen.new(cfg_arch).generate(cfg_arch.possible_instructions.select { |i| i.rv32? }, 32, indent: 4) %> + } else { + <%= DecodeGen.new(cfg_arch).generate(cfg_arch.possible_instructions.select { |i| i.rv64? }, 64, indent: 4) %> + } + <%- else # just need a single decode -%> + <%= DecodeGen.new(cfg_arch).generate(cfg_arch.possible_instructions, cfg_arch.possible_xlens[0], indent: 2) %> + <%- end -%> + + // fall through: no instruction found + return false; + } + + template + int <%= name_of(:hart, cfg_arch) %>::_run_one() + { + Bits enc; + try { + enc = _fetch(); + } catch(const AbortInstruction& e) { + return StopReason::Exception; + } + InstBase* inst = reinterpret_cast(m_run_one_inst_storage.data()); + if (_decode(m_pc, enc, inst) == false) { + try { + raise(ExceptionCode::IllegalInstruction, mode(), m_params.REPORT_ENCODING_IN_MTVAL_ON_ILLEGAL_INSTRUCTION ? static_cast(enc) : 0ull); + } catch (const AbortInstruction& e) { + return StopReason::Exception; + } + } + + // set the fall-through next pc + m_next_pc = m_pc + inst->enc_len(); + + try { + inst->execute(); + } catch (const udb::WfiException& e) { + std::destroy_at(inst); + advance_pc(); + return StopReason::Wfi; + } catch (const udb::PauseException& e) { + std::destroy_at(inst); + advance_pc(); + return StopReason::Pause; + } catch (const udb::UnpredictableBehaviorException& e) { + std::destroy_at(inst); + advance_pc(); + return StopReason::UnpredictableBehavior; + } catch (const udb::ExitEvent& e) { + this->m_exit_code = e.code(); + this->m_exit_reason = e.what(); + advance_pc(); + std::destroy_at(inst); + if (e.code() == 0) { + return StopReason::ExitSuccess; + } else { + return StopReason::ExitFailure; + } + } + advance_pc(); + + return StopReason::InstLimitReached; + } + + template + int <%= name_of(:hart, cfg_arch) %>::_run_bb() + { + auto current_bb = m_bb_cache.get(m_pc); + InstBase* inst; + + try { + if (current_bb->start_pc() == m_pc) { + current_bb->reset(); // hit, reset to the top of the basic block + + // now execute the entire bb + const auto bb_size = current_bb->size(); + + for (unsigned b = 0; b < bb_size; b++) { + inst = current_bb->pop(); + + // set the fall-through next pc + m_next_pc = m_pc + inst->enc_len(); + + inst->execute(); + if (this->m_exit_requested) { + this->m_exit_requested = false; // reset the request + break; + } + advance_pc(); + } + } else { + // miss, need to create the bb + current_bb->recycle(m_pc); + do { + Bits enc; + enc = _fetch(); + + inst = current_bb->alloc_inst(); + if (_decode(m_pc, enc, inst) == false) { + raise(ExceptionCode::IllegalInstruction, mode(), m_params.REPORT_ENCODING_IN_MTVAL_ON_ILLEGAL_INSTRUCTION ? static_cast(enc) : 0ull); + } + + fmt::print("PC {:x} {}\n", m_pc, inst->disassemble()); + for (auto r : inst->srcRegs()) { + fmt::print("R {} {:x}\n", r.to_string(), m_xregs[r.get_num()]); + } + + // set the fall-through next pc + m_next_pc = m_pc + inst->enc_len(); + + inst->execute(); + + for (auto r : inst->dstRegs()) { + fmt::print("R= {} {:x}\n", r.to_string(), m_xregs[r.get_num()]); + } + + if (this->m_exit_requested) { + this->m_exit_requested = false; // reset the request + break; + } + + advance_pc(); + } while (!(current_bb->full() || inst->control_flow())); + } + } catch (const AbortInstruction& e) { + current_bb->invalidate(); + advance_pc(); + return StopReason::Exception; + } catch (const udb::WfiException& e) { + current_bb->invalidate(); + advance_pc(); + return StopReason::Wfi; + } catch (const udb::PauseException& e) { + current_bb->invalidate(); + advance_pc(); + return StopReason::Pause; + } catch (const udb::UnpredictableBehaviorException& e) { + current_bb->invalidate(); + advance_pc(); + return StopReason::UnpredictableBehavior; + } catch (const udb::ExitEvent& e) { + current_bb->invalidate(); + advance_pc(); + this->m_exit_code = e.code(); + this->m_exit_reason = e.what(); + if (e.code() == 0) { + return StopReason::ExitSuccess; + } else { + return StopReason::ExitFailure; + } + } + + return StopReason::InstLimitReached; + } + + + template + int <%= name_of(:hart, cfg_arch) %>::_run_n(uint64_t n) + { + while (n > 0) { + if (n >= decltype(m_bb_cache)::MAX_BASIC_BLOCK_SIZE) { + // safe to execute a bb + auto before = this->m_num_inst_exec; + auto reason = _run_bb(); + if (reason != StopReason::InstLimitReached) { + return reason; + } + n -= (this->m_num_inst_exec - before); + } else { + auto reason = _run_one(); + if (reason != StopReason::InstLimitReached) { + return reason; + } + n--; + } + } + + return StopReason::InstLimitReached; + } + + +#undef __UDB_CONST_GLOBAL +#undef __UDB_MUTABLE_GLOBAL +#undef __UDB_FUNC_CALL +#undef __UDB_PC +} diff --git a/backends/cpp_hart_gen/templates/idl_funcs_impl.hxx.erb b/backends/cpp_hart_gen/templates/idl_funcs_impl.hxx.erb new file mode 100644 index 0000000000..c584bbd4e1 --- /dev/null +++ b/backends/cpp_hart_gen/templates/idl_funcs_impl.hxx.erb @@ -0,0 +1,88 @@ +#pragma once + +// THIS FILE IS AUTOGENERATED +// IT IS NOT STANDALONE. IT IS A FUNCTION LIST FOR THE Hart CLASS + +// This contains all IDL functions that either templates or constexpr +// Since either needs to be inlined, they must go in the hart header rather than a cxx file + +#include "udb/hart.hpp" + +#define __UDB_CONSTEXPR_FUNC_CALL <%= name_of(:hart, cfg_arch) %>:: +#define __UDB_CONST_GLOBAL(global_name) <%= name_of(:hart, cfg_arch) %>::global_name +#define __UDB_MUTABLE_GLOBAL(global_name) global_name +#define __UDB_STRUCT(struct_name) <%= name_of(:cfg, cfg_arch) %>_ ## struct_name ## _Struct +#define __UDB_STATIC_PARAM(param_name) <%= name_of(:params, cfg_arch) %>::param_name ## _VALUE +#define __UDB_RUNTIME_PARAM(param_name) m_params.param_name +#define __UDB_CSR_BY_NAME(csr_name) m_csrs.csr_name +#define __UDB_CSR_BY_ADDR(csr_addr) (*csr(csr_addr)) +#define __UDB_FUNC_CALL this-> +#define __UDB_PC m_pc +#define __UDB_SET_PC(new_pc) m_next_pc = new_pc +#define __UDB_XLEN xlen() + +namespace udb { + +<%# need to get symtab at function scope -%> +<%- cfg_arch.reachable_functions.each do |func| -%> +<%- next if func.builtin? || func.generated? -%> +<%- next unless func.templated? || func.constexpr?(cfg_arch.symtab) # non-templated functions come next -%> +<%- symtab = cfg_arch.symtab.global_clone.push(func) -%> + +<%- if func.name == "ary_includes?" -%> +template +template +constexpr bool <%= name_of(:hart, cfg_arch) %>::ary_includes_Q_(const std::array& ary, const ValueType& value) { + return std::ranges::find(ary, static_cast(value)) != std::ranges::end(ary); +} +template +template +bool <%= name_of(:hart, cfg_arch) %>::ary_includes_Q_(const std::vector& ary, const ValueType& value) { + return std::ranges::find(ary, static_cast(value)) != std::ranges::end(ary); +} +<%- else -%> +// +// <%= func.description.gsub("\n", "\n// ") %> +template +<%= func.gen_cpp_prototype(symtab, 0, include_semi: false, qualifiers: func.constexpr?(cfg_arch.symtab) ? "constexpr" : "", cpp_class: "#{name_of(:hart, cfg_arch)}") %> { + <%- func.apply_template_and_arg_syms(symtab) -%> + <%- pruned_func = func.prune(symtab).freeze_tree(symtab) -%> + <%= pruned_func.body.gen_cpp(symtab) %> +} +<%- end -%> +<%- symtab.release -%> +<%- end -%> + + +<%- cfg_arch.reachable_functions.each do |func| -%> +<%- next if func.builtin? || func.generated? -%> +<%- next if func.constexpr?(cfg_arch.symtab) # constexpr funcs are class methods -%> +<%- next if func.templated? # templated functions have to go in idl_funcs_impl.hxx -%> +<%- symtab = cfg_arch.symtab.global_clone.push(nil) -%> + +// +// <%= func.description.gsub("\n", "\n// ") %> +template +<%= func.gen_cpp_prototype(symtab, 0, include_semi: false, cpp_class: "#{name_of(:hart, cfg_arch)}") %> { + <%- func.apply_template_and_arg_syms(symtab) -%> + <%- pruned_func = func.prune(symtab).freeze_tree(symtab) -%> + <%= pruned_func.body.gen_cpp(symtab) %> +} + +<%- symtab.release -%> +<%- end -%> + +} // namespace udb + +#undef __UDB_CONSTEXPR_FUNC_CALL +#undef __UDB_CONST_GLOBAL +#undef __UDB_MUTABLE_GLOBAL +#undef __UDB_STRUCT +#undef __UDB_STATIC_PARAM +#undef __UDB_RUNTIME_PARAM +#undef __UDB_CSR_BY_NAME +#undef __UDB_CSR_BY_ADDR +#undef __UDB_FUNC_CALL +#undef __UDB_PC +#undef __UDB_SET_PC +#undef __UDB_XLEN diff --git a/backends/cpp_hart_gen/templates/inst.hxx.erb b/backends/cpp_hart_gen/templates/inst.hxx.erb new file mode 100644 index 0000000000..b3b42c9c87 --- /dev/null +++ b/backends/cpp_hart_gen/templates/inst.hxx.erb @@ -0,0 +1,315 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "udb/cpp_exceptions.hpp" +#include "udb/cfgs/<%= cfg_arch.name %>/structs.hxx" + +#ifdef assert +#undef assert +#endif + +<%- ilist = cfg_arch.possible_instructions -%> + +<% + def extract_decode(dv,inst) + ops = [] + so_far = 0 + dv.bits.each do |b| + if b.is_a?(Integer) + op = "extract<#{b}, 1, #{inst.encoding_width}>(m_encoding)" + ops << op + so_far += 1 + elsif b.is_a?(Range) + op = "extract<#{b.first}, #{b.size}, #{inst.encoding_width}>(m_encoding)" + ops << op + so_far += b.size + end + end + ops << "Bits<#{dv.left_shift}>{0}" unless dv.left_shift.zero? + ops = + if ops.size > 1 + "concat(#{ops.join(', ')})" + else + ops[0] + end + ops + end +-%> + +namespace udb { + +#define __UDB_FUNC_CALL m_parent-> +#define __UDB_CSR_BY_ADDR(addr) (*(m_parent->csr(addr))) +#define __UDB_CSR_BY_NAME(csr_name) m_parent->m_csrs.csr_name +#define __UDB_ENCODING this->encoding() +#define __UDB_RUNTIME_PARAM(field) m_parent->params().field +#define __UDB_STATIC_PARAM(param_name) <%= name_of(:params, cfg_arch) %>::param_name ## _VALUE +#define __UDB_STRUCT(type) <%= cfg_arch.name %> ## type ##_Struct +#define __UDB_SET_PC(new_pc) m_parent->set_next_pc(new_pc) +#define __UDB_PC m_parent->m_pc +#define __UDB_MUTABLE_GLOBAL(x) m_parent->x +#define __UDB_CONSTEXPR_FUNC_CALL <%= name_of(:hart, cfg_arch)%>:: +#define __UDB_CONST_GLOBAL(F) <%= name_of(:hart, cfg_arch)%>:: F +#define __UDB_XLEN m_parent->xlen() +#define __UDB_HART m_parent + + <%- ilist.each do |inst| -%> + <%- needs_rv32 = inst.rv32? && cfg_arch.possible_xlens.include?(32) -%> + <%- needs_rv64 = inst.rv64? && cfg_arch.possible_xlens.include?(64) -%> + template + class <%= name_of(:inst, cfg_arch, inst.name) %> : public InstBase { + + + // normal allocation is disallowed (pool allocator) + void* operator new(size_t size) = delete; + + public: + // we use placement allocation via the pool allocator + void * operator new (std::size_t, void * p) throw() { return p ; } + + void operator delete(void* ptr); + + public: + using XReg = Bits<<%= cfg_arch.possible_xlens.max %>>; + <%= name_of(:inst, cfg_arch, inst.name) %>(<%= name_of(:hart, cfg_arch) %>* parent, XReg pc, uint64_t encoding) + : InstBase(pc, encoding), + m_parent(parent) + { + } + + virtual ~<%= name_of(:inst, cfg_arch, inst.name) %>() {} + + <%= name_of(:hart, cfg_arch) %>* parent() { return m_parent; } + + size_t enc_len() const override { return <%= inst.encoding_width / 8 %>; } + bool control_flow() const override { + <%- if inst.operation_ast.nil? -%> + return false; + <%- else -%> + <%- if !inst.base.nil? -%> + <%- pruned_ast = inst.pruned_operation_ast(inst.base) -%> + return <%= pruned_ast.control_flow?(cfg_arch.symtab) %>; + <%- elsif !cfg_arch.multi_xlen? -%> + <%- pruned_ast = inst.pruned_operation_ast(cfg_arch.possible_xlens[0]) -%> + return <%= pruned_ast.control_flow?(cfg_arch.symtab) %>; + <%- else -%> + if (m_parent->xlen() == 32) { + <%- pruned_ast = inst.pruned_operation_ast(32) -%> + return <%= pruned_ast.control_flow?(cfg_arch.symtab) %>; + } else { + <%- pruned_ast = inst.pruned_operation_ast(64) -%> + return <%= pruned_ast.control_flow?(cfg_arch.symtab) %>; + } + <%- end -%> + <%- end -%> + } + + // + // Decode variables + // + <%- if needs_rv32 && needs_rv64 -%> + <%- inst.decode_variables(32).each do |dv| -%> + template + Bits<<%= dv.size %>> <%= dv.name %>() const requires (_XLEN==32) { + return <%= extract_decode(dv,inst) %>; + } + <%- end -%> + <%- inst.decode_variables(64).each do |dv| -%> + template + Bits<<%= dv.size %>> <%= dv.name %>() const requires (_XLEN==64) { + return <%= extract_decode(dv,inst) %>; + } + <%- end -%> + <%- elsif needs_rv32 -%> + <%- inst.decode_variables(32).each do |dv| -%> + Bits<<%= dv.size %>> <%= dv.name %>() const { + return <%= extract_decode(dv,inst) %>; + } + <%- end -%> + <%- else -%> + <%- inst.decode_variables(64).each do |dv| -%> + Bits<<%= dv.size %>> <%= dv.name %>() const { + return <%= extract_decode(dv,inst) %>; + } + <%- end -%> + <%- end -%> + + void execute() override { + + <%- if inst.operation_ast.nil? -%> + m_parent->assert(false, "There is no operation() defined for this instruction"); + <%- else -%> + <%- if !inst.base.nil? -%> + <%- pruned_ast = inst.pruned_operation_ast(inst.base) -%> + <%- symtab = inst.fill_symtab(inst.base, pruned_ast) -%> + <%= pruned_ast.gen_cpp(symtab, 6) %> + <%- symtab.release -%> + <%- elsif !cfg_arch.multi_xlen? -%> + <%- pruned_ast = inst.pruned_operation_ast(cfg_arch.possible_xlens[0]) -%> + <%- symtab = inst.fill_symtab(cfg_arch.possible_xlens[0], pruned_ast) -%> + <%= pruned_ast.gen_cpp(symtab, 6) %> + <%- symtab.release -%> + <%- else -%> + if (m_parent->xlen() == 32) { + <%- pruned_ast = inst.pruned_operation_ast(32) -%> + <%- symtab = inst.fill_symtab(32, pruned_ast) -%> + <%= pruned_ast.gen_cpp(symtab, 8) %> + <%- symtab.release -%> + } else { + <%- pruned_ast = inst.pruned_operation_ast(64) -%> + <%- symtab = inst.fill_symtab(64, pruned_ast) -%> + <%= pruned_ast.gen_cpp(symtab, 8) %> + <%- symtab.release -%> + } + <%- end -%> + <%- end -%> + } + + constexpr static std::string_view m_name = "<%= inst.name %>"; + const std::string_view& name() override { return m_name; } + std::string disassemble(bool use_abi_reg_names = 0) const override { + if constexpr (XLEN == 32) { + <%- if inst.defined_in_base?(32) -%> + return fmt::format("{} <%= inst.assembly_fmt(32) %>", m_name <%= inst.assembly_fmt_args(32) %>); + <%- else -%> + udb_assert(false, "Not defined"); + __builtin_unreachable(); + <%- end -%> + } else { + <%- if inst.defined_in_base?(32) -%> + return fmt::format("{} <%= inst.assembly_fmt(64) %>", m_name <%= inst.assembly_fmt_args(64) %>); + <%- else -%> + udb_assert(false, "Not defined"); + __builtin_unreachable(); + <%- end -%> + } + } + std::vector srcRegs() const override { + <%- if inst.operation_ast.nil? -%> + m_parent->assert(false, "There is no operation() defined for this instruction"); + __builtin_unreachable(); + <%- else -%> + <%- src_regs = nil -%> + <%- if !inst.base.nil? -%> + <%- pruned_ast = inst.pruned_operation_ast(inst.base) -%> + <%- symtab = inst.fill_symtab(cfg_arch.possible_xlens[0], pruned_ast) -%> + <%- begin -%> + <%- src_regs = pruned_ast.find_src_registers(symtab) %> + <%- symtab.release -%> + return {<%= src_regs.map { |r| r.is_a?(Integer) ? "{Reg::X#{r}}" : "{#{r}}" }.join(", ") %>}; + <%- rescue Idl::ComplexRegDetermination -%> + throw ComplexRegDetermination(); + <%- end -%> + <%- elsif !cfg_arch.multi_xlen? -%> + <%- pruned_ast = inst.pruned_operation_ast(cfg_arch.possible_xlens[0]) -%> + <%- symtab = inst.fill_symtab(cfg_arch.possible_xlens[0], pruned_ast) -%> + <%- begin -%> + <%- src_regs = pruned_ast.find_src_registers(symtab) %> + <%- symtab.release -%> + return {<%= src_regs.map { |r| r.is_a?(Integer) ? "{Reg::X#{r}}" : "{#{r}}" }.join(", ") %>}; + <%- rescue Idl::ComplexRegDetermination -%> + throw ComplexRegDetermination(); + <%- end -%> + <%- else -%> + if (m_parent->xlen() == 32) { + <%- pruned_ast = inst.pruned_operation_ast(32) -%> + <%- symtab = inst.fill_symtab(32, pruned_ast) -%> + <%- begin -%> + <%- src_regs = pruned_ast.find_src_registers(symtab) %> + <%- symtab.release -%> + return {<%= src_regs.map { |r| r.is_a?(Integer) ? "{Reg::X#{r}}" : "{#{r}}" }.join(", ") %>}; + <%- rescue Idl::ComplexRegDetermination -%> + throw ComplexRegDetermination(); + <%- end -%> + } else { + <%- pruned_ast = inst.pruned_operation_ast(64) -%> + <%- symtab = inst.fill_symtab(64, pruned_ast) -%> + <%- begin -%> + <%- src_regs = pruned_ast.find_src_registers(symtab) %> + <%- symtab.release -%> + return {<%= src_regs.map { |r| r.is_a?(Integer) ? "{Reg::X#{r}}" : "{#{r}}" }.join(", ") %>}; + <%- rescue Idl::ComplexRegDetermination -%> + throw ComplexRegDetermination(); + <%- end -%> + } + <%- end -%> + <%- end -%> + } + + std::vector dstRegs() const override { + <%- if inst.operation_ast.nil? -%> + m_parent->assert(false, "There is no operation() defined for this instruction"); + __builtin_unreachable(); + <%- else -%> + <%- dst_regs = nil -%> + <%- if !inst.base.nil? -%> + <%- pruned_ast = inst.pruned_operation_ast(inst.base) -%> + <%- symtab = inst.fill_symtab(cfg_arch.possible_xlens[0], pruned_ast) -%> + <%- begin -%> + <%- dst_regs = pruned_ast.find_dst_registers(symtab) %> + <%- symtab.release -%> + return {<%= dst_regs.map { |r| r.is_a?(Integer) ? "{Reg::X#{r}}" : "{#{r}}" }.join(", ") %>}; + <%- rescue Idl::ComplexRegDetermination -%> + throw ComplexRegDetermination(); + <%- end -%> + <%- elsif !cfg_arch.multi_xlen? -%> + <%- pruned_ast = inst.pruned_operation_ast(cfg_arch.possible_xlens[0]) -%> + <%- symtab = inst.fill_symtab(cfg_arch.possible_xlens[0], pruned_ast) -%> + <%- begin -%> + <%- dst_regs = pruned_ast.find_dst_registers(symtab) %> + <%- symtab.release -%> + return {<%= dst_regs.map { |r| r.is_a?(Integer) ? "{Reg::X#{r}}" : "{#{r}}" }.join(", ") %>}; + <%- rescue Idl::ComplexRegDetermination -%> + throw ComplexRegDetermination(); + <%- end -%> + <%- else -%> + if (m_parent->xlen() == 32) { + <%- pruned_ast = inst.pruned_operation_ast(32) -%> + <%- symtab = inst.fill_symtab(32, pruned_ast) -%> + <%- begin -%> + <%- dst_regs = pruned_ast.find_dst_registers(symtab) %> + <%- symtab.release -%> + return {<%= dst_regs.map { |r| r.is_a?(Integer) ? "{Reg::X#{r}}" : "{#{r}}" }.join(", ") %>}; + <%- rescue Idl::ComplexRegDetermination -%> + throw ComplexRegDetermination(); + <%- end -%> + } else { + <%- pruned_ast = inst.pruned_operation_ast(64) -%> + <%- symtab = inst.fill_symtab(64, pruned_ast) -%> + <%- begin -%> + <%- dst_regs = pruned_ast.find_dst_registers(symtab) %> + <%- symtab.release -%> + return {<%= dst_regs.map { |r| r.is_a?(Integer) ? "{Reg::X#{r}}" : "{#{r}}" }.join(", ") %>}; + <%- rescue Idl::ComplexRegDetermination -%> + throw ComplexRegDetermination(); + <%- end -%> + } + <%- end -%> + <%- end -%> + } + + private: + <%= name_of(:hart, cfg_arch) %> * const m_parent; + }; + <%- end -%> + +#undef __UDB_FUNC_CALL +#undef __UDB_CSR_BY_ADDR +#undef __UDB_CSR_BY_NAME +#undef __UDB_ENCODING +#undef __UDB_RUNTIME_PARAM +#undef __UDB_STATIC_PARAM +#undef __UDB_STRUCT +#undef __UDB_PC +#undef __UDB_MUTABLE_GLOBAL +#undef __UDB_CONSTEXPR_FUNC_CALL +#undef __UDB_SET_PC +#undef __UDB_CONST_GLOBAL +#undef __UDB_XLEN +#undef __UDB_HART +} diff --git a/backends/cpp_hart_gen/templates/inst_impl.hxx.erb b/backends/cpp_hart_gen/templates/inst_impl.hxx.erb new file mode 100644 index 0000000000..bc09a5c0e7 --- /dev/null +++ b/backends/cpp_hart_gen/templates/inst_impl.hxx.erb @@ -0,0 +1,13 @@ +#pragma once + +#include "udb/soc_model.hpp" + +<%- ilist = cfg_arch.possible_instructions -%> + +<%- ilist.each do |inst| -%> +template +void udb::<%= name_of(:inst, cfg_arch, inst.name) %>::operator delete(void* ptr) { + <%= name_of(:hart, cfg_arch) %>::inst_allocator.free(reinterpret_cast(ptr)); +} + +<%- end -%> diff --git a/backends/cpp_hart_gen/templates/libhart.h.erb b/backends/cpp_hart_gen/templates/libhart.h.erb new file mode 100644 index 0000000000..fad10d515a --- /dev/null +++ b/backends/cpp_hart_gen/templates/libhart.h.erb @@ -0,0 +1,122 @@ +#pragma once + + +#ifdef __cplusplus +#define LINKAGE extern "C" + +#include +#include +#else +#define LINKAGE + +#include +#include + +#endif + +// expose IDL enum values to C +LINKAGE { +<%- cfg_arch.global_ast.enums.each do |enum| -%> +<%- underlying_type = enum.type(cfg_arch.symtab).width <= 32 ? "uint32_t" : "uint64_t" -%> +typedef struct _<%= enum.name %>{ + <%- element_names = enum.type(cfg_arch.symtab).element_names -%> + <%- element_values = enum.type(cfg_arch.symtab).element_values -%> + <%- element_names.each_index do |idx| -%> + static const <%= underlying_type %> <%= element_names[idx] %> = <%= element_values[idx] %>; + <%- end -%> + <%= underlying_type %> m_value; +} <%= enum.name %>; +typedef <%= underlying_type %> <%= enum.name %>ValueType; +<%- end -%> +} + +LINKAGE { + typedef int StopReasonValueType; +} + +// callback functions for the SoC model +LINKAGE typedef struct { + uint64_t (*read_hpm_counter)(uint64_t counternum); + + uint64_t (*read_mcycle)(); + uint64_t (*read_mtime)(); + + // returns new value of mcycle (coudl be different than new_value) + uint64_t (*sw_write_mcycle)(uint64_t new_value); + + void (*cache_block_zero)(uint64_t paddr); + + // eei_* occur when the configuration indicates that ecall/ebreak don't cause exceptions + void (*eei_ecall_from_m)(); + void (*eei_ecall_from_s)(); + void (*eei_ecall_from_u)(); + void (*eei_ecall_from_vs)(); + void (*eei_ebreak)(); + + void (*memory_model_acquire)(); + void (*memory_model_release)(); + void (*notify_mode_change)(PrivilegeModeValueType from, PrivilegeModeValueType to); + void (*prefetch_instruction)(uint64_t paddr); + void (*prefetch_read)(uint64_t paddr); + void (*prefetch_write)(uint64_t paddr); + void (*fence)( + uint8_t pi, uint8_t pr, uint8_t po, uint8_t pw, + uint8_t si, uint8_t sr, uint8_t so, uint8_t sw + ); + void (*fence_tso)(); + void (*ifence)(); + void (*order_pgtbl_writes_before_vmafence)(); + void (*order_pgtbl_reads_after_vmafence)(); + + uint64_t (*read_physical_memory_8)(uint64_t paddr); + uint64_t (*read_physical_memory_16)(uint64_t paddr); + uint64_t (*read_physical_memory_32)(uint64_t paddr); + uint64_t (*read_physical_memory_64)(uint64_t paddr); + void (*write_physical_memory_8)(uint64_t paddr, uint64_t value); + void (*write_physical_memory_16)(uint64_t paddr, uint64_t value); + void (*write_physical_memory_32)(uint64_t paddr, uint64_t value); + void (*write_physical_memory_64)(uint64_t paddr, uint64_t value); + + int (*memcpy_from_host)(uint64_t guest_paddr, const uint8_t* host_ptr, uint64_t size); + int (*memcpy_to_host)(uint8_t* host_ptr, uint64_t guest_paddr, uint64_t size); + + uint8_t (*atomic_check_then_write_32)(uint64_t, uint32_t, uint32_t); + uint8_t (*atomic_check_then_write_64)(uint64_t, uint64_t, uint64_t); + uint8_t (*atomically_set_pte_a)(uint64_t, uint64_t, uint32_t); + uint8_t (*atomically_set_pte_a_d)(uint64_t, uint64_t, uint32_t); + uint64_t (*atomic_read_modify_write_32)(uint64_t, uint64_t, AmoOperationValueType); + uint64_t (*atomic_read_modify_write_64)(uint64_t, uint64_t, AmoOperationValueType); + + // returns 1 if pma applies to the *entire* region [paddr, paddr + len) + // returns 0 otherwise + uint8_t (*pma_applies_Q_)(PmaAttributeValueType pma, uint64_t paddr, uint32_t len); +} FnPointerSocModel; + +typedef void UdbHart; +LINKAGE UdbHart* libhart_create( + const char* config_name, + uint64_t hart_id, + const char* config_file_path, // path to a UDB full configuration + FnPointerSocModel callbacks); + +LINKAGE void libhart_set_pc(UdbHart* hart, uint64_t pc); + +// run a single instruction +LINKAGE StopReasonValueType libhart_run_one(UdbHart* hart); + +// run a basic block +LINKAGE StopReasonValueType libhart_run_bb(UdbHart* hart); + +// run N instructions +LINKAGE StopReasonValueType libhart_run_n(UdbHart* hart, uint64_t n); + + +LINKAGE typedef struct _ExceptionClass { + static const unsigned External = 1; + static const unsigned Software = 2; + static const unsigned Timer = 3; +} ExceptionClass; +LINKAGE typedef int ExceptionClassValueType; + +LINKAGE void libhart_set_int(UdbHart* hart, PrivilegeModeValueType target_mode, ExceptionClassValueType klass); +LINKAGE void libhart_clear_int(UdbHart* hart, PrivilegeModeValueType target_mode, ExceptionClassValueType klass); diff --git a/backends/cpp_hart_gen/templates/libhart_renode.h.erb b/backends/cpp_hart_gen/templates/libhart_renode.h.erb new file mode 100644 index 0000000000..dd709091eb --- /dev/null +++ b/backends/cpp_hart_gen/templates/libhart_renode.h.erb @@ -0,0 +1,100 @@ +#pragma once + +#include + +#ifdef __cplusplus +#define LINKAGE extern "C" + +#include +#include +#else +#define LINKAGE + +#include +#include + +#endif + +// expose IDL enum values to C +LINKAGE { +<%- cfg_arch.global_ast.enums.each do |enum| -%> +<%- underlying_type = enum.type(cfg_arch.symtab).width <= 32 ? "uint32_t" : "uint64_t" -%> +typedef struct _<%= enum.name %>{ + <%- element_names = enum.type(cfg_arch.symtab).element_names -%> + <%- element_values = enum.type(cfg_arch.symtab).element_values -%> + <%- element_names.each_index do |idx| -%> + static const <%= underlying_type %> <%= element_names[idx] %> = <%= element_values[idx] %>; + <%- end -%> + <%= underlying_type %> m_value; +} <%= enum.name %>; +typedef <%= underlying_type %> <%= enum.name %>ValueType; +<%- end -%> +} + +LINKAGE { + #include "udb/stop_reason.h" +} + +LINKAGE { + typedef int StopReasonValueType; +} + +// callback functions for the SoC model +LINKAGE typedef struct { + uint64_t (*read_hpm_counter)(uint64_t counternum); + + uint64_t (*read_mcycle)(); + uint64_t (*read_mtime)(); + + // returns new value of mcycle (coudl be different than new_value) + uint64_t (*sw_write_mcycle)(uint64_t new_value); + + void (*cache_block_zero)(uint64_t paddr); + + // eei_* occur when the configuration indicates that ecall/ebreak don't cause exceptions + void (*eei_ecall_from_m)(); + void (*eei_ecall_from_s)(); + void (*eei_ecall_from_u)(); + void (*eei_ecall_from_vs)(); + void (*eei_ebreak)(); + + void (*memory_model_acquire)(); + void (*memory_model_release)(); + void (*notify_mode_change)(PrivilegeModeValueType from, PrivilegeModeValueType to); + void (*prefetch_instruction)(uint64_t paddr); + void (*prefetch_read)(uint64_t paddr); + void (*prefetch_write)(uint64_t paddr); + void (*fence)( + uint8_t pi, uint8_t pr, uint8_t po, uint8_t pw, + uint8_t si, uint8_t sr, uint8_t so, uint8_t sw + ); + void (*fence_tso)(); + void (*ifence)(); + void (*order_pgtbl_writes_before_vmafence)(); + void (*order_pgtbl_reads_after_vmafence)(); + + uint64_t (*read_physical_memory_8)(uint64_t paddr); + uint64_t (*read_physical_memory_16)(uint64_t paddr); + uint64_t (*read_physical_memory_32)(uint64_t paddr); + uint64_t (*read_physical_memory_64)(uint64_t paddr); + void (*write_physical_memory_8)(uint64_t paddr, uint64_t value); + void (*write_physical_memory_16)(uint64_t paddr, uint64_t value); + void (*write_physical_memory_32)(uint64_t paddr, uint64_t value); + void (*write_physical_memory_64)(uint64_t paddr, uint64_t value); + + int (*memcpy_from_host)(uint64_t guest_paddr, const uint8_t* host_ptr, uint64_t size); + int (*memcpy_to_host)(uint8_t* host_ptr, uint64_t guest_paddr, uint64_t size); + + uint8_t (*atomic_check_then_write_32)(uint64_t, uint32_t, uint32_t); + uint8_t (*atomic_check_then_write_64)(uint64_t, uint64_t, uint64_t); + uint8_t (*atomically_set_pte_a)(uint64_t, uint64_t, uint32_t); + uint8_t (*atomically_set_pte_a_d)(uint64_t, uint64_t, uint32_t); + uint64_t (*atomic_read_modify_write_32)(uint64_t, uint64_t, AmoOperationValueType); + uint64_t (*atomic_read_modify_write_64)(uint64_t, uint64_t, AmoOperationValueType); + + // returns 1 if pma applies to the *entire* region [paddr, paddr + len) + // returns 0 otherwise + uint8_t (*pma_applies_Q_)(PmaAttributeValueType pma, uint64_t paddr, uint32_t len); +} FnPointerSocModel; + +LINKAGE int32_t tlib_init(char *cpu_name); diff --git a/backends/cpp_hart_gen/templates/params.cxx.erb b/backends/cpp_hart_gen/templates/params.cxx.erb new file mode 100644 index 0000000000..2b988a62e7 --- /dev/null +++ b/backends/cpp_hart_gen/templates/params.cxx.erb @@ -0,0 +1,100 @@ + +#include + +#include "udb/bits.hpp" +#include "udb/db_data.hxx" +#include "udb/cfgs/<%= cfg_arch.name %>/params.hxx" + +namespace udb { + template + void to_json(const nlohmann::json& j, _Bits& b) { + j = nlohmann::json { b.get() }; + } + + template + void from_json(const nlohmann::json& j, _Bits& b) { + b = j.get::StorageType>(); + } +} + +void udb::<%= name_of(:params, cfg_arch) %>::set_param(const std::string& param_name, const nlohmann::json& json) +{ + <%- i = 0 -%> + <%- cfg_arch.params_without_value.each do |param| -%> + <%= i.zero? ? "" : "else "%>if (param_name == "<%= param.name %>") { + <%= param.name %> = json; + } + <%- i += 1 -%> + <%- end -%> + else { + throw Error(fmt::format("{} is not a settable parameter", param_name)); + } +} + +void udb::<%= name_of(:params, cfg_arch) %>::set_param_default(const std::string& param_name) +{ + <%- i = 0 -%> + <%- cfg_arch.params_without_value.each do |param| -%> + <%- unless param.default.nil? -%> + <%= i.zero? ? "" : "else "%>if (param_name == "<%= param.name %>") { + <%= param.name %> = <%= param.default %>; + } + <%- i += 1 -%> + <%- end -%> + <%- end -%> + else { + throw Error(fmt::format("Missing required parameter '{}'", param_name)); + } +} + +static bool builtin_param(const std::string& param_name) +{ + <%- cfg_arch.params_with_value.each do |param| -%> + if (param_name == "<%= param.name %>") { + return true; + } + <%- end -%> + return false; +} + +void udb::<%= name_of(:params, cfg_arch) %>::init(const nlohmann::json& cfg) +{ + nlohmann::json params = cfg["params"]; + nlohmann::json exts = cfg["implemented_extensions"]; + + // first, check that any provided built-in parameters match the built-in value + <%- cfg_arch.params_with_value.each do |param| -%> + if (params.contains("<%= param.name %>")) { + if (params["<%= param.name %>"].get<<%= param.idl_type.to_cxx_no_qualifiers %>>() != <%= param.value.to_cxx %>) { + throw Error("Parameter '<%= param.name %>' must be '<%= param.value %>'"); + } + }; + <%- end -%> + + std::vector assigned_params; + // now check that we have values for all required non-built-in parameters + for (auto ext : exts) { + for (auto param_name : DbData::params_for(ext[0].get(), ext[1].get())) { + if (builtin_param(param_name)) { + continue; + } + if (!params.contains(param_name)) { + set_param_default(param_name); + } else { + set_param(param_name, params[param_name]); + } + assigned_params.emplace_back(param_name); + } + } + + // now make sure there isn't a parameter in the config that doesn't belong + for (auto& param : params.items()) { + bool was_assigned = + std::find(assigned_params.begin(), assigned_params.end(), param.key()) != assigned_params.end(); + bool is_builtin = builtin_param(param.key()); + + if (!(was_assigned || is_builtin)) { + throw Error(fmt::format("Parameter '{}' is not a parameter for model '<%= cfg_arch.name %>'", param.key())); + } + } +} diff --git a/backends/cpp_hart_gen/templates/params.hxx.erb b/backends/cpp_hart_gen/templates/params.hxx.erb new file mode 100644 index 0000000000..6df7483564 --- /dev/null +++ b/backends/cpp_hart_gen/templates/params.hxx.erb @@ -0,0 +1,50 @@ +#pragma once + +#include +#include + +#include "udb/defines.hpp" + +#if !defined(JSON_ASSERT) +#define JSON_ASSERT(cond) udb_assert(cond, "JSON assert"); +#endif +#include + +#include "udb/bits-yaml.hpp" + +namespace udb { + struct <%= name_of(:params, cfg_arch) %> + { + class Error : public std::runtime_error { + public: + explicit Error(const std::string& msg) : std::runtime_error(msg) {} + explicit Error(const char* msg) : std::runtime_error(msg) {} + }; + + // all the compile-time-known parameters + <%- cfg_arch.params_with_value.each do |param| -%> + static constexpr <%= param.idl_type.to_cxx_no_qualifiers %> <%= param.name %>_VALUE = <%= param.value.to_cxx %>; + <%- end -%> + + // read parameters out of a YAML (or JSON) config file + <%= name_of(:params, cfg_arch) %>(const nlohmann::json& cfg) + { + init(cfg); + } + + void init(const nlohmann::json& params); + + <%- declared_params = Set.new -%> + <%- cfg_arch.params_without_value.each do |param| -%> + <%- next if declared_params.include?(param.name) -%> + <%- declared_params << param.name -%> + + // <%= param.desc.gsub("\n", "\n // ") %> + <%= param.idl_type.to_cxx_no_qualifiers %> <%= param.name %>; + <%- end -%> + + private: + void set_param(const std::string& param_name, const nlohmann::json& json); + void set_param_default(const std::string& param_name); + }; +} diff --git a/backends/cpp_hart_gen/templates/structs.hxx.erb b/backends/cpp_hart_gen/templates/structs.hxx.erb new file mode 100644 index 0000000000..e425780529 --- /dev/null +++ b/backends/cpp_hart_gen/templates/structs.hxx.erb @@ -0,0 +1,38 @@ +// THIS FILE IS AUTOGENERATED + +#pragma once + +#include +#include +#include "udb/bits.hpp" +#include "udb/bitfield.hpp" + +namespace udb { + +#define __UDB_STRUCT(name) <%= name_of(:cfg, cfg_arch) %>_ ## name ## _Struct + + <%- cfg_arch.global_ast.structs.each do |struct| -%> + struct <%= name_of(:struct, cfg_arch, struct.name) %> { + <%- struct.member_types.each_index do |idx| -%> + <%= struct.member_types[idx].gen_cpp(cfg_arch.symtab, 0) %> <%= struct.member_names[idx] %>; + <%- end -%> + + <%= name_of(:struct, cfg_arch, struct.name) %>() = default; + ~<%= name_of(:struct, cfg_arch, struct.name) %>() = default; + + <%= name_of(:struct, cfg_arch, struct.name) %>& operator=(const <%= name_of(:struct, cfg_arch, struct.name) %>& other) + { + if (this == &other) { return *this; } + + <%- struct.member_types.each_index do |idx| -%> + <%= struct.member_names[idx] %> = other.<%= struct.member_names[idx] %>; + <%- end -%> + + return *this; + } + }; + <%- end -%> + +#undef __UDB_STRUCT + +} diff --git a/backends/cpp_hart_gen/templates/types.hxx.erb b/backends/cpp_hart_gen/templates/types.hxx.erb new file mode 100644 index 0000000000..6e35a47da4 --- /dev/null +++ b/backends/cpp_hart_gen/templates/types.hxx.erb @@ -0,0 +1,18 @@ +// THIS FILE IS AUTOGENERATED + +#pragma once + +#include +#include +#include + +namespace riscv { + using XReg = uint<%= symtab.mxlen %>_t; + + constexpr XReg operator""_ux(unsigned long long int i) { + return i; + } + constexpr XReg operator""_sx(unsigned long long int i) { + return static_cast_t>(i); + } +} diff --git a/backends/ext_pdf_doc/tasks.rake b/backends/ext_pdf_doc/tasks.rake index bb8a0343ca..0ae83d56d2 100644 --- a/backends/ext_pdf_doc/tasks.rake +++ b/backends/ext_pdf_doc/tasks.rake @@ -112,24 +112,21 @@ end rule %r{#{$root}/gen/ext_pdf_doc/.*/adoc/.*_extension\.adoc} => proc { |tname| config_name = Pathname.new(tname).relative_path_from("#{$root}/gen/ext_pdf_doc").to_s.split("/")[0] - ext_name = Pathname.new(tname).basename(".adoc").to_s.split("_")[0..-2].join("_") - arch_yaml_paths = - if File.exist?("#{$root}/arch/ext/#{ext_name}.yaml") - ["#{$root}/arch/ext/#{ext_name}.yaml"] + Dir.glob("#{$root}/cfgs/*/arch_overlay/ext/#{ext_name}.yaml") - else - Dir.glob("#{$root}/cfgs/*/arch_overlay/ext/#{ext_name}.yaml") - end - raise "Can't find extension '#{ext_name}'" if arch_yaml_paths.empty? - + arch_yaml_paths = Dir.glob("#{$root}/arch/**/*.yaml") + cfg_path = $root / "gen" / "ext_pdf_doc" / "#{config_name}.yaml" + cfg = Config.create(cfg_path) + arch_yaml_paths += Dir.glob("#{cfg.arch_overlay_abs}/**/*.yaml") unless cfg.arch_overlay.nil? [ (EXT_PDF_DOC_DIR / "templates" / "ext_pdf.adoc.erb").to_s, arch_yaml_paths, + (cfg_path).to_s, __FILE__ ].flatten } do |t| config_name = Pathname.new(t.name).relative_path_from("#{$root}/gen/ext_pdf_doc").to_s.split("/")[0] + config_path = $root / "gen" / "ext_pdf_doc" / "#{config_name}.yaml" - cfg_arch = cfg_arch_for(config_name) + cfg_arch = cfg_arch_for(config_path) ext_name = Pathname.new(t.name).basename(".adoc").to_s.split("_")[0..-2].join("_") @@ -164,14 +161,15 @@ namespace :gen do Options: - * EXT - The extension name - * CFG - The config name, required only when an overlay is required + * EXT - The extension name + * CFG - Path to cfg with arch overlay, if needed. Can be either the name of a .yaml file in cfgs, + a relative path from CWD, or an absolute path. * VERSION - A list of versions to include. May also be "all" or "latest". - * THEME - path to an AsciidocPDF theme file. If not set, will use default RVI theme. + * THEME - path to an AsciidocPDF theme file. If not set, will use default RVI theme. Examples: - ./do gen:ext_pdf EXT=Xqci CFG=qc_iu VERSION=latest THEME=cfgs/qc_iu/qc_theme.yaml + ./do gen:ext_pdf EXT=Xqci CFG=qc_iu VERSION=latest THEME=my_theme.yaml ./do gen:ext_pdf EXT=B VERSION=all ./do gen:ext_pdf EXT=B VERSION=1.0.0 ./do gen:ext_pdf EXT=B VERSION=1.0.0,1.1.0 @@ -190,14 +188,29 @@ namespace :gen do Pathname.new(ENV["THEME"]).realpath.to_s end + cfg = + if cfg.nil? + "#{$root}/cfgs/_.yaml" + elsif File.exist?("#{$root}/cfgs/#{cfg}.yaml") + "#{$root}/cfgs/#{cfg}.yaml" + elsif File.exist?("#{$root}/cfgs/#{cfg}") + "#{$root}/cfgs/#{cfg}" + elsif File.exist?(cfg) + File.realpath(cfg) + else + raise "Cannot find config '#{config}'" + end + + config_name = File.basename(cfg, ".yaml") + versions = version.split(",") raise ArgumentError, "Nothing else should be specified with 'all'" if versions.include?("all") && versions.size > 1 - if cfg.nil? - Rake::Task[$root / "gen" / "ext_pdf_doc" / "_" / "pdf" / "#{extension}_extension.pdf"].invoke - else - Rake::Task[$root / "gen" / "ext_pdf_doc" / cfg / "pdf" / "#{extension}_extension.pdf"].invoke + unless File.exist?($root / "gen" / "ext_pdf_doc" / File.basename(cfg)) + FileUtils.mkdir_p($root / "gen" / "ext_pdf_doc") + FileUtils.ln_s(cfg, $root / "gen" / "ext_pdf_doc" / File.basename(cfg)) end + Rake::Task[$root / "gen" / "ext_pdf_doc" / config_name / "pdf" / "#{extension}_extension.pdf"].invoke end desc <<~DESC diff --git a/backends/ext_pdf_doc/templates/ext_pdf.adoc.erb b/backends/ext_pdf_doc/templates/ext_pdf.adoc.erb index bd9568a1ec..61e0ffbb68 100644 --- a/backends/ext_pdf_doc/templates/ext_pdf.adoc.erb +++ b/backends/ext_pdf_doc/templates/ext_pdf.adoc.erb @@ -173,7 +173,7 @@ Changes:: <%- end -%> <%- unless version.implications.empty? -%> Implies:: -* <%= version.implications.map { |i| "#{i.name} (#{i.version_spec})" }.join("\n* ") %> +<%= version.implications.each { |i| "* #{i[:ext_ver].name} (#{i[:ext_ver].version_spec}) #{i[:cond].empty? ? '' : i[:cond].to_asciidoc(join: ', ')}" }.join("\n* ") %> <%- unless version.requirement_condition.empty? -%> Requires:: <%= version.requirement_condition.to_asciidoc %> @@ -206,67 +206,68 @@ Version requirements are specified as conditions using the following operators: <%= ext.description %> :leveloffset: -2 -<%- implications = versions.map { |v| v.implications }.flatten.uniq -%> -<%- unless implications.empty? -%> +<%- has_implications = versions.any? { |v| !v.implications.empty? } -%> +<%- if has_implications -%> === Sub-extensions -<%- if implications.size > 1 -%> -<%= ext.name %> defines the following #{implications.size} sub-extensions: -<%- else -%> -<%= ext.name %> defines a single sub-extension: -<%- end -%> - -<%- implications.each do |sub_ext| -%> -==== <%= sub_ext.name %> (<%= sub_ext.version_spec %>) - -<%- if versions.size > 1 -%> -<%= sub_ext.name %> (<%= sub_ext.version %>) is implied by -version <%= versions.select { |v| v.implications.include?(sub_ext)}.map(&:version).join(", ") %> -of <%= ext.name %>. -<%- end -%> +<%- versions.each do |version| -%> +<%- next if version.implications.empty? -%> +<%- if version.implications.size > 1 -%> +<%= version.name %> defines the following <%= version.implications.size %> sub-extensions: +<%- else -%> +<%= version.name %> defines a single sub-extension: +<%- end -%> + +<%- version.implications.each do |implication_hsh| -%> +<%- sub_ext = implication_hsh[:ext_ver] -%> +<%- cond = implication_hsh[:cond] -%> +==== <%= sub_ext.name %> (<%= sub_ext.version_spec %>) <%= "if #{cond.to_asciidoc(join: ', ')}" unless cond.empty? %> <%= cfg_arch.extension(sub_ext.name).description %> -<%- unless sub_ext.requirement_condition.empty? -%> +<%- unless sub_ext.requirement_condition.empty? -%> <%= sub_ext.name %> requires: <%= sub_ext.requirement_condition.to_asciidoc %> -<%- end -%> - -<%- end -%> +<%- end -%> +<%- end -%> +<%- end -%> <%- end -%> <%- unless ext.instructions.empty? -%> <<< == Instruction summary -The following <%= ext.instructions.size %> instructions are added by this extension: +The following <%= ext.instructions.size %> instructions are affected by this extension: [%autowidth] |=== | RV32 | RV64 | Mnemonic | Instruction <%- if versions.size > 1 -%>| <%= versions.map { |v| "v#{v.version}" }.join(" | ") %><%- end -%> -<%- ext.instructions.each do |i| -%> +<%- inst_list = ext.instructions.select { |inst| versions.any? { |ext_ver| inst.defined_by_condition.possibly_satisfied_by?(ext_ver) } } -%> +<%- inst_list.each do |i| -%> | <%= i.rv32? ? "✓" : "" %> | <%= i.rv64? ? "✓" : "" %> | `<%= i.name %> <%= i.assembly.gsub("x", "r").strip %>` | xref:insns-<%= i.name.gsub('.', '_') %>[<%= i.long_name %>] <%- if versions.size > 1 -%> -| <%= ext.versions.map { |v| i.defined_by?(ext.name, v.version) ? "✓" : "" }.join(" | ") %> +| <%= ext.versions.map { |v| i.defined_by_condition.possibly_satisfied_by?(v) ? "✓" : "" }.join(" | ") %> <%- end -%> <%- end -%> |=== -<%- unless implications.empty? -%> +<%- if has_implications -%> === Instructions by sub-extension +<%- implications = versions.map { |v| v.implications.to_a }.flatten -%> + [%autowidth] |=== -| Mnemonic | <%= implications.map { |e| "`#{e.name}`" }.join(" | ") %> +| Mnemonic | <%= implications.map { |e| "`#{e[:ext_ver].name}`" }.join(" | ") %> -<%- ext.instructions.each do |i| -%> +<%- inst_list.each do |i| -%> | `<%= i.name %>` -| <%= implications.map { |e| i.defined_by?(e) ? "✓" : "" }.join(" | ") %> +| <%= implications.map { |e| i.defined_by_condition.possibly_satisfied_by?(e[:ext_ver]) ? "✓" : "" }.join(" | ") %> <%- end -%> |=== @@ -278,19 +279,21 @@ The following <%= ext.instructions.size %> instructions are added by this extens <<< == CSR summary -The following <%= ext.csrs.size %> are added by this extension. +<%- csr_list = ext.csrs.select { |csr| versions.any? { |ext_ver| csr.affected_by?(ext_ver) } } -%> + +The following <%= csr_list.size %> are affected by this extension. [%autowidth] |=== | RV32 | RV64 | CSR | Name <%- if versions.size > 1 -%>| <%= versions.map { |v| "v#{v.version}" }.join(" | ") %><%- end -%> -<%- ext.csrs.each do |csr| -%> +<%- csr_list.each do |csr| -%> | <%= csr.defined_in_base32? ? "✓" : "" %> | <%= csr.defined_in_base64? ? "✓" : "" %> | xref:csrs-<%= csr.name.gsub('.', '_') %>[<%= csr.name %>] | <%= csr.long_name %> <%- if versions.size > 1 -%> -| <%= ext.versions.map { |v| csr.defined_by?(ext.name, v.version) ? "✓" : "" }.join(" | ") %> +| <%= ext.versions.map { |v| csr.affected_by?(v) ? "✓" : "" }.join(" | ") %> <%- end -%> <%- end -%> @@ -326,9 +329,9 @@ 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.sort { |a,b| a.name <=> b.name }.each do |f| -%> [#<%= f.name %>-func-def] -=== <%= f.name %><%- if f.builtin? -%> (builtin)<%- end -%> +=== <%= f.name %><%- if f.builtin? -%> (builtin)<%- end -%><%- if f.generated? -%> (generated)<%- end -%> <%= f.description %> @@ -337,7 +340,7 @@ h| Return Type l| <%= f.return_type_list_str.join(', ') %> h| Arguments l| <%= f.arguments_list_str.join (', ') %> |=== -<%- unless f.builtin? -%> +<%- unless f.builtin? || f.generated? -%> <%- body_ast = f.body -%> [source,idl,subs="specialchars,macros"] ---- diff --git a/backends/manual/templates/ext.adoc.erb b/backends/manual/templates/ext.adoc.erb index 71a46f8574..e0c6af8d19 100644 --- a/backends/manual/templates/ext.adoc.erb +++ b/backends/manual/templates/ext.adoc.erb @@ -36,11 +36,11 @@ <%= ext.description %> -<%- insts = cfg_arch.instructions.select { |i| ext.versions.any? { |v| i.defined_by?(v) } } -%> +<%- insts = ext.instructions.select { |inst| ext.versions.any? { |ext_ver| inst.defined_by_condition.possibly_satisfied_by?(ext_ver) } } -%> <%- unless insts.empty? -%> == Instructions -The following instructions are defined by this extension: +The following instructions are affected by this extension: [cols="1,3"] |=== diff --git a/backends/manual/templates/func.adoc.erb b/backends/manual/templates/func.adoc.erb index a448d555cf..00eb81a0cf 100644 --- a/backends/manual/templates/func.adoc.erb +++ b/backends/manual/templates/func.adoc.erb @@ -6,7 +6,7 @@ <%- cfg_arch.functions.each do |f| -%> [#<%= f.name %>-func-def] -== <%= f.name %><%- if f.builtin? -%> (builtin)<%- end -%> +== <%= f.name %><%- if f.builtin? -%> (builtin)<%- end -%><%- if f.generated? -%> (generated)<%- end -%> <%= f.description %> @@ -31,7 +31,7 @@ None <%- end -%> |=== -<%- unless f.builtin? -%> +<%- unless f.builtin? || f.generated? -%> <%- body_ast = f.body -%> [source,idl,subs="specialchars,macros"] ---- diff --git a/backends/manual/templates/instruction.adoc.erb b/backends/manual/templates/instruction.adoc.erb index 1c67a226c4..429f981558 100644 --- a/backends/manual/templates/instruction.adoc.erb +++ b/backends/manual/templates/instruction.adoc.erb @@ -15,11 +15,11 @@ This instruction is included in the following profiles: <%- in_profile_mandatory = profile.mandatory_ext_reqs.any? do |ext_req| ext_versions = ext_req.satisfying_versions - ext_versions.any? { |ext_ver| inst.defined_by?(ext_ver) } + ext_versions.any? { |ext_ver| inst.defined_by_condition.possibly_satisfied_by?(ext_ver) } end - in_profile_optional = profile.optional_ext_reqs.any? do |ext_req| + in_profile_optional = !in_profile_mandatory && profile.optional_ext_reqs.any? do |ext_req| ext_versions = ext_req.satisfying_versions - ext_versions.any? { |ext_ver| inst.defined_by?(ext_ver) } + ext_versions.any? { |ext_ver| inst.defined_by_condition.possibly_satisfied_by?(ext_ver) } end if in_profile_mandatory -%> @@ -128,7 +128,7 @@ IDL:: + [source,idl,subs="specialchars,macros"] ---- -<%= inst.operation_ast(inst.cfg_arch.symtab).gen_adoc %> +<%= inst.operation_ast.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(64) -%> <%- unless exception_list.empty? -%> == Exceptions diff --git a/backends/profile_doc/templates/profile.adoc.erb b/backends/profile_doc/templates/profile.adoc.erb index beed6be901..d4d1d40ada 100644 --- a/backends/profile_doc/templates/profile.adoc.erb +++ b/backends/profile_doc/templates/profile.adoc.erb @@ -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 = ext.instructions -%> <%- unless insts.empty? -%> ==== Instructions @@ -649,13 +649,13 @@ RV64:: <% if inst.key?("operation()") -%> [source,idl,subs="specialchars,macros"] ---- -<%= inst.operation_ast(cfg_arch.symtab).gen_adoc %> +<%= inst.operation_ast.gen_adoc %> ---- <% end -%> ==== Exceptions -<% exception_list = inst.reachable_exceptions_str(cfg_arch.symtab) -%> +<% exception_list = inst.reachable_exceptions_str -%> <% if exception_list.empty? -%> This instruction does not generate synchronous exceptions. <% else -%> @@ -702,17 +702,17 @@ 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? -%> +h| Length | <%= csr.length_pretty %> <% else -%> -h| Length | <%= csr.length_pretty(cfg_arch) %> +h| Length | <%= csr.length_pretty %> <% 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? || csr.fields.any? { |f| f.dynamic_location? } -%> <%# CSR has a known static length, so there is only one format to display -%> .<%= csr.name %> format [wavedrom, ,svg,subs='attributes',width="100%"] diff --git a/bin/.container-tag b/bin/.container-tag index bd73f47072..2eb3c4fe4e 100644 --- a/bin/.container-tag +++ b/bin/.container-tag @@ -1 +1 @@ -0.4 +0.5 diff --git a/bin/clang-format b/bin/clang-format new file mode 100755 index 0000000000..6bdcef8201 --- /dev/null +++ b/bin/clang-format @@ -0,0 +1,6 @@ +#!/bin/bash + +ROOT=$(dirname $(realpath ${BASH_SOURCE[0]})) +source $ROOT/setup + +$CLANG_FORMAT "$@" diff --git a/bin/clang-tidy b/bin/clang-tidy new file mode 100755 index 0000000000..731d55481f --- /dev/null +++ b/bin/clang-tidy @@ -0,0 +1,6 @@ +#!/bin/bash + +ROOT=$(dirname $(realpath ${BASH_SOURCE[0]})) +source $ROOT/setup + +$CLANG_TIDY "$@" diff --git a/bin/clobber b/bin/clobber index 22e665c5f6..97f206daa2 100755 --- a/bin/clobber +++ b/bin/clobber @@ -1,5 +1,5 @@ #!/bin/bash -ROOT=$(dirname $(dirname $(realpath $BASH_SOURCE[0]))) +ROOT=$(dirname $(dirname $(realpath ${BASH_SOURCE[0]}))) rm -rf ${ROOT}/.stamps ${ROOT}/.home ${ROOT}/.bundle ${ROOT}/.singularity ${ROOT}/gen ${ROOT}/node_modules diff --git a/bin/g++ b/bin/g++ new file mode 100644 index 0000000000..0bb5c70df6 --- /dev/null +++ b/bin/g++ @@ -0,0 +1,6 @@ +#!/bin/bash + +ROOT=$(dirname $(realpath ${BASH_SOURCE[0]})) +source $ROOT/setup + +$GPP "$@" diff --git a/bin/gdb b/bin/gdb new file mode 100755 index 0000000000..e158fb5d2f --- /dev/null +++ b/bin/gdb @@ -0,0 +1,6 @@ +#!/bin/bash + +ROOT=$(dirname $(realpath ${BASH_SOURCE[0]})) +source $ROOT/setup + +$GDB "$@" diff --git a/bin/make b/bin/make new file mode 100755 index 0000000000..3e795c59ca --- /dev/null +++ b/bin/make @@ -0,0 +1,6 @@ +#!/bin/bash + +ROOT=$(dirname $(realpath ${BASH_SOURCE[0]})) +source $ROOT/setup + +$MAKE "$@" diff --git a/bin/rake b/bin/rake new file mode 100755 index 0000000000..8a33b87480 --- /dev/null +++ b/bin/rake @@ -0,0 +1,6 @@ +#!/bin/bash + +ROOT=$(dirname $(realpath ${BASH_SOURCE[0]})) +source $ROOT/setup + +$BUNDLE exec rake "$@" diff --git a/bin/rdbg b/bin/rdbg new file mode 100755 index 0000000000..d999326ad2 --- /dev/null +++ b/bin/rdbg @@ -0,0 +1,6 @@ +#!/bin/bash + +ROOT=$(dirname $(realpath ${BASH_SOURCE[0]})) +source $ROOT/setup + +$BUNDLE exec rdbg "$@" diff --git a/bin/setup b/bin/setup index 52aa3f3685..913b992618 100755 --- a/bin/setup +++ b/bin/setup @@ -180,6 +180,11 @@ if [ "${CONTAINER_TYPE}" == "devcontainer" ]; then PYTHON="${ROOT}/.home/.venv/bin/python3" PIP="${ROOT}/.home/.venv/bin/pip" BASH="bash" + GPP="g++" + GDB="gdb" + CLANG_FORMAT="clang-format" + CLANG_TIDY="clang-tidy" + MAKE="make" elif [ "${CONTAINER_TYPE}" == "docker" ]; then BUNDLE="${DOCKER_BASE} bundle" RUBY="${DOCKER_BASE} bundle exec ruby" @@ -190,6 +195,11 @@ elif [ "${CONTAINER_TYPE}" == "docker" ]; then PYTHON="${DOCKER_BASE} ${ROOT}/.home/.venv/bin/python3" PIP="${DOCKER_BASE} ${ROOT}/.home/.venv/bin/pip" BASH="${DOCKER_BASE} bash" + GPP="${DOCKER_BASE} g++" + GDB="${DOCKER_BASE} gdb" + CLANG_FORMAT="${DOCKER_BASE} clang-format" + CLANG_TIDY="${DOCKER_BASE} clang-tidy" + MAKE="${DOCKER_BASE} make" elif [ "${CONTAINER_TYPE}" == "singularity" ]; then BUNDLE="singularity run ${HOME_OPT} ${CONTAINER_PATH} bundle" RUBY="singularity run ${HOME_OPT} ${CONTAINER_PATH} bundle exec ruby" @@ -200,6 +210,11 @@ elif [ "${CONTAINER_TYPE}" == "singularity" ]; then PYTHON="singularity run ${HOME_OPT} ${CONTAINER_PATH} ${ROOT}/.home/.venv/bin/python3" PIP="singularity run ${HOME_OPT} ${CONTAINER_PATH} ${ROOT}/.home/.venv/bin/pip" BASH="singularity run ${HOME_OPT} ${CONTAINER_PATH} bash" + GPP="singularity run ${HOME_OPT} ${CONTAINER_PATH} g++" + GDB="singularity run ${HOME_OPT} ${CONTAINER_PATH} gdb" + CLANG_FORMAT="singularity run ${HOME_OPT} ${CONTAINER_PATH} clang-format" + CLANG_TIDY="singularity run ${HOME_OPT} ${CONTAINER_PATH} clang-tidy" + MAKE="singularity run ${HOME_OPT} ${CONTAINER_PATH} make" else echo "Bad container type: ${CONTAINER_TYPE}" 1>&2 exit 1 diff --git a/cfgs/MC100-32.yaml b/cfgs/MC100-32.yaml new file mode 100644 index 0000000000..d4538cbbcd --- /dev/null +++ b/cfgs/MC100-32.yaml @@ -0,0 +1,22 @@ +# yaml-language-server: $schema=../schemas/config_schema.json +--- +$schema: config_schema.json# +kind: architecture configuration +type: partially configured +name: MC100-32 +description: An MC100-32 compliant system +mandatory_extensions: + - { name: Sm, version: "~> 1.11.0" } + - { 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" } +additional_extensions: false +params: + XLEN: 32 + MISALIGNED_SPLIT_STRATEGY: by_byte + PRECISE_SYNCHRONOUS_EXCEPTIONS: true + TRAP_ON_ECALL_FROM_M: true + TRAP_ON_EBREAK: true + M_MODE_ENDIANESS: little diff --git a/cfgs/_/cfg.yaml b/cfgs/_.yaml similarity index 74% rename from cfgs/_/cfg.yaml rename to cfgs/_.yaml index 107a2c62e4..afc1919d87 100644 --- a/cfgs/_/cfg.yaml +++ b/cfgs/_.yaml @@ -1,4 +1,4 @@ -# yaml-language-server: $schema=../../schemas/config_schema.json +# yaml-language-server: $schema=../schemas/config_schema.json --- $schema: config_schema.json# kind: architecture configuration diff --git a/cfgs/generic_rv64/cfg.yaml b/cfgs/example_rv64_with_overlay.yaml similarity index 99% rename from cfgs/generic_rv64/cfg.yaml rename to cfgs/example_rv64_with_overlay.yaml index 097651383b..eb4dbd93f9 100644 --- a/cfgs/generic_rv64/cfg.yaml +++ b/cfgs/example_rv64_with_overlay.yaml @@ -1,8 +1,11 @@ +# yaml-language-server: $schema=../schemas/config_schema.json +--- $schema: config_schema.json# kind: architecture configuration type: fully configured -name: generic_rv64 +name: example_rv64_with_overlay description: An example fully-specified RV64 system +arch_overlay: example implemented_extensions: - [A, "2.1.0"] - [B, "1.0.0"] @@ -37,7 +40,7 @@ params: XLEN: 64 # name of the configuration - NAME: generic_rv64 + NAME: example_rv64_with_overlay # vendor-specific architecture ID in marchid ARCH_ID: 0x1000000000000000 diff --git a/cfgs/mc100-32-full-example.yaml b/cfgs/mc100-32-full-example.yaml new file mode 100644 index 0000000000..27fa0dc32e --- /dev/null +++ b/cfgs/mc100-32-full-example.yaml @@ -0,0 +1,50 @@ +# yaml-language-server: $schema=../schemas/config_schema.json +--- +$schema: https://riscv.org/udb/schemas/config_schema-0.1.0.json +kind: architecture configuration +type: fully configured +name: MC100-32-Full +description: An example MC100-32-copliant full config +implemented_extensions: + - [Sm, "1.11.0"] + - [I, "2.1"] + - [C, "2.0"] + - [M, "2.0"] + - [Zicsr, "2.0"] + - [Zicntr, "2.0"] +params: + XLEN: 32 + ARCH_ID: 0 + IMP_ID: 0 + VENDOR_ID_BANK: 1 + VENDOR_ID_OFFSET: 1 + MISALIGNED_LDST: true + MISALIGNED_LDST_EXCEPTION_PRIORITY: low + MISALIGNED_MAX_ATOMICITY_GRANULE_SIZE: 4 + MISALIGNED_SPLIT_STRATEGY: by_byte + PRECISE_SYNCHRONOUS_EXCEPTIONS: true + TRAP_ON_ECALL_FROM_M: true + TRAP_ON_EBREAK: true + M_MODE_ENDIANESS: little + TRAP_ON_ILLEGAL_WLRL: true + TRAP_ON_UNIMPLEMENTED_INSTRUCTION: true + TRAP_ON_RESERVED_INSTRUCTION: true + TRAP_ON_UNIMPLEMENTED_CSR: true + REPORT_VA_IN_MTVAL_ON_BREAKPOINT: true + REPORT_VA_IN_MTVAL_ON_LOAD_MISALIGNED: true + REPORT_VA_IN_MTVAL_ON_STORE_AMO_MISALIGNED: true + REPORT_VA_IN_MTVAL_ON_INSTRUCTION_MISALIGNED: true + REPORT_VA_IN_MTVAL_ON_LOAD_ACCESS_FAULT: true + REPORT_VA_IN_MTVAL_ON_STORE_AMO_ACCESS_FAULT: true + REPORT_VA_IN_MTVAL_ON_INSTRUCTION_ACCESS_FAULT: true + REPORT_ENCODING_IN_MTVAL_ON_ILLEGAL_INSTRUCTION: true + MTVAL_WIDTH: 32 + PMA_GRANULARITY: 12 + PHYS_ADDR_WIDTH: 32 + MISA_CSR_IMPLEMENTED: true + MTVEC_MODES: [0, 1] + MTVEC_BASE_ALIGNMENT_DIRECT: 4 + MTVEC_BASE_ALIGNMENT_VECTORED: 4 + MUTABLE_MISA_C: false + MUTABLE_MISA_M: false + TIME_CSR_IMPLEMENTED: false diff --git a/cfgs/qc_iu/cfg.yaml b/cfgs/qc_iu.yaml similarity index 96% rename from cfgs/qc_iu/cfg.yaml rename to cfgs/qc_iu.yaml index f25ac6616b..69168fd468 100644 --- a/cfgs/qc_iu/cfg.yaml +++ b/cfgs/qc_iu.yaml @@ -4,6 +4,7 @@ $schema: config_schema.json# kind: architecture configuration type: fully configured name: qc_iu +arch_overlay: qc_iu description: Configuration with the Xqci and Xqccmp custom extensions. implemented_extensions: - { name: Sm, version: "1.13" } @@ -18,8 +19,8 @@ implemented_extensions: - { name: Zicntr, version: "2.0" } - { name: Zicsr, version: "2.0" } - { name: Zihpm, version: "2.0" } - - { name: Xqccmp, version: "0.1" } - - { name: Xqci, version: "0.7" } + - { name: Xqccmp, version: "0.3" } + - { name: Xqci, version: "0.8" } exception_codes: - num: 27 name: Illegal Stack Pointer diff --git a/cfgs/qc_iu/qc_theme.yml b/cfgs/qc_iu/qc_theme.yml deleted file mode 100644 index a9fd9df9be..0000000000 --- a/cfgs/qc_iu/qc_theme.yml +++ /dev/null @@ -1,330 +0,0 @@ -extends: default -font: - catalog: - merge: true - sans-serif: GEM_FONTS_DIR/mplus1p-regular-fallback.ttf - #Petrona - body: - normal: QualcommNext-Regular.ttf - bold: QualcommNext-Medium.ttf - italic: QualcommNext-Italic.ttf - bold_italic: QualcommNext-MediumItalic.ttf - header_thin: QualcommNext-Thin.ttf - #Montserrat - headings: - normal: QualcommNext-Medium.ttf - italic: QualcommNext-Italic.ttf - bold: QualcommNext-Medium.ttf - light: QualcommNext-Thin.ttf - code: - normal: cmunbtl.ttf - bold: cmunbtl.ttf - italic: cmunbto.ttf - bold_italic: cmunbto.ttf - # M+ 1mn supports ASCII and the circled numbers used for conums - M+ 1mn: - normal: mplus-1mn-regular.ttf - bold: mplus-1mn-bold.ttf - italic: mplus-1mn-light.ttf - bold_italic: mplus-1mn-medium.ttf - M+ 1p Fallback: - normal: mplus-1p-regular-fallback.ttf - bold: mplus-1p-regular-fallback.ttf - italic: mplus-1p-regular-fallback.ttf - bold_italic: mplus-1p-regular-fallback.ttf - Droid Fallback: - normal: droid-sans-fallback.ttf - italic: droid-sans-fallback.ttf - bold: droid-sans-fallback.ttf - bold_italic: droid-sans-fallback.ttf - # M+ 1p supports Latin, Latin-1 Supplement, Latin Extended, Greek, Cyrillic, Vietnamese, Japanese & an assortment of symbols - # It also provides arrows for ->, <-, => and <= replacements in case these glyphs are missing from font - fallbacks: - - M+ 1p Fallback - - Droid Fallback - svg: - fallback-font-family: M+ 1mn -page: - background_color: ffffff - layout: portrait - margin: [0.5in, 0.67in, 0.67in, 0.67in] - # margin_inner and margin_outer keys are used for recto/verso print margins when media=prepress - margin_inner: 0.75in - margin_outer: 0.59in - size: A4 -base: - font-family: body - font_size: 11.5 - line_height_length: 12 - font_style: normal - font_size_large: round($base_font_size * 1.25) - font_size_small: round($base_font_size * 0.85) - font_size_min: $base_font_size * 0.75 - border_radius: 3 - border_width: 0.25 - border_color: EEEEEE -vertical_rhythm: $base_line_height_length -horizontal_rhythm: - $base_line_height_length - # QUESTION should vertical_spacing be block_spacing instead? -vertical_spacing: $vertical_rhythm -link: - font_color: 428bca - # codespan is currently used for inline monospaced in prose and table cells -codespan: - font-color: 000000 - # font_family: code - font_family: M+ 1mn - #font_family: Droid Fallback - font_style: normal -menu_caret_content: " \u203a " -heading: - align: left - margin_bottom: $block_margin_bottom - margin_top: $block_margin_bottom - min_height_after: 0.25in - font_color: 000000 - font_family: headings - font_style: bold - h1_font_size: floor($base_font_size * 2.8) - # h2 is used for chapter titles (book doctype only) - h2_font_size: 11.5 - h3_font_size: 11.5 - h4_font_size: 11.5 - h5_font_size: 11.5 - h6_font_size: $base_font_size_small -title_page: - align: center - logo: - top: 10% - title: - font_family: headings - font_style: light - font_size: floor($base_font_size * 2.8) - top: 55% - font_color: 3e058e - subtitle: - font_family: headings - font_style: light - font_size: floor($base_font_size * 1.2) - margin-top: 25 - authors: - font_family: headings - font_color: 3e058e - font_style: light - font_size: floor($base_font_size * .8) - revision: - margin_top: $base_font_size * 1.25 -block: - margin_top: 0 - margin_bottom: $vertical_rhythm -caption: - align: left - font_size: $base_font_size * 0.95 - font_style: italic - # FIXME perhaps set line_height instead of / in addition to margins? - margin_inside: $vertical_rhythm / 3 - #margin_inside: $vertical_rhythm / 4 - margin_outside: 0 -lead: - font_size: $base_font_size_large - line_height: 1.4 -abstract: - font_color: 5c6266 - font_size: $lead_font_size - line_height: $lead_line_height - font_style: italic - first_line_font_style: bold - title: - align: left - font_color: $heading_font_color - font_family: $heading_font_family - font_size: $heading_h4_font_size - font_style: $heading_font_style -sidebar: - font-style: italic - background-color: f5f5fc - border-color: 8d81b8 - border-radius: 3 - border-width: 0.2 -sidebar-title: - font_family: $heading_font_family - font-style: light - font-color: $heading-font-color - font-size: 11 - align: left -admonition: - font-style: italic - column_rule_color: $base_border_color - column_rule_width: $base_border_width - padding: [0, $horizontal_rhythm, 0, $horizontal_rhythm] - icon: - note: - # name: pencil-square-o - name: fas-info-circle - stroke_color: 6489b3 - tip: - # name: comments-o - name: far-comments - stroke_color: 646b74 - size: 24 - important: - # name: info - name: fas-info-circle - stroke_color: 5f8c8b - warning: - stroke_color: 9c4d4b - caution: - stroke_color: c99a2c - label: - text_transform: uppercase - font_style: bold -#blockquote: -# font_color: $base_font_color -# font_size: $base_font_size_large -# border_color: $base_border_color -# border_width: 2 -# FIXME disable negative padding bottom once margin collapsing is implemented -# padding: [0, $horizontal_rhythm, $block_margin_bottom * -0.75, $horizontal_rhythm + $blockquote_border_width / 2] -# cite_font_size: $base_font_size_small -# cite_font_color: 51278d -# code is used for source blocks (perhaps change to source or listing?) -code: - #font_color: $base_font_color - font-color: 000000 - font_family: $codespan_font_family - #font_size: ceil($base_font_size) - font-size: 11 - padding: $code_font_size - line_height: 1.15 - # line_gap is an experimental property to control how a background color is applied to an inline block element - line_gap: 3.8 - #background_color: f4f4fb - background_color: ffffff - #border_color: cccccc - #border_radius: $base_border_radius - #border_width: 0.2 - caption: - end: bottom -conum: - font_family: M+ 1mn - font_color: $codespan_font_color - font_size: $base_font_size - line_height: 4 / 3 -example: - border_color: $base_border_color - border_radius: $base_border_radius - border_width: 0.2 - background_color: ffffff - # FIXME re-enable padding bottom once margin collapsing is implemented - padding: [$vertical_rhythm, $horizontal_rhythm, 0, $horizontal_rhythm] -image: - align: left - caption: - align: center -prose: - margin_top: $block_margin_top - margin_bottom: $block_margin_bottom -thematic_break: - border_color: $base_border_color - border_style: solid - border_width: $base_border_width - margin_top: $vertical_rhythm * 0.5 - margin_bottom: $vertical_rhythm * 1.5 -description_list: - term_font_style: bold - term_spacing: $vertical_rhythm / 4 - description_indent: $horizontal_rhythm * 1.25 -list: - indent: $horizontal_rhythm * 1.5 - #marker_font_color: 404040 - # NOTE outline_list_item_spacing applies to list items that do not have complex content - item_spacing: $vertical_rhythm / 2 -figure: - caption: - end: bottom - align: center -table: - background_color: $page_background_color - #head_background_color: #2596be - #head_font_color: $base_font_color - head_font_style: bold - #body_background_color: - body_stripe_background_color: d7d7d7 - foot_background_color: f0f0f0 - border_color: dddddd - border_width: $base_border_width - cell_padding: 3 - caption: - end: bottom - align: center - text-align: center - max-width: none -toc: - indent: $horizontal_rhythm - line_height: 1.4 - dot_leader: - #content: ". " - font_color: a9a9a9 - #levels: 2 3 -# NOTE in addition to footer, header is also supported -header: - font_size: $base_font_size_small - # NOTE if background_color is set, background and border will span width of page - border_color: dddddd - border_width: 0.35 - height: $base_line_height_length * 2.6 - line_height: 1 - padding: [$base_line_height_length / 1, 1, 0, 1] - vertical_align: margin_inside - #image_vertical_align: or - # additional attributes for content: - # * {page-count} - # * {page-number} - # * {document-title} - # * {document-subtitle} - # * {chapter-title} - # * {section-title} - # * {section-or-chapter-title} - recto: - right: - content: "{section-or-chapter-title} | Page {page-number}" - verso: - left: - content: "{section-or-chapter-title} | Page {page-number}" - # left: 'Page {page-number} | {section-or-chapter-title}' -footer: - font_size: $base_font_size_small - # NOTE if background_color is set, background and border will span width of page - border_color: dddddd - border_width: 0.35 - height: $base_line_height_length * 2.6 - line_height: 1 - padding: [$base_line_height_length / 1, 1, .5, 1] - vertical_align: top - #image_vertical_align: or - # additional attributes for content: - # content: '{company}' - # * {page-count} - # * {page-number} - #center: - #content: '{document-title}' - # * {document-subtitle} - # * {chapter-title} - # * {section-title} - # * {section-or-chapter-title} - recto: - #columns: "<50% =0% >50%" - right: - #content: '{page-number}' - # content: 'Confidential - © Qualcomm Technologies, Inc. and/or its affiliated companies. May contain trade secrets.' - #content: '{document-title} | © RISC-V' - content: "{document-title}" - #center: '{page-number}' - #content: '{revdate}' - verso: - #columns: $footer_recto_columns - left: - content: $footer_recto_right_content - #center: '{page-number}' - #content: '{page-number}' diff --git a/cfgs/rv32/cfg.yaml b/cfgs/rv32.yaml similarity index 80% rename from cfgs/rv32/cfg.yaml rename to cfgs/rv32.yaml index ca61792af2..ab3912961b 100644 --- a/cfgs/rv32/cfg.yaml +++ b/cfgs/rv32.yaml @@ -1,4 +1,4 @@ -# yaml-language-server: $schema=../../schemas/config_schema.json +# yaml-language-server: $schema=../schemas/config_schema.json --- $schema: config_schema.json# kind: architecture configuration diff --git a/cfgs/rv64/cfg.yaml b/cfgs/rv64.yaml similarity index 80% rename from cfgs/rv64/cfg.yaml rename to cfgs/rv64.yaml index f75b8da9e0..6f5eae32c2 100644 --- a/cfgs/rv64/cfg.yaml +++ b/cfgs/rv64.yaml @@ -1,4 +1,4 @@ -# yaml-language-server: $schema=../../schemas/config_schema.json +# yaml-language-server: $schema=../schemas/config_schema.json --- $schema: config_schema.json# kind: architecture configuration diff --git a/container.def b/container.def index 3bb2c11042..2754ebb8c6 100644 --- a/container.def +++ b/container.def @@ -11,25 +11,30 @@ From: ubuntu:24.04 apt-get update - apt-get install -y --no-install-recommends git - apt-get install -y --no-install-recommends gh - - apt-get install -y --no-install-recommends less - - apt-get install -y --no-install-recommends python3 - apt-get install -y --no-install-recommends python3.12-venv python3-pip - - apt-get install -y --no-install-recommends build-essential - apt-get install -y --no-install-recommends ruby ruby-dev - apt-get install -y --no-install-recommends bundler - apt-get install -y --no-install-recommends nodejs - apt-get install -y --no-install-recommends npm - - apt-get install -y --no-install-recommends ditaa - apt-get install -y --no-install-recommends libyaml-dev - - apt-get install -y --no-install-recommends libyaml-dev - + apt-get install -y --no-install-recommends git \ + gh \ + less \ + python3 \ + python3.12-venv \ + python3-pip \ + build-essential \ + ruby \ + ruby-dev \ + libyaml-dev \ + bundler \ + nodejs \ + npm \ + ditaa \ + cmake \ + g++ \ + gdb \ + libgmp-dev \ + clang-format \ + clang-tidy \ + libelf-dev \ + gcc-riscv64-linux-gnu \ + gcc-riscv64-unknown-elf \ + libc6-dev-riscv64-cross # cleanup apt-get clean autoclean apt-get autoremove -y diff --git a/ext/riscv-tests b/ext/riscv-tests new file mode 160000 index 0000000000..0494f954a3 --- /dev/null +++ b/ext/riscv-tests @@ -0,0 +1 @@ +Subproject commit 0494f954a3d8d2ca9e4972da7a01e94b6a909bce diff --git a/lib/DB_MODEL.README.adoc b/lib/DB_MODEL.README.adoc index d53b5d6e16..981bda2cf5 100644 --- a/lib/DB_MODEL.README.adoc +++ b/lib/DB_MODEL.README.adoc @@ -14,7 +14,7 @@ A configuration consists of a folder under `cfgs`. Inside that folder, there are A YAML object (hash) that currently contains only one field `type`. `type` can be one of: * "partially configured": The configuration has some parameters and/or implemented extensions known, but others are not known. Examples of a _partially configured_ configuration are the generic _32/_64 configs and a profile (which has some known/mandatory extensions, but also many unknown/optional extensions). -* "fully configured": The configuration exhaustively lists a set of implemented extensions and parameters. An example of a _fully configured_ configuration is the `generic_rv64` example, which represents a theoretical implementation of RV64. In a _fully configured_ configuration, any extension that isn't known to be implemented is treated as unimplemented, and will be pruned out of the database for certain operations. +* "fully configured": The configuration exhaustively lists a set of implemented extensions and parameters. An example of a _fully configured_ configuration is the `example_rv64_with_overlay` example, which represents a theoretical implementation of RV64. In a _fully configured_ configuration, any extension that isn't known to be implemented is treated as unimplemented, and will be pruned out of the database for certain operations. `implemented_exts.yaml`:: @@ -51,7 +51,7 @@ params: A configuration can customize the standard RISC-V specification by providing an `arch_overlay` directory. This can be used to, for example, describe a custom extension or to create custom behavior of a standard instructions. -The `arch_overlay` directory is treated as an overlay on the standard `arch` directory. The contents of any file found in `arch_overlay` is either merged on top of the corresponding file in `arch`, if such a file exists, or added to the overall specification (_e.g._, when defining a new extension). An example of an overlay can be found in `cfgs/generic_rv64/arch_overlay`. +The `arch_overlay` directory is treated as an overlay on the standard `arch` directory. The contents of any file found in `arch_overlay` is either merged on top of the corresponding file in `arch`, if such a file exists, or added to the overall specification (_e.g._, when defining a new extension). An example of an overlay can be found in `cfgs/example_rv64_with_overlay/arch_overlay`. == ArchDef interface diff --git a/lib/arch_obj_models/csr.rb b/lib/arch_obj_models/csr.rb index 38bbc99e51..ff1639856b 100644 --- a/lib/arch_obj_models/csr.rb +++ b/lib/arch_obj_models/csr.rb @@ -46,68 +46,56 @@ 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? + # @return [Boolean] true if this CSR is defined when XLEN is xlen + # @param xlen [32,64] base + def defined_in_base?(xlen) = @data["base"].nil? || @data["base"] == xlen + # @param cfg_arch [ConfiguredArchitecture] 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) - end + def format_changes_with_xlen? + dynamic_length? || possible_fields.any?(&:dynamic_location?) end - # @param cfg_arch [ConfiguredArchitecture] 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(effective_xlen = nil) 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 - symtab.push(ast) - fns.concat(ast.reachable_functions(symtab)) + xlens = + if cfg_arch.multi_xlen? + defined_in_all_bases? ? [32, 64] : [base] + else + [cfg_arch.possible_xlens[0]] + end + xlens.each do |xlen| + ast = pruned_sw_read_ast(xlen) + symtab = cfg_arch.symtab.deep_clone + symtab.push(ast) + fns.concat(ast.reachable_functions(symtab)) + end end if cfg_arch.multi_xlen? - implemented_fields_for(cfg_arch, 32).each do |field| - fns.concat(field.reachable_functions(cfg_arch, 32)) + possible_fields_for(32).each do |field| + fns.concat(field.reachable_functions(32)) end - implemented_fields_for(cfg_arch, 64).each do |field| - fns.concat(field.reachable_functions(cfg_arch, 64)) + possible_fields_for(64).each do |field| + fns.concat(field.reachable_functions(64)) end else - implemented_fields_for(cfg_arch, cfg_arch.mxlen).each do |field| - fns.concat(field.reachable_functions(cfg_arch, cfg_arch.mxlen)) + possible_fields_for(cfg_arch.mxlen).each do |field| + fns.concat(field.reachable_functions(cfg_arch.mxlen)) end end @reachable_functions = fns.uniq end - # @param cfg_arch [ConfiguredArchitecture] Architecture definition - # @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) - 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)) - end - - fields.each do |field| - fns.concat(field.reachable_functions_unevaluated(cfg_arch)) - end - - @reachable_functions_unevaluated = fns.uniq - end - - # @param cfg_arch [ConfiguredArchitecture] 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? return false if @data["length"].is_a?(Integer) # when a CSR is only defined in one base, its length can't change @@ -131,10 +119,10 @@ def dynamic_length?(cfg_arch) # @param cfg_arch [ConfiguredArchitecture] Architecture definition # @return [Integer] Smallest length of the CSR in any mode - def min_length(cfg_arch) + def min_length case @data["length"] when "MXLEN", "SXLEN", "VSXLEN" - 32 + @cfg_arch.possible_xlens.min when Integer @data["length"] else @@ -146,7 +134,7 @@ def min_length(cfg_arch) # @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) + def length(effective_xlen = nil) case @data["length"] when "MXLEN" return cfg_arch.mxlen unless cfg_arch.mxlen.nil? @@ -193,7 +181,7 @@ 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 return @data["base"] unless @data["base"].nil? case @data["length"] @@ -256,8 +244,8 @@ def length_cond64 # @param cfg_arch [ConfiguredArchitecture] A configuration # @return [String] Pretty-printed length string - def length_pretty(cfg_arch, effective_xlen=nil) - if dynamic_length?(cfg_arch) + def length_pretty(effective_xlen=nil) + if dynamic_length? cond = case @data["length"] when "MXLEN" @@ -272,14 +260,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(32)} when #{cond.sub('%%', '0')} + #{length(64)} when #{cond.sub('%%', '1')} LENGTH else - "#{length(cfg_arch, effective_xlen)}-bit" + "#{length(effective_xlen)}-bit" end else - "#{length(cfg_arch)}-bit" + "#{length()}-bit" end end @@ -309,35 +297,17 @@ def description_html # @param cfg_arch [ConfiguredArchitecture] 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) - @implemented_fields_for ||= {} - key = [cfg_arch.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| + def possible_fields_for(effective_xlen) + @possible_fields_for ||= + possible_fields.select do |f| !f.key?("base") || f.base == effective_xlen end end - # @param cfg_arch [ConfiguredArchitecture] A configuration # @return [Array] All implemented fields for this CSR # Excluded any fields that are defined by unimplemented extensions - def implemented_fields(cfg_arch) - 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 - [32, 64] - else - [cfg_arch.param_values["XLEN"]] - end - - @implemented_fields = fields.select do |f| + def possible_fields + @possible_fields ||= fields.select do |f| f.exists_in_cfg?(cfg_arch) end end @@ -383,9 +353,9 @@ def field(field_name) def bitfield_type(cfg_arch, effective_xlen = nil) Idl::BitfieldType.new( "Csr#{name.capitalize}Bitfield", - length(cfg_arch, effective_xlen), + length(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(effective_xlen) } ) end @@ -395,15 +365,20 @@ def has_custom_sw_read? end # @param symtab [Idl::SymbolTable] Symbol table with globals - def type_checked_sw_read_ast(symtab) + def type_checked_sw_read_ast(effective_xlen) @type_checked_sw_read_asts ||= {} - ast = @type_checked_sw_read_asts[symtab.hash] + ast = @type_checked_sw_read_asts[effective_xlen.nil? ? :none : effective_xlen] return ast unless ast.nil? - symtab_hash = symtab.hash - symtab = symtab.global_clone + symtab = cfg_arch.symtab.global_clone symtab.push(ast) # all CSR instructions are 32-bit + unless effective_xlen.nil? + symtab.add( + "__effective_xlen", + Idl::Var.new("__effective_xlen", Idl::Type.new(:bits, width: 6), effective_xlen) + ) + end symtab.add( "__instruction_encoding_size", Idl::Var.new("__instruction_encoding_size", Idl::Type.new(:bits, width: 6), 32) @@ -421,7 +396,7 @@ def type_checked_sw_read_ast(symtab) ) symtab.pop symtab.release - @type_checked_sw_read_asts[symtab_hash] = ast + @type_checked_sw_read_asts[effective_xlen.nil? ? :none : effective_xlen] = ast end # @return [FunctionBodyAst] The abstract syntax tree of the sw_read() function @@ -450,14 +425,10 @@ def sw_read_ast(symtab) @sw_read_ast end - def pruned_sw_read_ast(cfg_arch) - @pruned_sw_read_asts ||= {} - ast = @pruned_sw_read_asts[cfg_arch.name] - return ast unless ast.nil? - - ast = type_checked_sw_read_ast(cfg_arch.symtab) - - symtab = cfg_arch.symtab.global_clone + # @param ast [Idl::AstNode] An abstract syntax tree that will be evaluated with the returned symbol table + # @return [IdL::SymbolTable] A symbol table populated with globals and syms specific to this CSR + def fill_symtab(ast, effective_xlen) + symtab = @cfg_arch.symtab.global_clone symtab.push(ast) # all CSR instructions are 32-bit symtab.add( @@ -468,11 +439,32 @@ def pruned_sw_read_ast(cfg_arch) "__expected_return_type", Idl::Type.new(:bits, width: 128) ) + if symtab.get("XLEN").value.nil? + symtab.add( + "XLEN", + Idl::Var.new( + "XLEN", + Idl::Type.new(:bits, width: 6, qualifiers: [:const]), + effective_xlen, + param: true + ) + ) + end + symtab + end + + def pruned_sw_read_ast(effective_xlen) + @pruned_sw_read_ast ||= {} + return @pruned_sw_read_ast[effective_xlen] unless @pruned_sw_read_ast[effective_xlen].nil? + + ast = type_checked_sw_read_ast(effective_xlen) + + symtab = fill_symtab(ast, effective_xlen) ast = ast.prune(symtab) - ast.freeze_tree(cfg_arch.symtab) + ast.freeze_tree(@cfg_arch.symtab) - cfg_arch.idl_compiler.type_check( + @cfg_arch.idl_compiler.type_check( ast, symtab, "CSR[#{name}].sw_read()" @@ -481,7 +473,7 @@ def pruned_sw_read_ast(cfg_arch) symtab.pop symtab.release - @pruned_sw_read_asts[cfg_arch.name] = ast + @pruned_sw_read_ast[effective_xlen] = ast end # @example Result for an I-type instruction @@ -506,48 +498,43 @@ def wavedrom_desc(cfg_arch, effective_xlen, exclude_unimplemented: false, option field_list = if exclude_unimplemented - implemented_fields_for(cfg_arch, effective_xlen) + possible_fields_for(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(effective_xlen).min <=> b.location(effective_xlen).min } field_list.each do |field| - if field.location(cfg_arch, effective_xlen).min != last_idx + 1 + if field.location(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(effective_xlen).min - last_idx - 1 + raise "negative reserved space? #{n} #{name} #{field.location(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 } + desc["reg"] << { "bits" => field.location(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(effective_xlen).size, "name" => field.name, type: 3 } end - last_idx = field.location(cfg_arch, effective_xlen).max + last_idx = field.location(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(effective_xlen).max != (length(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(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(effective_xlen) } + desc["config"]["lanes"] = length(effective_xlen) / 16 desc end # @param cfg_arch [ConfiguredArchitecture] Architecture def - # @return [Boolean] whether or not the CSR is possibly implemented given the supplies config options + # @return [Boolean] whether or not the CSR is possibly implemented given the supplied 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) } - 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) } } - end + @exists_in_cfg ||= + cfg_arch.possible_csrs.include?(self) end # @param cfg_arch [ConfiguredArchitecture] Architecture def @@ -555,11 +542,20 @@ def exists_in_cfg?(cfg_arch) 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? - exists_in_cfg?(cfg_arch) && - cfg_arch.mandatory_extensions.all? do |ext_req| - ext_req.satisfying_versions.none? do |ext_ver| - defined_by?(ext_ver) + # exists in config and isn't satisfied by some combo of mandatory extensions + @optional_in_cfg ||= + exists_in_cfg?(cfg_arch) && + !defined_by_condition.satisfied_by? do |defining_ext_req| + cfg_arch.mandatory_extension_reqs.any? do |mand_ext_req| + mand_ext_req.satisfying_versions.any? do |mand_ext_ver| + defining_ext_req.satisfied_by?(mand_ext_ver) + end end end end + + # @return [Boolean] Whether or not the presence of ext_ver affects this CSR definition + def affected_by?(ext_ver) + defined_by_condition.possibly_satisfied_by?(ext_ver) || fields.any? { |field| field.affected_by?(ext_ver) } + end end diff --git a/lib/arch_obj_models/csr_field.rb b/lib/arch_obj_models/csr_field.rb index df4b532615..fac8982a53 100644 --- a/lib/arch_obj_models/csr_field.rb +++ b/lib/arch_obj_models/csr_field.rb @@ -29,20 +29,21 @@ def initialize(parent_csr, field_name, field_data) @parent = parent_csr 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 + # For a full config, whether or not the field is implemented + # For a partial config, whether or the it is possible for the field to be implemented + # + # @return [Boolean] True if this field might exist in a config 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) }) - else - raise "unexpected type" unless cfg_arch.partially_configured? - + (@data["definedBy"].nil? || cfg_arch.transitive_implemented_extension_versions.any? { |ext_ver| defined_by_condition.possibly_satisfied_by?(ext_ver) }) + elsif cfg_arch.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) } }) + (@data["definedBy"].nil? || cfg_arch.possible_extension_versions.any? { |ext_ver| defined_by_condition.possibly_satisfied_by?(ext_ver) } ) + else + true end end @@ -55,31 +56,37 @@ def optional_in_cfg?(cfg_arch) if data["definedBy"].nil? parent.optional_in_cfg?(cfg_arch) else - cfg_arch.mandatory_extensions.all? do |ext_req| - ext_req.satisfying_versions.none? do |ext_ver| - defined_by?(ext_ver) - end + cfg_arch.prohibited_extension_versions.none? do |ext_ver| + defined_by_condition.possibly_satisfied_by?(ext_ver) end end ) end + # @return [Boolean] Whether or not the presence of ext_ver affects this CSR Field definition + # This does not take the parent CSR into account, i.e., a field can be unaffected + # by ext_ver even if the parent CSR is affected + def affected_by?(ext_ver) + @data["definedBy"].nil? ? false : defined_by_condition.possibly_satisfied_by?(version) + end + # @return [Idl::FunctionBodyAst] Abstract syntax tree of the type() function # @return [nil] if the type property is not a function - # @param symtab [SymbolTable] Symbol table with execution context - def type_ast(symtab) + def type_ast 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 = @cfg_arch.idl_compiler.compile_func_body( @data["type()"], name: "CSR[#{csr.name}].#{name}.type()", input_file: csr.__source, input_line: csr.source_line("fields", name, "type()"), - symtab:, + symtab: @cfg_arch.symtab, type_check: false ) + raise "ast is nil?" if @type_ast.nil? + raise "unexpected #{@type_ast.class}" unless @type_ast.is_a?(Idl::FunctionBodyAst) @type_ast @@ -87,54 +94,51 @@ def type_ast(symtab) # @return [Idl::FunctionBodyAst] Abstract syntax tree of the type() function, after it has been type checked # @return [nil] if the type property is not a function - # @param symtab [Idl::SymbolTable] Symbol table - def type_checked_type_ast(symtab) - @type_checked_type_asts ||= {} - ast = @type_checked_type_asts[symtab.hash] - return ast unless ast.nil? + # @param effective_xlen [32, 64] The effective xlen to evaluate for + def type_checked_type_ast(effective_xlen) + @type_checked_type_ast ||= { 32 => nil, 64 => nil } + return @type_checked_type_ast[effective_xlen] unless @type_checked_type_ast[effective_xlen].nil? - symtab_hash = symtab.hash + ast = type_ast - symtab = symtab.global_clone + if ast.nil? + # there is no type() (it must be constant) + return nil + end - symtab.push(ast) - # all CSR instructions are 32-bit - symtab.add( - "__expected_return_type", - Idl::Type.new(:enum_ref, enum_class: symtab.get("CsrFieldType")) - ) + symtab = fill_symtab_for_type(effective_xlen, ast) - ast = type_ast(symtab) symtab.cfg_arch.idl_compiler.type_check( ast, symtab, "CSR[#{name}].type()" ) + symtab.pop symtab.release - @type_checked_type_asts[symtab_hash] = ast + @type_checked_type_ast[effective_xlen] = ast end # @return [Idl::FunctionBodyAst] Abstract syntax tree of the type() function, after it has been type checked and pruned # @return [nil] if the type property is not a function - # @param symtab [Idl::SymbolTable] Global symbols - def pruned_type_ast(symtab) - @pruned_type_asts ||= {} - ast = @pruned_type_asts[symtab.hash] - return ast unless ast.nil? + # @param effective_xlen [32, 64] The effective xlen to evaluate for + def pruned_type_ast(effective_xlen) + @pruned_type_ast ||= { 32 => nil, 64 => nil } + return @pruned_type_ast[effective_xlen] unless @pruned_type_ast[effective_xlen].nil? - ast = type_checked_type_ast(symtab).prune(symtab.deep_clone) + ast = type_checked_type_ast(effective_xlen) - symtab_hash = symtab.hash - symtab = symtab.global_clone - symtab.push(ast) - # all CSR instructions are 32-bit - symtab.add( - "__expected_return_type", - Idl::Type.new(:enum_ref, enum_class: symtab.get("CsrFieldType")) - ) + if ast.nil? + # there is no type() (it must be constant) + return nil + end + symtab = fill_symtab_for_type(effective_xlen, ast) + ast = ast.prune(symtab) + symtab.release + + symtab = fill_symtab_for_type(effective_xlen, ast) ast.freeze_tree(symtab) symtab.cfg_arch.idl_compiler.type_check( @@ -142,14 +146,16 @@ def pruned_type_ast(symtab) symtab, "CSR[#{name}].type()" ) + symtab.pop symtab.release - @pruned_type_asts[symtab_hash] = ast + + @pruned_type_ast[effective_xlen] = ast end # returns the definitive type for a configuration # - # @param symtab [SymbolTable] Symbol table + # @param effective_xlen [32, 64] The effective xlen to evaluate for # @return [String] # The type of the field. One of: # 'RO' => Read-only @@ -158,14 +164,9 @@ def pruned_type_ast(symtab) # 'RW-R' => Read-write, with a restricted set of legal values # 'RW-H' => Read-write, with a hardware update # 'RW-RH' => Read-write, with a hardware update and a restricted set of legal values - 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) - - return @type_cache[symtab.cfg_arch] - end + def type(effective_xlen = nil) + @type ||= { 32 => nil, 64 => nil } + return @type[effective_xlen] unless @type[effective_xlen].nil? type = if @data.key?("type") @@ -176,27 +177,31 @@ def type(symtab) raise "type() is nil for #{csr.name}.#{name} #{@data}?" if idl.nil? # value_result = Idl::AstNode.value_try do - ast = type_checked_type_ast(symtab) + ast = type_checked_type_ast(effective_xlen) begin - symtab = symtab.global_clone - - symtab.push(ast) - type = case ast.return_value(symtab) - when 0 - "RO" - when 1 - "RO-H" - when 2 - "RW" - when 3 - "RW-R" - when 4 - "RW-H" - when 5 - "RW-RH" - else - raise "Unhandled CsrFieldType value" - end + symtab = fill_symtab_for_type(effective_xlen, ast) + + value_result = ast.value_try do + type = case ast.return_value(symtab) + when 0 + "RO" + when 1 + "RO-H" + when 2 + "RW" + when 3 + "RW-R" + when 4 + "RW-H" + when 5 + "RW-RH" + else + raise "Unhandled CsrFieldType value" + end + end + ast.value_else(value_result) do + type = nil + end ensure symtab.pop symtab.release @@ -210,20 +215,20 @@ def type(symtab) # end end - @type_cache ||= {} - @type_cache[symtab.cfg_arch] = type + @type[effective_xlen] = type end # @return [String] A pretty-printed type string - def type_pretty(symtab) - raise ArgumentError, "Expecting SymbolTable" unless symtab.is_a?(Idl::SymbolTable) + # @param effective_xlen [32, 64] The effective xlen to evaluate for + def type_pretty(effective_xlen = nil) + raise ArgumentError, "Expecting Integer" unless effective_xlen.nil? || effective_xlen.is_a?(Integer) str = nil value_result = Idl::AstNode.value_try do - str = type(symtab) + str = type(effective_xlen) end Idl::AstNode.value_else(value_result) do - ast = type_ast(symtab) + ast = type_ast str = ast.gen_option_adoc end str @@ -259,108 +264,64 @@ def alias # @return [Array] List of functions called through this field # @param cfg_arch [ConfiguredArchitecture] a configuration # @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(effective_xlen) return @reachable_functions unless @reachable_functions.nil? - symtab = - if (cfg_arch.configured?) - cfg_arch.symtab - else - raise ArgumentError, "Must supply effective_xlen for generic ConfiguredArchitecture" if effective_xlen.nil? - - if effective_xlen == 32 - cfg_arch.symtab_32 - else - cfg_arch.symtab_64 - end - end - fns = [] if has_custom_sw_write? - ast = pruned_sw_write_ast(cfg_arch, effective_xlen) + ast = pruned_sw_write_ast(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 = fill_symtab_for_sw_write(effective_xlen, ast) fns.concat ast.reachable_functions(sw_write_symtab) + sw_write_symtab.release end end if @data.key?("type()") - ast = pruned_type_ast(symtab.deep_clone) + ast = pruned_type_ast(effective_xlen) unless ast.nil? - fns.concat ast.reachable_functions(symtab.deep_clone.push(ast)) + type_symtab = fill_symtab_for_type(effective_xlen, ast) + fns.concat ast.reachable_functions(type_symtab) + type_symtab.release end end if @data.key?("reset_value()") - ast = pruned_reset_value_ast(symtab.deep_clone) + ast = pruned_reset_value_ast unless ast.nil? - fns.concat ast.reachable_functions(symtab.deep_clone.push(ast)) + symtab = fill_symtab_for_reset(ast) + fns.concat ast.reachable_functions(symtab) + symtab.release end end @reachable_functions = fns.uniq end - # @return [Array] List of functions called through this field, irrespective of context - # @param symtab [SymbolTable] - def reachable_functions_unevaluated(symtab) - raise ArgumentError, "Argument should be a symtab" unless symtab.is_a?(Idl::SymbolTable) - - return @reachable_functions_unevaluated unless @reachable_functions_unevaluated.nil? - - fns = [] - if has_custom_sw_write? - ast = sw_write_ast(symtab) - unless ast.nil? - fns.concat ast.reachable_functions_unevaluated(symtab) - end - end - if @data.key?("type()") - ast = type_ast(symtab) - unless ast.nil? - fns.concat ast.reachable_functions_unevaluated(symtab) - end - end - if @data.key?("reset_value()") - ast = reset_value_ast(symtab) - unless ast.nil? - fns.concat ast.reachable_functions_unevalutated(symtab) - end - end - - @reachable_functions_unevaluated = fns.uniq - end - # @return [Csr] Parent CSR for this field alias csr parent - # @param cfg_arch [ConfiguredArchitecture] A configuration # @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? # 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| @cfg_arch.multi_xlen_in_mode?(mode) } end - # @param cfg_arch [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) - raise ArgumentError, "Argument should be a symtab (is a #{symtab.class.name})" unless symtab.is_a?(Idl::SymbolTable) - + def reset_value_ast 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 = cfg_arch.idl_compiler.compile_func_body( @data["reset_value()"], - return_type: Idl::Type.new(:bits, width: 64), + return_type: Idl::Type.new(:bits, width: max_width), name: "CSR[#{parent.name}].#{name}.reset_value()", input_file: csr.__source, input_line: csr.source_line("fields", name, "reset_value()"), - symtab:, + symtab: cfg_arch.symtab, type_check: false ) end @@ -368,105 +329,92 @@ def reset_value_ast(symtab) # @param symtab [Idl::SymbolTable] A symbol table with globals # @return [Idl::FunctionBodyAst] Abstract syntax tree of the reset_value function, after being type checked # @return [nil] If the reset_value is not a function - def type_checked_reset_value_ast(symtab) - raise ArgumentError, "Expecting Idl::SymbolTable" unless symtab.is_a?(Idl::SymbolTable) - - @type_checked_reset_value_asts ||= {} - ast = @type_checked_reset_value_asts[symtab.hash] - return ast unless ast.nil? + def type_checked_reset_value_ast + return @type_checked_reset_value_ast unless @type_checked_reset_value_ast.nil? return nil unless @data.key?("reset_value()") - ast = reset_value_ast(symtab) + ast = reset_value_ast - symtab_hash = symtab.hash - 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 = fill_symtab_for_reset(ast) + cfg_arch.idl_compiler.type_check( ast, symtab, "CSR[#{csr.name}].reset_value()" ) - @type_checked_reset_value_asts[symtab_hash] = ast + symtab.release + + @type_checked_reset_value_ast = ast end - # @param symtab [Idl::SymbolTable] Global symbol table # @return [Idl::FunctionBodyAst] Abstract syntax tree of the reset_value function, type checked and pruned # @return [nil] If the reset_value is not a function - def pruned_reset_value_ast(symtab) - @pruned_reset_value_asts ||= {} - ast = @pruned_reset_value_asts[symtab.hash] - return ast unless ast.nil? + def pruned_reset_value_ast + return @pruned_reset_value_ast unless @pruned_reset_value_ast.nil? return nil unless @data.key?("reset_value()") - ast = type_checked_reset_value_ast(symtab) - - symtab_hash = symtab.hash - symtab = symtab.deep_clone - symtab.push(ast) - symtab.add("__expected_return_type", Idl::Type.new(:bits, width: 64)) + ast = type_checked_reset_value_ast + symtab = fill_symtab_for_reset(ast) ast = ast.prune(symtab) - symtab.pop - ast.freeze_tree(symtab) + symtab.release - symtab.push(ast) - symtab.add("__expected_return_type", Idl::Type.new(:bits, width: 64)) - symtab.cfg_arch.idl_compiler.type_check( - ast, - symtab, - "CSR[#{csr.name}].#{name}.reset_value()" - ) - - @type_checked_reset_value_asts[symtab_hash] = ast + @pruned_reset_value_ast = ast end - # @param cfg_arch [ConfiguredArchitecture] A config # @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] - return cached_value if cached_value - - @reset_value_cache ||= {} - - @reset_value_cache[cfg_arch] = + def reset_value + defer :reset_value do if @data.key?("reset_value") @data["reset_value"] else - symtab = cfg_arch.symtab - ast = pruned_reset_value_ast(symtab.deep_clone) - val = ast.return_value(symtab.deep_clone.push(ast)) + ast = pruned_reset_value_ast + symtab = fill_symtab_for_reset(ast) + val = nil + value_result = Idl::AstNode.value_try do + val = ast.return_value(symtab) + end + Idl::AstNode.value_else(value_result) do + val = "UNDEFINED_LEGAL" + end val = "UNDEFINED_LEGAL" if val == 0x1_0000_0000_0000_0000 + symtab.release val end + end end - def dynamic_reset_value?(cfg_arch) + def dynamic_reset_value? return false unless @data["reset_value"].nil? - value_result = Idl::AstNode.value_try do - reset_value(cfg_arch) + Idl::AstNode.value_try do + reset_value false end || true end - def reset_value_pretty(cfg_arch) + def reset_value_pretty str = nil value_result = Idl::AstNode.value_try do - str = reset_value(cfg_arch) + str = reset_value end Idl::AstNode.value_else(value_result) do - ast = reset_value_ast(cfg_arch.symtab) + ast = reset_value_ast str = ast.gen_option_adoc end str end + # @return [Boolean] true if the field could have an undefined value at any point + def could_be_undefined? + (reset_value == "UNDEFINED_LEGAL") || \ + (has_custom_sw_write? && sw_write_ast(cfg_arch.symtab).could_return_undefined?(cfg_arch.symtab)) + end + # @return [Boolean] true if the CSR field has a custom sw_write function def has_custom_sw_write? @data.key?("sw_write(csr_value)") && !@data["sw_write(csr_value)"].empty? @@ -535,21 +483,10 @@ def sw_write_ast(symtab) @sw_write_ast end - # @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) - @pruned_sw_write_asts ||= {} - ast = @pruned_sw_write_asts[cfg_arch.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? - + def fill_symtab_for_sw_write(effective_xlen, ast) symtab = cfg_arch.symtab.global_clone symtab.push(ast) + # all CSR instructions are 32-bit symtab.add( "__instruction_encoding_size", @@ -561,16 +498,84 @@ 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(@cfg_arch, effective_xlen)) ) + if symtab.get("XLEN").value.nil? + symtab.add( + "XLEN", + Idl::Var.new( + "XLEN", + Idl::Type.new(:bits, width: 6, qualifiers: [:const]), + effective_xlen, + param: true + ) + ) + end + symtab + end + + def fill_symtab_for_type(effective_xlen, ast) + symtab = cfg_arch.symtab.global_clone + symtab.push(ast) + + # all CSR instructions are 32-bit + symtab.add( + "__instruction_encoding_size", + Idl::Var.new("__instruction_encoding_size", Idl::Type.new(:bits, width: 6), 32) + ) + symtab.add( + "__expected_return_type", + Idl::Type.new(:enum_ref, enum_class: symtab.get("CsrFieldType")) + ) + if symtab.get("XLEN").value.nil? + symtab.add( + "XLEN", + Idl::Var.new( + "XLEN", + Idl::Type.new(:bits, width: 6, qualifiers: [:const]), + effective_xlen, + param: true + ) + ) + end + + symtab + end + + def fill_symtab_for_reset(ast) + symtab = cfg_arch.symtab.global_clone + symtab.push(ast) + + symtab.add("__expected_return_type", Idl::Type.new(:bits, width: max_width)) + + # XLEN at reset is always mxlen + symtab.add( + "__effective_xlen", + Idl::Var.new("__effective_xlen", Idl::Type.new(:bits, width: 6), cfg_arch.mxlen) + ) + + symtab + end + + # @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 + def pruned_sw_write_ast(effective_xlen) + return @pruned_sw_write_ast unless @pruned_sw_write_ast.nil? + + return nil unless @data.key?("sw_write(csr_value)") ast = type_checked_sw_write_ast(cfg_arch.symtab, effective_xlen) + + return ast if cfg_arch.unconfigured? + + symtab = fill_symtab_for_sw_write(effective_xlen, ast) + ast = ast.prune(symtab) raise "Symbol table didn't come back at global + 1" unless symtab.levels == 2 ast.freeze_tree(cfg_arch.symtab) - cfg_arch.idl_compiler.type_check( ast, symtab, @@ -580,13 +585,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_ast = ast end # @param cfg_arch [ConfiguredArchitecture] A config. May be nil if the location is not configturation-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(effective_xlen = nil) key = if @data.key?("location") "location" @@ -599,14 +604,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(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 + raise "Location (#{key} = #{@data[key]}) is past the max csr length (#{csr.max_length}) 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(effective_xlen)}) in #{csr.name}.#{name}" end @data[key]..@data[key] @@ -614,11 +619,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(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 + raise "Location (#{key} = #{@data[key]}) is past the max csr length (#{csr.max_length}) 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}" @@ -637,15 +642,28 @@ def base32_only? = @data.key?("base") && @data["base"] == 32 def defined_in_base32? = @data["base"].nil? || @data["base"] == 32 def defined_in_base64? = @data["base"].nil? || @data["base"] == 64 + def defined_in_base?(xlen) = @data["base"].nil? || @data["base"] == xlen # @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 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(effective_xlen) + location(effective_xlen).size + end + + def max_width + @max_width ||= + if base64_only? + cfg_arch.possible_xlens.include?(64) ? width(64) : 0 + elsif base32_only? + cfg_arch.possible_xlens.include?(32) ? width(32) : 0 + else + @cfg_arch.possible_xlens.map do |xlen| + width(xlen) + end.max + end end def location_cond32 @@ -675,14 +693,14 @@ def location_cond64 end # @return [String] Pretty-printed location string - def location_pretty(cfg_arch, effective_xlen = nil) + def location_pretty(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? condition = case csr.priv_mode when "M" @@ -697,14 +715,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(32))} when #{condition.sub('%%', '0')} + * #{derangeify.call(location(64))} when #{condition.sub('%%', '1')} LOC else - derangeify.call(location(cfg_arch, effective_xlen)) + derangeify.call(location(effective_xlen)) end else - derangeify.call(location(cfg_arch, cfg_arch.mxlen)) + derangeify.call(location(cfg_arch.mxlen)) end end @@ -758,7 +776,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(effective_xlen=nil) + TYPE_DESC_MAP[type(effective_xlen)] end end diff --git a/lib/arch_obj_models/extension.rb b/lib/arch_obj_models/extension.rb index 120154da73..36ffd2cfac 100644 --- a/lib/arch_obj_models/extension.rb +++ b/lib/arch_obj_models/extension.rb @@ -36,6 +36,12 @@ def schema_type @schema.to_pretty_s end + def default + if @data["schema"].key?("default") + @data["schema"]["default"] + end + end + # @param ext [Extension] # @param name [String] # @param data [Hash] Array of extensions implied by any version of this extension meeting version_requirement + # @return [Array] Array of extensions implied by the largest 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) + max_version.implications else - return [] unless ExtensionRequirement.new(@new, version_requirement, arch: @arch).satisfied_by?(max_version.version) + mv = ExtensionRequirement.new(@name, version_requirement, cfg_arch: @cfg_arch).max_version + mv.implications end - - max_version.implications end - # @return [Array] List of conflicting extension requirements - def conflicts - return [] if @data["conflicts"].nil? - - if @data["conflicts"].is_a?(String) - [ExtensionRequirement.new(@data["conflicts"], arch: @arch)] - elsif @data["conflicts"].is_a?(Hash) - [ExtensionRequirement.new(@data["conflicts"]["name"], @data["conflicts"]["version"], arch: @arch)] - elsif @data["conflicts"].is_a?(Array) - @data["conflicts"].map do |conflict| - if conflict.is_a?(String) - ExtensionRequirement.new(conflict, arch: @arch) - elsif conflict.is_a?(Array) - ExtensionRequirement.new(conflict["name"], conflict["version"], arch: @arch) - else - raise "Invalid conflicts data: #{conflict.inspect}" - end + # @return [ExtensionRequirementExpression] Logic expression for conflicts + def conflicts_condition + @conflicts_condition ||= + if @data["conflicts"].nil? + AlwaysFalseExtensionRequirementExpression.new + else + ExtensionRequirementExpression.new(@data["conflicts"], @cfg_arch) end - else - raise "Invalid conflicts data: #{@data["conflicts"].inspect}" - end end # @return [Array] the list of instructions implemented by *any version* of this extension (may be empty) def instructions - return @instructions unless @instructions.nil? - - @instructions = arch.instructions.select { |i| versions.any? { |v| i.defined_by?(v) }} + @instructions ||= cfg_arch.instructions.select { |i| versions.any? { |v| i.defined_by_condition.possibly_satisfied_by?(v) }} end # @return [Array] the list of CSRs implemented by *any version* of this extension (may be empty) def csrs - return @csrs unless @csrs.nil? - - @csrs = arch.csrs.select { |csr| versions.any? { |v| csr.defined_by?(v) } } + @csrs ||= cfg_arch.csrs.select { |csr| versions.any? { |v| csr.defined_by_condition.possibly_satisfied_by?(v) } } end # return the set of reachable functions from any of this extensions's CSRs or instructions in the given evaluation context # - # @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? + def reachable_functions + return @reachable_functions unless @reachable_functions.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) + funcs += inst.reachable_functions(32) if inst.defined_in_base?(32) + funcs += inst.reachable_functions(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) + funcs += csr.reachable_functions end - @reachable_functions[symtab] = funcs.uniq + @reachable_functions = funcs.uniq + end + + def <=>(other_ext) + raise ArgumentError, "Can only compare two Extensions" unless other_ext.is_a?(Extension) + other_ext.name <=> name end end @@ -282,6 +274,8 @@ class ExtensionVersion # @return [String] attr_reader :version_str + attr_reader :arch + # @param name [#to_s] The extension name # @param version [String] The version specifier # @param arch [Architecture] The architecture definition @@ -383,100 +377,88 @@ def to_s "#{name}@#{@version_spec.canonical}" end - # @return [SchemaCondition] Condition that must be met for this version to be allowed. - # Transitively includes any requirements from an implied extension. + # @return [ExtensionRequirementExpression] Condition that must be met for this version to be allowed. def requirement_condition @requirement_condition ||= begin r = case @data["requires"] when nil - AlwaysTrueSchemaCondition.new + AlwaysTrueExtensionRequirementExpression.new when Hash - SchemaCondition.new(@data["requires"], @arch) + ExtensionRequirementExpression.new(@data["requires"], @arch) else - SchemaCondition.new({ "oneOf" => [@data["requires"]] }, @arch) + ExtensionRequirementExpression.new({ "oneOf" => [@data["requires"]] }, @arch) end - if @data.key?("implies") - rs = [r] + implications.map(&:requirement_condition) - rs = rs.reject(&:empty?) - r = SchemaCondition.all_of(*rs.map(&:to_h), arch: @arch) unless rs.empty? - end r end end - # @return [Array] List of extensions that conflict with this ExtensionVersion - # The list is *not* transitive; if conflict C1 implies C2, - # only C1 shows up in the list - def conflicts - @conflicts ||= extension.conflicts.map(&:satisfying_versions).flatten.uniq.sort + # @return [Array] List of extensions that conflict with this ExtensionVersion + # The list is *not* transitive; if conflict C1 implies C2, + # only C1 shows up in the list + def conflicts_condition + ext.conflicts_condition end - # @return [Array] List of extensions that conflict with this ExtensionVersion - # The list *is* transitive; if conflict C1 implies C2, - # both C1 and C2 show up in the list - def transitive_conflicts - return @transitive_conflicts unless @transive_conflicts.nil? + # Returns array of ExtensionVersions implied by this ExtensionVersion, along with a condition + # under which it is in the list (which may be an AlwaysTrueExtensionRequirementExpression) + # + # @example + # ext_ver.implicaitons #=> { :ext_ver => ExtensionVersion.new(:A, "2.1.0"), :cond => ExtensionRequirementExpression.new(...) } + # + # @return [Array ExtensionVersion, ExtensionRequirementExpression}>] + # List of extension versions that this ExtensionVersion implies + # This list is *not* transitive; if an implication I1 implies another extension I2, + # only I1 shows up in the list + def implications + return [] if @data["implies"].nil? - @transitive_conflicts = [] - conflicts.each do |c| - @transitive_conflicts << c - @transitive_conflicts.concat(c.transitive_implications) - end - @transitive_conflicts.uniq! - @transitive_conflicts.sort! - @transitive_conflicts + ConditionalExtensionVersionList.new(@data["implies"], @arch) end - # @return [Array] List of extension versions that are implied by with this ExtensionVersion - # This list is *not* transitive; if an implication I1 implies another extension I2, - # only I1 shows up in the list - def implications - return @implications unless @implications.nil? - - @implications = [] - case @data["implies"] - when nil - return @implications - when Array - if @data["implies"][0].is_a?(Array) - @implications.concat(@data["implies"].map { |e| ExtensionVersion.new(e[0], e[1], @arch) }) - else - @implications << ExtensionVersion.new(@data["implies"][0], @data["implies"][1], @arch) + # @return [Array] List of extension versions that might imply this ExtensionVersion + # + # Note that the list returned could include extension versions that conditionally imply this extension version + # For example, Zcd.implied_by will return C, even though C only implies Zcd if D is also implemented + def implied_by + return @implied_by unless @implied_by.nil? + + @implied_by = [] + @arch.extensions.each do |ext| + next if ext.name == name + + ext.versions.each do |ext_ver| + ext_ver.implications.each do |implication| + @implied_by << ext_ver if implication[:ext_ver] == self + end end end - @implications.sort! - @implications - end - - # @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 - return @transitive_implications unless @transitive_implications.nil? - - @transitive_implications = [] - case @data["implies"] - when nil - return @transitive_implications - when Array - if @data["implies"][0].is_a?(Array) - impls = @data["implies"].map { |e| ExtensionVersion.new(e[0], e[1], @arch) } - @transitive_implications.concat(impls) - impls.each do |i| - transitive_impls = i.implications - @transitive_implications.concat(transitive_impls) unless transitive_impls.empty? + @implied_by + end + + # @return [Array ExtensionVersion, ExtensionRequirementExpression>] + # List of extension versions that might imply this ExtensionVersion, along with the condition under which it applies + # + # @example + # zcd_ext_ver.implied_by_with_condition #=> [{ ext_ver: "C 1.0", cond: "D ~> 1.0"}] + # + # @example + # zba_ext_ver.implied_by_with_condition #=> [{ ext_ver: "B 1.0", cond: AlwaysTrueExtensionRequirementExpression}] + def implied_by_with_condition + return @implied_by_with_condition unless @implied_by_with_condition.nil? + + @implied_by_with_condition = [] + @arch.extensions.each do |ext| + next if ext.name == name + + ext.versions.each do |ext_ver| + raise "????" if ext_ver.arch.nil? + ext_ver.implications.each do |implication| + @implied_by_with_condition << { ext_ver: ext_ver, cond: implication[:cond] } if implication[:ext_ver] == self end - else - impl = ExtensionVersion.new(@data["implies"][0], @data["implies"][1], @arch) - @transitive_implications << impl - transitive_impls = impl.implications - @transitive_implications.concat(transitive_impls) unless transitive_impls.empty? end end - @transitive_implications.uniq! - @transitive_implications.sort! - @transitive_implications + @implied_by_with_condition end # @param ext_name [String] Extension name @@ -499,12 +481,22 @@ def <=>(other) end end + def eql?(other) + unless other.is_a?(ExtensionVersion) + raise ArgumentError, "ExtensionVersions are only comparable to other extension versions" + end + + @name == other.name && @version_spec == other.version_spec + end + # @return [Array] the list of CSRs implemented by this extension version (may be empty) def implemented_csrs return @implemented_csrs unless @implemented_csrs.nil? - @implemented_csrs = @arch.csrs.select do |csr| - csr.defined_by?(self) + raise "implemented_csrs needs an cfg_arch" if @cfg_arch.nil? + + @implemented_csrs = @cfg_arch.csrs.select do |csr| + csr.defined_by_condition.possibly_satisfied_by?(self) end end @@ -512,8 +504,10 @@ def implemented_csrs def implemented_instructions return @implemented_instructions unless @implemented_instructions.nil? - @implemented_instructions = @arch.instructions.select do |inst| - inst.defined_by?(self) + raise "implemented_instructions needs an cfg_arch" if @cfg_arch.nil? + + @implemented_instructions = @cfg_arch.instructions.select do |inst| + inst.defined_by_condition.possibly_satisfied_by?(self) end end end @@ -696,6 +690,19 @@ def extension @extension = @arch.extension(@name) end + def self.create(yaml_req, arch) + if yaml_req.is_a?(String) + ExtensionRequirement.new(yaml_req, ">= #{arch.extension(yaml_req).versions.min}") + elsif yaml_req.is_a?(Hash) + raise "schema error" unless yaml_req.key?("name") + + req = yaml_req.key?("version") ? yaml_req["version"] : ">= #{arch.extension(yaml_req['name']).versions.min}" + ExtensionRequirement.new(yaml_req["name"], req, arch:) + else + raise "unexpected" + end + end + # @param name [#to_s] Extension name # @param requirements [String] Single requirement # @param requirements [Array] List of requirements, all of which must hold @@ -722,6 +729,10 @@ def initialize(name, *requirements, arch: nil, note: nil, req_id: nil, presence: @presence = presence.freeze end + def invert! + @requirements.each(&:invert!) + end + # @return [Array] The list of extension versions that satisfy this extension requirement def satisfying_versions ext = @arch.extension(@name) @@ -773,7 +784,7 @@ def satisfied_by?(*args) def csrs @csrs ||= @arch.csrs.select do |csr| satisfying_versions.any? do |ext_ver| - csr.defined_by?(ext_ver) + csr.defined_by_condition.possibly_satisfied_by?(ext_ver) end end end diff --git a/lib/arch_obj_models/instruction.rb b/lib/arch_obj_models/instruction.rb index 077a60f6c5..738e73cc34 100644 --- a/lib/arch_obj_models/instruction.rb +++ b/lib/arch_obj_models/instruction.rb @@ -3,7 +3,7 @@ require 'ruby-prof-flamegraph' require_relative "obj" - +require "awesome_print" # model of a specific instruction in a specific base (RV32/RV64) class Instruction < DatabaseObject @@ -100,7 +100,7 @@ def data_independent_timing? = @data["data_independent_timing"] # @param xlen [Integer] 32 or 64, the target xlen # @return [Boolean] whethen or not instruction is defined in base +xlen+ def defined_in_base?(xlen) - base == xlen + base.nil? || (base == xlen) end # @return [String] Assembly format @@ -108,8 +108,8 @@ def assembly @data["assembly"] end - def fill_symtab(global_symtab, effective_xlen, ast) - symtab = global_symtab.deep_clone + def fill_symtab(effective_xlen, ast) + symtab = cfg_arch.symtab.global_clone symtab.push(ast) symtab.add( "__instruction_encoding_size", @@ -120,7 +120,7 @@ def fill_symtab(global_symtab, effective_xlen, ast) Idl::Var.new("__effective_xlen", Idl::Type.new(:bits, width: 7), effective_xlen) ) @encodings[effective_xlen].decode_variables.each do |d| - qualifiers = [] + qualifiers = [:const] qualifiers << :signed if d.sext? width = d.size @@ -130,49 +130,42 @@ def fill_symtab(global_symtab, effective_xlen, ast) symtab end - private :fill_symtab # @param global_symtab [Idl::SymbolTable] Symbol table with global scope populated and a configuration loaded # @return [Idl::FunctionBodyAst] A pruned abstract syntax tree - def pruned_operation_ast(global_symtab, effective_xlen) - @pruned_asts ||= {} - - cfg_arch = global_symtab.cfg_arch - - pruned_ast = @pruned_asts[cfg_arch.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) - 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( - pruned_ast, - fill_symtab(global_symtab, effective_xlen, pruned_ast), - "#{name}.operation() (pruned)" - ) + def pruned_operation_ast(effective_xlen) + defer :pruned_operation_ast do + return nil unless @data.key?("operation()") + + type_checked_ast = type_checked_operation_ast(effective_xlen) + print "Pruning #{name} operation()..." + symtab = fill_symtab(effective_xlen, type_checked_ast) + pruned_ast = type_checked_ast.prune(symtab) + puts "done" + pruned_ast.freeze_tree(symtab) - @pruned_asts[cfg_arch.name] = pruned_ast + symtab.release + pruned_ast + end end # @param symtab [Idl::SymbolTable] Symbol table with global scope populated # @param effective_xlen [Integer] The effective XLEN to evaluate against # @return [Array] List of all functions that can be reached from operation() - def reachable_functions(symtab, effective_xlen) + def reachable_functions(effective_xlen) if @data["operation()"].nil? [] else # RubyProf.start - ast = type_checked_operation_ast(symtab.cfg_arch.idl_compiler, symtab, effective_xlen) - print "Determining reachable funcs from #{name}..." - fns = ast.reachable_functions(fill_symtab(symtab, effective_xlen, ast)) + ast = type_checked_operation_ast(effective_xlen) + print "Determining reachable funcs from #{name} (#{effective_xlen})..." + symtab = fill_symtab(effective_xlen, ast) + fns = ast.reachable_functions(symtab) puts "done" # result = RubyProf.stop # RubyProf::FlatPrinter.new(result).print($stdout) # exit + symtab.release fns end end @@ -180,15 +173,16 @@ def reachable_functions(symtab, effective_xlen) # @param symtab [Idl::SymbolTable] Symbol table with global scope populated # @param effective_xlen [Integer] Effective XLEN to evaluate against # @return [Integer] Mask of all exceptions that can be reached from operation() - def reachable_exceptions(symtab, effective_xlen) + def reachable_exceptions(effective_xlen) if @data["operation()"].nil? [] 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) - symtab = fill_symtab(symtab, effective_xlen, pruned_ast) + type_checked_ast = type_checked_operation_ast( effective_xlen) + symtab = fill_symtab(effective_xlen, pruned_ast) type_checked_ast.reachable_exceptions(symtab) + symtab.release end end @@ -205,58 +199,68 @@ def mask_to_array(int) elems end - # @param symtab [Idl::SymbolTable] Symbol table with global scope populated # @param effective_xlen [Integer] Effective XLEN to evaluate against. If nil, evaluate against all valid XLENs # @return [Array] List of all exceptions that can be reached from operation() - def reachable_exceptions_str(symtab, effective_xlen=nil) + def reachable_exceptions_str(effective_xlen=nil) if @data["operation()"].nil? [] else + symtab = cfg_arch.symtab etype = symtab.get("ExceptionCode") if effective_xlen.nil? - if symtab.cfg_arch.multi_xlen? + if cfg_arch.multi_xlen? if base.nil? ( - pruned_ast = pruned_operation_ast(symtab, 32) + pruned_ast = pruned_operation_ast(32) print "Determining reachable exceptions from #{name}#RV32..." - e32 = mask_to_array(pruned_ast.reachable_exceptions(fill_symtab(symtab, 32, pruned_ast))).map { |code| + symtab = fill_symtab(32, pruned_ast) + e32 = mask_to_array(pruned_ast.reachable_exceptions(symtab)).map { |code| etype.element_name(code) } + symtab.release puts "done" - pruned_ast = pruned_operation_ast(symtab, 64) + pruned_ast = pruned_operation_ast(64) print "Determining reachable exceptions from #{name}#RV64..." - e64 = mask_to_array(pruned_ast.reachable_exceptions(fill_symtab(symtab, 64, pruned_ast))).map { |code| + symtab = fill_symtab(64, pruned_ast) + e64 = mask_to_array(pruned_ast.reachable_exceptions(symtab)).map { |code| etype.element_name(code) } + symtab.release puts "done" e32 + e64 ).uniq else - pruned_ast = pruned_operation_ast(symtab, base) + pruned_ast = pruned_operation_ast(base) print "Determining reachable exceptions from #{name}..." - e = mask_to_array(pruned_ast.reachable_exceptions(fill_symtab(symtab, base, pruned_ast))).map { |code| + symtab = fill_symtab(base, pruned_ast) + e = mask_to_array(pruned_ast.reachable_exceptions(symtab)).map { |code| etype.element_name(code) } + symtab.release puts "done" e end else - effective_xlen = symtab.cfg_arch.mxlen - pruned_ast = pruned_operation_ast(symtab, effective_xlen) + effective_xlen = cfg_arch.mxlen + pruned_ast = pruned_operation_ast(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| + symtab = fill_symtab(effective_xlen, pruned_ast) + e = mask_to_array(pruned_ast.reachable_exceptions(symtab)).map { |code| etype.element_name(code) } + symtab.release puts "done" e end else - pruned_ast = pruned_operation_ast(symtab, effective_xlen) + pruned_ast = pruned_operation_ast(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| + symtab = fill_symtab(effective_xlen, pruned_ast) + e = mask_to_array(pruned_ast.reachable_exceptions(symtab)).map { |code| etype.element_name(code) } + symtab.release puts "done" e end @@ -323,6 +327,8 @@ class DecodeVariable # @return [Array] Specific values that are prohibited for this variable attr_reader :excludes + attr_reader :encoding_fields + # @return [String] Name, along with any != constraints, # @example # pretty_name #=> "rd != 0" @@ -377,6 +383,23 @@ def inst_pos_to_var_pos map end + # @param encoding [String] Encoding, as a string of 1, 0, and - with MSB at index 0 + # @param value [Integer] Value of the decode variable + # @return [String] encoding, with the decode variable replaced with value + def encoding_repl(encoding, value) + raise ArgumentError, "Expecting string" unless encoding.is_a?(String) + raise ArgumentError, "Expecting Integer" unless value.is_a?(Integer) + + new_encoding = encoding.dup + inst_pos_to_var_pos.each_with_index do |pos, idx| + next if pos.nil? + raise "Bad encoding" if idx >= encoding.size + + new_encoding[encoding.size - idx - 1] = ((value >> pos) & 1).to_s + end + new_encoding + end + # given a range of the instruction, return a string representing the bits of the field the range # represents def inst_range_to_var_range(r) @@ -477,7 +500,7 @@ def size size_in_encoding + @left_shift end - # the number of bits in the field, _not including any implicit ones_ + # the number of bits in the field, _not including any implicit zeros_ def size_in_encoding bits.reduce(0) { |sum, f| sum + (f.is_a?(Integer) ? 1 : f.size) } end @@ -558,6 +581,54 @@ def to_s end end + def self.overlapping_format?(format1, format2) + format1.size.times.all? do |i| + rev_idx = (format1.size - 1) - i + other_rev_idx = (format2.size - 1) - i + format1[rev_idx] == "-" \ + || (i >= format2.size) \ + || (format1[rev_idx] == format2[other_rev_idx]) + end + end + + # @return [Boolean] true if self and other_encoding cannot be distinguished, i.e., they share the same encoding + def indistinguishable?(other_encoding, check_other: true) + other_format = other_encoding.format + same = Encoding.overlapping_format?(format, other_format) + + if same + # the mask can't be distinguished; is there one or more exclusions that distinguishes them? + + # we have to check all combinations of dvs with exlcusions, and their values + exclusion_dvs = @decode_variables.reject { |dv| dv.excludes.empty? } + exclusion_dv_values = [] + def expand(exclusion_dvs, exclusion_dv_values, base, idx) + other_dv = exclusion_dvs[idx] + other_dv.excludes.each do |other_exclusion_value| + exclusion_dv_values << base + [[other_dv, other_exclusion_value]] + if (idx + 1) < exclusion_dvs.size + expand(exclusion_dvs, exclusion_dv_values, exclusion_dv_values.last, idx + 1) + end + end + end + exclusion_dvs.each_index do |idx| + expand(exclusion_dvs, exclusion_dv_values, [], idx) + end + + exclusion_dv_values.each do |dv_values| + repl_format = format.dup + dv_values.each { |dv_and_value| repl_format = dv_and_value[0].encoding_repl(repl_format, dv_and_value[1]) } + + if Encoding.overlapping_format?(repl_format, other_format) + same = false + break + end + end + end + + check_other ? same || other_encoding.indistinguishable?(self, check_other: false) : same + end + # @param format [String] Format of the encoding, as 0's, 1's and -'s (for decode variables) # @param decode_vars [Array>] List of decode variable definitions from the arch spec def initialize(format, decode_vars) @@ -619,41 +690,81 @@ def multi_encoding? @data.key?("encoding") && @data["encoding"].key?("RV32") end + # @return [Boolean] true if self and other_inst have indistinguishable encodings and can be simultaneously implemented in some design + def bad_encoding_conflict?(xlen, other_inst) + return false if !defined_in_base?(xlen) || !other_inst.defined_in_base?(xlen) + return false unless encoding(xlen).indistinguishable?(other_inst.encoding(xlen)) + + # ok, so they have the same encoding. can they be present at the same time? + return false if !defined_by_condition.compatible?(other_inst.defined_by_condition) + + # is this a hint? + !(hints.include?(other_inst) || other_inst.hints.include?(self)) + end + + # @return [Array] List of instructions that re-use this instruction's encoding, + # but can't be present in the same system because their defining + # extensions conflict + def conflicting_instructions(xlen) + raise "Bad xlen (#{xlen}) for instruction #{name}" unless defined_in_base?(xlen) + + @conflicting_instructions ||= {} + return @conflicting_instructions[xlen] unless @conflicting_instructions[xlen].nil? + + @conflicting_instructions[xlen] = [] + + @arch.instructions.each do |other_inst| + next unless other_inst.defined_in_base?(xlen) + next if other_inst == self + + next unless encoding(xlen).indistinguishable?(other_inst.encoding(xlen)) + + # is this a hint? + next if hints.include?(other_inst) || other_inst.hints.include?(self) + + if defined_by_condition.compatible?(other_inst.defined_by_condition) + raise "bad encoding conflict found between #{name} and #{other_inst.name}" + end + + @conflicting_instructions[xlen] << other_inst + end + @conflicting_instructions[xlen] + end + # @return [FunctionBodyAst] A type-checked abstract syntax tree of the operation - # @param idl_compiler [Idl::Compiler] Compiler - # @param symtab [Idl::SymbolTable] Symbol table with globals # @param effective_xlen [Integer] 32 or 64, the effective xlen to type check against - def type_checked_operation_ast(idl_compiler, symtab, effective_xlen) - @type_checked_operation_ast ||= {} - ast = @type_checked_operation_ast[symtab.hash] - return ast unless ast.nil? + def type_checked_operation_ast(effective_xlen) + defer :type_checked_operation_ast do + return nil unless @data.key?("operation()") - return nil unless @data.key?("operation()") + ast = operation_ast - ast = operation_ast(symtab) + symtab = fill_symtab(effective_xlen, ast) + ast.freeze_tree(symtab) + cfg_arch.idl_compiler.type_check(ast, symtab, "#{name}.operation()") + symtab.release - idl_compiler.type_check(ast, fill_symtab(symtab, effective_xlen, ast), "#{name}.operation()") - - @type_checked_operation_ast[symtab.hash] = ast + ast + end end - # @param symtab [SymbolTable] Symbol table with compilation context # @return [FunctionBodyAst] The abstract syntax tree of the instruction operation - def operation_ast(symtab) - return @operation_ast unless @operation_ast.nil? - return nil if @data["operation()"].nil? - - # now, parse the operation - @operation_ast = symtab.cfg_arch.idl_compiler.compile_inst_operation( - self, - symtab:, - input_file: @data["$source"], - input_line: source_line("operation()") - ) + def operation_ast + defer :operation_ast do + return nil if @data["operation()"].nil? + + # now, parse the operation + ast = cfg_arch.idl_compiler.compile_inst_operation( + self, + symtab: cfg_arch.symtab, + input_file: @data["$source"], + input_line: source_line("operation()") + ) - raise "unexpected #{@operation_ast.class}" unless @operation_ast.is_a?(Idl::FunctionBodyAst) + raise "unexpected #{ast.class}" unless ast.is_a?(Idl::FunctionBodyAst) - @operation_ast + ast + end end # @param base [Integer] 32 or 64 @@ -671,6 +782,11 @@ def encoding_width encoding(64).size end + # @return [Integer] the largest encoding width of the instruction, in any XLEN for which this instruction is valid + def max_encoding_width + [(rv32? ? encoding(32).size : 0), (rv64? ? encoding(64).size : 0)].max + end + # @return [Array] The decode variables def decode_variables(base) encoding(base).decode_variables @@ -709,48 +825,22 @@ def rv64? !@data.key?("base") || base == 64 end - # @overload excluded_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 excluded by extension `ext`, version `version` - # @overload excluded_by?(ext_version) - # @param ext_version [ExtensionVersion] An extension version - # @return [Boolean] Whether or not the instruction is excluded by ext_version - def excluded_by?(*args) - return false if @data["excludedBy"].nil? - - excluded_by = SchemaCondition.new(@data["excludedBy"], @cfg_arch) - - ext_ver = - if args.size == 1 - raise ArgumentError, "Parameter must be an ExtensionVersion" unless args[0].is_a?(ExtensionVersion) - - args[0] - elsif args.size == 2 - 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) - end - - excluded_by.satisfied_by? do |r| - r.satisfied_by?(ext_ver) - end + # @return [Array] List of HINTs based on this instruction encoding + def hints + @hints ||= @data.key?("hints") ? @data["hints"].map { |ref| @cfg_arch.ref(ref["$ref"]) } : [] end # @param cfg_arch [ConfiguredArchitecture] The architecture definition - # @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 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) } + cfg_arch.implemented_extension_versions.any? { |ext_ver| defined_by_condition.possibly_satisfied_by?(ext_ver) } else raise "unexpected cfg_arch type" unless cfg_arch.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) } + cfg_arch.prohibited_extension_versions.none? { |ext_ver| defined_by_condition.possibly_satisfied_by?(ext_ver) } end end end diff --git a/lib/arch_obj_models/manual.rb b/lib/arch_obj_models/manual.rb index 396cb041d9..58d3ff3c02 100644 --- a/lib/arch_obj_models/manual.rb +++ b/lib/arch_obj_models/manual.rb @@ -92,16 +92,16 @@ def extensions @extensions = [] return @extensions if @data["extensions"].nil? - @data["extensions"].each do |ext| - ext_obj = cfg_arch.extension(ext[0]) + @data["extensions"].each do |ext_hsh| + ext_obj = cfg_arch.extension(ext_hsh["name"]) if ext_obj.nil? - warn "Extension '#{ext[0]}' is not in the database" + warn "Extension '#{ext_hsh['name']}' is not in the database" next end - ext_ver = ExtensionVersion.new(ext[0], ext[1], cfg_arch) + ext_ver = ExtensionVersion.new(ext_hsh["name"], ext_hsh["version"], cfg_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" + warn "Extension '#{ext_hsh['name']}', version '#{ext_hsh['version']}' is not defined in the database" next end diff --git a/lib/arch_obj_models/obj.rb b/lib/arch_obj_models/obj.rb index e1ec1a01a9..7bd6d97dc2 100644 --- a/lib/arch_obj_models/obj.rb +++ b/lib/arch_obj_models/obj.rb @@ -175,7 +175,7 @@ def __source end # The raw content of definedBy in the data. - # @note Generally, you should prefer to use {#defined_by?}, etc. from Ruby + # @note Generally, you should prefer to use {#defined_by_condition}, etc. from Ruby # # @return [String] An extension name # @return [Array(String, Number)] An extension name and versions @@ -198,6 +198,9 @@ def initialize(data, data_path, arch: nil) @name = data["name"] @long_name = data["long_name"] @description = data["description"] + + @sem = Concurrent::Semaphore.new(1) + @cache = Concurrent::Hash.new end def inspect @@ -215,46 +218,22 @@ def keys = @data.keys # @return (see Hash#key?) 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 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 - def defined_by?(*args) - ext_ver = - if args.size == 1 - raise ArgumentError, "Parameter must be an ExtensionVersion" unless args[0].is_a?(ExtensionVersion) - - args[0] - elsif args.size == 2 - raise ArgumentError, "First parameter must be an extension name" unless args[0].respond_to?(:to_s) - raise ArgumentError, "First parameter must be an extension version" unless args[1].respond_to?(:to_s) - - ExtensionVersion.new(args[0], args[1], arch) - else - raise ArgumentError, "Unsupported number of arguments (#{args.size})" - end - - defined_by_condition.satisfied_by? { |req| req.satisfied_by?(ext_ver) } - end + def defer(fn_name, &block) + cache_value = @cache[fn_name] + return cache_value unless cache_value.nil? - # because of multiple ("allOf") conditions, we generally can't return a list of extension versions here.... - # # @return [Array] Extension(s) that define the instruction. If *any* requirement is met, the instruction is defined. - # def defined_by - # raise "ERROR: definedBy is nul for #{name}" if @data["definedBy"].nil? + raise "Missing block" unless block_given? - # SchemaCondition.new(@data["definedBy"], @arch).satisfying_ext_versions - # end + @cache[fn_name] ||= yield + end - # @return [SchemaCondition] Extension(s) that define the instruction. If *any* requirement is met, the instruction is defined. + # @return [ExtensionRequirementExpression] Extension(s) that define the instruction. If *any* requirement is met, the instruction is defined. def defined_by_condition @defined_by_condition ||= begin raise "ERROR: definedBy is nul for #{name}" if @data["definedBy"].nil? - SchemaCondition.new(@data["definedBy"], @arch) + ExtensionRequirementExpression.new(@data["definedBy"], @cfg_arch) end end @@ -376,7 +355,61 @@ def <=>(other) end end -# represents a JSON Schema compoisition, e.g.: +# represents an `implies:` entry for an extension +# which is a list of extension versions, zero or more of which +# may be conditional (via an ExtensionRequirementExpression) +class ConditionalExtensionVersionList + def initialize(ary, cfg_arch) + @ary = ary + @cfg_arch = cfg_arch + end + + def empty? = @ary.nil? || @ary.empty? + + def size = empty? ? 0 : eval.size + + def each(&block) + raise "Missing block" unless block_given? + + eval.each(&block) + end + + def map(&block) + eval.map(&block) + end + + # Returns array of ExtensionVersions, along with a condition under which it is in the list + # + # @example + # list.eval #=> [{ :ext_ver => ExtensionVersion.new(:A, "2.1.0"), :cond => ExtensionRequirementExpression.new(...) }] + # + # @return [Array ExtensionVersion, ExtensionRequirementExpression}>] + # The extension versions in the list after evaluation, and the condition under which it applies + def eval + result = [] + if @ary.is_a?(Hash) + result << { ext_ver: entry_to_ext_ver(@ary), cond: AlwaysTrueExtensionRequirementExpression.new } + else + @ary.each do |elem| + if elem.is_a?(Hash) && elem.keys[0] == "if" + cond_expr = ExtensionRequirementExpression.new(elem["if"], @cfg_arch) + result << { ext_ver: entry_to_ext_ver(elem["then"]), cond: cond_expr } + else + result << { ext_ver: entry_to_ext_ver(elem), cond: AlwaysTrueExtensionRequirementExpression.new } + end + end + end + result + end + alias to_a eval + + def entry_to_ext_ver(entry) + ExtensionVersion.new(entry["name"], entry["version"], @cfg_arch, fail_if_version_does_not_exist: true) + end + private :entry_to_ext_ver +end + +# represents a JSON Schema composition of extension requirements, e.g.: # # anyOf: # - oneOf: @@ -384,34 +417,36 @@ def <=>(other) # - B # - C # -class SchemaCondition - # @param composition_hash [Hash] A possibly recursive hash of "allOf", "anyOf", "oneOf" - def initialize(composition_hash, arch) +class ExtensionRequirementExpression + # @param composition_hash [Hash] A possibly recursive hash of "allOf", "anyOf", "oneOf", "not", "if" + def initialize(composition_hash, cfg_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})" end + raise ArgumentError, "Must provide a cfg_arch" unless cfg_arch.is_a?(ConfiguredArchitecture) + @hsh = composition_hash - @arch = arch + @arch = cfg_arch end def to_h = @hsh def empty? = false - VERSION_REQ_REGEX = /^((>=)|(>)|(~>)|(<)|(<=)|(=))?\s*[0-9]+(\.[0-9]+(\.[0-9]+(-[a-fA-F0-9]+)?)?)?$/ def is_a_version_requirement(ver) case ver when String - ver =~ VERSION_REQ_REGEX + ver =~ RequirementSpec::REQUIREMENT_REGEX when Array - ver.all? { |v| v =~ VERSION_REQ_REGEX } + ver.all? { |v| v =~ RequirementSpec::REQUIREMENT_REGEX } else false end end + private :is_a_version_requirement # @return [Boolean] True if the condition is a join of N terms over the same operator # @@ -473,22 +508,22 @@ def flat_versions end end - def to_asciidoc(cond = @hsh, indent = 0) + def to_asciidoc(cond = @hsh, indent = 0, join: "\n") case cond when String - "#{'*' * indent}* #{cond}, version >= 0" + "#{'*' * indent}* #{cond}, version >= #{@arch.extension(cond).min_version}" when Hash if cond.key?("name") if cond.key?("version") - "#{'*' * indent}* #{cond['name']}, version #{cond['version']}\n" + "#{'*' * indent}* #{cond['name']}, version #{cond['version']}#{join}" else - "#{'*' * indent}* #{cond['name']}, version >= 0\n" + "#{'*' * indent}* #{cond['name']}, version >= #{@arch.extension(cond['name']).min_version}#{join}" end else - "#{'*' * indent}* #{cond.keys[0]}:\n" + to_asciidoc(cond[cond.keys[0]], indent + 2) + "#{'*' * indent}* #{cond.keys[0]}:#{join}" + to_asciidoc(cond[cond.keys[0]], indent + 2) end when Array - cond.map { |e| to_asciidoc(e, indent) }.join("\n") + cond.map { |e| to_asciidoc(e, indent) }.join(join) else raise "Unknown condition type: #{cond}" end @@ -508,10 +543,15 @@ def is_a_condition?(hsh) return false unless is_a_version_requirement(hsh["version"]) end + elsif hsh.key?("not") + return false unless hsh.size == 1 + + return is_a_condition?(hsh["not"]) + else return false unless hsh.size == 1 - return false unless ["allOf", "anyOf", "oneOf"].include?(hsh.keys[0]) + return false unless ["allOf", "anyOf", "oneOf", "if"].include?(hsh.keys[0]) hsh[hsh.keys[0]].each do |element| return false unless is_a_condition?(element) @@ -548,15 +588,15 @@ def first_requirement(req = @hsh) end # combine all conds into one using AND - def self.all_of(*conds, arch:) - cond = SchemaCondition.new({ + def self.all_of(*conds, cfg_arch:) + cond = ExtensionRequirementExpression.new({ "allOf" => conds - }, arch) + }, cfg_arch) - SchemaCondition.new(cond.minimize, arch) + ExtensionRequirementExpression.new(cond.minimize, cfg_arch) end - # @return [Object] Schema for this condition, with basic logic minimization + # @return [Object] Schema for this expression, with basic logic minimization def minimize(hsh = @hsh) case hsh when Hash @@ -573,8 +613,13 @@ def minimize(hsh = @hsh) elsif hsh.key?("oneOf") min_ary = hsh["oneOf"].map { |element| minimize(element) } key = "oneOf" + elsif hsh.key?("not") + min_ary = hsh.dup + key = "not" + elsif hsh.key?("if") + return hsh end - min_ary = min_ary.uniq! + min_ary = min_ary.uniq if min_ary.size == 1 min_ary.first else @@ -613,6 +658,13 @@ def to_rb_helper(hsh) when "oneOf" rb_str = hsh[key].map { |element| to_rb_helper(element) }.join(', ') "([#{rb_str}].count(true) == 1)" + when "not" + rb_str = to_rb_helper(hsh[key]) + "(!#{rb_str})" + when "if" + cond_rb_str = to_rb_helper(hsh["if"]) + body_rb_str = to_rb_helper(hsh["body"]) + "(#{body_rb_str}) if (#{cond_rb_str})" else raise "Unexpected" "(yield #{hsh})" @@ -633,6 +685,280 @@ def to_rb to_rb_helper(@hsh) end + # Abstract syntax tree of the logic + class LogicNode + attr_accessor :type + + TYPES = [ :term, :not, :and, :or, :if ] + + def initialize(type, children, term_idx: nil) + raise ArgumentError, "Bad type" unless TYPES.include?(type) + raise ArgumentError, "Children must be an array" unless children.is_a?(Array) + + raise ArgumentError, "Children must be singular" if [:term, :not].include?(type) && children.size != 1 + raise ArgumentError, "Children must have two elements" if [:and, :or, :if].include?(type) && children.size != 2 + + if type == :term + raise ArgumentError, "Term must be an ExtensionRequirement (found #{children[0]})" unless children[0].is_a?(ExtensionRequirement) + else + raise ArgumentError, "All Children must be LogicNodes" unless children.all? { |child| child.is_a?(LogicNode) } + end + + @type = type + @children = children + + raise ArgumentError, "Need term_idx" if term_idx.nil? && type == :term + raise ArgumentError, "term_idx isn't an int" if !term_idx.is_a?(Integer) && type == :term + + @term_idx = term_idx + end + + # @return [Array] The terms (leafs) of this tree + def terms + @terms ||= + if @type == :term + [@children[0]] + else + @children.map(&:terms).flatten.uniq + end + end + + def eval(term_values) + if @type == :term + ext_ret = @children[0] + term_value = term_values.find { |tv| tv.name == ext_ret.name } + @children[0].satisfied_by?(term_value) + elsif @type == :if + cond_ext_ret = @children[0] + if cond_ext_ret.eval(term_values) + @children[1].eval(term_values) + else + false + end + elsif @type == :not + !@children[0].eval(term_values) + elsif @type == :and + @children.all? { |child| child.eval(term_values) } + elsif @type == :or + @children.any? { |child| child.eval(term_values) } + end + end + + def to_s + if @type == :term + "(#{@children[0].to_s})" + elsif @type == :not + "!#{@children[0]}" + elsif @type == :and + "(#{@children[0]} ^ #{@children[1]})" + elsif @type == :or + "(#{@children[0]} v #{@children[1]})" + elsif @type == :if + "(#{@children[0]} -> #{@children[1]})" + end + end + end + + # given an extension requirement, convert it to a LogicNode term, and optionally expand it to + # exclude any conflicts and include any implications + # + # @param ext_req [ExtensionRequirement] An extension requirement + # @param expand [Boolean] Whether or not to expand the node to include conflicts / implications + # @return [LogicNode] Logic tree for ext_req + def ext_req_to_logic_node(ext_req, term_idx, expand: true) + n = LogicNode.new(:term, [ext_req], term_idx: term_idx[0]) + term_idx[0] += 1 + if expand + c = ext_req.extension.conflicts_condition + unless c.empty? + c = LogicNode.new(:not, [to_logic_tree(ext_req.extension.data["conflicts"], term_idx:)]) + n = LogicNode.new(:and, [c, n]) + end + + ext_req.satisfying_versions.each do |ext_ver| + ext_ver.implied_by_with_condition.each do |implied_by| + implying_ext_ver = implied_by[:ext_ver] + implying_cond = implied_by[:cond] + implying_ext_req = ExtensionRequirement.new(implying_ext_ver.name, "= #{implying_ext_ver.version_str}", arch: @arch) + if implying_cond.empty? + # convert to an ext_req + n = LogicNode.new(:or, [n, ext_req_to_logic_node(implying_ext_req, term_idx)]) + else + # conditional + # convert to an ext_req + cond_node = implying_cond.to_logic_tree(term_idx:, expand:) + cond = LogicNode.new(:if, [cond_node, ext_req_to_logic_node(implying_ext_req, term_idx)]) + n = LogicNode.new(:or, [n, cond]) + end + end + end + end + + n + end + + # convert the YAML representation of an Extension Requirement Expression into + # a tree of LogicNodes. + # Also expands any Extension Requirement to include its conflicts / implications + def to_logic_tree(hsh = @hsh, term_idx: [0], expand: true) + if hsh.is_a?(Hash) + if hsh.key?("name") + if hsh.key?("version") + if hsh["version"].is_a?(String) + ext_req_to_logic_node(ExtensionRequirement.new(hsh["name"], hsh["version"], arch: @arch), term_idx, expand:) + elsif hsh["version"].is_a?(Array) + ext_req_to_logic_node(ExtensionRequirement.new(hsh["name"], hsh["version"].map { |v| "'#{v}'" }.join(', '), arch: @arch), term_idx, expand:) + else + raise "unexpected" + end + else + ext_req_to_logic_node(ExtensionRequirement.new(hsh["name"], arch: @arch), term_idx, expand:) + end + else + key = hsh.keys[0] + + case key + when "allOf" + raise "unexpected" unless hsh[key].is_a?(Array) && hsh[key].size > 1 + + root = LogicNode.new(:and, [to_logic_tree(hsh[key][0], term_idx:, expand:), to_logic_tree(hsh[key][1], term_idx:, expand:)]) + (2...hsh[key].size).each do |i| + root = LogicNode.new(:and, [root, to_logic_tree(hsh[key][i], term_idx:, expand:)]) + end + root + when "anyOf" + raise "unexpected: #{hsh}" unless hsh[key].is_a?(Array) && hsh[key].size > 1 + + root = LogicNode.new(:or, [to_logic_tree(hsh[key][0], term_idx:, expand:), to_logic_tree(hsh[key][1], term_idx:, expand:)]) + (2...hsh[key].size).each do |i| + root = LogicNode.new(:or, [root, to_logic_tree(hsh[key][i], term_idx:, expand:)]) + end + root + when "if" + raise "unexpected" unless hsh.keys.size == 2 && hsh.keys[1] == "then" + + cond = to_logic_tree(hsh[key], term_idx:, expand:) + body = to_logic_tree(hsh["then"], term_idx:, expand:) + LogicNode.new(:if, [cond, body]) + when "oneOf" + # expand oneOf into AND + roots = [] + hsh[key].size.times do |k| + root = + if k.zero? + LogicNode.new(:and, [to_logic_tree(hsh[key][0], term_idx:, expand:), LogicNode.new(:not, [to_logic_tree(hsh[key][1], term_idx:, expand:)])]) + elsif k == 1 + LogicNode.new(:and, [LogicNode.new(:not, [to_logic_tree(hsh[key][0], term_idx:, expand:)]), to_logic_tree(hsh[key][1], term_idx:, expand:)]) + else + LogicNode.new(:and, [LogicNode.new(:not, [to_logic_tree(hsh[key][0], term_idx:, expand:)]), LogicNode.new(:not, [to_logic_tree(hsh[key][1], term_idx:, expand:)])]) + end + (2...hsh[key].size).each do |i| + root = + if k == i + LogicNode.new(:and, [root, to_logic_tree(hsh[key][i], term_idx:, expand:)]) + else + LogicNode.new(:and, [root, LogicNode.new(:not, [to_logic_tree(hsh[key][i], term_idx:, expand:)])]) + end + end + roots << root + end + root = LogicNode.new(:or, [roots[0], roots[1]]) + (2...roots.size).each do |i| + root = LogicNode.new(:or, [root, roots[i]]) + end + root + when "not" + LogicNode.new(:not, [to_logic_tree(hsh[key], term_idx:, expand:)]) + else + raise "Unexpected" + end + end + else + ext_req_to_logic_node(ExtensionRequirement.new(hsh, arch: @arch), term_idx, expand:) + end + end + + # convert to Negation Normal Form + def nnf(logic_tree) + if logic_tree.type == :not + # distribute + if logic_tree.children.size == 1 && logic_tree.children[0].type == :term + logic_tree + else + # distribute NOT + child = logic_tree.children[0] + + if child.type == :and + LogicNode.new(:or, child.children.map { |child2| LogicNode.new(:not, [child2]) }) + elsif child.type == :or + LogicNode.new(:and, child.children.map { |child2| LogicNode.new(:not, [child2]) }) + elsif child.type == :xor + raise "TODO" + elsif child.type == :not + child + else + raise "?" + end + end + else + LogicNode.new(logic_tree.type, logic_tree.children.map { |child| nnf(child) }) + end + end + + # convert to Disjunctive Normal Form + def dnf(logic_tree) + logic_tree = nnf(logic_tree) + if logic_tree.type == :and + # distribute + if logic_tree.children.all? { |child| child.type == :term } + logic_tree + else + LogicTree.new(:or, logic_tree.children.map { |child| LogicTree.new(:and, dnf(child)) }) + end + else + logic_tree + end + end + + def combos_for(extension_versions) + ncombos = extension_versions.reduce(1) { |prod, vers| prod * vers.size } + combos = [] + ncombos.times do |i| + combos << [] + extension_versions.size.times do |j| + m = extension_versions[j].size + d = j.zero? ? 1 : extension_versions[j..0].reduce(1) { |prod, vers| prod * vers.size } + + combos.last << extension_versions[j][(i / d) % m] + end + end + # get rid of any combos that can't happen because of extension conflicts + combos.reject do |combo| + combo.any? { |ext_ver1| (combo - [ext_ver1]).any? { |ext_ver2| ext_ver1.conflicts_condition.satisfied_by? { |ext_req| ext_req.satisfied_by?(ext_ver2) } } } + end + end + + # @param other [ExtensionRequirementExpression] Another condition + # @return [Boolean] if it's possible for both to be simultaneously true + def compatible?(other) + raise ArgumentError, "Expecting a ExtensionRequirementExpression" unless other.is_a?(ExtensionRequirementExpression) + + tree1 = to_logic_tree(@hsh) + tree2 = to_logic_tree(other.to_h) + + extensions = (tree1.terms + tree2.terms).map(&:extension).uniq + + extension_versions = extensions.map(&:versions) + + combos = combos_for(extension_versions) + combos.each do |combo| + return true if tree1.eval(combo) && tree2.eval(combo) + end + + # there is no combination in which both self and other can be true + false + end + # @example See if a string satisfies # cond = { "anyOf" => ["A", "B", "C"] } # string = "A" @@ -658,25 +984,49 @@ def satisfied_by?(&block) eval to_rb end - def satisfying_ext_versions - list = [] - arch.extensions.each do |ext| - ext.versions.each do |ext_ver| - list << ext_ver if satisfied_by? { |ext_req| ext_req.satisfied_by?(ext_ver) } - end + # yes if: + # - ext_ver affects this condition + # - it is is possible for this condition to be true is ext_ver is implemented + def possibly_satisfied_by?(ext_ver) + logic_tree = to_logic_tree + + return false unless logic_tree.terms.any? { |ext_req| ext_req.satisfying_versions.include?(ext_ver) } + + # ok, so ext_ver affects this condition + # is it possible to be true with ext_ver implemented? + extensions = logic_tree.terms.map(&:extension).uniq + + extension_versions = extensions.map(&:versions) + + combos = combos_for(extension_versions) + combos.any? do |combo| + # replace ext_ver, since it doesn't change + logic_tree.eval(combo.map { |ev| ev.name == ext_ver.name ? ext_ver : ev }) end - list end end -class AlwaysTrueSchemaCondition +class AlwaysTrueExtensionRequirementExpression def to_rb = "true" def satisfied_by? = true def empty? = true - def flat? = false + def compatible?(_other) = true + + def to_h = {} + def minimize = {} +end + +class AlwaysFalseExtensionRequirementExpression + def to_rb = "false" + + def satisfied_by? = false + + def empty? = true + + def compatible?(_other) = false def to_h = {} def minimize = {} diff --git a/lib/arch_obj_models/portfolio.rb b/lib/arch_obj_models/portfolio.rb index 5c5747f206..eace808328 100644 --- a/lib/arch_obj_models/portfolio.rb +++ b/lib/arch_obj_models/portfolio.rb @@ -249,13 +249,11 @@ def to_cfg_arch } # TODO: Add list of prohibited_extensions + gen_dir = $root / "gen" / "cfgs" + FileUtils.mkdir_p gen_dir + File.write("#{gen_dir}/#{name}.yaml", YAML.safe_dump(config_data, permitted_classes: [Date])) - @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 + @generated_cfg_arch = ConfiguredArchitecture.new(name, @arch.path) end ################################### @@ -417,27 +415,8 @@ def out_of_scope_params(ext_name) 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 |in_scope_ext| - if ext.name == in_scope_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 + param.exts.select { |ext| in_scope_extensions.include?(ext) } end # @return [Array] diff --git a/lib/arch_obj_models/schema.rb b/lib/arch_obj_models/schema.rb index ff11d6b942..776654eb97 100644 --- a/lib/arch_obj_models/schema.rb +++ b/lib/arch_obj_models/schema.rb @@ -144,6 +144,48 @@ def value @schema_hash["const"] end + # @return [Boolean] if the maximum value of the schema is known, i.e., is a restricted integer + def max_val_known? + to_idl_type.kind == :bits && \ + (@schema_hash.key?("const") || \ + @schema_hash.key?("maximum") || \ + @schema_hash.key?("enum")) + end + + # @return [Boolean] if the minimum value of the schema is known, i.e., is a restricted integer + def min_val_known? + to_idl_type.kind == :bits && \ + (@schema_hash.key?("const") || \ + @schema_hash.key?("minimum") || \ + @schema_hash.key?("enum")) + end + + # @return [Integer] The maximum value the schema allows. Only valid if #max_val_known? is true + def max_val + if @schema_hash.key?("const") + @schema_hash["const"] + elsif @schema_hash.key?("enum") + @schema_hash["enum"].max + elsif @schema_hash.key?("maximum") + @schema_hash["maximum"] + else + raise "unexpected" + end + end + + # @return [Integer] The minimum value the schema allows. Only valid if #min_val_known? is true + def min_val + if @schema_hash.key?("const") + @schema_hash["const"] + elsif @schema_hash.key?("enum") + @schema_hash["enum"].min + elsif @schema_hash.key?("minimum") + @schema_hash["minimum"] + else + raise "unexpected" + end + end + def is_power_of_two?(num) return false if num < 1 return (num & (num-1)) == 0 diff --git a/lib/architecture.rb b/lib/architecture.rb index 0ff77ba801..2e1b90a3e5 100644 --- a/lib/architecture.rb +++ b/lib/architecture.rb @@ -34,6 +34,7 @@ require "active_support/inflector/methods" +require "concurrent" require "json" require "json_schemer" require "pathname" @@ -62,8 +63,8 @@ def initialize(arch_dir) @arch_dir = @arch_dir.realpath @path = @arch_dir # alias - @objects ||= {} - @object_hashes ||= {} + @objects = Concurrent::Hash.new + @object_hashes = Concurrent::Hash.new end # validate the architecture against JSON Schema and any object-specific verification @@ -94,10 +95,13 @@ def self.generate_obj_methods(fn_name, arch_dir, obj_class) define_method(plural_fn) do return @objects[arch_dir] unless @objects[arch_dir].nil? - @objects[arch_dir] = [] - @object_hashes[arch_dir] = {} + @objects[arch_dir] = Concurrent::Array.new + @object_hashes[arch_dir] = Concurrent::Hash.new Dir.glob(@arch_dir / arch_dir / "**" / "*.yaml") do |obj_path| - obj_yaml = YAML.load_file(obj_path, permitted_classes: [Date]) + f = File.open(obj_path) + f.flock(File::LOCK_EX) + obj_yaml = YAML.load(f.read, filename: obj_path, permitted_classes: [Date]) + f.flock(File::LOCK_UN) @objects[arch_dir] << obj_class.new(obj_yaml, Pathname.new(obj_path).realpath, arch: self) @object_hashes[arch_dir][@objects[arch_dir].last.name] = @objects[arch_dir].last end diff --git a/lib/cfg_arch.rb b/lib/cfg_arch.rb index 2d6f6fa8e5..05d4531912 100644 --- a/lib/cfg_arch.rb +++ b/lib/cfg_arch.rb @@ -5,6 +5,7 @@ # 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. +require "concurrent" require "forwardable" require "ruby-prof" require "tilt" @@ -166,7 +167,7 @@ def config_type = @config.type # # @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") + def initialize(config_name, arch_path) super(arch_path) @name = config_name.to_s.freeze @@ -174,14 +175,23 @@ def initialize(config_name, arch_path, overlay_path: nil, cfg_path: "#{$root}/cf @obj_cache = {} - @config = Config.create("#{cfg_path}/#{config_name}/cfg.yaml") + @config = Config.create("#{$root}/gen/cfgs/#{config_name}.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" + overlay_path = + if @config.arch_overlay.nil? + "/does/not/exist" + elsif File.exist?(@config.arch_overlay) + File.realpath(@config.arch_overlay) + else + "#{$root}/arch_overlay/#{@config.arch_overlay}" + end + + custom_globals_path = Pathname.new "#{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 @@ -211,10 +221,10 @@ def type_check(show_progress: true, io: $stdout) instructions.each do |inst| progressbar.increment if show_progress if @mxlen == 32 - inst.type_checked_operation_ast(@idl_compiler, @symtab, 32) if inst.rv32? + inst.type_checked_operation_ast(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? + inst.type_checked_operation_ast(64) if inst.rv64? + inst.type_checked_operation_ast(32) if possible_xlens.include?(32) && inst.rv32? end end @@ -226,21 +236,26 @@ def type_check(show_progress: true, io: $stdout) 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) + if (possible_xlens.include?(32) && csr.defined_in_base32?) + csr.type_checked_sw_read_ast(32) + end + if (possible_xlens.include?(64) && csr.defined_in_base64?) + csr.type_checked_sw_read_ast(64) 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) + csr.possible_fields.each do |field| + unless field.type_ast.nil? + if possible_xlens.include?(32) && csr.defined_in_base32? && field.defined_in_base32? + field.type_checked_type_ast(32) + end + if possible_xlens.include?(64) && csr.defined_in_base64? && field.defined_in_base64? + field.type_checked_type_ast(64) end end - unless field.reset_value_ast(@symtab).nil? + unless field.reset_value_ast.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? + field.type_checked_reset_value_ast if csr.defined_in_base32? && field.defined_in_base32? end end unless field.sw_write_ast(@symtab).nil? @@ -270,7 +285,7 @@ def params_with_value return @params_with_value if @config.unconfigured? if @config.fully_configured? - transitive_implemented_extensions.each do |ext_version| + transitive_implemented_extension_versions.each do |ext_version| ext = extension(ext_version.name) ext.params.each do |ext_param| next unless @config.param_values.key?(ext_param.name) @@ -282,7 +297,7 @@ def params_with_value end end elsif @config.partially_configured? - mandatory_extensions.each do |ext_requirement| + mandatory_extension_reqs.each do |ext_requirement| ext = extension(ext_requirement.name) ext.params.each do |ext_param| # Params listed in the config always only have one value. @@ -316,30 +331,66 @@ def params_without_value @params_without_value end - def implemented_extensions - @implemented_extensions ||= + # Returns a string representation of the object, suitable for debugging. + # @return [String] A string representation of the object. + def inspect = "ConfiguredArchitecture##{name}" + + # @return [Array] List of extension versions explictly marked as implemented in the config. + # Does *not* include extensions implied by explictly implemented extensions. + def explicitly_implemented_extension_versions + return @explicitly_implemented_extension_versions unless @explicitly_implemented_extension_versions.nil? + + unless fully_configured? + raise ArgumentError, "implemented_extension_versions only valid for fully configured systems" + end + + @explicitly_implemented_extension_versions ||= @config.implemented_extensions.map do |e| ExtensionVersion.new(e["name"], e["version"], self, fail_if_version_does_not_exist: true) end end # @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? + def transitive_implemented_extension_versions + return @transitive_implemented_extension_versions unless @transitive_implemented_extension_versions.nil? - raise "implemented_extensions is only valid for a fully configured definition" unless @config.fully_configured? + raise "transitive_implemented_extension_versions is only valid for a fully configured definition" unless @config.fully_configured? - list = implemented_extensions - list.each do |e| - implications = e.transitive_implications - list.concat(implications) unless implications.empty? + @transitive_implemented_extension_versions = explicitly_implemented_extension_versions.dup + + added_ext_vers = [] + loop do + @transitive_implemented_extension_versions.each do |ext_ver| + ext_ver.implications.each do |implication| + applies = implication[:cond].satisfied_by? do |ext_req| + @transitive_implemented_extension_versions.any? do |inner_ext_ver| + next false if ext_ver == inner_ext_ver + + ext_req.satisfied_by?(inner_ext_ver) + end + end + if applies && !@transitive_implemented_extension_versions.include?(implication[:ext_ver]) + added_ext_vers << implication[:ext_ver] + end + end + end + break if added_ext_vers.empty? + + added_ext_vers.each { |ext_ver| @transitive_implemented_extension_versions << ext_ver } + + added_ext_vers = [] end - @transitive_implemented_extensions = list.uniq.sort + + @transitive_implemented_extension_versions.sort! + @transitive_implemented_extension_versions end + alias implemented_extension_versions transitive_implemented_extension_versions + + # @return [Array] List of all mandatory extension requirements (not transitive) + def mandatory_extension_reqs + return @mandatory_extension_reqs unless @mandatory_extension_reqs.nil? - # @return [Array] List of all mandatory extension requirements - def mandatory_extensions - @mandatory_extensions ||= + @mandatory_extension_reqs ||= @config.mandatory_extensions.map do |e| ext = extension(e["name"]) raise "Cannot find extension #{e['name']} in the architecture definition" if ext.nil? @@ -348,75 +399,92 @@ def mandatory_extensions end end - # @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? + # @return [Array] List of extensions that are possibly supported + def not_prohibited_extensions + return @not_prohibited_extensions unless @not_prohibited_extensions.nil? + + @not_prohibited_extensions ||= + if @config.fully_configured? + transitive_implemented_extension_versions.map { |ext_ver| ext_ver.extension }.uniq + elsif @config.partially_configured? + # reject any extension in which all of the extension versions are prohibited + extensions.reject { |ext| (ext.versions - transitive_prohibited_extension_versions).empty? } + else + extensions + end + end + alias possible_extensions not_prohibited_extensions + + # @return [Array] List of all ExtensionVersions that are possible to support + def not_prohibited_extension_versions + return @not_prohibited_extension_versions unless @not_prohibited_extension_versions.nil? + + @not_prohibited_extension_versions ||= + if @config.fully_configured? + transitive_implemented_extension_versions + elsif @config.partially_configured? + extensions.map(&:versions).flatten.reject { |ext_ver| transitive_prohibited_extension_versions.include?(ext_ver) } + else + extensions.map(&:version).flatten + end + end + alias possible_extension_versions not_prohibited_extension_versions + + # @return [Array] List of all extension versions that are prohibited. + # This includes extensions explicitly prohibited by the config file + # and extensions that conflict with a mandatory extension. + def transitive_prohibited_extension_versions + return @transitive_prohibited_extension_versions unless @transitive_prohibited_extension_versions.nil? + + @transitive_prohibited_extension_versions = [] if @config.partially_configured? - @prohibited_extensions = - @config.prohibited_extensions.map do |e| - ext = extension(e["name"]) - raise "Cannot find extension #{e['name']} in the architecture definition" if ext.nil? + add_ext_ver_and_conflicts = lambda do |ext_ver| + @transitive_prohibited_extension_versions << ext_ver + ext_ver.conflicts.each do |conflict_ext_ver| + add_ext_ver_and_conflicts.call(conflict_ext_ver) + end + end - ExtensionRequirement.new(e["name"], *e["version"], presence: "mandatory", arch: self) + @transitive_prohibited_extension_versions = + @config.prohibited_extensions.map do |ext_req_data| + ext_req = ExtensionRequirement.new(ext_req_data["name"], ext_req_data["version"], cfg_arch: self) + ext_req.satisfying_versions.each { |ext_ver| add_ext_ver_and_conflicts.call(ext_ver) } end # now add any extensions that are prohibited by a mandatory extension - mandatory_extensions.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 - else - # pick whichever requirement is more expansive - p = @prohibited_extensions.find { |prohibited_ext| prohibited_ext.name == conflict.name } - if p.version_requirement.subsumes?(conflict.version_requirement) - @prohibited_extensions.delete(p) - @prohibited_extensions << conflict - end + mandatory_extension_reqs.each do |ext_req| + ext_req.satisfying_versions do |ext_ver| + add_ext_ver_and_conflicts.call(ext_ver) + end + end + + # now add everything that is not mandatory or implied by mandatory, if addtional extensions are not allowed + unless @config.additional_extensions_allowed? + extensions.each do |ext| + ext.versions.each do |ext_ver| + next if mandatory_extension_reqs.any? { |ext_req| ext_req.satisfied_by?(ext_ver) } + next if mandatory_extension_reqs.any? { |ext_req| ext_req.extension.implies.include?(ext_ver) } + + @transitive_prohibited_extension_versions << ext_ver end end end - @prohibited_extensions elsif @config.fully_configured? - prohibited_ext_versions = [] extensions.each do |ext| ext.versions.each do |ext_ver| - prohibited_ext_versions << ext_ver unless transitive_implemented_extensions.include?(ext_ver) - end - end - @prohibited_extensions = [] - prohibited_ext_versions.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 << - ExtensionRequirement.new( - ext_ver_list[0].ext.name, ">= #{ext_ver_list.min.version_spec.canonical}", - presence: "prohibited", arch: self - ) - elsif ext_ver_list.size == (ext_ver_list[0].ext.versions.size - 1) - # excludes all but one version - allowed_version_list = (ext_ver_list[0].ext.versions - ext_ver_list) - raise "Expected only a single element" unless allowed_version_list.size == 1 - - 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 - ) - else - # need to group - raise "TODO" + @transitive_prohibited_extension_versions << ext_ver unless transitive_implemented_extension_versions.include?(ext_ver) end end - else - @prohibited_extensions = [] + + # else, unconfigured....nothing to do # rubocop:disable Layout/CommentIndentation + end - @prohibited_extensions + + @transitive_prohibited_extension_versions end + alias prohibited_extension_versions transitive_prohibited_extension_versions # @overload prohibited_ext?(ext) # Returns true if the ExtensionVersion +ext+ is prohibited @@ -429,9 +497,9 @@ def prohibited_extensions # @return [Boolean] def prohibited_ext?(ext) if ext.is_a?(ExtensionVersion) - prohibited_extensions.any? { |ext_req| ext_req.satisfied_by?(ext) } + transitive_prohibited_extension_versions.include?(ext_ver) elsif ext.is_a?(String) || ext.is_a?(Symbol) - prohibited_extensions.any? { |ext_req| ext_req.name == ext.to_s } + transitive_prohibited_extension_versions.any? { |ext_ver| ext_ver.name == ext.to_s } else raise ArgumentError, "Argument to prohibited_ext? should be an ExtensionVersion or a String" end @@ -457,7 +525,7 @@ def ext?(ext_name, *ext_version_requirements) result = if @config.fully_configured? - transitive_implemented_extensions.any? do |e| + transitive_implemented_extension_versions.any? do |e| if ext_version_requirements.empty? e.name == ext_name.to_s else @@ -466,7 +534,7 @@ def ext?(ext_name, *ext_version_requirements) end end elsif @config.partially_configured? - mandatory_extensions.any? do |e| + mandatory_extension_reqs.any? do |e| if ext_version_requirements.empty? e.name == ext_name.to_s else @@ -532,21 +600,121 @@ def implemented_interrupt_codes # @return [Array] List of all functions defined by the architecture def functions - return @functions unless @functions.nil? + @functions ||= @global_ast.functions + end - @functions = @global_ast.functions + # @return [Idl::FetchAst] Fetch block + def fetch + @fetch ||= @global_ast.fetch + end + + # @return [Array] List of globals + def globals + return @globals unless @globals.nil? + + @globals = @global_ast.globals end # @return [Array] List of all implemented CSRs def transitive_implemented_csrs + unless fully_configured? + raise ArgumentError, "transitive_implemented_csrs is only defined for fully configured systems" + end + @transitive_implemented_csrs ||= - transitive_implemented_extensions.map(&:implemented_csrs).flatten.uniq.sort + csrs.select do |csr| + csr.defined_by_condition.satisfied_by? do |ext_req| + transitive_implemented_extension_versions.any? { |ext_ver| ext_req.satisfied_by?(ext_ver) } + end + end + end + alias implemented_csrs transitive_implemented_csrs + + # @return [Array] List of all CSRs that it is possible to implement + def not_prohibited_csrs + @not_prohibited_csrs = + if @config.fully_configured? + transitive_implemented_csrs + elsif @config.partially_configured? + csrs.select do |csr| + csr.defined_by_condition.satisfied_by? do |ext_req| + not_prohibited_extension_versions.any? { |ext_ver| ext_req.satisfied_by?(ext_ver) } + end + end + else + csrs + end end + alias possible_csrs not_prohibited_csrs - # @return [Array] List of all implemented instructions + # @return [Array] List of all implemented instructions, sorted by name def transitive_implemented_instructions + unless fully_configured? + raise ArgumentError, "transitive_implemented_instructions is only defined for fully configured systems" + end + @transitive_implemented_instructions ||= - transitive_implemented_extensions.map(&:implemented_instructions).flatten.uniq.sort + instructions.select do |inst| + inst.defined_by_condition.satisfied_by? do |ext_req| + transitive_implemented_extension_versions.any? { |ext_ver| ext_req.satisfied_by?(ext_ver) } + end + end + end + alias implemented_instructions transitive_implemented_instructions + + # @return [Array] List of all prohibited instructions, sorted by name + def transitive_prohibited_instructions + # an instruction is prohibited if it is not defined by any .... TODO LEFT OFF HERE.... + @transitive_prohibited_instructions ||= + if fully_configured? + instructions - transitive_implemented_instructions + elsif partially_configured? + instructions.select do |inst| + inst.defined_by_condition.satisfied_by? do |ext_req| + not_prohibited_extension_versions.none? { |ext_ver| ext_req.satisfied_by?(ext_ver) } + end + end + else + [] + end + end + alias prohibited_instructions transitive_prohibited_instructions + + # @return [Array] List of all instructions that are not prohibited by the config, sorted by name + def not_prohibited_instructions + return @not_prohibited_instructions unless @not_prohibited_instructions.nil? + + @not_prohibited_instructions_mutex ||= Thread::Mutex.new + @not_prohibited_instructions_mutex.synchronize do + @not_prohibited_instructions ||= + if @config.fully_configured? + transitive_implemented_instructions + elsif @config.partially_configured? + instructions.select do |inst| + possible_xlens.any? { |xlen| inst.defined_in_base?(xlen) } && \ + inst.defined_by_condition.satisfied_by? do |ext_req| + not_prohibited_extension_versions.any? { |ext_ver| ext_req.satisfied_by?(ext_ver) } + end + end + else + instructions + end + end + + @not_prohibited_instructions + end + alias possible_instructions not_prohibited_instructions + + # @return [Integer] The largest instruction encoding in the config + def largest_encoding + @largest_encoding ||= + if fully_configured? + transitive_implemented_instructions.map(&:max_encoding_width).max + elsif partially_configured? + not_prohibited_instructions.map(&:max_encoding_width).max + else + instructions.map(&:max_encoding_width).max + end end # @return [Array] List of all reachable IDL functions for the config @@ -561,13 +729,13 @@ def implemented_functions @implemented_functions << if inst.base.nil? if multi_xlen? - (inst.reachable_functions(symtab, 32) + - inst.reachable_functions(symtab, 64)) + (inst.reachable_functions(32) + + inst.reachable_functions(64)) else - inst.reachable_functions(symtab, mxlen) + inst.reachable_functions(mxlen) end else - inst.reachable_functions(symtab, inst.base) + inst.reachable_functions(inst.base) end end raise "?" unless @implemented_functions.is_a?(Array) @@ -577,15 +745,71 @@ def implemented_functions puts " Finding all reachable functions from CSR operations" transitive_implemented_csrs.each do |csr| - csr_funcs = csr.reachable_functions(self) + csr_funcs = csr.reachable_functions csr_funcs.each do |f| @implemented_functions << f unless @implemented_functions.any? { |i| i.name == f.name } end end + # now add everything from fetch + symtab = @symtab.global_clone + symtab.push(@global_ast.fetch.body) + fetch_fns = @global_ast.fetch.body.reachable_functions(symtab) + fetch_fns.each do |f| + @implemented_functions << f unless @implemented_functions.any? { |i| i.name == f.name } + end + symtab.release + @implemented_functions end + # @return [Array] List of functions that can be reached by the configuration + def reachable_functions + return @reachable_functions unless @reachable_functions.nil? + + insts = not_prohibited_instructions + @reachable_functions = [] + + insts.each do |inst| + fns = + if inst.base.nil? + if multi_xlen? + (inst.reachable_functions(32) + + inst.reachable_functions(64)) + else + inst.reachable_functions(mxlen) + end + else + inst.reachable_functions(inst.base) + end + + @reachable_functions.concat(fns) + end + + @reachable_functions += + not_prohibited_csrs.flat_map(&:reachable_functions).uniq + + # now add everything from fetch + symtab = @symtab.global_clone + symtab.push(@global_ast.fetch.body) + @reachable_functions += @global_ast.fetch.body.reachable_functions(symtab) + symtab.release + + # now add everything from external functions + symtab = @symtab.global_clone + @global_ast.functions.select { |fn| fn.external? }.each do |fn| + symtab.push(fn) + @reachable_functions << fn + fn.apply_template_and_arg_syms(symtab) + @reachable_functions += fn.reachable_functions(symtab) + symtab.pop + end + symtab.release + + @reachable_functions.uniq! + @reachable_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 # diff --git a/lib/config.rb b/lib/config.rb index 5efe7b684b..6a20ae113c 100644 --- a/lib/config.rb +++ b/lib/config.rb @@ -9,6 +9,26 @@ class Config # been configured with a value. May be empty. attr_reader :param_values + def overlay? = !(@data["arch_overlay"].nil? || @data["arch_overlay"].empty?) + + # @return [String] Either a path to an overlay directory, or the name of a folder under arch_overlay/ + # @return [nil] No arch_overlay for this config + def arch_overlay = @data["arch_overlay"] + + # @return [String] Absolute path to the arch_overlay + # @return [nil] No arch_overlay for this config + def arch_overlay_abs + return nil unless @data.key?("arch_overlay") + + if File.directory?("#{$root}/arch_overlay/#{@data['arch_overlay']}") + "#{$root}/arch_overlay/#{@data['arch_overlay']}" + elsif File.directory?(@data['arch_overlay']) + @data['arch_overlay'] + else + raise "Cannot find arch_overlay '#{@data['arch_overlay']}'" + end + end + # use Config#create instead private_class_method :new @@ -101,7 +121,9 @@ def initialize(cfg_file_path, data) @mxlen.freeze end - def implemented_extensions = raise "implemented_extensions is only available for a FullConfig" + def additional_extensions_allowed? = @data.key?("additional_extensions") ? @data["additional_extensions"] : true + + def implemented_extensions = raise "implemented_extensions is only availabe for a FullConfig" # @return [Array String,Array] # List of all extensions that must be implemented, as specified in the config file diff --git a/lib/deploy.sh b/lib/deploy.sh index f5c80c79f1..8f6c35372d 100644 --- a/lib/deploy.sh +++ b/lib/deploy.sh @@ -22,6 +22,7 @@ mkdir -p $DEPLOY_DIR/pdfs echo "Resolve / Create Index" ./do gen:resolved_arch +cp -R gen/resolved_arch/_ $DEPLOY_DIR/resolved_arch echo "Build manual" ./do gen:html_manual MANUAL_NAME=isa VERSIONS=all @@ -29,8 +30,8 @@ echo "Build manual" echo "Copy manual html" cp -R gen/manual/isa/top/all/html $DEPLOY_DIR/manual -echo "Build html documentation for generic_rv64" -./do gen:html[generic_rv64] +echo "Build html documentation for example_rv64_with_overlay" +./do gen:html[example_rv64_with_overlay] echo "Generate YARD docs" ./do gen:tool_doc @@ -39,7 +40,7 @@ echo "Create _site/htmls" mkdir mkdir -p $DEPLOY_DIR/htmls echo "Copy cfg html" -cp -R gen/cfg_html_doc/generic_rv64/html $DEPLOY_DIR/example_cfg +cp -R gen/cfg_html_doc/example_rv64_with_overlay/html $DEPLOY_DIR/example_cfg echo "Create RVA20 Profile Release PDF Spec" ./do gen:profile[RVA20] diff --git a/lib/idl.rb b/lib/idl.rb index 287761e222..c2212c15bf 100644 --- a/lib/idl.rb +++ b/lib/idl.rb @@ -35,6 +35,8 @@ def instantiate_node(node_type, *args) module Idl # the Idl compiler class Compiler + attr_reader :parser + def initialize @parser = IdlParser.new end diff --git a/lib/idl/ast.rb b/lib/idl/ast.rb index d77aaa27cb..23ac6dd49a 100644 --- a/lib/idl/ast.rb +++ b/lib/idl/ast.rb @@ -241,6 +241,7 @@ def lines_around while cnt < 3 cnt += 1 if input[interval_end] == "\n" break if interval_end >= (input.size - 1) + break if cnt == 3 interval_end += 1 end @@ -503,6 +504,24 @@ def type(symtab) = raise NotImplementedError, "#{self.class.name} has no type" # @abstract def value(symtab) = raise NotImplementedError, "#{self.class.name} must implement value(symtab)" + def max_value(symtab) + value_result = value_try do + return value(symtab) + end + value_else(value_result) do + return :unknown + end + end + + def min_value(symtab) + value_result = value_try do + return value(symtab) + end + value_else(value_result) do + return :unknown + end + end + # @!macro [new] values # Return a complete list of possible compile-time-known values of the node, or raise a ValueError if # the full list cannot be determined @@ -568,7 +587,7 @@ def type_check(symtab) def type(symtab) return @type unless @type.nil? - internal_error "Symbol '#{name}' not found" if symtab.get(name).nil? + type_error "Symbol '#{name}' not found" if symtab.get(name).nil? sym = symtab.get(name) # @type = @@ -612,6 +631,40 @@ def value(symtab) v end + def max_value(symtab) + max = :unknown + value_result = value_try do + max = value(symtab) + end + value_else(value_result) do + var = symtab.get(name) + if !var.nil? && var.param? + param = symtab.cfg_arch.param(text_value) + if param.schema.max_val_known? + max = param.schema.max_val + end + end + end + max + end + + def min_value(symtab) + min = :unknown + value_result = value_try do + min = value(symtab) + end + value_else(value_result) do + var = symtab.get(name) + if !var.nil? && var.param? + param = symtab.cfg_arch.param(text_value) + if param.schema.min_val_known? + min = param.schema.min_val + end + end + end + min + end + # @!macro to_idl def to_idl = name end @@ -635,7 +688,10 @@ class GlobalWithInitializationAst < AstNode include Executable include Declaration - # @return [VariableDeclationWithInitializationAst] The initializer + def id = var_decl_with_init.id + def rhs = var_decl_with_init.rhs + + # @return [VariableDeclarationWithInitializationAst] The initializer def var_decl_with_init @children[0] end @@ -652,7 +708,7 @@ def type_check(symtab) # @1macro type def type(symtab) - var_decl_with_init.type(symtab) + var_decl_with_init.lhs_type(symtab) end # @1macro value @@ -682,6 +738,10 @@ def to_ast class GlobalAst < AstNode include Declaration + def id + declaration.id.text_value + end + # @return [VariableDeclarationAst] The decl def declaration @children[0] @@ -737,9 +797,12 @@ def bitfields = definitions.select { |e| e.is_a?(BitfieldDefinitionAst) } # @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) } + # @return [FetchAst] Fetch body + def fetch = definitions.select { |e| e.is_a?(FetchAst )}[0] + # Add all the global symbols to symtab # # @param symtab [SymbolTable] symbol table @@ -770,6 +833,10 @@ def replace_include!(include_ast, isa_ast) # @!macro type_check def type_check(symtab) definitions.each { |d| d.type_check(symtab) } + + fetch_blocks = definitions.select { |d| d.is_a?(FetchAst) } + type_error "Multiple fetch blocks defined" if fetch_blocks.size > 1 + type_error "No fetch block defined" if fetch_blocks.size.zero? end end @@ -802,7 +869,9 @@ def type(symtab) if expression.type(symtab).width == :unknown Type.new(:bits, width: :unknown, qualifiers: [:const]) else - Type.new(:bits, width: expression.type(symtab).width.bit_length, qualifiers: [:const]) + len = expression.type(symtab).width.bit_length + len = len.zero? ? 1 : len + Type.new(:bits, width: len, qualifiers: [:const]) end end @@ -1088,7 +1157,7 @@ def to_ast # represents a builtin (auto-generated from config) enum definition # # # this will result in a BuiltinEnumDefinitionAst - # builtin enum ExtensionName + # generated enum ExtensionName # class BuiltinEnumDefinitionAst < AstNode include Declaration @@ -1103,6 +1172,7 @@ def freeze_tree(global_symtab) # call type to get it set before we freeze the object type(global_symtab) + @children.each { |child| child.freeze_tree(global_symtab) } freeze end @@ -1112,7 +1182,7 @@ def type_check(_symtab) when "ExtensionName", "ExceptionCode", "InterruptCode" # OK else - type_error "Unsupported builtin enum type '#{@user_type.text_value}'" + type_error "Unsupported generated enum type '#{@user_type.text_value}'" end end @@ -1125,7 +1195,7 @@ def element_names(symtab) when "InterruptCode" symtab.cfg_arch.interrupt_codes.map(&:var) else - type_error "Unknown builtin enum type '#{name}'" + type_error "Unknown generated enum type '#{name}'" end end @@ -1138,7 +1208,7 @@ def element_values(symtab) when "InterruptCode" symtab.cfg_arch.interrupt_codes.map(&:num) else - type_error "Unknown builtin enum type '#{name}'" + type_error "Unknown generated enum type '#{name}'" end end @@ -1160,7 +1230,7 @@ def add_symbol(symtab) def name = @user_type.text_value # @!macro to_idl - def to_idl = "builtin enum #{@user_type.text_value}" + def to_idl = "generated enum #{@user_type.text_value}" end class BitfieldFieldDefinitionAst < AstNode @@ -1266,6 +1336,9 @@ def freeze_tree(global_symtab) return if frozen? type(global_symtab) + + @children.each { |child| child.freeze_tree(global_symtab) } + freeze end @@ -1706,12 +1779,14 @@ def execute(symtab) internal_error "No variable #{lhs.text_value}" if variable.nil? - value_result = value_try do - variable.value = rhs.value(symtab) - end - value_else(value_result) do - variable.value = nil - value_error "" + unless variable.type.global? + value_result = value_try do + variable.value = rhs.value(symtab) + end + value_else(value_result) do + variable.value = nil + value_error "" + end end end end @@ -2024,14 +2099,14 @@ 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) + Type.new(:bits, width: [field(symtab).location(32).size, field(symtab).location(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.cfg_arch.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(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(32).size) else internal_error "Unexpected base for field" end @@ -2043,11 +2118,11 @@ def field(symtab) def type_check(symtab) csr_field.type_check(symtab) - value_try do - if ["RO", "RO-H"].any?(csr_field.field_def(symtab).type(symtab)) - type_error "Cannot write to read-only CSR field" - end - end + # value_try do + # if ["RO"].any?(csr_field.field_def(symtab).type(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 write_value.type_check(symtab) @@ -2275,7 +2350,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.cfg_arch.fully_configured? dtype = Type.new(:array, width: :unknown, sub_type: dtype, qualifiers:) end end @@ -2360,6 +2435,8 @@ def lhs = @children[1] def ary_size = @children[3] def rhs = @children[2] + def id = lhs.text_value + def initialize(input, interval, type_name_ast, var_write_ast, ary_size, rval_ast) if ary_size.nil? super(input, interval, [type_name_ast, var_write_ast, rval_ast]) @@ -2555,7 +2632,7 @@ def to_idl = "$signed(#{expression.to_idl})" class BitsCastSyntaxNode < Treetop::Runtime::SyntaxNode def to_ast - BitsCastAst.new(input, interval, expression.to_ast) + BitsCastAst.new(input, interval, expr.to_ast) end end @@ -2568,22 +2645,22 @@ class BitsCastAst < AstNode include Rvalue # @return [AstNode] The casted expression - def expression = @children[0] + def expr = @children[0] def initialize(input, interval, exp) = super(input, interval, [exp]) # @!macro type_check def type_check(symtab) - expression.type_check(symtab) + expr.type_check(symtab) - unless [:bits, :enum_ref, :csr].include?(expression.type(symtab).kind) - type_error "#{expression.type(symtab)} Cannot be cast to bits" + unless [:bits, :enum_ref, :csr].include?(expr.type(symtab).kind) + type_error "#{expr.type(symtab)} Cannot be cast to bits" end end # @!macro type def type(symtab) - etype = expression.type(symtab) + etype = expr.type(symtab) case etype.kind when :bits @@ -2591,38 +2668,40 @@ 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.is_a?(Symbol) && etype.csr == :unknown) || etype.csr.dynamic_length? Type.new(:bits, width: :unknown) else Type.new(:bits, width: etype.csr.length(symtab.cfg_arch)) end + else + type_error "$bits cast is only defined for CSRs and Enum references" end end # @!macro value def value(symtab) - etype = expression.type(symtab) + etype = expr.type(symtab) case etype.kind - when :bits - expression.value(symtab) + # when :bits + # expr.value(symtab) when :enum_ref - if expression.is_a?(EnumRefAst) - element_name = expression.text_value.split(":")[2] + if expr.is_a?(EnumRefAst) + element_name = expr.text_value.split(":")[2] etype.enum_class.value(element_name) else # this is an expression with an EnumRef type - expression.value(symtab) + expr.value(symtab) end when :csr - expression.value(symtab) + expr.value(symtab) else - internal_error "TODO: Bits cast for #{etype.kind}" + type_error "TODO: Bits cast for #{etype.kind}" end end # @!macro to_idl - def to_idl = "$signed(#{expression.to_idl})" + def to_idl = "$signed(#{expr.to_idl})" end class BinaryExpressionAst < AstNode @@ -2825,6 +2904,63 @@ def type_check(symtab) end end + def max_value(symtab) + lhs_max_value = :unknown + value_result = value_try do + lhs_max_value = lhs.value(symtab) + end + value_else(value_result) do + lhs_max_value = lhs.max_value(symtab) + end + rhs_max_value = :unknown + value_result = value_try do + rhs_max_value = rhs.value(symtab) + end + value_else(value_result) do + rhs_max_value = rhs.max_value(symtab) + end + rhs_min_value = :unknown + value_result = value_try do + rhs_min_value = rhs.value(symtab) + end + value_else(value_result) do + rhs_min_value = rhs.min_value(symtab) + end + + max_value = + case op + when "+" + return :unknown if [lhs_max_value, rhs_max_value].include?(:unknown) + + lhs_max_value + rhs_max_value + when "-" + return :unknown if [lhs_max_value, rhs_min_value].include?(:unknown) + + lhs_max_value - rhs_min_value + when "*" + return :unknown if [lhs_max_value, rhs_max_value].include?(:unknown) + + lhs_max_value * rhs_max_value + else + raise "TODO: op '#{op}'" + end + + v_trunc = + if !lhs.type(symtab).const? || !rhs.type(symtab).const? + # when both sides are constant, the value is not truncated + width = type(symtab).width + if width == :unknown + value_error("unknown width in op that possibly truncates") + end + max_value & ((1 << type(symtab).width) - 1) + else + max_value + end + + warn "WARNING: The value of '#{text_value}' (#{lhs.type(symtab).const?}, #{rhs.type(symtab).const?}) is truncated from #{v} to #{v_trunc} because the result is only #{type(symtab).width} bits" if max_value != v_trunc + v_trunc + end + # @!macro value def value(symtab) # cached_value = @value_cache[symtab] @@ -3481,6 +3617,14 @@ def type(symtab) # @!macro value_no_cfg_arch def value(symtab) + @enum_def_type ||= begin + enum_def_ast = symtab.cfg_arch.global_ast.enums.find { |e| e.name == @enum_class_name } + if enum_def_ast.is_a?(BuiltinEnumDefinitionAst) + enum_def_ast&.type(symtab) + else + enum_def_ast&.type(nil) + end + end internal_error "Must call type_check first" if @enum_def_type.nil? @enum_def_type.value(@member_name) @@ -3973,7 +4117,14 @@ def to_idl = "#{return_expression.to_idl};" class ReturnExpressionSyntaxNode < Treetop::Runtime::SyntaxNode def to_ast - ReturnExpressionAst.new(input, interval, [first.to_ast] + rest.elements.map { |r| r.e.to_ast }) + return_asts = + if vals.empty? + [] + else + [vals.first.e.to_ast] + \ + vals.rest.elements.map { |r| r.e.to_ast } + end + ReturnExpressionAst.new(input, interval, return_asts) end end @@ -3987,7 +4138,9 @@ def initialize(input, interval, return_nodes) # @return [Array] List of actual return types def return_types(symtab) - if return_value_nodes[0].type(symtab).kind == :tuple + if return_value_nodes.empty? + [Type.new(:void)] + elsif return_value_nodes[0].type(symtab).kind == :tuple return_value_nodes[0].type(symtab).tuple_types else return_value_nodes.map{ |v| v.type(symtab) } @@ -3997,7 +4150,9 @@ def return_types(symtab) # @return [Type] The actual return type def return_type(symtab) types = return_types(symtab) - if types.size > 1 + if types.empty? + return Type.new(:void) + elsif types.size > 1 Type.new(:tuple, tuple_types: types) else types[0] @@ -4047,7 +4202,7 @@ def type_check(symtab) type_error "Unknown type for #{v.text_value}" if v.type(symtab).nil? end - if return_value_nodes[0].type(symtab).kind == :tuple + if !return_value_nodes.empty? && return_value_nodes[0].type(symtab).kind == :tuple type_error("Can't combine tuple types in return") unless return_value_nodes.size == 1 end @@ -4062,7 +4217,9 @@ def enclosing_function # @!macro return_value def return_value(symtab) - if return_value_nodes.size == 1 + if return_value_nodes.empty? + :void + elsif return_value_nodes.size == 1 return_value_nodes[0].value(symtab) else return_value_nodes.map { |v| v.value(symtab) } @@ -4071,7 +4228,9 @@ def return_value(symtab) # @!macro return_values def return_values(symtab) - if return_value_nodes.size == 1 + if return_value_nodes.empty? + [:void] + elsif return_value_nodes.size == 1 return_value_nodes[0].values(symtab) else return_value_nodes.map { |v| v.values(symtab) } @@ -4228,7 +4387,7 @@ def freeze_tree(symtab) rescue TypeError # ok, probably in a function template end - bits_expression.freeze_tree(symtab) + bits_expression&.freeze_tree(symtab) end freeze end @@ -4314,6 +4473,13 @@ def to_ast end end + class UnknownLiteral + def initialize(known_value, unknown_mask) + @known_value = known_value + @unknown_mask = unknown_mask + end + end + # represents an integer literal class IntLiteralAst < AstNode include Rvalue @@ -4474,15 +4640,42 @@ def unsigned_value radix_id = "d" if radix_id.empty? - case radix_id - when "b" - value.to_i(2) - when "o" - value.to_i(8) - when "d" - value.to_i(10) - when "h" - value.to_i(16) + if value.index("x").nil? && value.index("X").nil? + case radix_id + when "b" + value.to_i(2) + when "o" + value.to_i(8) + when "d" + value.to_i(10) + when "h" + value.to_i(16) + end + else + # there is unknown bit(s) in the value + known_value = + case radix_id + when "b" + value.gsub(/xX/, "0").to_i(2) + when "o" + value.gsub(/xX/, "0").to_i(8) + when "d" + raise "impossible" + when "h" + value.gsub(/xX/, "0").to_i(16) + end + unknown_mask = + case radix_id + when "b" + value.gsub("1", "0").gsub(/xX/, "1").to_i(2) + when "o" + value.gsub(/[0-7]/, "0").gsub(/xX/, "7").to_i(8) + when "d" + raise "impossible" + when "h" + value.gsub(/[0-9a-fA-F]/, "0").gsub(/xX/, "f").to_i(16) + end + UnknownLiteral.new(known_value, unknown_mask) end when /^0([bdx]?)([0-9a-fA-F]*)(s?)$/ # C++-style literal @@ -4650,7 +4843,7 @@ def type_check(symtab) # @!macro type def type(symtab) - return ConstBoolType if name == "implemented?" + return ConstBoolType if name == "implemented?" || name == "implemented_version?" || name == "implemented_csr?" func_type(symtab).return_type(template_values(symtab, unknown_ok: symtab.cfg_arch.partially_configured?), self) end @@ -4664,7 +4857,7 @@ def value(symtab) func_def_type = func_type(symtab) type_error "#{name} is not a function" unless func_def_type.is_a?(FunctionType) - if func_def_type.builtin? + if func_def_type.generated? if name == "implemented?" 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" @@ -4675,11 +4868,51 @@ def value(symtab) # we can know if it is implemented, but not if it's not implemented for a partially configured return true end + if symtab.cfg_arch.prohibited_ext?(arg_nodes[0].member_name) + return false + end value_error "implemented? is only known when evaluating in the context of a fully-configured arch def" + elsif name == "implemented_version?" + 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" + + ver_req = arg_nodes[1].text_value[1..-2] + + return symtab.cfg_arch.ext?(arg_nodes[0].member_name, ver_req) if symtab.cfg_arch.fully_configured? + + if symtab.cfg_arch.ext?(arg_nodes[0].member_name, ver_req) + # we can know if it is implemented, but not if it's not implemented for a partially configured + return true + end + if symtab.cfg_arch.prohibited_ext?(arg_nodes[0].member_name) + return false + end + value_error "implemented_version? is only known when evaluating in the context of a fully-configured arch def" + elsif name == "implemented_csr?" + csr_addr = arg_nodes[0].value(symtab) + if symtab.cfg_arch.fully_configured? + if symtab.cfg_arch.transitive_implemented_csrs.any? { |csr| csr.address == csr_addr } + return true + end + else + if symtab.cfg_arch.not_prohibited_csrs.none? { |csr| csr.address == csr_addr } + return false + end + end + value_error "implemented_csr? is only known when evaluating in the context of a fully-configured arch def" + elsif name == "cached_translation" + value_error "cached_translation is not compile-time-knowable" + elsif name == "maybe_cache_translation" + value_error "maybe_cache_translation is not compile-time-knowable" + elsif name == "invalidate_translations" + value_error "invalidate_translations is not compile-time-knowable" else - value_error "value of builtin function cannot be known" + internal_error "Unimplemented generated: '#{name}'" end end + if func_def_type.builtin? + value_error "value of builtin functions aren't knowable" + end template_values = if !template? @@ -4853,16 +5086,39 @@ def to_idl end end + class FetchSyntaxNode < Treetop::Runtime::SyntaxNode + def to_ast + FetchAst.new(input, interval, function_body.to_ast) + end + end + + class FetchAst < AstNode + def body = @children[0] + + def initialize(input, interval, body) + super(input, interval, [body]) + end + + def type_check(symtab) + body.type_check(symtab) + end + + def return_type(symtab) + @ret_type = Type.new(:bits, width: symtab.get("INSTR_ENC_WIDTH").value) + end + end + class FunctionDefSyntaxNode < Treetop::Runtime::SyntaxNode def to_ast FunctionDefAst.new( input, interval, function_name.text_value, - targs.empty? ? [] : [targs.first.to_ast] + targs.rest.elements.map { |r| r.single_declaration.to_ast }, - ret.empty? ? [] : [ret.first.to_ast] + ret.rest.elements.map { |r| r.type_name.to_ast }, + (!respond_to?(:targs) || targs.empty?) ? [] : [targs.first.to_ast] + targs.rest.elements.map { |r| r.single_declaration.to_ast }, + ret.empty? ? [] : [ret.first.to_ast] + (ret.respond_to?(:rest) ? ret.rest.elements.map { |r| r.type_name.to_ast } : []), args.empty? ? [] : [args.first.to_ast] + args.rest.elements.map { |r| r.single_declaration.to_ast}, desc.text_value, + respond_to?(:type) ? type.text_value.strip.to_sym : :normal, respond_to?(:body_block) ? body_block.function_body.to_ast : nil ) end @@ -4871,6 +5127,8 @@ def to_ast class FunctionDefAst < AstNode include Declaration + attr_reader :return_type_nodes + # @param input [String] The source code # @param interval [Range] The range in the source code for this function definition # @param name [String] The name of the function @@ -4878,8 +5136,9 @@ class FunctionDefAst < AstNode # @params return_types [Array] Return types # @param arguments [Array] Arguments # @param desc [String] Description + # @param type [:normal, :builtin, :generated, :external] Type of function # @param body [AstNode,nil] Body, unless the function is builtin - def initialize(input, interval, name, targs, return_types, arguments, desc, body) + def initialize(input, interval, name, targs, return_types, arguments, desc, type, body) if body.nil? super(input, interval, targs + return_types + arguments) else @@ -4892,6 +5151,9 @@ def initialize(input, interval, name, targs, return_types, arguments, desc, body @argument_nodes = arguments @desc = desc @body = body + @builtin = type == :builtin + @generated = type == :generated + @external = type == :external @cached_return_type = {} @reachable_functions_cache ||= {} @@ -5071,6 +5333,16 @@ def type_check_from_call(symtab) type_check_body(symtab) end + def apply_template_and_arg_syms(symtab) + template_names.each_with_index do |tname, index| + symtab.add(tname, Var.new(tname, template_types(symtab)[index], template_index: index, function_name: name)) + end + + arguments(symtab).each do |arg_type, arg_name| + symtab.add(arg_name, Var.new(arg_name, arg_type)) + end + end + # @!macro type_check def type_check(symtab) internal_error "Functions must be declared at global scope (at #{symtab.levels})" unless symtab.levels == 1 @@ -5151,13 +5423,21 @@ def type_check_body(symtab) end def body - internal_error "Function has no body" if builtin? + internal_error "Function has no body" if builtin? || generated? @body end def builtin? - @body.nil? + @builtin + end + + def generated? + @generated + end + + def external? + @external end end @@ -5721,53 +6001,45 @@ def to_idl class CsrFieldReadExpressionAst < AstNode include Rvalue - def initialize(input, interval, idx, field_name) - if idx.is_a?(AstNode) - super(input, interval, [idx]) - else - super(input, interval, EMPTY_ARRAY) - end + def initialize(input, interval, csr, field_name) + super(input, interval, [csr]) - @idx = idx + @csr = csr @field_name = field_name end def freeze_tree(symtab) return if frozen? + @children.each { |child| child.freeze_tree(symtab) } + + @csr_obj = @csr.csr_def(symtab) + type_error "No CSR '#{@csr.text_value}'" if @csr_obj.nil? + value_result = value_try do @value = calc_value(symtab) end value_else(value_result) do @value = nil end + @type = calc_type(symtab) @cfg_arch = symtab.cfg_arch # remember cfg_arch, used in gen_adoc pass + freeze end # @!macro type_check def type_check(symtab) - if @idx.is_a?(IntLiteralAst) - type_error "No CSR at address #{@idx.text_value}" if csr_def(symtab).nil? - else - # idx is a csr name - csr_name = @idx - type_error "No CSR named #{csr_name}" if csr_def(symtab).nil? - end + @csr.type_check(symtab) + 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? end def csr_def(symtab) - cfg_arch = symtab.cfg_arch - - if @idx.is_a?(IntLiteralAst) - cfg_arch.csrs.find { |c| c.address == @idx.value(symtab) } - else - cfg_arch.csr(@idx) - end + @csr_obj end def csr_name(symtab) @@ -5775,7 +6047,7 @@ def csr_name(symtab) end def field_def(symtab) - csr_def(symtab).fields.find { |f| f.name == @field_name } + @csr_obj.fields.find { |f| f.name == @field_name } end def field_name(symtab) @@ -5784,11 +6056,7 @@ def field_name(symtab) # @!macro to_idl def to_idl - if @idx.is_a?(IntLiteralAst) - "CSR[#{@idx.to_idl}].#{@field_name}" - else - "CSR[#{@idx}].#{@field_name}" - end + "CSR[#{@csr_obj.name}].#{@field_name}" end # @!macro type @@ -5798,22 +6066,17 @@ def type(symtab) def calc_type(symtab) fd = field_def(symtab) - if fd.nil? - if @idx.is_a?(IntLiteralAst) - internal_error "Could not find CSR[#{@idx.to_idl}].#{@field_name}" - else - internal_error "Could not find CSR[#{@idx}].#{@field_name}" - end - end + internal_error "Could not find #{@csr.text_value}.#{@field_name}" if fd.nil? + 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.cfg_arch.possible_xlens.map{ |xlen| fd.width(xlen) }.max) elsif fd.base64_only? if symtab.cfg_arch.possible_xlens.include?(64) - Type.new(:bits, width: fd.width(symtab.cfg_arch, 64)) + Type.new(:bits, width: fd.width(64)) end elsif fd.base32_only? if symtab.cfg_arch.possible_xlens.include?(32) - Type.new(:bits, width: fd.width(symtab.cfg_arch, 32)) + Type.new(:bits, width: fd.width(32)) end else internal_error "unexpected field base" @@ -5831,54 +6094,61 @@ def value(symtab) def calc_value(symtab) # field isn't implemented, so it must be zero - return 0 if field_def(symtab).nil? + return 0 if field_def(symtab).nil? || !field_def(symtab).exists_in_cfg?(symtab.cfg_arch) - unless field_def(symtab).type(symtab) == "RO" - value_error "'#{csr_name(symtab)}.#{field_name(symtab)}' is not RO" + symtab.cfg_arch.possible_xlens.each do |effective_xlen| + unless field_def(symtab).type(effective_xlen) == "RO" + value_error "'#{csr_name(symtab)}.#{field_name(symtab)}' is not RO" + end end - field_def(symtab).reset_value(symtab.cfg_arch) + v = field_def(symtab).reset_value + v = nil if v == "UNDEFINED_LEGAL" end end class CsrReadExpressionSyntaxNode < Treetop::Runtime::SyntaxNode def to_ast - if idx.respond_to?(:to_ast) - CsrReadExpressionAst.new(input, interval, idx.to_ast) - else - CsrReadExpressionAst.new(input, interval, idx.text_value) - end + CsrReadExpressionAst.new(input, interval, idx.text_value) end end class CsrFieldReadExpressionSyntaxNode < Treetop::Runtime::SyntaxNode def to_ast - if idx.respond_to?(:to_ast) - CsrFieldReadExpressionAst.new(input, interval, idx.to_ast, csr_field_name.text_value) - else - CsrFieldReadExpressionAst.new(input, interval, idx.text_value, csr_field_name.text_value) - end + CsrFieldReadExpressionAst.new(input, interval, csr.to_ast, csr_field_name.text_value) end end class CsrReadExpressionAst < AstNode include Rvalue + attr_reader :idx_text + attr_reader :idx_expr + def initialize(input, interval, idx) - if idx.is_a?(AstNode) - super(input, interval, [idx]) - else - super(input, interval, EMPTY_ARRAY) - end + super(input, interval, []) - @idx = idx + @idx_text = idx end def freeze_tree(symtab) return if frozen? @cfg_arch = symtab.cfg_arch # remember cfg_arch, used by gen_adoc pass - @idx.freeze_tree(symtab) + + if symtab.cfg_arch.csr(@idx_text).nil? + parser = symtab.cfg_arch.idl_compiler.parser + expr = parser.parse(@idx_text, root: :expression) + + type_error "#{@idx_text} is not a CSR; it must be an expression" if expr.nil? + + @idx_expr = expr.to_ast + @children << @idx_expr + else + @csr_obj = symtab.cfg_arch.csr(@idx_text) + end + + @children.each { |child| child.freeze_tree(symtab) } freeze end @@ -5890,11 +6160,7 @@ def type(symtab) if cd.nil? # we don't know anything about this index, so we can only # treat this as a generic - if symtab.mxlen == 32 - Bits32Type - else - Bits64Type - end + CsrType.new(:unknown, cfg_arch) else CsrType.new(cd, cfg_arch) end @@ -5904,18 +6170,17 @@ def type(symtab) def type_check(symtab) cfg_arch = symtab.cfg_arch - idx_text = @idx.is_a?(String) ? @idx : @idx.text_value - if !cfg_arch.csr(idx_text).nil? + if !@csr_obj.nil? # this is a known csr name # nothing else to check else # this is an expression - @idx.type_check(symtab) - type_error "Csr index must be integral" unless @idx.type(symtab).integral? + @idx_expr.type_check(symtab) + type_error "Csr index must be integral" unless @idx_expr.type(symtab).integral? - value_result = value_try do - idx_value = @idx.value(symtab) + value_try do + idx_value = @idx_expr.value(symtab) csr_index = cfg_arch.csrs.index { |csr| csr.address == idx_value } type_error "No csr number '#{idx_value}' was found" if csr_index.nil? :ok @@ -5926,15 +6191,13 @@ def type_check(symtab) def csr_def(symtab) cfg_arch = symtab.cfg_arch - idx_text = @idx.is_a?(String) ? @idx : @idx.text_value - csr = cfg_arch.csr(idx_text) - if !csr.nil? + if !@csr_obj.nil? # this is a known csr name - csr + @csr_obj else # this is an expression - value_result = value_try do - idx_value = @idx.value(symtab) + value_try do + idx_value = @idx_expr.value(symtab) return cfg_arch.csrs.find { |csr| csr.address == idx_value } end # || we don't know at compile time which CSR this is... @@ -6024,7 +6287,11 @@ def to_idl = "#{csr.to_idl}.sw_write(#{expression.to_idl})" # @api private class CsrFunctionCallSyntaxNode < Treetop::Runtime::SyntaxNode def to_ast - CsrFunctionCallAst.new(input, interval, function_name.text_value, csr.to_ast) + args = [] + args << function_arg_list.first.to_ast unless function_arg_list.first.empty? + args += function_arg_list.rest.elements.map { |e| e.expression.to_ast } + + CsrFunctionCallAst.new(input, interval, function_name.text_value, csr.to_ast, args) end end @@ -6040,18 +6307,24 @@ class CsrFunctionCallAst < AstNode attr_reader :function_name def csr = @children[0] + def args = @children[1..] - def initialize(input, interval, function_name, csr) - super(input, interval, [csr]) + def initialize(input, interval, function_name, csr, args) + super(input, interval, [csr] + args) @function_name = function_name end def type_check(symtab) - unless ["sw_read", "address"].include?(function_name) + csr.type_check(symtab) + + if ["sw_read", "address"].include?(function_name) + type_error "unexpected argument(s)" unless args.empty? + elsif ["implemented_without?"].include?(function_name) + type_error "Expecting one argument" unless args.size == 1 + type_error "Expecting an ExtensionName" unless args[0].type(symtab).kind == :enum_ref && args[0].class_name == "ExtensionName" + else type_error "'#{function_name}' is not a supported CSR function call" end - - csr.type_check(symtab) end def type(symtab) @@ -6060,12 +6333,15 @@ def type(symtab) case function_name when "sw_read" if csr_known?(symtab) - Type.new(:bits, width: cfg_arch.csr(csr.csr_name(symtab)).length(cfg_arch)) + l = cfg_arch.csr(csr.csr_name(symtab)).length + Type.new(:bits, width: (l.nil? ? :unknown : l)) else Type.new(:bits, width: symtab.mxlen.nil? ? :unknown : symtab.mxlen) end when "address" Type.new(:bits, width: 12) + when "implemented_without?" + ConstBoolType else internal_error "No function '#{function_name}' for CSR. call type check first!" end @@ -6096,6 +6372,15 @@ def value(symtab) value_error "CSR not knowable" unless csr_known?(symtab) cd = csr_def(symtab) cd.address + when "implemented_without?" + value_error "CSR not knowable" unless csr_known?(symtab) + cd = csr_def(symtab) + extension_name_enum_type = symtab.get("ExtensionName") + enum_value = args[0].value(symtab) + idx = extension_name_enum_type.element_values.index(enum_value) + ext_name = extension_name_enum_type.element_names[idx] + ext = symtab.cfg_arch.extension(ext_name) + cd.defined_by_condition.satisfied_by?(symtab.cfg_arch.possible_extensions - ext) else internal_error "TODO: #{function_name}" end diff --git a/lib/idl/idl.treetop b/lib/idl/idl.treetop index 43e9dae440..9cead8a2b2 100644 --- a/lib/idl/idl.treetop +++ b/lib/idl/idl.treetop @@ -1,4 +1,5 @@ -# grammar for the Isa Description Language +# grammar for the ISA Description Language + grammar Idl rule isa space* @@ -16,6 +17,8 @@ grammar Idl / function_definition / + fetch + / space+ )+ end @@ -26,13 +29,13 @@ grammar Idl # declaring a global variable or constant rule global_definition - single_declaration_with_initialization space* ';' + const:('const'? space+)? single_declaration_with_initialization space* ';' / declaration space* ';' end rule enum_definition - 'builtin' space+ 'enum' space+ user_type_name space* ';' + 'generated' space+ 'enum' space+ user_type_name space* ';' / 'enum' space+ user_type_name space+ '{' space* e:(user_type_name space+ i:(int space+)?)+ space* @@ -61,16 +64,16 @@ grammar Idl rule int # verilog style: explicit bit width - (([0-9]+)/'XLEN')? "'" 'b' [0-1] [0-1_]* / - (([0-9]+)/'XLEN')? "'" 'o' [0-7] [0-7_]* / + (([0-9]+)/'XLEN')? "'" 'b' [0-1xX] [0-1_xX]* / + (([0-9]+)/'XLEN')? "'" 'o' [0-7xX] [0-7_xX]* / (([0-9]+)/'XLEN')? "'" 'd'? [0-9] [0-9_]* / - (([0-9]+)/'XLEN')? "'" 'h' [0-9a-fA-F] [0-9a-fA-F_]* / + (([0-9]+)/'XLEN')? "'" 'h' [0-9a-fA-FxX] [0-9a-fA-F_xX]* / # verilog style: explicit bit width, signed - (([0-9]+)/'XLEN')? "'" 'sb' [0-1] [0-1_]* / - (([0-9]+)/'XLEN')? "'" 'so' [0-7] [0-7_]* / + (([0-9]+)/'XLEN')? "'" 'sb' [0-1xX] [0-1_xX]* / + (([0-9]+)/'XLEN')? "'" 'so' [0-7xX] [0-7_xX]* / (([0-9]+)/'XLEN')? "'" 's' 'd'? [0-9] [0-9_]* / - (([0-9]+)/'XLEN')? "'" 'sh' [0-9a-fA-F] [0-9a-fA-F_]* / + (([0-9]+)/'XLEN')? "'" 'sh' [0-9a-fA-FxX] [0-9a-fA-F_xX]* / # c++ style: signed '0b' [0-1] [0-1_]* 's' / @@ -88,10 +91,6 @@ grammar Idl '0' 's'? end - rule bits - "'" [01]+ "'" - end - rule p0_binary_operator # highest priority binary operators '/' # division with ignored remainder @@ -271,12 +270,12 @@ grammar Idl rule csr_field_access_expression # CSR field access - 'CSR' space* '[' space* idx:(csr_name / int) space* ']' space* '.' space* csr_field_name + csr:csr_register_access_expression space* '.' space* csr_field_name end rule csr_register_access_expression # CSR register access - 'CSR' space* '[' space* idx:expression space* ']' + 'CSR' space* '[' space* idx:(expression / csr_name) space* ']' end rule field_access_eligible_expression @@ -291,6 +290,7 @@ grammar Idl field_access_eligible_expression space* '.' space* field_name end + # expression that can accept an array index operator (expr[]) rule ary_eligible_expression paren_expression / @@ -304,8 +304,6 @@ grammar Idl / csr_field_access_expression / - csr_register_access_expression - / bits_cast / rval # must come last! @@ -324,7 +322,15 @@ grammar Idl end rule bits_cast - '$bits' space* '(' space* expression space* ')' + '$bits' space* '(' space* + expr:( + csr_register_access_expression + / + enum_ref + / + expression + ) + space* ')' end rule unary_expression @@ -364,8 +370,6 @@ grammar Idl / csr_field_access_expression / - csr_register_access_expression - / enum_ref / rval # must come last @@ -403,7 +407,7 @@ grammar Idl rule function_call csr:csr_register_access_expression space* '.' space* 'sw_write' space* '(' space* expression space* ')' / - csr:csr_register_access_expression space* '.' space* function_name space* '(' space* ')' + csr:csr_register_access_expression space* '.' space* function_name space* '(' space* function_arg_list space* ')' / function_name t:(space* '<' space* targs:function_call_template_arguments space* '>')? space* '(' space* function_arg_list space* ')' end @@ -417,7 +421,7 @@ grammar Idl end rule body_function_definition - 'function' space+ function_name space* '{' space* + type:('external' space+)? 'function' space+ function_name space* '{' space* targs:('template' space+ first:single_declaration rest:(space* ',' space* single_declaration)* space+)? ret:('returns' space+ first:type_name rest:(space* ',' space* type_name)* space+)? args:( @@ -438,9 +442,9 @@ grammar Idl end rule builtin_function_definition - 'builtin' space+ 'function' space+ function_name space* '{' space* - targs:('template' space+ first:single_declaration rest:(space* ',' space* single_declaration)* space+)? - ret:('returns' space+ first:type_name rest:(space* ',' space* type_name)* space+)? + type:('builtin' / 'generated') space+ 'function' space+ function_name space* '{' space* + # templates are not supported for builtins + ret:('returns' space+ first:type_name space+)? args:( 'arguments' space+ first:single_declaration @@ -453,6 +457,12 @@ grammar Idl '}' end + rule fetch + 'fetch' space* '{' space* + function_body space* + '}' + end + rule function_definition builtin_function_definition / body_function_definition end @@ -478,17 +488,17 @@ grammar Idl end rule single_declaration_with_initialization - q:type_qualifier? space* type_name space+ id space* ary_size:ary_size_decl? space* '=' space* rval:expression + type_name space+ id space* ary_size:ary_size_decl? space* '=' space* rval:expression end rule declaration - q:type_qualifier? space* type_name space+ first:id space* rest:(space* ',' space* id)+ space* + type_name space+ first:id space* rest:(space* ',' space* id)+ space* / single_declaration end rule single_declaration - q:type_qualifier? space* type_name space+ id ary_size:(space* ary_size_decl)? + type_name space+ id ary_size:(space* ary_size_decl)? end rule statement @@ -506,7 +516,7 @@ grammar Idl end rule return_expression - 'return' space+ first:(expression/dontcare_return) rest:(space* ',' space* e:(expression/dontcare_return))* + 'return' vals:(first:(space+ e:(expression/dontcare_return))? rest:(space* ',' space* e:(expression/dontcare_return))*) end rule return_statement @@ -553,10 +563,6 @@ grammar Idl '}' end - rule type_qualifier - '' # 'signed' - end - rule builtin_type_name # alias for Bits 'XReg' ![A-Za-z0-9] / @@ -585,8 +591,10 @@ grammar Idl 'body' ![A-Za-z0-9_] / 'function' ![A-Za-z0-9_] / 'builtin' ![A-Za-z0-9_] / + 'generated' ![A-Za-z0-9_] / 'enum' ![A-Za-z0-9_] / - 'bitfield' ![A-Za-z0-9_] + 'bitfield' ![A-Za-z0-9_] / + 'CSR' ![A-Za-z0-9_] end rule user_type_name @@ -636,7 +644,7 @@ grammar Idl end rule csr_name - [a-z] ([a-z0-9])* + [a-z] [a-z0-9_.]* end rule csr_field_name diff --git a/lib/idl/passes/find_src_registers.rb b/lib/idl/passes/find_src_registers.rb new file mode 100644 index 0000000000..17da4daf12 --- /dev/null +++ b/lib/idl/passes/find_src_registers.rb @@ -0,0 +1,120 @@ +module Idl + class ComplexRegDetermination < RuntimeError + end + + class AstNode + def find_src_registers(symtab) + # if is_a?(Executable) + # value_result = value_try do + # execute(symtab) + # end + # value_else(value_result) do + # execute_unknown(symtab) + # end + # end + add_symbol(symtab) if is_a?(Declaration) + + srcs = [] + @children.each do |child| + srcs.concat(child.find_src_registers(symtab)) + end + srcs.uniq + end + + def find_dst_registers(symtab) + # if is_a?(Executable) + # value_result = value_try do + # execute(symtab) + # end + # value_else(value_result) do + # execute_unknown(symtab) + # end + # end + add_symbol(symtab) if is_a?(Declaration) + + srcs = [] + @children.each do |child| + srcs.concat(child.find_dst_registers(symtab)) + end + srcs.uniq + end + end + + class ForLoopAst + # we don't unroll, but we don't add the index variable to the symtab, either + # that will cause any register accesses dependent on the index variable to raise Complex + def find_src_registers(symtab) + srcs = init.find_src_registers(symtab) + # don't add init to the symtab, since we don't want to use it... + srcs += condition.find_src_registers(symtab) + + stmts.each do |stmt| + srcs += stmt.find_src_registers(symtab) + end + srcs += update.find_src_registers(symtab) + + srcs + end + + # we don't unroll, but we don't add the index variable to the symtab, either + # that will cause any register accesses dependent on the index variable to raise Complex + def find_dst_registers(symtab) + dsts = init.find_dst_registers(symtab) + # don't add init to the symtab, since we don't want to use it... + dsts += condition.find_dst_registers(symtab) + + stmts.each do |stmt| + dsts += stmt.find_dst_registers(symtab) + end + dsts += update.find_dst_registers(symtab) + + dsts + end + end + + class AryElementAccessAst + def find_src_registers(symtab) + value_result = value_try do + if var.text_value == "X" + return [index.value(symtab)] + else + return [] + end + end + value_else(value_result) do + if var.text_value == "X" + if index.type(symtab).const? + return [index.gen_cpp(symtab, 0)] + else + raise ComplexRegDetermination + end + else + return [] + end + end + end + end + + class AryElementAssignmentAst + def find_dst_registers(symtab) + value_result = value_try do + if lhs.text_value == "X" + return [idx.value(symtab)] + else + return [] + end + end + value_else(value_result) do + if lhs.text_value == "X" + if idx.type(symtab).const? + return [idx.gen_cpp(symtab, 0)] + else + raise ComplexRegDetermination + end + else + return [] + end + end + end + end +end diff --git a/lib/idl/passes/gen_adoc.rb b/lib/idl/passes/gen_adoc.rb index 6213411fdc..f3c56d4a27 100644 --- a/lib/idl/passes/gen_adoc.rb +++ b/lib/idl/passes/gen_adoc.rb @@ -68,7 +68,8 @@ def gen_adoc(indent, indent_spaces: 2) end class CsrFunctionCallAst def gen_adoc(indent, indent_spaces: 2) - "#{' '*indent}#{csr.gen_adoc(indent, indent_spaces:)}.#{function_name}()" + args_adoc = args.map { |arg| arg.gen_adoc(0) } + "#{' '*indent}#{csr.gen_adoc(indent, indent_spaces:)}.#{function_name}(#{args_adoc.join(', ')})" end end class CsrSoftwareWriteAst @@ -93,7 +94,7 @@ def gen_adoc(indent, indent_spaces: 2) end class BitsCastAst def gen_adoc(indent, indent_spaces: 2) - "#{' '*indent}$bits(#{expression.gen_adoc(0, indent_spaces: )})" + "#{' '*indent}$bits(#{expr.gen_adoc(0, indent_spaces: )})" end end class EnumCastAst @@ -291,22 +292,8 @@ def gen_adoc(indent = 0, indent_spaces: 2) class CsrFieldReadExpressionAst def gen_adoc(indent = 0, indent_spaces: 2) - idx_text = - if @idx.is_a?(AstNode) - @idx.text_value - else - @idx - end - csr_text = "CSR[#{idx_text}].#{@field_name}" - if idx_text =~ /[0-9]+/ - "#{' '*indent}#{csr_text}" - else - if @cfg_arch.csr(csr_text).nil? - "#{' '*indent}#{csr_text}" - else - "#{' '*indent}%%LINK%csr_field;#{idx_text}.#{@field_name};#{csr_text}%%" - end - end + csr_text = "CSR[#{@csr_obj.name}].#{@field_name}" + "#{' '*indent}%%LINK%csr_field;#{@csr_obj.name}.#{@field_name};#{csr_text}%%" end end diff --git a/lib/idl/passes/prune.rb b/lib/idl/passes/prune.rb index ca5bedde07..0c49cf183d 100644 --- a/lib/idl/passes/prune.rb +++ b/lib/idl/passes/prune.rb @@ -20,13 +20,18 @@ def create_bool_literal(value) end end -def create_literal(value) - if value.is_a?(Integer) +def create_literal(symtab, value, type) + case type.kind + when :enum_ref + member_name = type.enum_class.element_names[type.enum_class.element_values.index(value)] + str = "#{type.enum_class.name}::#{member_name}" + Idl::EnumRefAst.new(str, 0...str.size, type.enum_class.name, member_name) + when :bits create_int_literal(value) - elsif value.is_a?(TrueClass) || value.is_a?(FalseClass) + when :boolean create_bool_literal(value) else - raise "TODO: #{value.class.name}" + raise "TODO: #{type}" end end @@ -51,12 +56,29 @@ def prune(symtab) new_node end + + def nullify_assignments(symtab) + children.each { |child| child.nullify_assignments(symtab) } + end + end + class VariableAssignmentAst + def prune(symtab) + new_ast = VariableAssignmentAst.new(input, interval, lhs.dup, rhs.prune(symtab)) + new_ast.execute_unknown(symtab) + new_ast + end + def nullify_assignments(symtab) + sym = symtab.get(lhs.text_value) + unless sym.nil? + sym.value = nil + end + end end class FunctionCallExpressionAst def prune(symtab) value_result = value_try do v = value(symtab) - return create_literal(v) + return create_literal(symtab, v, type(symtab)) end value_else(value_result) do FunctionCallExpressionAst.new(input, interval, name, targs.map { |t| t.prune(symtab) }, args.map { |a| a.prune(symtab)} ) @@ -65,6 +87,19 @@ def prune(symtab) end class VariableDeclarationWithInitializationAst def prune(symtab) + add_symbol(symtab) + + # do we want to remove a constant? If so, need to add a prune for IdAst that + # spits out a literal + # + # if lhs.const? + # value_try do + # rhs.value(symtab) + # # rhs value is known, and variable is const. it can be removed + # return NoopAst.new + # end + # end + VariableDeclarationWithInitializationAst.new( input, interval, type_name.dup, @@ -78,6 +113,10 @@ class ForLoopAst def prune(symtab) symtab.push(self) symtab.add(init.lhs.name, Var.new(init.lhs.name, init.lhs_type(symtab))) + + # make sure that any variable assigned within the loop is considered unknown + stmts.each { |stmt| stmt.nullify_assignments(symtab) } + begin new_loop = ForLoopAst.new( @@ -93,6 +132,26 @@ def prune(symtab) new_loop end end + class FunctionDefAst + def prune(symtab) + pruned_body = + unless builtin? || generated? + apply_template_and_arg_syms(symtab) + @body.prune(symtab, args_already_applied: true) + end + + FunctionDefAst.new( + input, interval, + name, + @targs.map(&:dup), + @return_type_nodes.map(&:dup), + @argument_nodes.map(&:dup), + @desc, + @type, + pruned_body + ) + end + end class FunctionBodyAst def prune(symtab, args_already_applied: false) symtab.push(self) @@ -100,9 +159,9 @@ def prune(symtab, args_already_applied: false) begin func_def = find_ancestor(FunctionDefAst) unless args_already_applied || func_def.nil? - if func_def.templated? # can't prune a template because we don't have all types - return dup - end + # if func_def.templated? # can't prune a template because we don't have all types + # return dup + # end # push template values func_def.template_names.each_with_index do |tname, idx| @@ -175,7 +234,7 @@ class BinaryExpressionAst def prune(symtab) value_try do val = value(symtab) - return create_literal(val) + return create_literal(symtab, val, type(symtab)) end # fall through @@ -191,27 +250,39 @@ def prune(symtab) end if op == "&&" - if lhs_value == false + if !lhs_value.nil? && !rhs_value.nil? + create_bool_literal(lhs_value && rhs_value) + elsif lhs_value == true rhs.prune(symtab) + elsif rhs_value == true + lhs.prune(symtab) + elsif lhs_value == false || rhs_value == false + create_bool_literal(false) else BinaryExpressionAst.new(input, interval, lhs.prune(symtab), @op, rhs.prune(symtab)) end elsif op == "||" - if lhs_value == true + if !lhs_value.nil? && !rhs_value.nil? + create_bool_literal(lhs_value || rhs_value) + elsif lhs_value == true || rhs_value == true + create_bool_literal(true) + elsif lhs_value == false rhs.prune(symtab) + elsif rhs_value == false + lhs.prune(symtab) else BinaryExpressionAst.new(input, interval, lhs.prune(symtab), @op, rhs.prune(symtab)) end elsif op == "&" if lhs_value == 0 # 0 & anything == 0 - create_literal(0) + create_literal(symtab, 0, type(symtab)) elsif (rhs.type(symtab).width != :unknown) && lhs_value == ((1 << rhs.type(symtab).width) - 1) # rhs idenntity rhs.prune(symtab) elsif rhs_value == 0 # anything & 0 == 0 - create_literal(0) + create_literal(symtab, 0, type(symtab)) elsif (lhs.type(symtab).width != :unknown) && rhs_value == (1 << lhs.type(symtab).width - 1) # lhs identity lhs.prune(symtab) @@ -228,17 +299,23 @@ def prune(symtab) rhs.prune(symtab) elsif rhs_type.width != :unknown && lhs_value == ((1 << rhs.type(symtab).width) - 1) # ~0 | anything == ~0 - create_literal(lhs_value) + create_literal(symtab, lhs_value, type(symtab)) elsif rhs_value == 0 # lhs identity lhs.prune(symtab) elsif lhs_type.width != :unknown && rhs_value == (1 << lhs.type(symtab).width - 1) # anything | ~0 == ~0 - create_literal(rhs_value) + create_literal(symtab, rhs_value, type(symtab)) else # neither lhs nor rhs were prunable BinaryExpressionAst.new(input, interval, lhs.prune(symtab), @op, rhs.prune(symtab)) end + elsif op == "==" + if !lhs_value.nil? && !rhs_value.nil? + create_bool_literal(lhs_value == rhs_value) + else + BinaryExpressionAst.new(input, interval, lhs.prune(symtab), @op, rhs.prune(symtab)) + end else BinaryExpressionAst.new(input, interval, lhs.prune(symtab), @op, rhs.prune(symtab)) end diff --git a/lib/idl/passes/reachable_exceptions.rb b/lib/idl/passes/reachable_exceptions.rb index 3daad890b8..fbdd938dd7 100644 --- a/lib/idl/passes/reachable_exceptions.rb +++ b/lib/idl/passes/reachable_exceptions.rb @@ -7,19 +7,19 @@ module Idl class AstNode # @return [Array] List of all functions that can be reached (via function calls) from this node - def reachable_exceptions(symtab) + def reachable_exceptions(symtab, cache = {}) return 0 if @children.empty? mask = 0 @children.size.times do |i| - mask |= @children[i].reachable_exceptions(symtab) + mask |= @children[i].reachable_exceptions(symtab, cache) end mask end end class FunctionCallExpressionAst - def reachable_exceptions(symtab) + def reachable_exceptions(symtab, cache = {}) if name == "raise" || name == "raise_precise" # first argument is the exception code_ast = arg_nodes[0] @@ -37,23 +37,33 @@ def reachable_exceptions(symtab) func_def_type = func_type(symtab) + tvals = template_values(symtab) + mask = 0 if template? template_arg_nodes.each do |t| - mask |= t.reachable_exceptions(symtab) if t.is_a?(FunctionCallExpressionAst) + mask |= t.reachable_exceptions(symtab, cache) if t.is_a?(FunctionCallExpressionAst) end end arg_nodes.each do |a| - mask |= a.reachable_exceptions(symtab) if a.is_a?(FunctionCallExpressionAst) + mask |= a.reachable_exceptions(symtab, cache) if a.is_a?(FunctionCallExpressionAst) end - unless func_def_type.builtin? + unless func_def_type.builtin? || func_def_type.generated? body_symtab = func_def_type.apply_template_values(template_values(symtab), self) - func_def_type.apply_arguments(body_symtab, arg_nodes, symtab, self) + avals = func_def_type.apply_arguments(body_symtab, arg_nodes, symtab, self) + + idx = [name, tvals, avals].hash begin - mask |= func_def_type.body.reachable_exceptions(body_symtab) + body_mask = + if cache.key?(idx) + cache[idx] + else + cache[idx] = func_def_type.body.reachable_exceptions(body_symtab, cache) + end + mask |= body_mask ensure body_symtab.pop body_symtab.release @@ -66,10 +76,10 @@ def reachable_exceptions(symtab) end class StatementAst - def reachable_exceptions(symtab) + def reachable_exceptions(symtab, cache = {}) mask = # if action.is_a?(FunctionCallExpressionAst) - action.reachable_exceptions(symtab) + action.reachable_exceptions(symtab, cache) # else # 0 # end @@ -85,48 +95,48 @@ def reachable_exceptions(symtab) end class IfAst - def reachable_exceptions(symtab) + def reachable_exceptions(symtab, cache = {}) mask = 0 value_try do - mask = if_cond.reachable_exceptions(symtab) if if_cond.is_a?(FunctionCallExpressionAst) + mask = if_cond.reachable_exceptions(symtab, cache) if if_cond.is_a?(FunctionCallExpressionAst) value_result = value_try do if (if_cond.value(symtab)) - mask |= if_body.reachable_exceptions(symtab) + mask |= if_body.reachable_exceptions(symtab, cache) return mask # no need to continue else elseifs.each do |eif| - mask |= eif.cond.reachable_exceptions(symtab) if eif.cond.is_a?(FunctionCallExpressionAst) + mask |= eif.cond.reachable_exceptions(symtab, cache) if eif.cond.is_a?(FunctionCallExpressionAst) value_result = value_try do if (eif.cond.value(symtab)) - mask |= eif.body.reachable_exceptions(symtab) + mask |= eif.body.reachable_exceptions(symtab, cache) return mask # no need to keep going end end value_else(value_result) do # condition isn't known; body is potentially reachable - mask |= eif.body.reachable_exceptions(symtab) + mask |= eif.body.reachable_exceptions(symtab, cache) end end - mask |= final_else_body.reachable_exceptions(symtab) + mask |= final_else_body.reachable_exceptions(symtab, cache) end end value_else(value_result) do - mask |= if_body.reachable_exceptions(symtab) + mask |= if_body.reachable_exceptions(symtab, cache) elseifs.each do |eif| - mask |= eif.cond.reachable_exceptions(symtab) if eif.cond.is_a?(FunctionCallExpressionAst) + mask |= eif.cond.reachable_exceptions(symtab, cache) if eif.cond.is_a?(FunctionCallExpressionAst) value_result = value_try do if (eif.cond.value(symtab)) - mask |= eif.body.reachable_exceptions(symtab) + mask |= eif.body.reachable_exceptions(symtab, cache) return mask # no need to keep going end end value_else(value_result) do # condition isn't known; body is potentially reachable - mask |= eif.body.reachable_exceptions(symtab) + mask |= eif.body.reachable_exceptions(symtab, cache) end end - mask |= final_else_body.reachable_exceptions(symtab) + mask |= final_else_body.reachable_exceptions(symtab, cache) end end return mask @@ -134,28 +144,28 @@ def reachable_exceptions(symtab) end class ConditionalReturnStatementAst - def reachable_exceptions(symtab) - mask = condition.is_a?(FunctionCallExpressionAst) ? condition.reachable_exceptions(symtab) : 0 + def reachable_exceptions(symtab, cache = {}) + mask = condition.is_a?(FunctionCallExpressionAst) ? condition.reachable_exceptions(symtab, cache) : 0 value_result = value_try do if condition.value(symtab) - mask |= return_expression.is_a?(FunctionCallExpressionAst) ? return_expression.reachable_exceptions(symtab) : 0 + mask |= return_expression.is_a?(FunctionCallExpressionAst) ? return_expression.reachable_exceptions(symtab, cache) : 0 # ok end end value_else(value_result) do - mask |= return_expression.is_a?(FunctionCallExpressionAst) ? return_expression.reachable_exceptions(symtab) : 0 + mask |= return_expression.is_a?(FunctionCallExpressionAst) ? return_expression.reachable_exceptions(symtab, cache) : 0 end mask end end class ConditionalStatementAst - def reachable_exceptions(symtab) + def reachable_exceptions(symtab, cache = {}) mask = 0 value_result = value_try do - mask |= condition.reachable_exceptions(symtab) + mask |= condition.reachable_exceptions(symtab, cache) if condition.value(symtab) - mask |= action.reachable_exceptions(symtab) + mask |= action.reachable_exceptions(symtab, cache) action.add_symbol(symtab) if action.is_a?(Declaration) if action.is_a?(Executable) value_result = value_try do @@ -167,8 +177,8 @@ def reachable_exceptions(symtab) value_else(value_result) do mask = 0 # condition not known - mask |= condition.reachable_exceptions(symtab) - mask |= action.reachable_exceptions(symtab) + mask |= condition.reachable_exceptions(symtab, cache) + mask |= action.reachable_exceptions(symtab, cache) action.add_symbol(symtab) if action.is_a?(Declaration) if action.is_a?(Executable) value_result = value_try do @@ -181,15 +191,15 @@ def reachable_exceptions(symtab) end class ForLoopAst - def reachable_exceptions(symtab) + def reachable_exceptions(symtab, cache = {}) symtab.push(self) begin symtab.add(init.lhs.name, Var.new(init.lhs.name, init.lhs_type(symtab))) - mask = init.is_a?(FunctionCallExpressionAst) ? init.reachable_exceptions(symtab) : 0 - mask |= condition.reachable_exceptions(symtab) if condition.is_a?(FunctionCallExpressionAst) - mask |= update.reachable_exceptions(symtab) if update.is_a?(FunctionCallExpressionAst) + mask = init.is_a?(FunctionCallExpressionAst) ? init.reachable_exceptions(symtab, cache) : 0 + mask |= condition.reachable_exceptions(symtab, cache) if condition.is_a?(FunctionCallExpressionAst) + mask |= update.reachable_exceptions(symtab, cache) if update.is_a?(FunctionCallExpressionAst) stmts.each do |stmt| - mask |= stmt.reachable_exceptions(symtab) + mask |= stmt.reachable_exceptions(symtab, cache) end ensure symtab.pop diff --git a/lib/idl/passes/reachable_functions.rb b/lib/idl/passes/reachable_functions.rb index ca4379501f..ebf882eb76 100644 --- a/lib/idl/passes/reachable_functions.rb +++ b/lib/idl/passes/reachable_functions.rb @@ -5,16 +5,16 @@ module Idl class AstNode # @return [Array] List of all functions that can be reached (via function calls) from this node - def reachable_functions(symtab) + def reachable_functions(symtab, cache = {}) children.reduce([]) do |list, e| - fns = e.reachable_functions(symtab) + fns = e.reachable_functions(symtab, cache) list.concat fns end.uniq(&:name) end end class FunctionCallExpressionAst - def reachable_functions(symtab) + def reachable_functions(symtab, cache = {}) func_def_type = func_type(symtab) tvals = template_values(symtab) @@ -26,18 +26,23 @@ def reachable_functions(symtab) begin if template? template_arg_nodes.each do |t| - fns.concat(t.reachable_functions(symtab)) if t.is_a?(FunctionCallExpressionAst) + fns.concat(t.reachable_functions(symtab, cache)) end end arg_nodes.each do |a| - fns.concat(a.reachable_functions(symtab)) if a.is_a?(FunctionCallExpressionAst) + fns.concat(a.reachable_functions(symtab, cache)) end - func_def_type.apply_arguments(body_symtab, arg_nodes, symtab, self) + unless func_def_type.builtin? || func_def_type.generated? + avals = func_def_type.apply_arguments(body_symtab, arg_nodes, symtab, self) - unless func_def_type.builtin? - fns.concat(func_def_type.body.reachable_functions(body_symtab)) + idx = [name, tvals, avals].hash + + unless cache.key?(idx) + fns.concat(func_def_type.body.reachable_functions(body_symtab, cache)) + cache[idx] = true + end end fns = fns.push(func_def_type.func_def_ast).uniq(&:name) @@ -51,8 +56,8 @@ def reachable_functions(symtab) end class StatementAst - def reachable_functions(symtab) - fns = action.reachable_functions(symtab) + def reachable_functions(symtab, cache = {}) + fns = action.reachable_functions(symtab, cache) action.add_symbol(symtab) if action.is_a?(Declaration) value_try do @@ -66,48 +71,52 @@ def reachable_functions(symtab) class IfAst - def reachable_functions(symtab) + def reachable_functions(symtab, cache = {}) fns = [] value_try do - fns.concat if_cond.reachable_functions(symtab) if if_cond.is_a?(FunctionCallExpressionAst) + fns.concat if_cond.reachable_functions(symtab, cache) value_result = value_try do if (if_cond.value(symtab)) - fns.concat if_body.reachable_functions(symtab) + fns.concat if_body.reachable_functions(symtab, cache) return fns # no need to continue else + if (if_cond.text_value == "pending_and_enabled_interrupts != 0") + warn symtab.get("pending_and_enabled_interrupts") + raise "???" + end elseifs.each do |eif| - fns.concat eif.cond.reachable_functions(symtab) if eif.cond.is_a?(FunctionCallExpressionAst) + fns.concat eif.cond.reachable_functions(symtab, cache) value_result = value_try do if (eif.cond.value(symtab)) - fns.concat eif.body.reachable_functions(symtab) + fns.concat eif.body.reachable_functions(symtab, cache) return fns # no need to keep going end end value_else(value_result) do # condition isn't known; body is potentially reachable - fns.concat eif.body.reachable_functions(symtab) + fns.concat eif.body.reachable_functions(symtab, cache) end end - fns.concat final_else_body.reachable_functions(symtab) + fns.concat final_else_body.reachable_functions(symtab, cache) end end value_else(value_result) do - fns.concat if_body.reachable_functions(symtab) + fns.concat if_body.reachable_functions(symtab, cache) elseifs.each do |eif| - fns.concat eif.cond.reachable_functions(symtab) if eif.cond.is_a?(FunctionCallExpressionAst) + fns.concat eif.cond.reachable_functions(symtab, cache) value_result = value_try do if (eif.cond.value(symtab)) - fns.concat eif.body.reachable_functions(symtab) + fns.concat eif.body.reachable_functions(symtab, cache) return fns # no need to keep going end end value_else(value_result) do # condition isn't known; body is potentially reachable - fns.concat eif.body.reachable_functions(symtab) + fns.concat eif.body.reachable_functions(symtab, cache) end end - fns.concat final_else_body.reachable_functions(symtab) + fns.concat final_else_body.reachable_functions(symtab, cache) end end return fns @@ -115,16 +124,16 @@ def reachable_functions(symtab) end class ConditionalReturnStatementAst - def reachable_functions(symtab) - fns = condition.is_a?(FunctionCallExpressionAst) ? condition.reachable_functions(symtab) : [] + def reachable_functions(symtab, cache) + fns = condition.is_a?(FunctionCallExpressionAst) ? condition.reachable_functions(symtab, cache) : [] value_result = value_try do cv = condition.value(symtab) if cv - fns.concat return_expression.reachable_functions(symtab) if return_expression.is_a?(FunctionCallExpressionAst) + fns.concat return_expression.reachable_functions(symtab, cache) end end value_else(value_result) do - fns.concat return_expression.reachable_functions(symtab) if return_expression.is_a?(FunctionCallExpressionAst) + fns.concat return_expression.reachable_functions(symtab, cache) end fns @@ -132,19 +141,19 @@ def reachable_functions(symtab) end class ConditionalStatementAst - def reachable_functions(symtab) + def reachable_functions(symtab, cache = {}) - fns = condition.is_a?(FunctionCallExpressionAst) ? condition.reachable_functions(symtab) : [] + fns = condition.is_a?(FunctionCallExpressionAst) ? condition.reachable_functions(symtab, cache) : [] value_result = value_try do if condition.value(symtab) - fns.concat action.reachable_functions(symtab) if action.is_a?(FunctionCallExpressionAst) + fns.concat action.reachable_functions(symtab, cache) # no need to execute action (return) end end value_else(value_result) do # condition not known - fns = fns.concat action.reachable_functions(symtab) if action.is_a?(FunctionCallExpressionAst) + fns = fns.concat action.reachable_functions(symtab, cache) end fns @@ -152,15 +161,15 @@ def reachable_functions(symtab) end class ForLoopAst - def reachable_functions(symtab) + def reachable_functions(symtab, cache = {}) symtab.push(self) begin symtab.add(init.lhs.name, Var.new(init.lhs.name, init.lhs_type(symtab))) - fns = init.is_a?(FunctionCallExpressionAst) ? init.reachable_functions(symtab) : [] - fns.concat(condition.reachable_functions(symtab)) if condition.is_a?(FunctionCallExpressionAst) - fns.concat(update.reachable_functions(symtab)) if update.is_a?(FunctionCallExpressionAst) + fns = init.is_a?(FunctionCallExpressionAst) ? init.reachable_functions(symtab, cache) : [] + fns.concat(condition.reachable_functions(symtab, cache)) + fns.concat(update.reachable_functions(symtab, cache)) stmts.each do |stmt| - fns.concat(stmt.reachable_functions(symtab)) + fns.concat(stmt.reachable_functions(symtab, cache)) end ensure symtab.pop diff --git a/lib/idl/symbol_table.rb b/lib/idl/symbol_table.rb index 466183f9f7..d59ee0422f 100644 --- a/lib/idl/symbol_table.rb +++ b/lib/idl/symbol_table.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +require "concurrent" + require_relative "type" module Idl @@ -7,7 +9,7 @@ module Idl class Var attr_reader :name, :type, :value - def initialize(name, type, value = nil, decode_var: false, template_index: nil, function_name: nil) + def initialize(name, type, value = nil, decode_var: false, template_index: nil, function_name: nil, param: false) @name = name raise ArgumentError, "Expecting a Type, got #{type.class.name}" unless type.is_a?(Type) @@ -19,10 +21,11 @@ def initialize(name, type, value = nil, decode_var: false, template_index: nil, @decode_var = decode_var @template_index = template_index @function_name = function_name + @param = param end def hash - [@name, @type, @value, @decode_var, @template_index, @function_name].hash + [@name, @type, @value, @decode_var, @template_index, @function_name, @param].hash end def to_s @@ -34,7 +37,10 @@ def clone name, type.clone, value&.clone, - decode_var: @decode_var + decode_var: @decode_var, + template_index: @template_index, + function_name: @function_name, + param: @param ) end @@ -46,12 +52,18 @@ def decode_var? @decode_var end + def param? + @param + end + # @param function_name [#to_s] 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) end + def template_value? = !@template_index.nil? + # @return [Integer] the template value position # @raise if Var is not a template value def template_index @@ -95,15 +107,16 @@ def initialize(cfg_arch) # See https://github.com/riscv-software-src/riscv-unified-db/pull/371 #raise "The cfg_arch must be a ConfiguredArchitecture but is a #{cfg_arch.class}" unless (cfg_arch.is_a?(ConfiguredArchitecture) || cfg_arch.is_a?(MockConfiguredArchitecture)) + @mutex = Thread::Mutex.new @cfg_arch = cfg_arch @mxlen = cfg_arch.unconfigured? ? nil : cfg_arch.mxlen @callstack = [nil] @scopes = [{ "X" => Var.new( "X", - Type.new(:array, sub_type: XregType.new(@mxlen.nil? ? :unknown : @mxlen), width: 32, qualifiers: [:global]) + Type.new(:array, sub_type: XregType.new(@mxlen.nil? ? 64 : @mxlen), width: 32, qualifiers: [:global]) ), - "XReg" => XregType.new(@mxlen.nil? ? :unknown : @mxlen), + "XReg" => XregType.new(@mxlen.nil? ? 64 : @mxlen), "Boolean" => Type.new(:boolean), "true" => Var.new( "true", @@ -126,7 +139,7 @@ def initialize(cfg_arch) # could already be present... existing_sym = get(param_with_value.name) if existing_sym.nil? - add!(param_with_value.name, Var.new(param_with_value.name, type, param_with_value.value)) + add!(param_with_value.name, Var.new(param_with_value.name, type, param_with_value.value, param: true)) else unless existing_sym.type.equal_to?(type) && existing_sym.value == param_with_value.value raise DuplicateSymError, "Definition error: Param #{param.name} is defined by multiple extensions and is not the same definition in each" @@ -137,12 +150,12 @@ def initialize(cfg_arch) # now add all parameters, even those not implemented cfg_arch.params_without_value.each do |param| if param.exts.size == 1 - add!(param.name, Var.new(param.name, param.idl_type.clone.make_const)) + add!(param.name, Var.new(param.name, param.idl_type.clone.make_const, param: true)) else # could already be present... existing_sym = get(param.name) if existing_sym.nil? - add!(param.name, Var.new(param.name, param.idl_type.clone.make_const)) + add!(param.name, Var.new(param.name, param.idl_type.clone.make_const, param: true)) else unless existing_sym.type.equal_to?(param.idl_type) raise "Definition error: Param #{param.name} is defined by multiple extensions and is not the same definition in each" @@ -164,7 +177,7 @@ def deep_freeze @frozen_hash = [@scopes.hash, @cfg_arch.hash].hash # set up the global clone that be used as a mutable table - @global_clone_pool = [] + @global_clone_pool = Concurrent::Array.new 5.times do copy = SymbolTable.allocate @@ -172,8 +185,9 @@ def deep_freeze copy.instance_variable_set(:@callstack, [@callstack[0]]) copy.instance_variable_set(:@cfg_arch, @cfg_arch) copy.instance_variable_set(:@mxlen, @mxlen) + copy.instance_variable_set(:@mutex, @mutex) copy.instance_variable_set(:@global_clone_pool, @global_clone_pool) - copy.instance_variable_set(:@in_use, false) + copy.instance_variable_set(:@in_use, Concurrent::Semaphore.new(1)) @global_clone_pool << copy end @@ -291,6 +305,16 @@ def add!(name, var) @scopes.last[name] = var end + # delete a new symbol at the outermost scopea + # + # @param name [#to_s] Symbol name + # @param var [Object] Symbol object (usually a Var or a Type) + def del(name) + raise "No symbol #{name} at outer scope" unless @scopes.last.key?(name) + + @scopes.last.delete(name) + end + # add to the scope above the tail, and make sure name is unique at that scope def add_above!(name, var) raise "There is only one scope" if @scopes.size <= 1 @@ -334,10 +358,7 @@ def global_clone # raise "global clone isn't at global scope" unless @global_clone.at_global_scope? @global_clone_pool.each do |symtab| - unless symtab.in_use? - symtab.instance_variable_set(:@in_use, true) - return symtab - end + return symtab if symtab.instance_variable_get(:@in_use).try_acquire end # need more! @@ -348,8 +369,9 @@ def global_clone copy.instance_variable_set(:@callstack, [@callstack[0]]) copy.instance_variable_set(:@cfg_arch, @cfg_arch) copy.instance_variable_set(:@mxlen, @mxlen) + copy.instance_variable_set(:@mutex, @mutex) copy.instance_variable_set(:@global_clone_pool, @global_clone_pool) - copy.instance_variable_set(:@in_use, false) + copy.instance_variable_set(:@in_use, Concurrent::Semaphore.new(1)) @global_clone_pool << copy end @@ -357,16 +379,17 @@ def global_clone end def release - pop while levels > 1 - raise "Clone isn't back in global scope" unless at_global_scope? - raise "You are calling release on the frozen SymbolTable" if frozen? - raise "??" if @in_use.nil? - raise "Double release detected" unless @in_use + @mutex.synchronize do + pop while levels > 1 + raise "Clone isn't back in global scope" unless at_global_scope? + raise "You are calling release on the frozen SymbolTable" if frozen? + raise "Double release detected" unless in_use? - @in_use = false + @in_use.release + end end - def in_use? = @in_use + def in_use? = @in_use.available_permits.zero? # @return [SymbolTable] a deep clone of this SymbolTable def deep_clone(clone_values: false, freeze_global: true) diff --git a/lib/idl/type.rb b/lib/idl/type.rb index 07d2f46a90..0b582e4584 100644 --- a/lib/idl/type.rb +++ b/lib/idl/type.rb @@ -219,6 +219,10 @@ def convertable_to?(type) when :dontcare return true when :bits + if type.kind == :enum_ref + warn "You seem to be missing an $enum cast" + return false + end return type.kind != :boolean when :function return @return_type.convertable_to?(type) @@ -259,7 +263,7 @@ def convertable_to?(type) when :string return type.kind == :string when :void - return false + return type.kind == :void when :struct return type.kind == :struct && (type.type_name == type_name) else @@ -300,36 +304,51 @@ def to_s alias fully_qualified_name to_s def to_cxx_no_qualifiers - if @kind == :bits - raise "@width is unknown" if @width == :unknown - raise "@width is a #{@width.class}" unless @width.is_a?(Integer) + case @kind + when :bits + raise "@width is a #{@width.class}" unless @width.is_a?(Integer) || @width == :unknown - if signed? - "SignedBits<#{@width.is_a?(Integer) ? @width : @width.to_cxx}>" + width_cxx = + if @width.is_a?(Integer) + @width + elsif @width == :unknown + "BitsInfinitePrecision" else - "Bits<#{@width.is_a?(Integer) ? @width : @width.to_cxx}>" + @width.to_cxx end - elsif @kind == :enum - "#{@name}" - elsif @kind == :boolean - "bool" - elsif @kind == :function - "std::function<#{@return_type.to_cxx}(...)>" - elsif @kind == :enum_ref - "#{@enum_class.name}" - elsif @kind == :tuple - "std::tuple<#{@tuple_types.map{ |t| t.to_cxx }.join(',')}>" - elsif @kind == :bitfield - "#{@name}" - elsif @kind == :array - "#{@sub_type}[]" - elsif @kind == :csr - "#{@csr.downcase.capitalize}Csr" - elsif @kind == :string - "std::string" + + if signed? + "SignedBits<#{width_cxx}>" else - raise @kind.to_s + "Bits<#{width_cxx}>" end + when :enum + @name + when :boolean + "bool" + when :function + "std::function<#{@return_type.to_cxx_no_qualifiers}(...)>" + when :enum_ref + @enum_class.name + when :tuple + "std::tuple<#{@tuple_types.map(&:to_cxx).join(',')}>" + when :bitfield + @name + when :array + if @width == :unknown + "std::vector<#{@sub_type.to_cxx_no_qualifiers}>" + else + "std::array<#{@sub_type.to_cxx_no_qualifiers}, #{@width}>" + end + when :csr + "#{CppHartGen::TemplateEnv.new(@csr.cfg_arch).name_of(:csr, @csr.cfg_arch, @csr.name)}" + when :string + "std::string" + when :void + "void" + else + raise @kind.to_s + end end def to_cxx @@ -610,10 +629,16 @@ 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) + if csr.is_a?(Symbol) && csr == :unknown + super(:csr, name: csr.name, csr: csr, width: :unknown, qualifiers: qualifiers) + else + super(:csr, name: csr.name, csr: csr, width: csr.max_length, qualifiers: qualifiers) + end end def fields + raise "fields are unknown" if @csr == :unknown + @csr.fields end end @@ -635,6 +660,10 @@ def clone def builtin? = @func_def_ast.builtin? + def generated? = @func_def_ast.generated? + + def external? = @func_def_ast.external? + def num_args = @func_def_ast.num_args def type_check_call(template_values, argument_nodes, call_site_symtab, func_call_ast) @@ -690,16 +719,21 @@ def apply_template_values(template_values, func_call_ast) # then add the value to the Var def apply_arguments(symtab, argument_nodes, call_site_symtab, func_call_ast) idx = 0 + values = [] @func_def_ast.arguments(symtab).each do |atype, aname| func_call_ast.type_error "Missing argument #{idx}" if idx >= argument_nodes.size value_result = Idl::AstNode.value_try do - symtab.add(aname, Var.new(aname, atype, argument_nodes[idx].value(call_site_symtab))) + value = argument_nodes[idx].value(call_site_symtab) + symtab.add(aname, Var.new(aname, atype, value)) + values << value end Idl::AstNode.value_else(value_result) do symtab.add(aname, Var.new(aname, atype)) + values << :unknown end idx += 1 end + values end # @return [Array] Array of argument values, if known @@ -746,6 +780,7 @@ def return_value(template_values, argument_nodes, call_site_symtab, func_call_as symtab.pop symtab.release end + raise "?" if value.is_a?(SymbolTable) value end diff --git a/lib/template_helpers.rb b/lib/template_helpers.rb index f42ef96036..35cb24462a 100644 --- a/lib/template_helpers.rb +++ b/lib/template_helpers.rb @@ -5,6 +5,7 @@ require "erb" require "pathname" +require "ostruct" # collection of functions that can be used inside ERB templates module TemplateHelpers diff --git a/lib/version.rb b/lib/version.rb index 6151c810a2..9df3ed11a5 100644 --- a/lib/version.rb +++ b/lib/version.rb @@ -148,7 +148,7 @@ def initialize(requirement) @version_str = m[2] @version_spec = VersionSpec.new(@version_str) else - raise ArgumentError, "Bad requirement string '#{requirement}'" + raise ArgumentError, "Bad requirement string '#{requirement}' #{REQUIREMENT_REGEX}" end end @@ -156,6 +156,27 @@ def to_s "#{@op} #{@version_str}" end + # invert the requirement + def invert! + case @op + when ">=" + @op = "<" + when ">" + @op = "<=" + when "<=" + @op = ">" + when "<" + @op = ">=" + when "=" + @op = "!=" + when "!=" + @op = "=" + when "~>" + @op = "!~>" + end + self + end + # @param version [String] A version string # @param version [VersionSpec] A version spec # @param ext [Extension] An extension, needed to evaluate the compatible (~>) operator @@ -189,6 +210,11 @@ def satisfied_by?(version, ext) raise "Can't find version?" if matching_ver.nil? matching_ver.compatible?(ExtensionVersion.new(ext.name, v_spec.to_s, ext.cfg_arch)) + when "!~>" # not a legal spec, but used for inversion + 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)) end end end diff --git a/package-lock.json b/package-lock.json index a51844f2eb..0dfcae6d4a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -297,9 +297,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.7.tgz", - "integrity": "sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.9.tgz", + "integrity": "sha512-aA63XwOkcl4xxQa3HjPMqOP6LiK0ZDv3mUPYEFXkpHbaFjtGggE1A61FjFzJnB+p7/oy2gA8E+rcBNl/zC1tMg==", "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -1227,15 +1227,14 @@ } }, "node_modules/call-bind": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", - "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", "dependencies": { + "call-bind-apply-helpers": "^1.0.0", "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.1" + "set-function-length": "^1.2.2" }, "engines": { "node": ">= 0.4" @@ -1244,6 +1243,41 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz", + "integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/centra": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/centra/-/centra-2.7.0.tgz", + "integrity": "sha512-PbFMgMSrmgx6uxCdm57RUos9Tc3fclMvhLSATYN39XsDV29B89zZ3KA89jmY0vwSGazyU+uerqwa6t+KaodPcg==", + "dependencies": { + "follow-redirects": "^1.15.6" + } + }, "node_modules/cheerio": { "version": "1.0.0-rc.10", "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.10.tgz", @@ -1563,6 +1597,19 @@ "url": "https://github.com/fb55/domutils?sponsor=1" } }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/duplexify": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.3.tgz", @@ -1596,12 +1643,9 @@ } }, "node_modules/es-define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", - "dependencies": { - "get-intrinsic": "^1.2.4" - }, + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", "engines": { "node": ">= 0.4" } @@ -1614,10 +1658,21 @@ "node": ">= 0.4" } }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/escalade": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", - "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "engines": { "node": ">=6" } @@ -1745,10 +1800,29 @@ "safe-buffer": "~5.1.0" } }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/fs-extra": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", - "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", + "version": "11.3.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz", + "integrity": "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==", "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", @@ -1792,15 +1866,20 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -1809,6 +1888,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/gifwrap": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/gifwrap/-/gifwrap-0.9.4.tgz", @@ -1896,11 +1987,11 @@ } }, "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dependencies": { - "get-intrinsic": "^1.1.3" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -1942,21 +2033,10 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/has-proto": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", - "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "engines": { "node": ">= 0.4" }, @@ -2083,9 +2163,9 @@ ] }, "node_modules/ignore": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", - "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "engines": { "node": ">= 4" } @@ -2368,16 +2448,16 @@ } }, "node_modules/load-bmfont": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/load-bmfont/-/load-bmfont-1.4.1.tgz", - "integrity": "sha512-8UyQoYmdRDy81Brz6aLAUhfZLwr5zV0L3taTQ4hju7m6biuwiWiJXjPhBJxbUQJA8PrkvJ/7Enqmwk2sM14soA==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/load-bmfont/-/load-bmfont-1.4.2.tgz", + "integrity": "sha512-qElWkmjW9Oq1F9EI5Gt7aD9zcdHb9spJCW1L/dmPf7KzCCEJxq8nhHz5eCgI9aMf7vrG/wyaCqdsI+Iy9ZTlog==", "dependencies": { "buffer-equal": "0.0.1", "mime": "^1.3.4", "parse-bmfont-ascii": "^1.0.3", "parse-bmfont-binary": "^1.0.5", "parse-bmfont-xml": "^1.1.4", - "phin": "^2.9.1", + "phin": "^3.7.1", "xhr": "^2.0.1", "xtend": "^4.0.0" } @@ -2390,6 +2470,17 @@ "node": ">=0.4.0" } }, + "node_modules/load-bmfont/node_modules/phin": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/phin/-/phin-3.7.1.tgz", + "integrity": "sha512-GEazpTWwTZaEQ9RhL7Nyz0WwqilbqgLahDM3D0hxWwmVDI52nXEybHqiN6/elwpkJBhcuj+WbBu+QfT0uhPGfQ==", + "dependencies": { + "centra": "^2.7.0" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/lodash.clonedeep": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", @@ -2414,6 +2505,14 @@ "resolved": "https://registry.npmjs.org/lunr-languages/-/lunr-languages-1.9.0.tgz", "integrity": "sha512-Be5vFuc8NAheOIjviCRms3ZqFFBlzns3u9DXpPSZvALetgnydAN0poV71pVLFn0keYy/s4VblMMkqewTLe+KPg==" }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/mime": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", @@ -2559,13 +2658,15 @@ } }, "node_modules/object.assign": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", - "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", "dependencies": { - "call-bind": "^1.0.5", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", "define-properties": "^1.2.1", - "has-symbols": "^1.0.3", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", "object-keys": "^1.1.1" }, "engines": { @@ -2790,9 +2891,9 @@ } }, "node_modules/pino-abstract-transport/node_modules/readable-stream": { - "version": "4.5.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz", - "integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==", + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", "dependencies": { "abort-controller": "^3.0.0", "buffer": "^6.0.3", @@ -2852,9 +2953,9 @@ } }, "node_modules/pino-pretty/node_modules/readable-stream": { - "version": "4.5.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz", - "integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==", + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", "dependencies": { "abort-controller": "^3.0.0", "buffer": "^6.0.3", @@ -2931,9 +3032,9 @@ } }, "node_modules/pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", + "integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==", "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" @@ -2976,11 +3077,11 @@ } }, "node_modules/readable-web-to-node-stream": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.2.tgz", - "integrity": "sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.4.tgz", + "integrity": "sha512-9nX56alTf5bwXQ3ZDipHJhusu9NTQJ/CVPtb/XHAJCXihZeitfJvIRS4GqQ/mfIoOE3IelHMrpayVrosdHBuLw==", "dependencies": { - "readable-stream": "^3.6.0" + "readable-stream": "^4.7.0" }, "engines": { "node": ">=8" @@ -2990,6 +3091,44 @@ "url": "https://github.com/sponsors/Borewit" } }, + "node_modules/readable-web-to-node-stream/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/readable-web-to-node-stream/node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, "node_modules/real-require": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz", @@ -3093,9 +3232,9 @@ ] }, "node_modules/safe-stable-stringify": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz", - "integrity": "sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", "engines": { "node": ">=10" } @@ -3407,9 +3546,9 @@ } }, "node_modules/tslib": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", - "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, "node_modules/tspan": { "version": "0.4.0", @@ -3417,9 +3556,9 @@ "integrity": "sha512-0ELL9tpLpTqLliFyQySaxgCO43buCML+j3TI4E1LuSI8wkzITGEVhZCyMvv/A+3ek9KpgALhhgnZESRLTbN+iw==" }, "node_modules/uglify-js": { - "version": "3.17.4", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", - "integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==", + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", "optional": true, "bin": { "uglifyjs": "bin/uglifyjs" diff --git a/schemas/config_schema.json b/schemas/config_schema.json index 63ef075abf..9eee5184e9 100644 --- a/schemas/config_schema.json +++ b/schemas/config_schema.json @@ -1,165 +1,190 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "title": "Architecture configuration", - "required": ["$schema", "kind", "type", "name", "description"], - "allOf": [ - { - "if": { - "properties": { - "type": { "const": "fully configured" } - } - }, - "then": { - "required": ["implemented_extensions", "params"], - "properties": { - "params": { - "type": "object" - }, - "mandatory_extensions": { - "type": "null" - }, - "non_mandatory_extensions": { - "type": "null" - }, - "prohibited_extensions": { - "type": "null" - }, - "implemented_extensions": { - "description": "Extensions implemented by this architecture", - "type": "array", - "items": { - "type": "object", - "required": ["name", "version"], - "properties": { - "name": { - "type": "string", - "pattern": "^([A-WY]|([SXZ][a-z0-9]+))$", - "description": "Extension name" - }, - "version": { - "oneOf": [ - { - "type": "string", - "description": "Extension version" - }, - { - "type": "number" - } - ] - } + "$defs": { + "full_configuration": { + "type": "object", + "required": [ + "$schema", + "kind", + "type", + "description", + "name", + "implemented_extensions", + "params" + ], + "properties": { + "$schema": { + "type": "string", + "format": "uri-reference", + "const": "config_schema.json#" + }, + "kind": { + "type": "string", + "const": "architecture configuration" + }, + "type": { + "type": "string", + "const": "fully configured" + }, + "name": { + "type": "string", + "description": "Name of the configuration" + }, + "description": { + "type": "string", + "description": "An asciidoc description of the configuration" + }, + "arch_overlay": { + "type": "string", + "description": "Optional arch overlay to apply. Can be either the name of a directory under arch_overlay/ or a (absolute or relative) path to an overlay directory" + }, + "$source": { + "type": "string", + "format": "uri", + "description": "Path to original file, when this is a copy" + }, + "params": { + "type": "object" + }, + "implemented_extensions": { + "description": "Extensions implemented by this architecture", + "type": "array", + "items": { + "type": "object", + "required": ["name", "version"], + "properties": { + "name": { + "$ref": "schema_defs.json#/$defs/extension_name", + "description": "Extension name" }, - "additionalProperties": false + "version": { + "$ref": "schema_defs.json#/$defs/extension_version" + } } } } - } - }, - { - "if": { - "properties": { - "type": { "const": "partially configured" } - } }, - "then": { - "anyOf": [ - { - "required": ["mandatory_extensions"] - }, - { - "required": ["params"] - } - ], - "properties": { - "params": { - "type": "object" - }, - "mandatory_extensions": { - "description": "Extensions mandatory in this architecture", - "type": "array", - "items": { - "$ref": "schema_defs.json#/$defs/extension_requirement" - }, - "default": { - "const": [] - } - }, - "non_mandatory_extensions": { - "description": "Extensions that are not mandatory but are still _special_ in this architecture. This could mean different things depending on the context: for certificates or generated IP, this would correspond to _optional supported_, and extensions not in non_mandatory are not possible. For profiles, this corresponds to some type of _profile optional_, but extensions in non_mandatory are still possible.", - "type": "array", - "items": { - "$ref": "schema_defs.json#/$defs/extension_requirement" - }, - "default": { - "const": [] - } - }, - "prohibited_extensions": { - "description": "Extensions explicitly prohibited in this architecture. Does *not* need to include extensions that are excluded because of a conflict-by-definition with a mandatory extension, as those will be calculated automatically", - "type": "array", - "items": { - "$ref": "schema_defs.json#/$defs/extension_requirement" - }, - "default": { - "const": [] - } - }, - "implemented_extensions": { - "type": "null" - } - } - } + "additionalProperties": false }, - { - "if": { - "properties": { - "type": { "const": "unconfigured" } - } - }, - "then": { + "partial_configuration": { + "type": "object", + "required": [ + "$schema", + "kind", + "type", + "name", + "description", + "mandatory_extensions" + ], + "properties": { + "$schema": { + "type": "string", + "format": "uri-reference", + "const": "config_schema.json#" + }, + "kind": { + "type": "string", + "const": "architecture configuration" + }, + "type": { + "type": "string", + "const": "partially configured" + }, + "name": { + "type": "string", + "description": "Name of the configuration" + }, + "description": { + "type": "string", + "description": "An asciidoc description of the configuration" + }, + "params": { + "type": "object" + }, + "arch_overlay": { + "type": "string", + "description": "Optional arch overlay to apply. Can be either the name of a directory under arch_overlay/ or a (absolute or relative) path to an overlay directory" + }, + "$source": { + "type": "string", + "format": "uri", + "description": "Path to original file, when this is a copy" + }, "mandatory_extensions": { - "type": "null" + "description": "Extensions mandatory in this architecture", + "type": "array", + "items": { + "$ref": "schema_defs.json#/$defs/extension_requirement" + } }, "non_mandatory_extensions": { - "type": "null" + "description": "Extensions that are not mandatory but are still _special_ in this architecture. This could mean different things depending on the context: for certificates or generated IP, this would correspond to _optional supported_, and extensions not in non_mandatory are not possible. For profiles, this corresponds to some type of _profile optional_, but extensions in non_mandatory are still possible.", + "type": "array", + "items": { + "$ref": "schema_defs.json#/$defs/extension_requirement" + } }, "prohibited_extensions": { - "type": "null" + "description": "Extensions explicitly prohibited in this architecture. Does *not* need to include extensions that are excluded because of a conflict-by-definition with a mandatory extension, as those will be calculated automatically", + "type": "array", + "items": { + "$ref": "schema_defs.json#/$defs/extension_requirement" + } }, - "params": { + "implemented_extensions": { "type": "null" + }, + "additional_extensions": { + "type": "boolean", + "default": true, + "description": "Whether or not a compliant instance of this partial config can have more extensions than those listed in mandatory_extensions/non_mandatory_extensions" } - } - } - ], - "properties": { - "type": { - "type": "string", - "description": "Type of the arch", - "enum": ["unconfigured", "partially configured", "fully configured"] - }, - "$schema": { - "type": "string", - "format": "uri-reference", - "const": "config_schema.json#" - }, - "kind": { - "type": "string", - "const": "architecture configuration" - }, - "name": { - "type": "string" - }, - "description": { - "type": "string", - "description": "An asciidoc description of the configuration" + }, + "additionalProperties": false }, - "params": true, - "mandatory_extensions": true, - "non_mandatory_extensions": true, - "prohibited_extensions": true, - "implemented_extensions": true + "unconfiguration": { + "type": "object", + "required": ["$schema", "kind", "type", "name", "description"], + "properties": { + "$schema": { + "type": "string", + "format": "uri-reference", + "const": "config_schema.json#" + }, + "kind": { + "type": "string", + "const": "architecture configuration" + }, + "type": { + "type": "string", + "const": "unconfigured" + }, + "name": { + "type": "string", + "description": "Name of the configuration" + }, + "arch_overlay": { + "type": "string", + "description": "Optional arch overlay to apply. Can be either the name of a directory under arch_overlay/ or a (absolute or relative) path to an overlay directory" + }, + "$source": { + "type": "string", + "format": "uri", + "description": "Path to original file, when this is a copy" + }, + "description": { + "type": "string", + "description": "An asciidoc description of the configuration" + } + }, + "additionalProperties": false + } }, - "additionalProperties": false + + "oneOf": [ + { "$ref": "#/$defs/full_configuration" }, + { "$ref": "#/$defs/partial_configuration" }, + { "$ref": "#/$defs/unconfiguration" } + ] } diff --git a/schemas/ext_schema.json b/schemas/ext_schema.json index 40d6d485cd..7393577e00 100644 --- a/schemas/ext_schema.json +++ b/schemas/ext_schema.json @@ -2,6 +2,36 @@ "$schema": "http://json-schema.org/draft-07/schema#", "$defs": { + "implies_entry": { + "oneOf": [ + { + "$ref": "schema_defs.json#/$defs/extension_with_version" + }, + { + "type": "object", + "required": ["if", "then"], + "additionalProperties": false, + "properties": { + "if": { + "$ref": "schema_defs.json#/$defs/requires_entry" + }, + "then": { + "oneOf": [ + { + "$ref": "schema_defs.json#/$defs/extension_with_version" + }, + { + "type": "array", + "items": { + "$ref": "schema_defs.json#/$defs/extension_with_version" + } + } + ] + } + } + } + ] + }, "param_data": { "type": "object", "required": ["description", "schema"], @@ -82,15 +112,7 @@ "type": { "enum": ["unprivileged", "privileged"] }, "conflicts": { "description": "Extension(s) that conflict with this extension; both cannot be implemented at the same time", - "oneOf": [ - { "$ref": "schema_defs.json#/$defs/extension_requirement" }, - { - "type": "array", - "items": { - "$ref": "schema_defs.json#/$defs/extension_requirement" - } - } - ] + "$ref": "schema_defs.json#/$defs/requires_entry" }, "versions": { "type": "array", @@ -166,12 +188,12 @@ "description": "Extension(s) implied by this extension (i.e., any subextensions)", "oneOf": [ { - "$ref": "schema_defs.json#/$defs/extension_name_and_version" + "$ref": "#/$defs/implies_entry" }, { "type": "array", "items": { - "$ref": "schema_defs.json#/$defs/extension_name_and_version" + "$ref": "#/$defs/implies_entry" } } ] diff --git a/schemas/inst_schema.json b/schemas/inst_schema.json index 637543623c..aba5229d26 100644 --- a/schemas/inst_schema.json +++ b/schemas/inst_schema.json @@ -190,10 +190,6 @@ "$ref": "schema_defs.json#/$defs/requires_entry", "description": "Extension(s) that defines the instruction" }, - "excludedBy": { - "$ref": "schema_defs.json#/$defs/requires_entry", - "description": "Extension(s) that, if implemented, exclude the instruction (even if the definedBy extension is also implemented)" - }, "encoding": { "description": "Instruction encoding and decode variables", "oneOf": [ @@ -215,6 +211,23 @@ } ] }, + "hints": { + "type": "array", + "items": { + "type": "object", + "properties": { + "$ref": { + "type": "string", + "format": "uri-reference", + "pattern": "^inst/.+\\.yaml#.*$", + "description": "Ref to an instruction that is using a HINT codepoint(s) of this instruciton" + } + }, + "required": ["$ref"], + "additionalProperties": false + }, + "description": "List of HINTs that use this instruction's codepoints" + }, "base": { "enum": [32, 64], "description": "When present, instruction is only defined for RV32 or RV64 base" diff --git a/schemas/manual_version_schema.json b/schemas/manual_version_schema.json index 98815be9d9..e240516cdd 100644 --- a/schemas/manual_version_schema.json +++ b/schemas/manual_version_schema.json @@ -30,7 +30,7 @@ "extensions": { "type": "array", "items": { - "$ref": "schema_defs.json#/$defs/extension_name_and_version" + "$ref": "schema_defs.json#/$defs/extension_with_version" }, "description": "List of extensions defined in this volume, with version numbers" }, diff --git a/schemas/schema_defs.json b/schemas/schema_defs.json index a5ee77adc9..fb60ff841e 100644 --- a/schemas/schema_defs.json +++ b/schemas/schema_defs.json @@ -152,18 +152,6 @@ } ] }, - "extension_name_and_version": { - "type": "array", - "items": [ - { - "$ref": "#/$defs/extension_name" - }, - { - "$ref": "#/$defs/extension_version" - } - ], - "additionalItems": false - }, "extension_requirement": { "description": "A requirement on an extension. Can either specify just an extension name, in which case version '>= 0' is implied, or both a name and a requirement", "oneOf": [ @@ -206,12 +194,28 @@ "items": { "$ref": "#/$defs/requires_entry" } + }, + "not": { + "type": "object", + "$ref": "#/$defs/requires_entry" } }, "additionalProperties": false } ] }, + "extension_with_version": { + "type": "object", + "required": ["name", "version"], + "properties": { + "name": { + "$ref": "#/$defs/extension_name" + }, + "version": { + "$ref": "#/$defs/extension_version" + } + } + }, "author": { "type": "object", "required": ["name"],