udb-gen 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +26 -0
  3. data/bin/udb-gen +57 -0
  4. data/lib/udb-gen/adoc_helpers.rb +83 -0
  5. data/lib/udb-gen/common_opts.rb +51 -0
  6. data/lib/udb-gen/defines.rb +14 -0
  7. data/lib/udb-gen/generators/ext_doc/generator.rb +228 -0
  8. data/lib/udb-gen/generators/ext_doc/helpers.rb +63 -0
  9. data/lib/udb-gen/generators/isa_explorer/generator.rb +247 -0
  10. data/lib/udb-gen/generators/isa_explorer/js_xlsx_writer.rb +140 -0
  11. data/lib/udb-gen/generators/isa_explorer/table_builder.rb +244 -0
  12. data/lib/udb-gen/generators/manual/generator.rb +124 -0
  13. data/lib/udb-gen/generators/manual/tasks.rake +472 -0
  14. data/lib/udb-gen/subcommand.rb +33 -0
  15. data/lib/udb-gen/template_helpers.rb +153 -0
  16. data/lib/udb-gen/version.rb +9 -0
  17. data/lib/udb-gen.rb +9 -0
  18. data/templates/common/csr.adoc.erb +133 -0
  19. data/templates/common/inst.adoc.erb +130 -0
  20. data/templates/common/mmr.adoc.erb +71 -0
  21. data/templates/ext_doc/_csr_list.adoc.erb +49 -0
  22. data/templates/ext_doc/_header.adoc.erb +57 -0
  23. data/templates/ext_doc/_idl_functions.adoc.erb +47 -0
  24. data/templates/ext_doc/_instruction_list.adoc.erb +85 -0
  25. data/templates/ext_doc/_preamble.adoc.erb +89 -0
  26. data/templates/ext_doc/ext_pdf.adoc.erb +144 -0
  27. data/templates/isa_explorer/csr-browser.html.erb +21 -0
  28. data/templates/isa_explorer/ext-browser.html.erb +21 -0
  29. data/templates/isa_explorer/inst-browser.html.erb +21 -0
  30. data/templates/isa_explorer/load_table.js +39 -0
  31. data/templates/manual/csr.adoc.erb +172 -0
  32. data/templates/manual/ext.adoc.erb +85 -0
  33. data/templates/manual/func.adoc.erb +45 -0
  34. data/templates/manual/highlight.js +3580 -0
  35. data/templates/manual/index.adoc.erb +16 -0
  36. data/templates/manual/instruction.adoc.erb +180 -0
  37. data/templates/manual/isa_nav.adoc.erb +29 -0
  38. data/templates/manual/isa_version_index.adoc.erb +12 -0
  39. data/templates/manual/param_list.adoc.erb +22 -0
  40. data/templates/manual/playbook.yml.erb +130 -0
  41. metadata +270 -0
