udb-gen 0.1.7 → 0.1.13

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 47ff086404150fb7e0031816bc827970c615792d60f1796cc55e36730fd9c6f2
4
- data.tar.gz: d257dfaa46302aed705df67c53db0602092b3e9dcc350a87a747e6b56d63dc35
3
+ metadata.gz: 3f2e3db34409108c4ac83f95a778d24936d06b3d5124ab6138af60020e94c740
4
+ data.tar.gz: f8274018357279d3aa28bfe47a7e77137f29d1238b73336ea8048575b23298d3
5
5
  SHA512:
6
- metadata.gz: 7b3e6888280edb39c4a05862768126db2e339dd795e89729c5da8692c7347b1f3af092211190b7dfaa026fa964b3fa9f041e6414e8ad8d5751708811aa95dcbb
7
- data.tar.gz: 142bb18d41aa582ddafc3265649b6d7da6cef6d5c99ffa478c08d2281ea865362f624bcfcd186bb042dbbfc07b2e582f0491a77c51b43cbc94b77e901ae6a1d7
6
+ metadata.gz: 11d8d5b8a08aa3aa2490c0998ec033538643680b266267f0b1265ea0fac15605e01e0ce8b72e1000fd4eb161e47a6f245f986b5f2170064a665c326c592b7b2b
7
+ data.tar.gz: 2fec5ed54797ae271ce320c31b20b549f559477cf72d9335cc6d9fdd6ea955b12b1a16f3f9717beee13ccdde5b433e2995c6e64fdeecadc0473a78eb93f3fa23
@@ -0,0 +1,208 @@
1
+ # Copyright (c) Jordan Carlin, Harvey Mudd College.
2
+ # SPDX-License-Identifier: BSD-3-Clause-Clear
3
+
4
+ # typed: true
5
+ # frozen_string_literal: true
6
+
7
+ require "fileutils"
8
+ require "pathname"
9
+ require "sorbet-runtime"
10
+ require "tty-exit"
11
+ require "udb/cfg_arch"
12
+
13
+ module UdbGen
14
+ # Shared logic for generating config header files across languages (C, SystemVerilog, etc.).
15
+ # Including classes must include TTY::Exit, inherit from SubcommandWithCommonOptions,
16
+ # and include this module; SubcommandWithCommonOptions provides cfg_arch, params,
17
+ # parse, help, and exit_with.
18
+ module CfgHeaderBase
19
+ extend T::Sig
20
+ extend T::Helpers
21
+
22
+ abstract!
23
+
24
+ sig { abstract.returns(Udb::ConfiguredArchitecture) }
25
+ def cfg_arch; end
26
+
27
+ sig { abstract.returns(T.untyped) }
28
+ def params; end
29
+
30
+ sig { abstract.params(argv: T::Array[String]).returns(T.untyped) }
31
+ def parse(argv); end
32
+
33
+ sig { abstract.returns(String) }
34
+ def help; end
35
+
36
+ sig { abstract.params(exit_code: Symbol, message: T.nilable(String)).returns(T.noreturn) }
37
+ def exit_with(exit_code, message = nil); end
38
+
39
+ sig { abstract.returns(String) }
40
+ def command_name; end
41
+
42
+ # The preprocessor define directive, e.g. "#define" or "`define"
43
+ sig { abstract.returns(String) }
44
+ def define_directive; end
45
+
46
+ # The begin-guard line, e.g. "#ifndef NAME" or "`ifndef NAME"
47
+ sig { abstract.params(guard_name: String).returns(String) }
48
+ def guard_begin(guard_name); end
49
+
50
+ # The end-guard line, e.g. "#endif /* NAME */" or "`endif // NAME"
51
+ sig { abstract.params(guard_name: String).returns(String) }
52
+ def guard_end(guard_name); end
53
+
54
+ # Guard name suffix, e.g. "_H" or "_SVH"
55
+ sig { abstract.returns(String) }
56
+ def guard_suffix; end
57
+
58
+ # A section comment line, e.g. "/* text */" or "// text"
59
+ sig { abstract.params(text: String).returns(String) }
60
+ def section_comment(text); end
61
+
62
+ # Multi-line file header comment block
63
+ sig { abstract.params(text_lines: T::Array[String]).returns(T::Array[String]) }
64
+ def header_comment(text_lines); end
65
+
66
+ # Human-readable file type for log messages, e.g. "C header" or "SystemVerilog header"
67
+ sig { abstract.returns(String) }
68
+ def file_type_name; end
69
+
70
+ # Format an integer value as a language-appropriate literal
71
+ sig { abstract.params(value: Integer).returns(String) }
72
+ def format_integer(value); end
73
+
74
+ sig { returns(String) }
75
+ def generate_header
76
+ unless cfg_arch.fully_configured?
77
+ exit_with(:data_err, "Config '#{cfg_arch.name}' is not fully configured. Only fully configured configs are supported.\n")
78
+ end
79
+
80
+ lines = []
81
+ guard_name = "UDB_CFG_#{cfg_arch.name.upcase.gsub(/[^A-Z0-9]/, "_")}#{guard_suffix}"
82
+
83
+ convention_lines = [
84
+ # REUSE-IgnoreStart
85
+ "SPDX-License-Identifier: BSD-3-Clause-Clear",
86
+ # REUSE-IgnoreEnd
87
+ "",
88
+ "Auto-generated by riscv-unified-db (udb-gen #{command_name})",
89
+ "Config: #{cfg_arch.name}",
90
+ "",
91
+ "Define conventions:",
92
+ " Extensions: #{define_directive} NAME_SUPPORTED and #{define_directive} NAMEverPver_SUPPORTED",
93
+ " Boolean params: #{define_directive} UDB_NAME (present when true, absent when false)",
94
+ " Integer params: #{define_directive} UDB_NAME value and #{define_directive} UDB_NAME_<value>",
95
+ " Enum (string) params: #{define_directive} UDB_NAME_VALUE (value sanitized to uppercase identifier)",
96
+ " Boolean arrays: #{define_directive} UDB_NAME_<index> (one per true element)",
97
+ " Integer arrays: #{define_directive} UDB_NAME_<value> (one per unique element, sorted)",
98
+ " String arrays: #{define_directive} UDB_NAME_<VALUE> (one per unique element, sanitized)"
99
+ ]
100
+
101
+ lines.concat(header_comment(convention_lines))
102
+ lines << ""
103
+ lines << guard_begin(guard_name)
104
+ lines << "#{define_directive} #{guard_name}"
105
+
106
+ lines << ""
107
+ lines << section_comment("Implemented extensions")
108
+ emit_extension_defines(lines)
109
+
110
+ lines << ""
111
+ lines << section_comment("Configuration parameters")
112
+ emit_param_defines(lines)
113
+
114
+ lines << ""
115
+ lines << guard_end(guard_name)
116
+ lines << ""
117
+
118
+ lines.join("\n")
119
+ end
120
+
121
+ sig { params(argv: T::Array[String]).returns(T.noreturn) }
122
+ def run_generator(argv)
123
+ parse(argv)
124
+
125
+ if params[:help]
126
+ Kernel.print help
127
+ exit_with(:success)
128
+ end
129
+
130
+ if params.errors.any?
131
+ exit_with(:usage_error, "#{params.errors.summary}\n\n#{help}")
132
+ end
133
+
134
+ unless params.remaining.empty?
135
+ exit_with(:usage_error, "Unknown arguments: #{params.remaining}\n")
136
+ end
137
+
138
+ header = generate_header
139
+
140
+ if params[:output].nil?
141
+ $stdout.write(header)
142
+ else
143
+ FileUtils.mkdir_p(params[:output].dirname)
144
+ File.write(params[:output], header)
145
+ Udb.logger.info "Generated #{file_type_name}: #{params[:output]}"
146
+ end
147
+
148
+ exit_with(:success)
149
+ end
150
+
151
+ private
152
+
153
+ sig { params(str: String).returns(String) }
154
+ def sanitize_to_identifier(str)
155
+ str.upcase.gsub(/[^A-Z0-9]/, "_").gsub(/_+/, "_").gsub(/\A_|_\z/, "")
156
+ end
157
+
158
+ sig { params(lines: T::Array[String]).void }
159
+ def emit_extension_defines(lines)
160
+ ext_versions = cfg_arch.implemented_extension_versions.sort_by { |ev| ev.name.upcase }
161
+ ext_versions.each do |ext_ver|
162
+ lines << "#{define_directive} #{ext_ver.name.upcase}_SUPPORTED"
163
+ versioned_name = "#{ext_ver.name.upcase}#{ext_ver.version_spec.to_rvi_s.upcase}"
164
+ lines << "#{define_directive} #{versioned_name}_SUPPORTED"
165
+ end
166
+ end
167
+
168
+ sig { params(lines: T::Array[String]).void }
169
+ def emit_param_defines(lines)
170
+ pv = cfg_arch.param_values
171
+ return if pv.empty?
172
+
173
+ pv.sort_by { |k, _| k }.each do |name, value|
174
+ prefixed = "UDB_#{name}"
175
+ case value
176
+ when true
177
+ lines << "#{define_directive} #{prefixed}"
178
+ when false
179
+ next
180
+ when Integer
181
+ lines << "#{define_directive} #{prefixed} #{format_integer(value)}"
182
+ lines << "#{define_directive} #{prefixed}_#{value}"
183
+ when String
184
+ lines << "#{define_directive} #{prefixed}_#{sanitize_to_identifier(value)}"
185
+ when Array
186
+ emit_array_param(lines, prefixed, value)
187
+ end
188
+ end
189
+ end
190
+
191
+ sig { params(lines: T::Array[String], name: String, value: T::Array[T.untyped]).void }
192
+ def emit_array_param(lines, name, value)
193
+ if value.all? { |v| v == true || v == false }
194
+ value.each_with_index do |v, i|
195
+ lines << "#{define_directive} #{name}_#{i}" if v
196
+ end
197
+ elsif value.all? { |v| v.is_a?(Integer) }
198
+ value.uniq.sort.each do |v|
199
+ lines << "#{define_directive} #{name}_#{v}"
200
+ end
201
+ elsif value.all? { |v| v.is_a?(String) }
202
+ value.map { |v| sanitize_to_identifier(v) }.uniq.sort.each do |sanitized|
203
+ lines << "#{define_directive} #{name}_#{sanitized}"
204
+ end
205
+ end
206
+ end
207
+ end
208
+ end
@@ -4,6 +4,7 @@
4
4
  # typed: true
