Class: Udb::Instruction

Inherits:
TopLevelDatabaseObject show all
Includes:
Helpers::WavedromUtil, CertifiableObject
Defined in:
lib/udb/obj/instruction.rb

Overview

model of a specific instruction in a specific base (RV32/RV64)

Defined Under Namespace

Classes: DecodeVariable, Encoding, EncodingField, MemoizedState, Opcode

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(data, data_path, arch)

Parameters:



122
123
124
125
# File 'lib/udb/obj/instruction.rb', line 122

def initialize(data, data_path, arch)
  super(data, data_path, arch)
  @memo = MemoizedState.new
end

Class Method Details

.ary_from_location(location_str_or_int) ⇒ Object



262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
# File 'lib/udb/obj/instruction.rb', line 262

def self.ary_from_location(location_str_or_int)
  return [location_str_or_int] if location_str_or_int.is_a?(Integer)

  bits = []
  parts = location_str_or_int.split("|")
  parts.each do |part|
    if part.include?("-")
      msb, lsb = part.split("-").map(&:to_i)
      (lsb..msb).each { |i| bits << i }
    else
      bits << part.to_i
    end
  end
  bits
end

.deprecated_validate_encoding(encoding, inst_name) ⇒ Object



299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
# File 'lib/udb/obj/instruction.rb', line 299

def self.deprecated_validate_encoding(encoding, inst_name)
  match = encoding["match"]
  raise "No match for instruction #{inst_name}?" if match.nil?

  variables = encoding.key?("variables") ? encoding["variables"] : []
  match.size.times do |i|
    if match[match.size - 1 - i] == "-"
      # make sure exactly one variable covers this bit
      vars_match = variables.count { |variable| ary_from_location(variable["location"]).include?(i) }
      if vars_match.zero?
        raise ValidationError, "In instruction #{inst_name}, no variable or encoding bit covers bit #{i}"
      elsif vars_match != 1
        raise ValidationError, "In instruction, #{inst_name}, bit #{i} is covered by more than one variable"
      end
    else
      # make sure no variable covers this bit
      unless variables.nil?
        unless variables.none? { |variable| ary_from_location(variable["location"]).include?(i) }
          raise ValidationError, "In instruction, #{inst_name}, bit #{i} is covered by both a variable and the match string"
        end
      end
    end
  end
end

.validate_encoding(inst, base)

This method returns an undefined value.

Parameters:



279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
# File 'lib/udb/obj/instruction.rb', line 279

def self.validate_encoding(inst, base)
  # make sure there is no overlap between variables/opcodes
  (inst.opcodes(base) + inst.decode_variables(base)).combination(2) do |field1, field2|
    raise "In instruction #{inst.name}, #{field1.name} and #{field2.name} overlap" if field1.overlaps?(field2)
  end

  # makes sure every bit is accounted for
  inst.type(base).length.times do |i|
    covered =
      inst.opcodes(base).any? { |opcode| opcode.range.cover?(i) } || \
      inst.decode_variables(base).any? { |var| var.location_bits.include?(i) }
    raise "In instruction #{inst.name}, there is no opcode or variable at bit #{i}" unless covered
  end

  # make sure opcode values fit
  inst.opcodes(base).each do |opcode|
    raise "In instruction #{inst.name}, opcode #{opcode.name}, value #{opcode.value} does not fit in #{opcode.range}" unless T.must(opcode.range.size) >= opcode.value.bit_length
  end
end

Instance Method Details

#<=>(other) ⇒ Object



371
372
373
374
375
376
377
# File 'lib/udb/obj/instruction.rb', line 371

def <=>(other)
  if other.is_a?(Instruction)
    name <=> other.name
  else
    nil
  end
end

#==(other) ⇒ Object



363
364
365
366
367
368
369
# File 'lib/udb/obj/instruction.rb', line 363

def ==(other)
  if other.is_a?(Instruction)
    name == other.name
  else
    raise ArgumentError, "Instruction is not comparable to a #{other.class.name}"
  end