@@ -0,0 +1,247 @@
1
+ # Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
2
+ # SPDX-License-Identifier: BSD-3-Clause-Clear
3
+
4
+ # typed: true
5
+ # frozen_string_literal: true
6
+
7
+ require "sorbet-runtime"
8
+ require "tty-exit"
9
+ require "tty-progressbar"
10
+ require "write_xlsx"
11
+
12
+ require_relative "../../common_opts"
13
+ require_relative "../../defines"
14
+ require_relative "../../template_helpers"
15
+ require_relative "table_builder"
16
+ require_relative "js_xlsx_writer"
17
+
18
+ require "udb/obj/extension"
19
+
20
+ module UdbGen
21
+ class GenIsaExplorerOptions < SubcommandWithCommonOptions
22
+ include TTY::Exit
23
+ include TemplateHelpers
24
+
25
+ NAME = "isa-explorer"
26
+
27
+ sig { void }
28
+ def initialize
29
+ super(name: NAME, desc: "Create ISA explorer tables / sites")
30
+ end
31
+
32
+ usage \
33
+ command: NAME,
34
+ desc: "Create static websites and/or spreadsheets populated with helpful ISA information",
35
+ example: <<~EXAMPLE
36
+ Generate a static HTML page with extension information
37
+ $ #{File.basename($PROGRAM_NAME)} #{NAME} -t ext-browser -o gen/isa_explorer
38
+
39
+ Generate a static HTML page with instruciton information
40
+ $ #{File.basename($PROGRAM_NAME)} #{NAME} -t inst-browser -o gen/isa_explorer
41
+
42
+ Generate a static HTML page with csr information
43
+ $ #{File.basename($PROGRAM_NAME)} #{NAME} -t csr-browser -o gen/isa_explorer
44
+
45
+ Generate an Excel spreadsheet with all info
46
+ $ #{File.basename($PROGRAM_NAME)} #{NAME} -t xlsx -o gen/isa_explorer
47
+ EXAMPLE
48
+
49
+ option :type do
50
+ T.bind(self, TTY::Option::Parameter::Option)
51
+ short "-t"
52
+ long "--type=type"
53
+ desc "The type of artifact to build"
54
+ permit ["ext-browser", "inst-browser", "csr-browser", "xlsx"]
55
+ required
56
+ end
57
+
58
+ option :skip do
59
+ T.bind(self, TTY::Option::Parameter::Option)
60
+ long "--skip=N"
61
+ desc "Only consider every Nth ext/inst/etc. (for testing)"
62
+ convert :integer
63
+ default 0
64
+ end
65
+
66
+ option :output_dir do
67
+ T.bind(self, TTY::Option::Parameter::Option)
68
+ required
69
+ short "-o"
70
+ long "--out=directory"
71
+ desc "Output directory"
72
+ convert :path
73
+ end
74
+
75
+ option :debug do
76
+ T.bind(self, TTY::Option::Parameter::Option)
77
+ desc "Set debug level"
78
+ long "--debug=level"
79
+ short "-d"
80
+ default "info"
81
+ permit ["debug", "info", "warn", "error", "fatal"]
82
+ end
83
+
84
+ sig { void }
85
+ def gen_ext_browser
86
+ FileUtils.mkdir_p params[:output_dir]
87
+
88
+ target_html_fn = params[:output_dir] / "ext-explorer.html"
89
+
90
+ # Delete target file if already present.
91
+ if target_html_fn.exist?
92
+ begin
93
+ File.delete(target_html_fn)
94
+ rescue StandardError => e
95
+ raise "Can't delete '#{target_html_fn}': #{e.message}"
96
+ end
97
+ end
98
+
99
+ builder = IsaExplorer::TableBuilder.new(cfg_arch, params[:skip])
100
+ writer = IsaExplorer::JsXlsxWriter.new
101
+ js_table = writer.js_table(builder.ext_table, "ext_table")
102
+
103
+ template_path = Pathname.new(Gem.loaded_specs["udb-gen"].full_gem_path) / "templates" / "isa_explorer" / "ext-browser.html.erb"
104
+
105
+ erb = ERB.new(template_path.read, trim_mode: "-")
106
+ erb.filename = template_path.to_s
107
+
108
+ Udb.logger.info "SUCCESS: Writing result to #{target_html_fn}"
109
+ target_html_fn.write erb.result(binding)
110
+ end
111
+
112
+ sig { void }
113
+ def gen_inst_browser
114
+ FileUtils.mkdir_p params[:output_dir]
115
+
116
+ target_html_fn = params[:output_dir] / "inst-explorer.html"
117
+
118
+ # Delete target file if already present.
119
+ if target_html_fn.exist?
120
+ begin
121
+ File.delete(target_html_fn)
122
+ rescue StandardError => e
123
+ raise "Can't delete '#{target_html_fn}': #{e.message}"
124
+ end
125
+ end
126
+
127
+ builder = IsaExplorer::TableBuilder.new(cfg_arch, params[:skip])
128
+ writer = IsaExplorer::JsXlsxWriter.new
129
+ js_table = writer.js_table(builder.inst_table, "inst_table")
130
+
131
+ template_path = Pathname.new(Gem.loaded_specs["udb-gen"].full_gem_path) / "templates" / "isa_explorer" / "inst-browser.html.erb"
132
+
133
+ erb = ERB.new(template_path.read, trim_mode: "-")
134
+ erb.filename = template_path.to_s
135
+
136
+ Udb.logger.info "SUCCESS: Writing result to #{target_html_fn}"
137
+ target_html_fn.write erb.result(binding)
138
+ end
139
+
140
+ sig { void }
141
+ def gen_csr_browser
142
+ FileUtils.mkdir_p params[:output_dir]
143
+
144
+ target_html_fn = params[:output_dir] / "csr-explorer.html"
145
+
146
+ # Delete target file if already present.
147
+ if target_html_fn.exist?
148
+ begin
149
+ File.delete(target_html_fn)
150
+ rescue StandardError => e
151
+ raise "Can't delete '#{target_html_fn}': #{e.message}"
152
+ end
153
+ end
154
+
155
+ builder = IsaExplorer::TableBuilder.new(cfg_arch, params[:skip])
156
+ writer = IsaExplorer::JsXlsxWriter.new
157
+ js_table = writer.js_table(builder.csr_table, "csr_table")
158
+
159
+ template_path = Pathname.new(Gem.loaded_specs["udb-gen"].full_gem_path) / "templates" / "isa_explorer" / "csr-browser.html.erb"
160
+
161
+ erb = ERB.new(template_path.read, trim_mode: "-")
162
+ erb.filename = template_path.to_s
163
+
164
+ Udb.logger.info "SUCCESS: Writing result to #{target_html_fn}"
165
+ target_html_fn.write erb.result(binding)
166
+ end
167
+
168
+ sig { void }
169
+ def gen_xlsx
170
+ FileUtils.mkdir_p params[:output_dir]
171
+
172
+ target_xlsx_fn = params[:output_dir] / "isa_explorer.xlsx"
173
+
174
+ # Delete target file if already present.
175
+ if target_xlsx_fn.exist?
176
+ begin
177
+ File.delete(target_xlsx_fn)
178
+ rescue StandardError => e
179
+ raise "Can't delete '#{target_xlsx_fn}': #{e.message}"
180
+ end
181
+ end
182
+
183
+ builder = IsaExplorer::TableBuilder.new(cfg_arch, params[:skip])
184
+ writer = IsaExplorer::JsXlsxWriter.new
185
+
186
+ # Create a new Excel workbook
187
+ Udb.logger.info "Creating Excel workboook #{target_xlsx_fn}"
188
+ workbook = WriteXLSX.new(target_xlsx_fn)
189
+
190
+ # Extension worksheet
191
+ Udb.logger.info "Creating extension table data structure"
192
+ ext_worksheet = workbook.add_worksheet("Extensions")
193
+ Udb.logger.info "Adding extension table to worksheet #{ext_worksheet.name}"
194
+ writer.xlsx_table(builder.ext_table, workbook, ext_worksheet)
195
+
196
+ # Instruction worksheet
197
+ Udb.logger.info "Creating instruction table data structure"
198
+ inst_worksheet = workbook.add_worksheet("Instructions")
199
+ Udb.logger.info "Adding instruction table to worksheet #{inst_worksheet.name}"
200
+ writer.xlsx_table(builder.inst_table, workbook, inst_worksheet)
201
+
202
+ # CSR worksheet
203
+ Udb.logger.info "Creating CSR table data structure"
204
+ csr_worksheet = workbook.add_worksheet("CSRs")
205
+ Udb.logger.info "Adding CSR table to worksheet #{csr_worksheet.name}"
206
+ writer.xlsx_table(builder.csr_table, workbook, csr_worksheet)
207
+
208
+ workbook.close
209
+
210
+ Udb.logger.info "SUCCESS: Wrote result to #{target_xlsx_fn}"
211
+ end
212
+
213
+ sig { override.params(argv: T::Array[String]).returns(T.noreturn) }
214
+ def run(argv)
215
+ parse(argv)
216
+
217
+ if params[:help]
218
+ print help
219
+ exit_with(:success)
220
+ end
221
+
222
+ if params.errors.any?
223
+ exit_with(:usage_error, "#{params.errors.summary}\n\n#{help}")
224
+ end
225
+
226
+ unless params.remaining.empty?
227
+ exit_with(:usage_error, "Unknown arguments: #{params.remaining}\n")
228
+ end
229
+
230
+ case params[:type]
231
+ when "ext-browser"
232
+ gen_ext_browser
233
+ when "inst-browser"
234
+ gen_inst_browser
235
+ when "csr-browser"
236
+ gen_csr_browser
237
+ when "xlsx"
238
+ gen_xlsx
239
+ else
240
+ Udb.logger.error "Unknown target type: #{params[:type]}"
241
+ end
242
+
243
+ exit_with(:success)
244
+ end
245
+
246
+ end
247
+ end
@@ -0,0 +1,140 @@
1
+ # Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
2
+ # SPDX-License-Identifier: BSD-3-Clause-Clear
3
+
4
+ # typed: true
5
+ # frozen_string_literal: true
6
+
7
+ require "sorbet-runtime"
8
+ require "write_xlsx"
9
+
10
+ module UdbGen
11
+ module IsaExplorer
12
+ class JsXlsxWriter
13
+ extend T::Sig
14
+
15
+ # Create ISA Explorer table as JavaScript file.
16
+ #
17
+ # @param table Table data
18
+ # @param div_name Name of div element in HTML
19
+ sig { params(table: T::Hash[String, T::Array[T.untyped]], div_name: String).returns(String) }
20
+ def js_table(table, div_name)
21
+ columns = table.fetch("columns")
22
+ rows = table.fetch("rows")
23
+
24
+ fp = StringIO.new
25
+ fp.write "// Define data array\n"
26
+ fp.write "\n"
27
+ fp.write "var tabledata = [\n"
28
+
29
+ rows.each do |row|
30
+ items = []
31
+ columns.each_index do |i|
32
+ column = columns.fetch(i)
33
+ column_name = column.fetch(:name).gsub("\n", " ")
34
+ cell = row.fetch(i)
35
+ if cell.is_a?(String)
36
+ cell_fmt = '"' + row.fetch(i).gsub("\n", "\\n") + '"'
37
+ elsif cell.is_a?(TrueClass) || cell.is_a?(FalseClass) || cell.is_a?(Integer)
38
+ cell_fmt = "#{cell}"
39
+ elsif cell.is_a?(Array)
40
+ cell_fmt = '"' + cell.join("\\n") + '"'
41
+ else
42
+ raise ArgumentError, "Unknown cell class of #{cell.class} for '#{cell}'"
43
+ end
44
+ items.append('"' + column_name + '":' + cell_fmt)
45
+ end
46
+ fp.write " {" + items.join(", ") + "},\n"
47
+ end
48
+
49
+ fp.write "];\n"
50
+ fp.write "\n"
51
+ fp.write "// Initialize table\n"
52
+ fp.write "var table = new Tabulator(\"##{div_name}\", {\n"
53
+ fp.write " height: window.innerHeight-25, // Set height to window less 25 pixels for horz scrollbar\n"
54
+ fp.write " data: tabledata, // Assign data to table\n"
55
+ fp.write " columns:[\n"
56
+ columns.each do |column|
57
+ column_name = column.fetch(:name).gsub("\n", " ")
58
+ sorter = column.fetch(:sorter)
59
+ formatter = column.fetch(:formatter)
60
+ fp.write " {title: \"#{column_name}\", field: \"#{column_name}\", sorter: \"#{sorter}\", formatter: \"#{formatter}\""
61
+
62
+ if column[:headerFilter] == true
63
+ fp.write ", headerFilter: true"
64
+ end
65
+ if column[:headerVertical] == true
66
+ fp.write ", headerVertical: true"
67
+ end
68
+ if column[:frozen] == true
69
+ fp.write ", frozen: true"
70
+ end
71
+
72
+ if formatter == "link"
73
+ formatterParams = column.fetch(:formatterParams)
74
+ urlPrefix = formatterParams.fetch(:urlPrefix)
75
+ fp.write ", formatterParams:{\n"
76
+ fp.write " labelField:\"#{column_name}\",\n"
77
+ fp.write " urlPrefix:\"#{urlPrefix}\"\n"
78
+ fp.write " }\n"
79
+ end
80
+ fp.write " },\n"
81
+ end
82
+ fp.write " ]\n"
83
+ fp.write "});\n"
84
+ fp.write "\n"
85
+
86
+ fp.write "// Load data in chunks after table is built\n"
87
+ fp.write "table.on(\"tableBuilt\", function() {\n"
88
+ fp.write " loadDataInChunks(tabledata);\n"
89
+ fp.write "});\n"
90
+ fp.write "\n"
91
+ fp.rewind
92
+ T.must(fp.read)
93
+ end
94
+
95
+ # Create ISA Explorer table as XLSX worksheet.
96
+ #
97
+ # @param table [Hash<String,Array<String>] Table data
98
+ # @param workbook
99
+ # @param worksheet
100
+ sig { params(table: T::Hash[String, T::Array[T.untyped]], workbook: WriteXLSX, worksheet: Writexlsx::Worksheet).void }
101
+ def xlsx_table(table, workbook, worksheet)
102
+ # Add and define a header format
103
+ header_format = workbook.add_format
104
+ header_format.set_bold
105
+ header_format.set_align("center")
106
+
107
+ # Add column names in 1st row (row 0).
108
+ col_num = 0
109
+ table.fetch("columns").each do |column|
110
+ worksheet.write(0, col_num, column.fetch(:name), header_format)
111
+ col_num += 1
112
+ end
113
+
114
+ # Add table information in rows
115
+ row_num = 1
116
+ table.fetch("rows").each do |row_cells|
117
+ col_num = 0
118
+ row_cells.each do |cell|
119
+ if cell.is_a?(String) || cell.is_a?(Integer)
120
+ cell_fmt = cell.to_s
121
+ elsif cell.is_a?(TrueClass) || cell.is_a?(FalseClass)
122
+ cell_fmt = cell ? "Y" : "N"
123
+ elsif cell.is_a?(Array)
124
+ cell_fmt = cell.join(", ")
125
+ else
126
+ raise ArgumentError, "Unknown cell class of #{cell.class} for '#{cell}'"
127
+ end
128
+
129
+ worksheet.write(row_num, col_num, cell_fmt)
130
+ col_num += 1
131
+ end
132
+ row_num += 1
133
+ end
134
+
135
+ # Set column widths to hold data width.
136
+ worksheet.autofit
137
+ end
138
+ end
139
+ end
140
+ end
@@ -0,0 +1,244 @@
1
+ # Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
2
+ # SPDX-License-Identifier: BSD-3-Clause-Clear
3
+
4
+ # typed: true
5
+ # frozen_string_literal: true
6
+
7
+ require "sorbet-runtime"
8
+ require "tty-progressbar"
9
+
10
+ module UdbGen
11
+ module IsaExplorer
12
+ class TableBuilder
13
+ extend T::Sig
14
+
15
+ sig { params(arch: Udb::ConfiguredArchitecture, skip: Integer).void }
16
+ def initialize(arch, skip)
17
+ @arch = arch
18
+ @skip = skip
19
+ end
20
+
21
+ # @return Extension table data
22
+ sig { returns(T::Hash[String, T::Array[T.untyped]]) }
23
+ def ext_table
24
+ sorted_releases = sorted_profile_releases
25
+
26
+ table = {
27
+ # Array of hashes
28
+ "columns" => [
29
+ { name: "Extension Name", formatter: "link", sorter: "alphanum", headerFilter: true, frozen: true, formatterParams:
30
+ {
31
+ labelField: "Extension Name",
32
+ urlPrefix: "https://riscv.github.io/riscv-unified-db/manual/html/isa/isa_20240411/exts/"
33
+ }
34
+ },
35
+ { name: "Description", formatter: "textarea", sorter: "alphanum", headerFilter: true },
36
+ { name: "IC", formatter: "textarea", sorter: "alphanum", headerFilter: true },
37
+ { name: "Requires\n(Exts)", formatter: "textarea", sorter: "alphanum" },
38
+ { name: "Transitive Requires\n(Ext)", formatter: "textarea", sorter: "alphanum" },
39
+ { name: "Incompatible\n(Ext Reqs)", formatter: "textarea", sorter: "alphanum" },
40
+ { name: "Ratified", formatter: "textarea", sorter: "boolean", headerFilter: true },
41
+ { name: "Ratification\nDate", formatter: "textarea", sorter: "alphanum", headerFilter: true },
42
+ sorted_releases.map do |pr|
43
+ { name: "#{pr.name}", formatter: "textarea", sorter: "alphanum", headerFilter: true }
44
+ end
45
+ ].flatten,
46
+
47
+ # Will eventually be an array containing arrays.
48
+ "rows" => []
49
+ }
50
+
51
+ pb = Udb.create_progressbar(
52
+ "Analyzing extensions [:bar] :current/:total",
53
+ total: @arch.extensions.size,
54
+ clear: true
55
+ )
56
+
57
+ @arch.extensions.sort_by!(&:name).each_with_index do |ext, idx|
58
+ pb.advance
59
+
60
+ if @skip != 0
61
+ next unless (idx % @skip) == 0
62
+ end
63
+
64
+ row = [
65
+ ext.name, # Name
66
+ ext.long_name, # Description
67
+ ext.compact_priv_type, # IC
68
+ ext.max_version.ext_requirements(expand: false).map do |cond_ext_req|
69
+ if cond_ext_req.cond.empty?
70
+ cond_ext_req.ext_req.name
71
+ else
72
+ "#{cond_ext_req.ext_req.name} if #{cond_ext_req.cond}"
73
+ end
74
+ end.uniq, # Requires
75
+ ext.max_version.ext_requirements(expand: true).map do |cond_ext_req|
76
+ if cond_ext_req.cond.empty?
77
+ cond_ext_req.ext_req.name
78
+ else
79
+ "#{cond_ext_req.ext_req.name} if #{cond_ext_req.cond}"
80
+ end
81
+ end.uniq, # Transitive Requires
82
+ ext.conflicting_extensions.map(&:name),
83
+ ext.ratified,
84
+ if ext.ratified
85
+ if ext.min_ratified_version.ratification_date.nil? || ext.min_ratified_version.ratification_date.empty?
86
+ "UDB MISSING"
87
+ else
88
+ ext.min_ratified_version.ratification_date
89
+ end
90
+ else
91
+ ""
92
+ end
93
+ ]
94
+
95
+ sorted_releases.each do |pr|
96
+ row.append(presence2char(pr.extension_presence(ext.name)))
97
+ end
98
+
99
+ table["rows"].append(row)
100
+ end
101
+
102
+ table
103
+ end
104
+
105
+ # @return Instruction table data
106
+ sig { returns(T::Hash[String, T::Array[T.untyped]]) }
107
+ def inst_table
108
+ sorted_releases = sorted_profile_releases
109
+
110
+ table = {
111
+ # Array of hashes
112
+ "columns" => [
113
+ { name: "Instruction Name", formatter: "link", sorter: "alphanum", headerFilter: true, frozen: true, formatterParams:
114
+ {
115
+ labelField: "Instruction Name",
116
+ urlPrefix: "https://riscv.github.io/riscv-unified-db/manual/html/isa/isa_20240411/insts/"
117
+ }
118
+ },
119
+ { name: "Description", formatter: "textarea", sorter: "alphanum", headerFilter: true },
120
+ { name: "Assembly", formatter: "textarea", sorter: "alphanum", headerFilter: true },
121
+ sorted_releases.map do |pr|
122
+ { name: "#{pr.name}", formatter: "textarea", sorter: "alphanum", headerFilter: true }
123
+ end
124
+ ].flatten,
125
+
126
+ # Will eventually be an array containing arrays.
127
+ "rows" => []
128
+ }
129
+
130
+ insts = @arch.instructions.sort_by!(&:name)
131
+ progressbar = TTY::ProgressBar.new("Instruction Table [:bar] :current/:total", total: insts.size, output: $stdout)
132
+
133
+ insts.each_with_index do |inst, idx|
134
+ progressbar.advance
135
+ if @skip != 0
136
+ next unless (idx % @skip) == 0
137
+ end
138
+
139
+ row = [
140
+ inst.name,
141
+ inst.long_name,
142
+ inst.name + " " + inst.assembly.gsub("x", "r")
143
+ ]
144
+
145
+ sorted_releases.each do |pr|
146
+ row.append(presence2char(pr.instruction_presence(inst.name)))
147
+ end
148
+
149
+ table["rows"].append(row)
150
+ end
151
+
152
+ table
153
+ end
154
+
155
+ # @return CSR table data
156
+ sig { returns(T::Hash[String, T::Array[T.untyped]]) }
157
+ def csr_table
158
+ sorted_releases = sorted_profile_releases
159
+
160
+ table = {
161
+ # Array of hashes
162
+ "columns" => [
163
+ { name: "CSR Name", formatter: "link", sorter: "alphanum", headerFilter: true, frozen: true, formatterParams:
164
+ {
165
+ labelField: "CSR Name",
166
+ urlPrefix: "https://riscv.github.io/riscv-unified-db/manual/html/isa/isa_20240411/csrs/"
167
+ }
168
+ },
169
+ { name: "Address", formatter: "textarea", sorter: "number", headerFilter: true },
170
+ { name: "Description", formatter: "textarea", sorter: "alphanum", headerFilter: true },
171
+ sorted_releases.map do |pr|
172
+ { name: "#{pr.name}", formatter: "textarea", sorter: "alphanum", headerFilter: true }
173
+ end
174
+ ].flatten,
175
+
176
+ # Will eventually be an array containing arrays.
177
+ "rows" => []
178
+ }
179
+
180
+ csrs = @arch.csrs.sort_by!(&:name)
181
+ progressbar = TTY::ProgressBar.new("CSR Table [:bar]", total: csrs.size, output: $stdout)
182
+
183
+ csrs.each_with_index do |csr, idx|
184
+ progressbar.advance
185
+
186
+ if @skip != 0
187
+ next unless (idx % @skip) == 0
188
+ end
189
+
190
+ raise "Indirect CSRs not yet supported for CSR #{csr.name}" if csr.address.nil?
191
+
192
+ row = [
193
+ csr.name,
194
+ csr.address,
195
+ csr.long_name,
196
+ ]
197
+
198
+ sorted_releases.each do |pr|
199
+ row.append(presence2char(pr.csr_presence(csr.name)))
200
+ end
201
+
202
+ table["rows"].append(row)
203
+ end
204
+
205
+ table
206
+ end
207
+
208
+ private
209
+
210
+ # return Nice list of profile release to use in a nice order
211
+ sig { returns(T::Array[Udb::ProfileRelease]) }
212
+ def sorted_profile_releases
213
+ # Get array of profile releases and sort by name
214
+ sorted = @arch.profile_releases.sort_by(&:name)
215
+
216
+ # Remove Mock profile release if present.
217
+ sorted.delete_if { |pr| pr.name == "Mock" }
218
+
219
+ # Move RVI20 to the beginning of the array if it exists.
220
+ if sorted.any? { |pr| pr.name == "RVI20" }
221
+ sorted.delete_if { |pr| pr.name == "RVI20" }
222
+ sorted.unshift(T.must(@arch.profile_release("RVI20")))
223
+ end
224
+
225
+ sorted
226
+ end
227
+
228
+ # @param presence [String] Can be nil
229
+ # @return [String] m=mandatory, o=optional, n=not present
230
+ sig { params(presence: String).returns(String) }
231
+ def presence2char(presence)
232
+ if presence == "mandatory"
233
+ "m"
234
+ elsif presence == "optional"
235
+ "o"
236
+ elsif presence == "-"
237
+ "n"
238
+ else
239
+ raise ArgumentError, "Unknown presence of #{presence}"
240
+ end
241
+ end
242
+ end
243
+ end
244
+ end