5
5
  # frozen_string_literal: true
6
6
 
7
+ require "pathname"
7
8
  require "sorbet-runtime"
8
9
 
9
10
  require_relative "subcommand"
@@ -40,7 +41,20 @@ module UdbGen
40
41
  sig { returns(Udb::ConfiguredArchitecture) }
41
42
  def cfg_arch
42
43
  @cfg_arch ||=
43
- resolver.cfg_arch_for(params[:cfg])
44
+ resolver.cfg_arch_for(resolve_cfg_arg(params[:cfg]))
45
+ end
46
+
47
+ # Accept either a known config name (looked up under @cfgs_path) or a
48
+ # filesystem path to a config YAML. Path-like arguments (containing a
49
+ # path separator or ending in .yaml/.yml, or actually existing on disk)
50
+ # are converted to Pathname so the resolver treats them as paths.
51
+ sig { params(arg: String).returns(T.any(String, Pathname)) }
52
+ def resolve_cfg_arg(arg)
53
+ return Pathname.new(arg) if arg.include?(File::SEPARATOR) ||
54
+ arg.end_with?(".yaml", ".yml") ||
55
+ File.file?(arg)
56
+
57
+ arg
44
58
  end
45
59
 
46
60
  sig { override.params(argv: T::Array[String]).returns(T.noreturn) }