end

#accessHash<String, String>

Returns Hash of access permissions for each mode. The key is the lowercase name of a privilege mode, and the value is one of [‘never’, ‘sometimes’, ‘always’].

Returns:

  • (Hash<String, String>)

    Hash of access permissions for each mode. The key is the lowercase name of a privilege mode, and the value is one of [‘never’, ‘sometimes’, ‘always’]



380
381
382
# File 'lib/udb/obj/instruction.rb', line 380

def access
  @data["access"]
end

#access_detailString?

Returns:

  • (String)

    Details of the access restrictions

  • (nil)

    if no details are available



386
387
388
# File 'lib/udb/obj/instruction.rb', line 386

def access_detail
  @data["access_detail"]
end

#access_detail?Boolean

Returns true if the instruction has an ‘access_detail’ field.

Returns:

  • (Boolean)

    true if the instruction has an ‘access_detail’ field



1148
1149
1150
# File 'lib/udb/obj/instruction.rb', line 1148

def access_detail?
  @data.key?("access_detail")
end

#assemblyString

Returns Assembly format.

Returns:

  • (String)

    Assembly format



414
415
416
# File 'lib/udb/obj/instruction.rb', line 414

def assembly
  @data["assembly"]
end

#bad_encoding_conflict?(xlen, other_inst) ⇒ Boolean

Returns true if self and other_inst have indistinguishable encodings and can be simultaneously implemented in some design.

Returns:

  • (Boolean)

    true if self and other_inst have indistinguishable encodings and can be simultaneously implemented in some design



1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
# File 'lib/udb/obj/instruction.rb', line 1030

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))

  puts "XXXXXXXXXXXXXXXXXXXXXX   #{name} and #{other_inst.name} are indistinguishable"

  # 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

#baseInteger?

Returns:

  • (Integer, nil)


391
392
393
394
395
396
397
398
399
400
401
402
# File 'lib/udb/obj/instruction.rb', line 391

def base
  return @base if defined?(@base)

  @base =
    if defined_by_condition.rv32_only?
      32
    elsif defined_by_condition.rv64_only?
      64
    else
      nil
    end
end

#cert_coverage_point(id) ⇒ CertNormativeRule? Originally defined in module CertifiableObject

Parameters:

  • id (String)

    Unique ID for the normative rule

Returns:

  • (CertNormativeRule)
  • (nil)

    if there is no certification normative ruleed with ID of id

#cert_coverage_point_hashHash<String, CertNormativeRule> Originally defined in module CertifiableObject

Returns Hash with ID as key of all normative rules defined by database object.

Returns:

  • (Hash<String, CertNormativeRule>)

    Hash with ID as key of all normative rules defined by database object

#cert_normative_rulesArray<CertNormativeRule> Originally defined in module CertifiableObject

Returns:

#cert_test_procedure(id) ⇒ CertTestProcedure? Originally defined in module CertifiableObject

Parameters:

  • id (String)

    Unique ID for test procedure

Returns:

  • (CertTestProcedure)
  • (nil)

    if there is no certification test procedure with ID id

#cert_test_procedure_hashHash<String, CertTestProcedure> Originally defined in module CertifiableObject

Returns Hash of all normative rules defined by database object.

Returns:

  • (Hash<String, CertTestProcedure>)

    Hash of all normative rules defined by database object

#cert_test_proceduresArray<CertTestProcedure> Originally defined in module CertifiableObject

Returns:

#conflicting_instructions(xlen) ⇒ Array<Instruction>

Returns List of instructions that reuse this instruction’s encoding, but can’t be present in the same system because their defining extensions conflict.

Returns:

  • (Array<Instruction>)

    List of instructions that reuse this instruction’s encoding, but can’t be present in the same system because their defining extensions conflict



1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
# File 'lib/udb/obj/instruction.rb', line 1046

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

#data_independent_timing?Boolean

