From 6580857abe914d19a9cd66dc70ce770234b1f77a Mon Sep 17 00:00:00 2001 From: Derek Hower Date: Fri, 20 Sep 2024 06:58:22 -0700 Subject: [PATCH] Add CSR list to manual --- .github/workflows/pages.yml | 4 +- arch/manual/isa/20240411/contents.yaml | 42 +++++- backends/manual/tasks.rake | 25 +++- backends/manual/templates/csr.adoc.erb | 125 ++++++++++++++++++ .../manual/templates/instruction.adoc.erb | 1 - backends/manual/templates/isa_nav.adoc.erb | 5 + lib/arch_obj_models/csr.rb | 30 ++++- lib/arch_obj_models/csr_field.rb | 24 +++- lib/arch_obj_models/manual.rb | 14 ++ 9 files changed, 258 insertions(+), 12 deletions(-) create mode 100644 backends/manual/templates/csr.adoc.erb diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml index 6078da442..ce781fa06 100644 --- a/.github/workflows/pages.yml +++ b/.github/workflows/pages.yml @@ -43,12 +43,12 @@ jobs: run: ./bin/build_container - name: Setup project run: ./bin/setup + - name: Build manual + run: ./do gen:html_manual MANUAL_NAME=isa VERSIONS=all - name: Build html documentation for generic_rv64 run: ./do gen:html[generic_rv64] - name: Generate YARD docs run: ./do gen:tool_doc - - name: Build manual - run: ./do gen:html_manual MANUAL_NAME=isa VERSIONS=all - name: Create _site/example_cfg run: mkdir -p _site/example_cfg - name: Create _site/manual diff --git a/arch/manual/isa/20240411/contents.yaml b/arch/manual/isa/20240411/contents.yaml index 70940660f..f906feb73 100644 --- a/arch/manual/isa/20240411/contents.yaml +++ b/arch/manual/isa/20240411/contents.yaml @@ -68,6 +68,7 @@ volumes: #/End of Vector appendices extensions: - [I, "2.1.0"] + - [U, "1.12.0"] # - [E, "2.0"] # - [RVI64, "2.1"] # - [RVI128, "1.7"] @@ -176,4 +177,43 @@ volumes: - name: Andrew Waterman email: awaterman@sifive.com organization: SiFive, Inc. - url: https://www.sifive.com/ \ No newline at end of file + url: https://www.sifive.com/ + chapters: + - riscv-isa-manual/src/priv-preface.adoc + - riscv-isa-manual/src/priv-intro.adoc + - riscv-isa-manual/src/priv-csrs.adoc + - riscv-isa-manual/src/machine.adoc + - riscv-isa-manual/src/smstateen.adoc + - riscv-isa-manual/src/indirect-csr.adoc + - riscv-isa-manual/src/smepmp.adoc + - riscv-isa-manual/src/smcntrpmf.adoc + - riscv-isa-manual/src/rnmi.adoc + - riscv-isa-manual/src/smcdeleg.adoc + - riscv-isa-manual/src/supervisor.adoc + - riscv-isa-manual/src/sstc.adoc + - riscv-isa-manual/src/sscofpmf.adoc + - riscv-isa-manual/src/hypervisor.adoc + - riscv-isa-manual/src/priv-insns.adoc + - riscv-isa-manual/src/priv-history.adoc + - riscv-isa-manual/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"] + - [Sv32, "1.13.0"] + - [Sv39, "1.13.0"] + - [Sv48, "1.13.0"] + - [Sv57, "1.13.0"] + - [Svnapot, "1.0.0"] + - [Svpbmt, "1.0.0"] + - [Svinval, "1.0.0"] + - [Svadu, "1.0.0"] + - [Svvptc, "1.0.0"] + - [Sstc, "1.0.0"] + - [Sscofpmf, "1.0.0"] + - [H, "1.0.0"] \ No newline at end of file diff --git a/backends/manual/tasks.rake b/backends/manual/tasks.rake index 49847df93..e12fae9ed 100644 --- a/backends/manual/tasks.rake +++ b/backends/manual/tasks.rake @@ -230,6 +230,26 @@ rule %r{#{MANUAL_GEN_DIR}/.*/.*/antora/modules/insts/pages/.*.adoc} => [ File.write t.name, AntoraUtils.resolve_links(erb.result(binding)) end +# rule to create csr appendix page +rule %r{#{MANUAL_GEN_DIR}/.*/.*/antora/modules/csrs/pages/.*.adoc} => [ + __FILE__, + "gen:arch", + ($root / "backends" / "manual" / "templates" / "csr.adoc.erb").to_s +] do |t| + csr_name = File.basename(t.name, ".adoc") + + arch_def = arch_def_for("_") + csr = arch_def.csr(csr_name) + raise "Can't find csr '#{csr_name}'" if csr.nil? + + csr_template_path = $root / "backends" / "manual" / "templates" / "csr.adoc.erb" + erb = ERB.new(csr_template_path.read, trim_mode: "-") + erb.filename = csr_template_path.to_s + + FileUtils.mkdir_p File.dirname(t.name) + File.write t.name, AntoraUtils.resolve_links(erb.result(binding)) +end + # rule to create IDL function appendix page rule %r{#{MANUAL_GEN_DIR}/.*/.*/antora/modules/funcs/pages/funcs.adoc} => [ __FILE__, @@ -274,7 +294,7 @@ rule %r{#{MANUAL_GEN_DIR}/.*/top/.*/antora/playbook.yml} => proc { |tname| File.write t.name, erb.result(binding) end -file $root / "ext" / "riscv-isa_manual" / "README.md" do +file $root / "ext" / "riscv-isa-manual" / "README.md" do sh "git submodule update --init ext/riscv-isa-manual" end @@ -345,6 +365,9 @@ namespace :gen do version.instructions.each do |inst| Rake::Task[antora_path / "modules" / "insts" / "pages" / "#{inst.name}.adoc"].invoke end + version.csrs.each do |csr| + Rake::Task[antora_path / "modules" / "csrs" / "pages" / "#{csr.name}.adoc"].invoke + end end playbook_path = MANUAL_GEN_DIR / ENV["MANUAL_NAME"] / "top" / output_hash / "antora" / "playbook.yml" diff --git a/backends/manual/templates/csr.adoc.erb b/backends/manual/templates/csr.adoc.erb new file mode 100644 index 000000000..fd17c47d1 --- /dev/null +++ b/backends/manual/templates/csr.adoc.erb @@ -0,0 +1,125 @@ +:tabs-sync-option: + +[#csrs-<%= csr.name.gsub('.', '_') %>,reftext=<%= csr.name %>] += <%= csr.name %> + +*<%= csr.long_name %>* + +<%= csr.description %> + +== Attributes +[%autowidth] +|=== +h| CSR Address | <%= "0x#{csr.address.to_s(16)}" %> +<%- if csr.priv_mode == 'VS' -%> +h| Virtual CSR Address | <%= "0x#{csr.virtual_address.to_s(16)}" %> +<%- end -%> +<%- if csr.dynamic_length?(arch_def) -%> +h| Length | <%= csr.length_pretty(arch_def) %> +<%- else -%> +h| Length | <%= csr.length_pretty(arch_def) %>-bit +<%- end -%> +h| Privilege Mode | <%= csr.priv_mode %> +|=== + +== Format +<%- unless csr.dynamic_length?(arch_def) || csr.fields.any? { |f| f.dynamic_location?(arch_def) } -%> +<%# CSR has a known static length, so there is only one format to display -%> +.<%= csr.name %> format +[wavedrom, ,svg,subs='attributes',width="100%"] +.... +<%= JSON.dump csr.wavedrom_desc(arch_def, 64) %> +.... +<%- else -%> +<%# CSR has a dynamic length, or a field has a dynamic location, + so there is more than one format to display -%> +This CSR format changes dynamically. + +.<%= csr.name %> Format when <%= csr.length_cond32 %> +[wavedrom, ,svg,subs='attributes',width="100%"] +.... +<%= JSON.dump csr.wavedrom_desc(arch_def, 32) %> +.... + +.<%= csr.name %> Format when <%= csr.length_cond64 %> +[wavedrom, ,svg,subs='attributes',width="100%"] +.... +<%= JSON.dump csr.wavedrom_desc(arch_def, 64) %> +.... +<%- end -%> + +== Field Summary + +[%autowidth,float="center",align="center",cols="^,<,<,<",options="header",role="stretch"] +|=== +|Name | Location | Type | Reset Value + +<%- csr.fields.each do |field| -%> +| xref:<%=csr.name%>-<%=field.name%>-def[`<%= field.name %>`] +| <%= field.location_pretty(arch_def) %> +| <%= field.type_pretty(arch_def) %> +| <%= field.reset_value_pretty(arch_def) %> + +<%- end -%> +|=== + +== Fields + +<%- if csr.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.fields.each do |field| -%> +[[<%=csr.name%>-<%=field.name%>-def]] +=== `<%= field.name %>` + +[example] +**** +Location:: +<%= field.location_pretty(arch_def) %> + +Description:: +<%= field.description %> + +Type:: +<%= field.type_pretty(arch_def) %> + +Reset value:: +<%= field.reset_value_pretty(arch_def) %> + +**** + +<%- end -%> +<%- end -%> + +<%- if csr.fields.map(&:has_custom_sw_write?).any? -%> +== Software write + +This CSR may store a value that is different from what software attempts to write. + +When a software write occurs (_e.g._, through `csrrw`), the following determines the +written value: + +[idl] +---- +<%- csr.fields.each do |field| -%> +<%- if field.has_custom_sw_write? -%> +<%= field.name %> = <%= field["sw_write(csr_value)"] %> +<%- else -%> +<%= field.name %> = csr_value.<%= field.name %> +<%- end -%> +<%- end -%> +---- +<%- end -%> + +<%- if csr.has_custom_sw_read? -%> +== Software read + +This CSR may return a value that is different from what is stored in hardware. + +[source,idl,subs="specialchars,macros"] +---- +<%= csr.sw_read_ast(arch_def.idl_compiler).gen_adoc %> +---- +<%- end -%> + diff --git a/backends/manual/templates/instruction.adoc.erb b/backends/manual/templates/instruction.adoc.erb index c3420fc48..128072d88 100644 --- a/backends/manual/templates/instruction.adoc.erb +++ b/backends/manual/templates/instruction.adoc.erb @@ -117,7 +117,6 @@ RV64:: ---- <%= inst.operation_ast(inst.arch_def.idl_compiler).gen_adoc %> ---- -==== <%- end -%> <%# exception_list = inst.reachable_exceptions_str(inst.arch_def.sym_table_64, 64) -%> diff --git a/backends/manual/templates/isa_nav.adoc.erb b/backends/manual/templates/isa_nav.adoc.erb index 6be07f584..06b8c9c37 100644 --- a/backends/manual/templates/isa_nav.adoc.erb +++ b/backends/manual/templates/isa_nav.adoc.erb @@ -11,5 +11,10 @@ * xref:insts:<%= inst.name %>.adoc[<%= inst.name %>] <%- end -%> +.Alphabetical list of CSRs +<%- manual_version.csrs.sort { |a, b| a.name <=> b.name }.each do |csr| -%> +* xref:csrs:<%= csr.name %>.adoc[<%= csr.name %>] +<%- end -%> + .Execution functions (IDL) * xref:funcs:funcs.adoc[Functions] diff --git a/lib/arch_obj_models/csr.rb b/lib/arch_obj_models/csr.rb index 37a97d167..e08a2c389 100644 --- a/lib/arch_obj_models/csr.rb +++ b/lib/arch_obj_models/csr.rb @@ -154,9 +154,35 @@ def dynamic_length?(arch_def) # !@data["length"].is_a?(Integer) && (@data["length"] != "MXLEN") end + # @param arch_def [ArchDef] Architecture definition + # @return [Integer] Smallest length of the CSR in any mode + def min_length(arch_def) + case @data["length"] + when "MXLEN", "SXLEN", "VSXLEN" + 32 + when Integer + @data["length"] + else + raise "Unexpected length" + end + end + + # @param arch_def [ArchDef] Architecture definition + # @return [Integer] Largest length of the CSR in any mode + def max_length(arch_def) + case @data["length"] + when "MXLEN", "SXLEN", "VSXLEN" + 64 + when Integer + @data["length"] + else + raise "Unexpected length" + end + end + # @param arch_def [ArchDef] A configuration (can be nil if the lenth is not dependent on a config parameter) # @param effective_xlen [Integer] The effective xlen, needed since some fields change location with XLEN. If the field location is not determined by XLEN, then this parameter can be nil - # @return [Integer] Length, in bits, of the CSR + # @return [Integer] Length, in bits, of the CSR, given effective_xlen def length(arch_def, effective_xlen = nil) case @data["length"] when "MXLEN" @@ -167,7 +193,7 @@ def length(arch_def, effective_xlen = nil) @data["base"] else # don't know MXLEN - raise ArgumentError, "effective_xlen is required when length is MXLEN and arch_def is generic" if effective_xlen.nil? + raise ArgumentError, "for CSR #{name}: effective_xlen is required when length is MXLEN and arch_def is generic" if effective_xlen.nil? effective_xlen end diff --git a/lib/arch_obj_models/csr_field.rb b/lib/arch_obj_models/csr_field.rb index 82e2c4b08..009c0266d 100644 --- a/lib/arch_obj_models/csr_field.rb +++ b/lib/arch_obj_models/csr_field.rb @@ -532,8 +532,14 @@ def location(arch_def, effective_xlen = nil) raise "Missing location for #{csr.name}.#{name} (#{key})?" unless @data.key?(key) if @data[key].is_a?(Integer) - if @data[key] > csr.length(arch_def, effective_xlen || @data["base"]) - raise "Location (#{key} = #{@data[key]}) is past the csr length (#{csr.length(arch_def, effective_xlen)}) in #{csr.name}.#{name}" + if (arch_def.is_a?(ImplArchDef)) + if @data[key] > csr.length(arch_def, effective_xlen || @data["base"]) + raise "Location (#{key} = #{@data[key]}) is past the csr length (#{csr.length(arch_def, effective_xlen)}) in #{csr.name}.#{name}" + end + else + if @data[key] > csr.max_length(arch_def) + raise "Location (#{key} = #{@data[key]}) is past the max csr length (#{csr.max_length(arch_def)}) in #{csr.name}.#{name}" + end end @data[key]..@data[key] @@ -541,8 +547,14 @@ def location(arch_def, effective_xlen = nil) e, s = @data[key].split("-").map(&:to_i) raise "Invalid location" if s > e - if e > csr.length(arch_def, effective_xlen) - raise "Location (#{key} = #{@data[key]}) is past the csr length (#{csr.length(arch_def, effective_xlen)}) in #{csr.name}.#{name}" + if (arch_def.is_a?(ImplArchDef)) + if e > csr.length(arch_def, effective_xlen) + raise "Location (#{key} = #{@data[key]}) is past the csr length (#{csr.length(arch_def, effective_xlen)}) in #{csr.name}.#{name}" + end + else + if e > csr.max_length(arch_def) + raise "Location (#{key} = #{@data[key]}) is past the max csr length (#{csr.max_length(arch_def)}) in #{csr.name}.#{name}" + end end s..e @@ -578,12 +590,14 @@ def location_pretty(arch_def) if dynamic_location?(arch_def) condition = case csr.priv_mode + when "M" + "CSR[misa].MXL == %%" when "S" "CSR[mstatus].SXL == %%" when "VS" "CSR[hstatus].VSXL == %%" else - raise "Unexpected priv mode" + raise "Unexpected priv mode #{csr.priv_mode} for #{csr.name}" end <<~LOC diff --git a/lib/arch_obj_models/manual.rb b/lib/arch_obj_models/manual.rb index 3f9b7bd9b..b93aecb29 100644 --- a/lib/arch_obj_models/manual.rb +++ b/lib/arch_obj_models/manual.rb @@ -171,4 +171,18 @@ def instructions end @instructions end + + # @return [Array] All csrs defined in this version + def csrs + return @csrs unless @csrs.nil? + + @csrs = [] + extensions.each do |ext| + ext_obj = @arch_def.extension(ext.name) + ext_obj.csrs.each do |csr| + @csrs << csr + end + end + @csrs + end end