@@ -0,0 +1,87 @@
1
+ # Copyright (c) Jordan Carlin, Harvey Mudd College.
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
+
10
+ require_relative "../../common_opts"
11
+ require_relative "../../defines"
12
+ require_relative "../../cfg_header_base"
13
+
14
+ module UdbGen
15
+ class GenCfgCHeaderOptions < SubcommandWithCommonOptions
16
+ include TTY::Exit
17
+ include CfgHeaderBase
18
+
19
+ NAME = "cfg-c-header"
20
+
21
+ sig { void }
22
+ def initialize
23
+ super(name: NAME, desc: "Generate a C header with #defines from a fully configured UDB config")
24
+ end
25
+
26
+ usage \
27
+ command: NAME,
28
+ desc: "Generate a C header file with #define directives derived from a fully configured UDB YAML config",
29
+ example: <<~EXAMPLE
30
+ Generate a C header for the rv64 config, printed to stdout
31
+ $ #{File.basename($PROGRAM_NAME)} #{NAME} -c rv64
32
+
33
+ Generate a C header for the rv64 config, written to a file
34
+ $ #{File.basename($PROGRAM_NAME)} #{NAME} -c rv64 -o config.h
35
+
36
+ Generate a C header for a custom config file
37
+ $ #{File.basename($PROGRAM_NAME)} #{NAME} -c /path/to/my_config.yaml
38
+ EXAMPLE
39
+
40
+ option :output do
41
+ T.bind(self, TTY::Option::Parameter::Option)
42
+ short "-o"
43
+ long "--output=file"
44
+ desc "Output file path (default: stdout)"
45
+ convert :path
46
+ end
47
+
48
+ sig { override.returns(String) }
49
+ def define_directive = "#define"
50
+
51
+ sig { override.returns(String) }
52
+ def command_name = NAME
53
+
54
+ sig { override.params(guard_name: String).returns(String) }
55
+ def guard_begin(guard_name) = "#ifndef #{guard_name}"
56
+
57
+ sig { override.params(guard_name: String).returns(String) }
58
+ def guard_end(guard_name) = "#endif /* #{guard_name} */"
59
+
60
+ sig { override.returns(String) }
61
+ def guard_suffix = "_H"
62
+
63
+ sig { override.params(text: String).returns(String) }
64
+ def section_comment(text) = "/* #{text} */"
65
+
66
+ sig { override.params(text_lines: T::Array[String]).returns(T::Array[String]) }
67
+ def header_comment(text_lines)
68
+ lines = ["/*"]
69
+ text_lines.each do |line|
70
+ lines << (line.empty? ? " *" : " * #{line}")
71
+ end
72
+ lines << " */"
73
+ lines
74
+ end
75
+
76
+ sig { override.returns(String) }
77
+ def file_type_name = "C header"
78
+
79
+ sig { override.params(value: Integer).returns(String) }
80
+ def format_integer(value) = value.to_s
81
+
82
+ sig { override.params(argv: T::Array[String]).returns(T.noreturn) }
83
+ def run(argv)
84
+ run_generator(argv)
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,87 @@
1
+ # Copyright (c) Jordan Carlin, Harvey Mudd College.
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
+
10
+ require_relative "../../common_opts"
11
+ require_relative "../../defines"
12
+ require_relative "../../cfg_header_base"
13
+
14
+ module UdbGen
15
+ class GenCfgSvhHeaderOptions < SubcommandWithCommonOptions
16
+ include TTY::Exit
17
+ include CfgHeaderBase
18
+
19
+ NAME = "cfg-svh-header"
20
+
21
+ sig { void }
22
+ def initialize
23
+ super(name: NAME, desc: "Generate a SystemVerilog header with `define directives from a fully configured UDB config")
24
+ end
25
+
26
+ usage \
27
+ command: NAME,
28
+ desc: "Generate a SystemVerilog header file with `define directives derived from a fully configured UDB YAML config",
29
+ example: <<~EXAMPLE
30
+ Generate a SystemVerilog header for the rv64 config, printed to stdout
31
+ $ #{File.basename($PROGRAM_NAME)} #{NAME} -c rv64
32
+
33
+ Generate a SystemVerilog header for the rv64 config, written to a file
34
+ $ #{File.basename($PROGRAM_NAME)} #{NAME} -c rv64 -o config.svh
35
+
36
+ Generate a SystemVerilog header for a custom config file
37
+ $ #{File.basename($PROGRAM_NAME)} #{NAME} -c /path/to/my_config.yaml
38
+ EXAMPLE
39
+
40
+ option :output do
41
+ T.bind(self, TTY::Option::Parameter::Option)
42
+ short "-o"
43
+ long "--output=file"
44
+ desc "Output file path (default: stdout)"
45
+ convert :path
46
+ end
47
+
48
+ sig { override.returns(String) }
49
+ def define_directive = "`define"
50
+
51
+ sig { override.returns(String) }
52
+ def command_name = NAME
53
+
54
+ sig { override.params(guard_name: String).returns(String) }
55
+ def guard_begin(guard_name) = "`ifndef #{guard_name}"
56
+
57
+ sig { override.params(guard_name: String).returns(String) }
58
+ def guard_end(guard_name) = "`endif // #{guard_name}"
59
+
60
+ sig { override.returns(String) }
61
+ def guard_suffix = "_SVH"
62
+
63
+ sig { override.params(text: String).returns(String) }
64
+ def section_comment(text) = "// #{text}"
65
+
66
+ sig { override.params(text_lines: T::Array[String]).returns(T::Array[String]) }
67
+ def header_comment(text_lines)
68
+ text_lines.map { |line| line.empty? ? "//" : "// #{line}" }
69
+ end
70
+
71
+ sig { override.returns(String) }
72
+ def file_type_name = "SystemVerilog header"
73
+
74
+ # Emit sized hex literals so large unsigned values are not misinterpreted
75
+ sig { override.params(value: Integer).returns(String) }
76
+ def format_integer(value)
77
+ bits = [32, value.bit_length].max
78
+ width = ((bits + 31) / 32) * 32
79
+ "#{width}'h#{value.to_s(16).upcase}"
80
+ end
81
+
82
+ sig { override.params(argv: T::Array[String]).returns(T.noreturn) }
83
+ def run(argv)
84
+ run_generator(argv)
85
+ end
86
+ end
87
+ end
@@ -142,7 +142,7 @@ module UdbGen
142
142
  when nil