Returns Whether or not the instruction must have data-independent timing when Zkt is enabled.

Returns:

  • (Boolean)

    Whether or not the instruction must have data-independent timing when Zkt is enabled.



405
# File 'lib/udb/obj/instruction.rb', line 405

def data_independent_timing? = @data["data_independent_timing"]

#decode_variables(base) ⇒ Array<DecodeVariable>

Returns The decode variables.

Returns:



1143
1144
1145
# File 'lib/udb/obj/instruction.rb', line 1143

def decode_variables(base)
  encoding(base).decode_variables
end

#defined_in_base?(xlen) ⇒ Boolean

Returns whethen or not instruction is defined in base xlen.

Parameters:

  • xlen (Integer)

    32 or 64, the target xlen

Returns:

  • (Boolean)

    whethen or not instruction is defined in base xlen



409
410
411
# File 'lib/udb/obj/instruction.rb', line 409

def defined_in_base?(xlen)
  base.nil? || (base == xlen)
end

#encoding(base) ⇒ Encoding

Returns the encoding.

Parameters:

  • base (Integer)

    32 or 64

Returns:



1111
1112
1113
1114
1115
1116
1117
# File 'lib/udb/obj/instruction.rb', line 1111

def encoding(base)
  raise "#{name} is not defined in #{base}" unless defined_in_base?(base)

  load_encoding if @encodings.nil?

  @encodings[base]
end

#encoding_format(base) ⇒ String

Returns format, as a string of 0,1 and -,.

Examples:

Format of ‘sd`

sd.format #=> '-----------------011-----0100011'

Parameters:

  • base (Integer)

Returns:

  • (String)

    format, as a string of 0,1 and -,

Raises:

  • (ArgumentError)


228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
# File 'lib/udb/obj/instruction.rb', line 228

def encoding_format(base)
  raise ArgumentError, "base must be 32 or 64" unless [32, 64].include?(base)

  if has_type?
    mask = "-" * type(base).length

    opcodes(base).each do |opcode|
      mask[type(base).length - opcode.range.end - 1, opcode.range.size] = opcode.value.to_s(2).rjust(T.must(opcode.range.size), "0")
    end

    mask
  else
    @encoding_format ||=
      if @data["encoding"].key?("RV32")
        {
          32 => @data["encoding"]["RV32"]["match"],
          64 => @data["encoding"]["RV64"]["match"]
        }
      else
        {
          32 => @data["encoding"]["match"],
          64 => @data["encoding"]["match"]
        }
      end
    @encoding_format[base]
  end
end

#encoding_widthInteger

Returns the width of the encoding.

Returns:

  • (Integer)

    the width of the encoding



1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
# File 'lib/udb/obj/instruction.rb', line 1121

def encoding_width
  if defined_in_base?(32) && defined_in_base?(64)
    raise "unexpected: encodings are different sizes" unless encoding(32).size == encoding(64).size

    encoding(64).size
  elsif defined_in_base?(32)
    encoding(32).size
  else
    raise "unexpected" unless defined_in_base?(64)

    encoding(64).size
  end

end

#eql?(other) ⇒ Boolean

Returns:

  • (Boolean)


127
128
129
130
131
# File 'lib/udb/obj/instruction.rb', line 127

def eql?(other)
  return nil unless other.is_a?(Instruction)

  @name.eql?(other.name)
end

#exists_in_cfg?(cfg_arch) ⇒ Boolean

Returns whether or not the instruction is implemented given the supplied config options.

Parameters:

Returns:

  • (Boolean)

    whether or not the instruction is implemented given the supplied config options



1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
# File 'lib/udb/obj/instruction.rb', line 1187

def exists_in_cfg?(cfg_arch)
  if cfg_arch.fully_configured?
    (base.nil? || (cfg_arch.possible_xlens.include? base)) &&
      (defined_by_condition.satisfied_by_cfg_arch?(cfg_arch) == SatisfiedResult::Yes)
  else
    raise "unexpected cfg_arch type" unless cfg_arch.partially_configured?

    (base.nil? || (cfg_arch.possible_xlens.include? base)) &&
      (defined_by_condition.satisfied_by_cfg_arch?(cfg_arch) != SatisfiedResult::No)
  end
end

#fill_symtab(effective_xlen, ast) ⇒ Object



418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
# File 'lib/udb/obj/instruction.rb', line 418

def fill_symtab(effective_xlen, ast)
  symtab = cfg_arch.symtab.global_clone
  symtab.push(ast)
  symtab.add(
    "__instruction_encoding_size",
    Idl::Var.new("__instruction_encoding_size", Idl::Type.new(:bits, width: encoding_width.bit_length), encoding_width)
  )
  symtab.add(
    "__effective_xlen",
    Idl::Var.new("__effective_xlen", Idl::Type.new(:bits, width: 7), effective_xlen)
  )
  encoding(effective_xlen).decode_variables.each do |d|
    qualifiers = [:const]
    qualifiers << :signed if d.sext?
    width = d.size

    var = Idl::Var.new(d.name, Idl::Type.new(:bits, qualifiers:, width:), decode_var: true)
    symtab.add(d.name, var)
  end

  symtab
end

#has_type?Boolean

Returns:

  • (Boolean)


134
# File 'lib/udb/obj/instruction.rb', line 134

def has_type? = @data.key?("format")

#hintsArray<Instruction>

Returns List of HINTs based on this instruction encoding.

Returns:

  • (Array<Instruction>)

    List of HINTs based on this instruction encoding



1181
1182
1183
# File 'lib/udb/obj/instruction.rb', line 1181

def hints
  @hints ||= @data.key?("hints") ? @data["hints"].map { |ref| @cfg_arch.ref(ref["$ref"]) } : []
end

#mask_to_array(int) ⇒ Object



496
497
498
499
500
501
502
503
504
505
506
507
# File 'lib/udb/obj/instruction.rb', line 496

def mask_to_array(int)
  elems = []
  idx = 0
  while int != 0
    if (int & (1 << idx)) != 0
      elems << idx
    end
    int &= ~(1 << idx)
    idx += 1
  end
  elems
end

#max_encoding_widthInteger

Returns the largest encoding width of the instruction, in any XLEN for which this instruction is valid.

Returns:

  • (Integer)

    the largest encoding width of the instruction, in any XLEN for which this instruction is valid



1138
1139
1140
# File 'lib/udb/obj/instruction.rb', line 1138

def max_encoding_width
  [(rv32? ? encoding(32).size : 0), (rv64? ? encoding(64).size : 0)].max
end

#multi_encoding?Boolean

Returns whether or not this instruction has different encodings depending on XLEN.

Returns:

  • (Boolean)

    whether or not this instruction has different encodings depending on XLEN



1021
1022
1023
1024
1025
1026
1027
# File 'lib/udb/obj/instruction.rb', line 1021

def multi_encoding?
  if has_type?
    @data["format"].key?("RV32")
  else
    @data.key?("encoding") && @data["encoding"].key?("RV32")
  end
end

#opcodes(base) ⇒ Array<Opcode>

Parameters:

  • base (Integer)

Returns:



194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
# File 'lib/udb/obj/instruction.rb', line 194

def opcodes(base)
  raise "Instruction #{name} is not defined in base RV#{base}" unless defined_in_base?(base)

  @opcodes ||= {}

  return @opcodes[base] unless @opcodes[base].nil?

  @opcodes[base] = @data["format"]["opcodes"].map do |opcode_name, opcode_data|
    next if opcode_name[0] == "$"

    raise "unexpected: opcode field is not contiguous" if opcode_data["location"].include?("|")

    loc = opcode_data["location"]
    range =
      if loc =~ /^([0-9]+)$/
        bit = ::Regexp.last_match(1)
        bit.to_i..bit.to_i
      elsif loc =~ /^([0-9]+)-([0-9]+)$/
        msb = ::Regexp.last_match(1)
        lsb = ::Regexp.last_match(2)
        raise "range must be specified 'msb-lsb'" unless msb.to_i >= lsb.to_i

        lsb.to_i..msb.to_i
      else
        raise "location format error"
      end
    Opcode.new(opcode_name, range, opcode_data["value"])
  end.reject(&:nil?)
end

#operation_astFunctionBodyAst

Returns The abstract syntax tree of the instruction operation.

Returns:

  • (FunctionBodyAst)

    The abstract syntax tree of the instruction operation



1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
# File 'lib/udb/obj/instruction.rb', line 1090

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 #{ast.class}" unless ast.is_a?(Idl::FunctionBodyAst)

    ast
  end
end

#other_requirements(expand: false) ⇒ Array<Condition>

definedBy requirements that are left if you take out all the unconditional extension requirements

Parameters:

  • expand (Boolean) (defaults to: false)

Returns:



1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
# File 'lib/udb/obj/instruction.rb', line 1239

def other_requirements(expand: false)
  # remove all the unconditional extension requirements
  cb = LogicNode.make_replace_cb do |node|
    next node unless node.type == LogicNodeType::Term
    rterm = node.children.fetch(0)
    next node unless rterm.is_a?(ExtensionTerm)

    # remove terms unconditionally true or false
    next LogicNode::True if unconditional_extension_requirements(expand: true).any? { |ext_req| ext_req.satisfied_by?(rterm.to_ext_req(@arch)) }
    # next LogicNode::False if unconditional_extension_conflicts(expand: true).any? { |ext_req| ext_req.satisfied_by?(rterm.to_ext_req(@arch)) }

    node
  end

  # remaining_requirements is the remainder of definedBy that is left if you remove unconditional
  # requirements
  remaining_requirements =
    defined_by_condition.to_logic_tree(expand:).replace_terms(cb).minimize(LogicNode::CanonicalizationType::SumOfProducts)

  t = remaining_requirements.type
  case t
  when LogicNodeType::True
    []
  when LogicNodeType::Or
    remaining_requirements.node_children.map { |child| LogicCondition.new(child, cfg_arch) }
  when LogicNodeType::And
    [LogicCondition.new(remaining_requirements.node_children.fetch(0), cfg_arch)]
  when LogicNodeType::Term, LogicNodeType::Not
    [LogicCondition.new(remaining_requirements, cfg_arch)]
  else
    raise "unexpected: #{t}"
  end
end

#processed_wavedrom_desc(base) ⇒ Object



256
257
258
259
260
# File 'lib/udb/obj/instruction.rb', line 256

def processed_wavedrom_desc(base)
  data = wavedrom_desc(base)
  processed_data = process_wavedrom(data)
  fix_entities(json_dump_with_hex_literals(processed_data))
end

#profiles_mandating_instArray<Profile>

return a list of profiles that mandate that this instruction be implemented

Returns:



1275
1276
1277
1278
1279
1280
1281
1282
# File 'lib/udb/obj/instruction.rb', line 1275

def profiles_mandating_inst
  @profiles_mandating_inst ||=
    cfg_arch.profiles.select do |profile|
      profile.mandatory_ext_reqs.any? do |ext_req|
        defined_by_condition.satisfiability_depends_on_ext_req?(ext_req.ext_req)
      end
    end
end

#profiles_optioning_instArray<Profile>

return a list of profiles in which this instruction is explicitly optional

Returns:



1286
1287
1288
1289
1290
1291
1292
1293
# File 'lib/udb/obj/instruction.rb', line 1286

def profiles_optioning_inst
  @profiles_optioning_inst ||=
    cfg_arch.profiles.select do |profile|
      profile.optional_ext_reqs.any? do |ext_req|
        defined_by_condition.satisfiability_depends_on_ext_req?(ext_req.ext_req)
      end
    end
end

#pruned_operation_ast(effective_xlen) ⇒ Idl::FunctionBodyAst

Returns A pruned abstract syntax tree.

Parameters:

  • global_symtab (Idl::SymbolTable)

    Symbol table with global scope populated and a configuration loaded

Returns:

  • (Idl::FunctionBodyAst)

    A pruned abstract syntax tree



443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
# File 'lib/udb/obj/instruction.rb', line 443

def pruned_operation_ast(effective_xlen)
  @pruned_operation_ast ||= {}
  @pruned_operation_ast[effective_xlen] ||=
    begin
      if @data.key?("operation()")

        type_checked_ast = type_checked_operation_ast(effective_xlen)
        symtab = fill_symtab(effective_xlen, type_checked_ast)
        pruned_ast = type_checked_ast.prune(symtab)
        pruned_ast.freeze_tree(symtab)

        symtab.release
        pruned_ast
      end
    end
end

#reachable_exceptions(effective_xlen) ⇒ Integer

Returns Mask of all exceptions that can be reached from operation().

Parameters:

  • symtab (Idl::SymbolTable)

    Symbol table with global scope populated

  • effective_xlen (Integer)

    Effective XLEN to evaluate against

Returns:

  • (Integer)

    Mask of all exceptions that can be reached from operation()



483
484
485
486
487
488
489
490
491
492
493
494
# File 'lib/udb/obj/instruction.rb', line 483

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(effective_xlen)
    symtab = fill_symtab(effective_xlen, type_checked_ast)
    type_checked_ast.reachable_exceptions(symtab)
    symtab.release
  end
end

#reachable_exceptions_str(effective_xlen = nil) ⇒ Array<Integer>

Returns List of all exceptions that can be reached from operation().

Parameters:

  • effective_xlen (Integer) (defaults to: nil)

    Effective XLEN to evaluate against. If nil, evaluate against all valid XLENs

Returns:

  • (Array<Integer>)

    List of all exceptions that can be reached from operation()

Raises:

  • (ArgumentError)


511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
# File 'lib/udb/obj/instruction.rb', line 511

def reachable_exceptions_str(effective_xlen = nil)
  raise ArgumentError, "effective_xlen is a #{effective_xlen.class} but must be an Integer or nil" unless effective_xlen.nil? || effective_xlen.is_a?(Integer)

  if @data["operation()"].nil?
    []
  else
    symtab = cfg_arch.symtab
    etype = symtab.get("ExceptionCode")
    if effective_xlen.nil?
      if cfg_arch.multi_xlen?
        if base.nil?
          (
            pruned_ast = pruned_operation_ast(32)
            symtab = fill_symtab(32, pruned_ast)
            e32 = mask_to_array(pruned_ast.reachable_exceptions(symtab)).map { |code|
              etype.element_name(code)
            }
            symtab.release
            pruned_ast = pruned_operation_ast(64)
            symtab = fill_symtab(64, pruned_ast)
            e64 = mask_to_array(pruned_ast.reachable_exceptions(symtab)).map { |code|
              etype.element_name(code)
            }
            symtab.release
            e32 + e64
          ).uniq
        else
          pruned_ast = pruned_operation_ast(base)
          symtab = fill_symtab(base, pruned_ast)
          e = mask_to_array(pruned_ast.reachable_exceptions(symtab)).map { |code|
            etype.element_name(code)
          }
          symtab.release
          e
        end
      else
        effective_xlen = cfg_arch.mxlen
        pruned_ast = pruned_operation_ast(effective_xlen)
        symtab = fill_symtab(effective_xlen, pruned_ast)
        e = mask_to_array(pruned_ast.reachable_exceptions(symtab)).map { |code|
          etype.element_name(code)
        }
        symtab.release
        e
      end
    else
      pruned_ast = pruned_operation_ast(effective_xlen)

      symtab = fill_symtab(effective_xlen, pruned_ast)
      e = mask_to_array(pruned_ast.reachable_exceptions(symtab)).map { |code|
        etype.element_name(code)
      }
      symtab.release
      e
    end
  end
end

#reachable_functions(effective_xlen) ⇒ Array<Idl::FunctionDefAst>

Returns List of all functions that can be reached from operation().

Parameters:

  • symtab (Idl::SymbolTable)

    Symbol table with global scope populated

  • effective_xlen (Integer)

    The effective XLEN to evaluate against

Returns:

  • (Array<Idl::FunctionDefAst>)

    List of all functions that can be reached from operation()



464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
# File 'lib/udb/obj/instruction.rb', line 464

def reachable_functions(effective_xlen)
  if @data["operation()"].nil?
    []
  else
    @memo.reachable_functions ||= T.let({}, T::Hash[Integer, Idl::FunctionDefAst])
    @memo.reachable_functions[effective_xlen] ||=
      begin
        ast = operation_ast
        symtab = fill_symtab(effective_xlen, ast)
        fns = ast.reachable_functions(symtab)
        symtab.release
        fns
      end
  end
end

#rv32?Boolean

Returns whether or not this instruction is defined for RV32.

Returns:

  • (Boolean)

    whether or not this instruction is defined for RV32



1171
1172
1173
# File 'lib/udb/obj/instruction.rb', line 1171

def rv32?
  base != 64
end

#rv64?Boolean

Returns whether or not this instruction is defined for RV64.

Returns:

  • (Boolean)

    whether or not this instruction is defined for RV64



1176
1177
1178
# File 'lib/udb/obj/instruction.rb', line 1176

def rv64?
  base != 32
end

#subtype(base) ⇒ InstructionSubtype

Parameters:

  • base (Integer)

Returns:



156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
# File 'lib/udb/obj/instruction.rb', line 156

def subtype(base)
  @subtype ||= {
    32 =>
      if @data["format"].key?("RV32")
        @arch.ref(@data["format"]["RV32"]["subtype"]["$ref"])
      else
        @arch.ref(@data["format"]["subtype"]["$ref"])
      end,
    64 =>
      if @data["format"].key?("RV64")
        @arch.ref(@data["format"]["RV64"]["subtype"]["$ref"])
      else
        @arch.ref(@data["format"]["subtype"]["$ref"])
      end
  }
  @subtype[base]
end

#type(base) ⇒ InstructionType

Parameters:

  • base (Integer)

Returns:



137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# File 'lib/udb/obj/instruction.rb', line 137

def type(base)
  @type ||= {
    32 =>
      if @data["format"].key?("RV32")
        @arch.ref(@data["format"]["RV32"]["type"]["$ref"])
      else
        @arch.ref(@data["format"]["type"]["$ref"])
      end,
    64 =>
      if @data["format"].key?("RV64")
        @arch.ref(@data["format"]["RV64"]["type"]["$ref"])
      else
        @arch.ref(@data["format"]["type"]["$ref"])
      end
  }
  @type[base]
end

#type_checked_operation_ast(effective_xlen) ⇒ FunctionBodyAst

Returns A type-checked abstract syntax tree of the operation.

Parameters:

  • effective_xlen (Integer)

    32 or 64, the effective xlen to type check against

Returns:

  • (FunctionBodyAst)

    A type-checked abstract syntax tree of the operation



1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
# File 'lib/udb/obj/instruction.rb', line 1074

def type_checked_operation_ast(effective_xlen)
  defer :type_checked_operation_ast do
    return nil unless @data.key?("operation()")

    ast = operation_ast

    symtab = fill_symtab(effective_xlen, ast)
    ast.freeze_tree(symtab)
    cfg_arch.idl_compiler.type_check(ast, symtab, "#{name}.operation()")
    symtab.release

    ast
  end
end

#unconditional_extension_conflicts(expand: false) ⇒ Array<ExtensionRequirement>

returns list of extension requirements that cannot be met for this instruction to be defined

if expand is true, expand the definedBy condition to also include transitive requirements

Parameters:

  • expand (Boolean) (defaults to: false)

Returns:



1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
# File 'lib/udb/obj/instruction.rb', line 1223

def unconditional_extension_conflicts(expand: false)
  ext_reqs = defined_by_condition.ext_req_terms(expand:)
  required_ext_reqs = ext_reqs.select do |ext_req|
    if defined_by_condition.mentions?(ext_req.extension)
      c = Condition.conjunction([defined_by_condition, ext_req.to_condition], cfg_arch)
      !c.satisfiable?
    end
  end

  required_ext_reqs.map(&:satisfying_versions).flatten.uniq.group_by { |ext_ver| ext_ver.name }.map do |ext_name, vers|
    ExtensionRequirement.create_from_ext_vers(vers)
  end
end

#unconditional_extension_requirements(expand: false) ⇒ Array<ExtensionRequirement>

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

returns list of extension requirements that must be met for this instruction to be defined

if expand is true, expand the definedBy condition to also include transitive requirements

Parameters:

  • expand (Boolean) (defaults to: false)

Returns:



1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
# File 'lib/udb/obj/instruction.rb', line 1205

def unconditional_extension_requirements(expand: false)
  ext_reqs = defined_by_condition.ext_req_terms(expand:)
  required_ext_reqs = ext_reqs.select do |ext_req|
    if defined_by_condition.mentions?(ext_req.extension)
      c = Condition.conjunction([defined_by_condition, Condition.not(ext_req.to_condition, cfg_arch)], cfg_arch)
      !c.satisfiable?
    end
  end

  required_ext_reqs.map(&:satisfying_versions).flatten.uniq.group_by { |ext_ver| ext_ver.name }.map do |ext_name, vers|
    ExtensionRequirement.create_from_ext_vers(vers)
  end
end

#validate(resolver)

This method returns an undefined value.

Parameters:



325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
# File 'lib/udb/obj/instruction.rb', line 325

def validate(resolver)
  super(resolver)

  if has_type?
    if @data["format"]["RV32"].nil?
      b = base.nil? ? 64 : T.cast(base, Integer)
      Instruction.validate_encoding(self, b)
    else
      Instruction.validate_encoding(self, 32)
      Instruction.validate_encoding(self, 64)
    end
  else
    if @data["encoding"]["RV32"].nil?
      Instruction.deprecated_validate_encoding(@data["encoding"], name)
    else
      Instruction.deprecated_validate_encoding(@data["encoding"]["RV32"], name)
      Instruction.deprecated_validate_encoding(@data["encoding"]["RV64"], name)
    end
  end

  # Validate hint references
  if @data.key?("hints")
    @data["hints"].each_with_index do |hint, index|
      if hint.key?("$ref")
        begin
          # Try to dereference the hint to validate it exists
          hint_inst = @cfg_arch.ref(hint["$ref"])
          if hint_inst.nil?
            raise "Invalid hint reference in instruction '#{name}' at hints[#{index}]: '#{hint["$ref"]}' - reference not found"
          end
        rescue => e
          raise "Invalid hint reference in instruction '#{name}' at hints[#{index}]: '#{hint["$ref"]}' - #{e.message}"
        end
      end
    end
  end
end

#wavedrom_desc(base) ⇒ String

Generates a wavedrom description of the instruction encoding

Parameters:

  • base (Integer)

    The XLEN (32 or 64), needed if the instruction is #multi_encoding?

Returns:

  • (String)

    The wavedrom JSON description



1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
# File 'lib/udb/obj/instruction.rb', line 1156

def wavedrom_desc(base)
  desc = {
    "reg" => []
  }
  display_fields = encoding(base).opcode_fields
  display_fields += encoding(base).decode_variables.map(&:grouped_encoding_fields).flatten

  display_fields.sort { |a, b| b.range.last <=> a.range.last }.reverse_each do |e|
    desc["reg"] << { "bits" => e.range.size, "name" => e.name, "type" => (e.opcode? ? 2 : 4) }
  end

  desc
end