143
143
  ">=0"
144
144
  when "latest"
145
- "=#{ext.versions.max}"
145
+ "=#{T.must(ext).versions.max}"
146
146
  else
147
147
  "=#{req}"
148
148
  end
@@ -181,7 +181,7 @@ module UdbGen
181
181
  "-a imagesdir=#{params[:images]}",
182
182
  "-r asciidoctor-diagram",
183
183
  "-r idl_highlighter",
184
- "-a wavedrom=/opt/node/node_modules/.bin/wavedrom-cli",
184
+ "-a wavedrom=#{Udb.repo_root}/node_modules/.bin/wavedrom-cli",
185
185
  "-o #{pdf_filename}",
186
186
  adoc_filename
187
187
  ].join(" ")
@@ -15,7 +15,7 @@ module UdbGen
15
15
  # Uses ratification_date year if available, otherwise current year.
16
16
  sig { params(version: Udb::ExtensionVersion).returns(String) }
17
17
  def copyright_year(version)
18
- version.ratification_date.nil? ? Date.today.year.to_s : T.must(version.ratification_date).split("-")[0]
18
+ version.ratification_date.nil? ? Date.today.year.to_s : T.must(T.must(version.ratification_date).split("-")[0])
19
19
  end
20
20
 
21
21
  # Returns the revdate for a version: release date (for nonstandard-released),
@@ -23,9 +23,9 @@ module UdbGen
23
23
  sig { params(version: Udb::ExtensionVersion).returns(T.any(String, Date)) }
24
24
  def revdate(version)
25
25
  if version.state == "nonstandard-released"
26
- version.release_date.nil? ? Date.today : version.release_date
26
+ version.release_date.nil? ? Date.today : T.must(version.release_date)
27
27
  elsif version.state == "ratified"
28
- version.ratification_date.nil? ? Date.today : version.ratification_date
28
+ version.ratification_date.nil? ? Date.today : T.must(version.ratification_date)
29
29
  else
30
30
  Date.today
31
31
  end
@@ -0,0 +1,81 @@
1
+ # SPDX-License-Identifier: BSD-3-Clause-Clear
2
+ # SPDX-FileCopyrightText: Copyright (c) Charlie Jenkins
3
+
4
+ # typed: true
5
+ # frozen_string_literal: true
6
+
7
+ require "tty-exit"
8
+
9
+ require_relative "../../common_opts"
10
+ require_relative "../../defines"
11
+ require_relative "../../template_helpers"
12
+ require_relative "table_builder"
13
+
14
+ require "udb/obj/extension"
15
+
16
+ module UdbGen
17
+ class InstTableOptions < SubcommandWithCommonOptions
18
+ include TTY::Exit
19
+
20
+ NAME = "inst-table"
21
+
22
+ sig { void }
23
+ def initialize
24
+ super(name: NAME, desc: "Generate an instruction table")
25
+ end
26
+
27
+ usage \
28
+ command: NAME,
29
+ desc: "Generate an instruction table for extensions defined in UDB",
30
+ example: <<~EXAMPLE
31
+ Generate an instruction table for all extensions, printed to stdout
32
+ $ #{File.basename($PROGRAM_NAME)} #{NAME}
33
+
34
+ Generate an instruction table for all extensions, written to a file
35
+ $ #{File.basename($PROGRAM_NAME)} #{NAME} -o inst_table.txt
36
+ EXAMPLE
37
+
38
+ option :output_file do
39
+ T.bind(self, TTY::Option::Parameter::Option)
40
+ short "-o"
41
+ long "--out=file"
42
+ desc "Output file (default: stdout)"
43
+ convert :path
44
+ end
45
+
46
+ sig { void }
47
+ def gen_inst_table
48
+ target_file = params[:output_file]
49
+ builder = InstTable::TableBuilder.new(cfg_arch, target_file&.basename&.to_s)
50
+
51
+ if target_file.nil?
52
+ $stdout.write(builder.generate)
53
+ else
54
+ File.write(target_file, builder.generate)
55
+ end
56
+ end
57
+
58
+ sig { override.params(argv: T::Array[String]).returns(T.noreturn) }
59
+ def run(argv)
60
+ parse(argv)
61
+
62
+ if params[:help]
63
+ print help
64
+ exit_with(:success)
65
+ end
66
+
67
+ if params.errors.any?
68
+ exit_with(:usage_error, "#{params.errors.summary}\n\n#{help}")
69
+ end
70
+
71
+ unless params.remaining.empty?
72
+ exit_with(:usage_error, "Unknown arguments: #{params.remaining}\n")
73
+ end
74
+
75
+ gen_inst_table
76
+
77
+ exit_with(:success)
78
+ end
79
+
80
+ end
81
+ end
@@ -0,0 +1,127 @@
1
+ # SPDX-License-Identifier: BSD-3-Clause-Clear
2
+ # SPDX-FileCopyrightText: Copyright (c) Charlie Jenkins
3
+
4
+ # typed: false
5
+ # frozen_string_literal: true
6
+
7
+ require "pathname"
8
+ require "fileutils"
9
+ require "optparse"
10
+ require "udb/resolver"
11
+
12
+ module UdbGen
13
+ module InstTable
14
+ class TableBuilder
15
+ extend T::Sig
16
+
17
+ sig { params(arch: Udb::ConfiguredArchitecture, file_name: T.nilable(String)).void }
18
+ def initialize(arch, file_name)
19
+ @arch = arch
20
+ @file_name = file_name
21
+ end
22
+
23
+ def get_encoding(inst, base)
24
+ return [] unless inst.defined_in_base?(base)
25
+
26
+ fields = []
27
+
28
+ enc_obj = inst.encoding(base)
29
+ fixed = enc_obj.opcode_fields.map do |fo|
30
+ "#{fo.name}<#{fo.range.first}"
31
+ end
32
+
33
+ fields.append(fixed.join("|"))
34
+
35
+ dvs = inst.decode_variables(base)
36
+
37
+ dvs.each do |d|
38
+ field = "#{d.name}"
39
+
40
+ if d.sext?
41
+ field += "~"
42
+ end
43
+
44
+ unless d.excludes.empty?
45
+ field += "!#{d.excludes.join('!')}"
46
+ end
47
+
48
+ if d.left_shift > 0
49
+ field += "<#{d.left_shift}"
50
+ end
51
+
52
+ field += "=#{d.location}"
53
+
54
+ fields.append(field)
55
+ end
56
+
57
+ return fields
58
+ end
59
+
60
+ sig { returns(String) }
61
+ def generate
62
+ lines = []
63
+ @arch.instructions.each do |inst|
64
+ enc_32 = get_encoding(inst, 32)
65
+ enc_64 = get_encoding(inst, 64)
66
+
67
+ if enc_32.empty? && enc_64.empty?
68
+ puts "instruction #{inst.name} not supported by 32-bit or 64-bit"
69
+ next
70
+ end
71
+
72
+ # Same encoding for 32/64-bit
73
+ if (enc_32 == enc_64)
74
+ lines << ([inst.name, "common"] + enc_64).join(" ")
75
+ next
76
+ end
77
+
78
+ # There are some instructions that have different encodings on 32/64-bit
79
+ unless enc_32.empty? || enc_64.empty?
80
+ lines << ([inst.name, "common,32"] + enc_32).join(" ")
81
+ lines << ([inst.name, "common,64"] + enc_64).join(" ")
82
+ next
83
+ end
84
+
85
+ if enc_32.any?
86
+ lines << ([inst.name, "32"] + enc_32).join(" ")
87
+ next
88
+ end
89
+
90
+ if enc_64.any?
91
+ lines << ([inst.name, "64"] + enc_64).join(" ")
92
+ next
93
+ end
94
+ end
95
+
96
+ command = "./bin/generate inst-table"
97
+ command += " -o #{@file_name}" unless @file_name.nil?
98
+
99
+ header = <<EOM
100
+ # SPDX-License-Identifier: BSD-3-Clause-Clear
101
+ #
102
+ # GENERATED WITH https://github.com/riscv-software-src/riscv-unified-db
103
+ # "#{command}"
104
+ #
105
+ # Each line of the instruction table should have the following format:
106
+ # NAME BASE FIXED_BITS [VARIABLE_LIST]
107
+ # NAME instruction name
108
+ # BASE instruction base size (common[,(32|64)])
109
+ # "common" means the instruction is valid on both architecture sizes
110
+ # "32" or "64" means the instruction is valid on that size
111
+ # if the instruction is valid on both architectures but has unique
112
+ # encodings, use a 32-bit entry "common,32" and 64-bit entry
113
+ # FIXED_BITS bitfields of the fixed bits of an instruction concatenated with '|'
114
+ # continuous grouping of fixed bits are in the form of 'bits<offset'
115
+ # VARIABLE_LIST a variable sized list of all variables in the instruction definition
116
+ # in the form of name[~][<num][!num...]=(high[-low])|...
117
+ # symbols after the name represent different modifiers:
118
+ # ~ sign extension, can only appear once
119
+ # < left shift by 'num' amount on extraction, can only appear once
120
+ # ! mark 'num' as an invalid input for this variable
121
+ EOM
122
+
123
+ header + lines.join("\n") + "\n"
124
+ end
125
+ end
126
+ end
127
+ end
@@ -74,7 +74,7 @@ module UdbGen
74
74
  urlPrefix = formatterParams.fetch(:urlPrefix)
75
75
  fp.write ", formatterParams:{\n"
76
76
  fp.write " labelField:\"#{column_name}\",\n"
77
- fp.write " urlPrefix:\"#{urlPrefix}\"\n"
77
+ fp.write " url: function(cell) { return \"#{urlPrefix}\" + cell.getValue().replace(/\\./g, \"_\"); }\n"
78
78
  fp.write " }\n"
79
79
  end
80
80
  fp.write " },\n"
@@ -82,10 +82,11 @@ module UdbGen
82
82
  ext.conflicting_extensions.map(&:name),
83
83
  ext.ratified,
84
84
  if ext.ratified
85
- if ext.min_ratified_version.ratification_date.nil? || ext.min_ratified_version.ratification_date.empty?
85
+ rat_date = T.must(ext.min_ratified_version).ratification_date
86
+ if rat_date.nil? || rat_date.empty?
86
87
  "UDB MISSING"
87
88
  else
88
- ext.min_ratified_version.ratification_date
89
+ rat_date
89
90
  end
90
91
  else
91
92
  ""
@@ -139,7 +140,7 @@ module UdbGen
139
140
  row = [
140
141
  inst.name,
141
142
  inst.long_name,
142
- inst.name + " " + inst.assembly.gsub("x", "r")
143
+ inst.name + " " + inst.assembly
143
144
  ]
144
145
 
145
146
  sorted_releases.each do |pr|
@@ -6,6 +6,14 @@
6
6
 
7
7
  require "digest"
8
8
 
9
+ def sanitized_inst_lookup
10
+ Rake.application.instance_variable_get(:@sanitized_inst_lookup) ||
11
+ Rake.application.instance_variable_set(
12
+ :@sanitized_inst_lookup,
13
+ Rake.application.cfg_arch.instructions.each_with_object({}) { |i, h| h[i.name.sanitize] = i }
14
+ )
15
+ end
16
+
9
17
  def versions_from_env(versions)
10
18
  manual_name = "isa"
11
19
  output_hash = nil
@@ -184,11 +192,11 @@ rule %r{#{Rake.application.gen_dir}/.*/.*/antora/modules/insts/pages/.*.adoc} =>
184
192
  __FILE__,
185
193
  (UdbGen.root / "templates" / "manual" / "instruction.adoc.erb").to_s
186
194
  ] do |t|
187
- inst_name = File.basename(t.name, ".adoc")
195
+ inst_sanitized_name = File.basename(t.name, ".adoc")
188
196
 
189
197
  cfg_arch = Rake.application.cfg_arch
190
- inst = cfg_arch.instruction(inst_name)
191
- raise "Can't find instruction '#{inst_name}'" if inst.nil?
198
+ inst = sanitized_inst_lookup[inst_sanitized_name]
199
+ raise "Can't find instruction with sanitized name '#{inst_sanitized_name}'" if inst.nil?
192
200
 
193
201
  inst_template_path = UdbGen.root / "templates" / "manual" / "instruction.adoc.erb"
194
202
  erb = ERB.new(inst_template_path.read, trim_mode: "-")
@@ -406,7 +414,7 @@ namespace :gen do
406
414
 
407
415
  Udb.logger.info "Generating Instructions"
408
416
  version_obj.instructions.each do |inst|
409
- Rake::Task[antora_path / "modules" / "insts" / "pages" / "#{inst.name}.adoc"].invoke
417
+ Rake::Task[antora_path / "modules" / "insts" / "pages" / "#{inst.name.sanitize}.adoc"].invoke
410
418
  end
411
419
 
412
420
  Udb.logger.info "Generating Extensions"
@@ -457,7 +465,7 @@ namespace :gen do
457
465
  end
458
466
 
459
467
  sh [
460
- "/opt/node/node_modules/.bin/antora",
468
+ "#{Udb.repo_root}/node_modules/.bin/antora",
461
469
  "--stacktrace",
462
470
  "generate",
463
471
  "--cache-dir=#{Udb.repo_root}/.home/.antora",
@@ -27,7 +27,7 @@ module UdbGen
27
27
  erb.result(context.instance_eval { binding })
28
28
  end
29
29
 
30
- LinkableObj = T.type_alias { T.any(Udb::Instruction, Udb::Csr, Udb::CsrField, Udb::Extension, Idl::FunctionDefAst) }
30
+ LinkableObj = T.type_alias { T.any(Udb::Instruction, Udb::Csr, Udb::CsrField, Udb::Extension, Udb::Parameter, Idl::FunctionDefAst) }
31
31
 
32
32
  # return an asciidoc link to obj, with text "text"
33
33
  sig { params(obj: LinkableObj, text: String).returns(String) }
@@ -56,6 +56,8 @@ module UdbGen
56
56
  "udb-extension-#{obj.name.gsub(".", "_")}"
57
57
  when Udb::Instruction
58
58
  "udb-insn-#{obj.name.gsub(".", "_")}"
59
+ when Udb::Parameter
60
+ "udb-param-#{obj.name.gsub(".", "_")}"
59
61
  else
60
62
  T.absurd(obj)
61
63
  end
@@ -64,17 +66,17 @@ module UdbGen
64
66
  sig { params(cfg_arch: Udb::ConfiguredArchitecture, adoc: String).returns(String) }
65
67
  def convert_monospace_to_links(cfg_arch, adoc)
66
68
  adoc.gsub(/`([\w.]+)`/) do |match|
67
- name = Regexp.last_match(1)
68
- csr_name, field_name = T.must(name).split(".")
69
+ name = T.must(Regexp.last_match(1))
70
+ csr_name, field_name = name.split(".")
69
71
  csr = cfg_arch.not_prohibited_csrs.find { |c| c.name == csr_name }
70
72
  if !field_name.nil? && !csr.nil? && csr.field?(field_name)
71
73
  link_to(csr.field(field_name), match)
72
74
  elsif !csr.nil?
73
75
  link_to(csr, match)
74
76
  elsif cfg_arch.not_prohibited_instructions.any? { |inst| inst.name == name }
75
- link_to(cfg_arch.instruction(name), match)
77
+ link_to(T.must(cfg_arch.instruction(name)), match)
76
78
  elsif cfg_arch.not_prohibited_extensions.any? { |ext| ext.name == name }
77
- link_to(cfg_arch.extension(name), match)
79
+ link_to(T.must(cfg_arch.extension(name)), match)
78
80
  else
79
81
  match
80
82
  end
@@ -92,7 +94,7 @@ module UdbGen
92
94
  when "ext"
93
95
  ext = cfg_arch.extension(name)
94
96
  if ext
95
- link_to(cfg_arch.extension(name), link_text)
97
+ link_to(ext, link_text)
96
98
  else
97
99
  Udb.logger.warn "Attempted link to undefined extension: #{name}"
98
100
  match
@@ -116,14 +118,14 @@ module UdbGen
116
118
  when "csr"
117
119
  csr = cfg_arch.csr(name)
118
120
  if csr
119
- link_to(cfg_arch.csr(name), link_text)
121
+ link_to(csr, link_text)
120
122
  else
121
123
  Udb.logger.warn "Attempted link to undefined CSR: #{name}"
122
124
  match
123
125
  end
124
126
  when "csr_field"
125
127
  csr_name, field_name = name.split("*")
126
- csr = cfg_arch.csr(csr_name)
128
+ csr = cfg_arch.csr(T.must(csr_name))
127
129
  if csr
128
130
  csr_field = csr.field(field_name)
129
131
  if csr_field
@@ -5,5 +5,5 @@
5
5
  # frozen_string_literal: true
6
6
 
7
7
  module UdbGen
8
- def self.version = "0.1.7"
8
+ def self.version = "0.1.13"
9
9
  end
@@ -36,7 +36,7 @@ h^ Privilege Mode ^ <%= csr.priv_mode %>
36
36
  .<%= csr.name %> format
37
37
  [wavedrom, ,svg,subs='attributes',width="100%"]
38
38
  ....
39
- <%= JSON.dump csr.wavedrom_desc(cfg_arch, 64) %>
39
+ <%= JSON.dump csr.wavedrom_desc(cfg_arch, csr.base || 64) %>
40
40
  ....
41
41
  <%- else -%>
42
42
  <%# CSR has a dynamic length, or a field has a dynamic location,
@@ -11,7 +11,7 @@ Synopsis::
11
11
 
12
12
  Mnemonic::
13
13
  ----
14
- <%= inst.name %> <%= inst.assembly.gsub('x', 'r') %>
14
+ <%= inst.name %> <%= inst.assembly %>
15
15
  ----
16
16
 
17
17
  Encoding::
@@ -27,7 +27,7 @@ The following <%= insts.size %> instructions must be implemented when <%= ext_re
27
27
  <%- insts.each do |i| -%>
28
28
  | <%= i.rv32? ? "&#x2713;" : "" %>
29
29
  | <%= i.rv64? ? "&#x2713;" : "" %>
30
- | `<%= [i.name, i.assembly.gsub("x", "r").strip].join(" ").strip %>`
30
+ | `<%= [i.name, i.assembly.strip].join(" ").strip %>`
31
31
  | <%= link_to(i, i.long_name) %>
32
32
  <%- if versions.size > 1 -%>
33
33
  | <%= versions.map { |v| (-i.defined_by_condition & v.to_condition_exclusive).unsatisfiable_by_arch?(cfg_arch) ? "&#x2713;" : "" }.join(" | ") %>
@@ -55,7 +55,7 @@ of this extension.
55
55
  ^ Mnemonic ^ Instruction ^ Defined by
56
56
 
57
57
  <%- implied_insts.sort.each do |i| -%>
58
- ^ `<%= [i.name, i.assembly.gsub("x", "r").strip].join(" ").strip %>`
58
+ ^ `<%= [i.name, i.assembly.strip].join(" ").strip %>`
59
59
  ^ <%= link_to(i, i.long_name) %>
60
60
  a^ <%= i.defined_by_condition.to_asciidoc %>
61
61
  <%- end -%>
@@ -17,7 +17,7 @@ This instruction must have data-independent timing when extension `Zkt` is enabl
17
17
 
18
18
  == Assembly format
19
19
 
20
- `<%= inst.name %> <%= inst.assembly.gsub('x', 'r') %>`
20
+ `<%= inst.name %> <%= inst.assembly %>`
21
21
 
22
22
  == Decode Variables
23
23
 
@@ -29,9 +29,9 @@ ui:
29
29
  snapshot: true
30
30
  supplemental_files:
31
31
  - path: css/vendor/tabs.css
32
- contents: /opt/node/node_modules/@asciidoctor/tabs/dist/css/tabs.css
32
+ contents: <%= Udb.repo_root %>/node_modules/@asciidoctor/tabs/dist/css/tabs.css
33
33
  - path: js/vendor/tabs.js
34
- contents: /opt/node/node_modules/@asciidoctor/tabs/dist/js/tabs.js
34
+ contents: <%= Udb.repo_root %>/node_modules/@asciidoctor/tabs/dist/js/tabs.js
35
35
  - path: partials/footer-scripts.hbs
36
36
  contents: |
37
37
  <script id="site-script" src="{{{uiRootPath}}}/js/site.js" data-ui-root-path="{{{uiRootPath}}}"></script>
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: udb-gen
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.7
4
+ version: 0.1.13
5
5
  platform: ruby
6
6
  authors:
7
7
  - Derek Hower
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2026-04-11 00:00:00.000000000 Z
10
+ date: 2026-07-18 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: asciidoctor
@@ -138,6 +137,20 @@ dependencies:
138
137
  version: '0'
139
138
  - !ruby/object:Gem::Dependency
140
139
  name: udb
140
+ requirement: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - '='
143
+ - !ruby/object:Gem::Version
144
+ version: 0.1.14
145
+ type: :runtime
146
+ prerelease: false
147
+ version_requirements: !ruby/object:Gem::Requirement
148
+ requirements:
149
+ - - '='
150
+ - !ruby/object:Gem::Version
151
+ version: 0.1.14
152
+ - !ruby/object:Gem::Dependency
153
+ name: write_xlsx
141
154
  requirement: !ruby/object:Gem::Requirement
142
155
  requirements:
143
156
  - - ">="
@@ -151,13 +164,13 @@ dependencies:
151
164
  - !ruby/object:Gem::Version
152
165
  version: '0'
153
166
  - !ruby/object:Gem::Dependency
154
- name: write_xlsx
167
+ name: mocha
155
168
  requirement: !ruby/object:Gem::Requirement
156
169
  requirements:
157
170
  - - ">="
158
171
  - !ruby/object:Gem::Version
159
172
  version: '0'
160
- type: :runtime
173
+ type: :development
161
174
  prerelease: false
162
175
  version_requirements: !ruby/object:Gem::Requirement
163
176
  requirements:
@@ -206,10 +219,15 @@ files:
206
219
  - bin/udb-gen
207
220
  - lib/udb-gen.rb
208
221
  - lib/udb-gen/adoc_helpers.rb
222
+ - lib/udb-gen/cfg_header_base.rb
209
223
  - lib/udb-gen/common_opts.rb
210
224
  - lib/udb-gen/defines.rb
225
+ - lib/udb-gen/generators/cfg_c_header/generator.rb
226
+ - lib/udb-gen/generators/cfg_svh_header/generator.rb
211
227
  - lib/udb-gen/generators/ext_doc/generator.rb
212
228
  - lib/udb-gen/generators/ext_doc/helpers.rb
229
+ - lib/udb-gen/generators/inst_table/generator.rb
230
+ - lib/udb-gen/generators/inst_table/table_builder.rb
213
231
  - lib/udb-gen/generators/isa_explorer/generator.rb
214
232
  - lib/udb-gen/generators/isa_explorer/js_xlsx_writer.rb
215
233
  - lib/udb-gen/generators/isa_explorer/table_builder.rb
@@ -248,7 +266,6 @@ metadata:
248
266
  homepage_uri: https://github.com/riscv/riscv-unified-db
249
267
  mailing_list_uri: https://lists.riscv.org/g/tech-unifieddb
250
268
  bug_tracker_uri: https://github.com/riscv/riscv-unified-db/issues
251
- post_install_message:
252
269
  rdoc_options: []
253
270
  require_paths:
254
271
  - lib
@@ -263,8 +280,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
263
280
  - !ruby/object:Gem::Version
264
281
  version: '0'
265
282
  requirements: []
266
- rubygems_version: 3.4.19
267
- signing_key:
283
+ rubygems_version: 3.6.9
268
284
  specification_version: 4
269
285
  summary: Command line interface for UDB-based generators
270
286
  test_files: []