diff --git a/arch/inst/Zba/add.uw.yaml b/arch/inst/Zba/add.uw.yaml index 2ce32623b..6f9f11183 100644 --- a/arch/inst/Zba/add.uw.yaml +++ b/arch/inst/Zba/add.uw.yaml @@ -10,15 +10,18 @@ description: | zero-extended least-significant word of rs1. definedBy: Zba assembly: xd, xs1, xs2 -encoding: - match: 0000100----------000-----0111011 - variables: - - name: rs2 - location: 24-20 - - name: rs1 - location: 19-15 - - name: rd - location: 11-7 +format: + $inherits: + - inst_subtype/R/R-x.yaml# + - inst_opcode/OP-32.yaml# + opcodes: + funct7: + display_name: ADD.UW + value: 0b0000100 + funct3: + display_name: ADD.UW + value: 0b000 + opcode: { $inherits: inst_opcode/OP-32.yaml#/data } access: s: always u: always @@ -32,7 +35,7 @@ operation(): | raise (ExceptionCode::IllegalInstruction, mode(), $encoding); } - X[rd] = X[rs2] + X[rs1][31:0]; + X[xd] = X[xs2] + X[xs1][31:0]; # SPDX-SnippetBegin # SPDX-FileCopyrightText: 2017-2025 Contributors to the RISCV Sail Model diff --git a/arch/inst_opcode/OP-32.yaml b/arch/inst_opcode/OP-32.yaml new file mode 100644 index 000000000..af2d80955 --- /dev/null +++ b/arch/inst_opcode/OP-32.yaml @@ -0,0 +1,9 @@ +# yaml-language-server: $schema=../../schemas/inst_opcode_schema.json# + +$schema: inst_opcode_schema.json# +kind: instruction_opcode +name: OP-32 + +data: + display_name: OP-32 + value: 0b0111011 diff --git a/arch/inst_opcode/funct3.yaml b/arch/inst_opcode/funct3.yaml new file mode 100644 index 000000000..cb97619eb --- /dev/null +++ b/arch/inst_opcode/funct3.yaml @@ -0,0 +1,8 @@ +# yaml-language-server: $schema=../../schemas/inst_opcode_schema.json# + +$schema: inst_opcode_schema.json# +kind: instruction_opcode +name: funct3 + +data: + location: 14-12 diff --git a/arch/inst_opcode/funct7.yaml b/arch/inst_opcode/funct7.yaml new file mode 100644 index 000000000..aa375dab0 --- /dev/null +++ b/arch/inst_opcode/funct7.yaml @@ -0,0 +1,8 @@ +# yaml-language-server: $schema=../../schemas/inst_opcode_schema.json# + +$schema: inst_opcode_schema.json# +kind: instruction_opcode +name: funct7 + +data: + location: 31-25 diff --git a/arch/inst_opcode/opcode.yaml b/arch/inst_opcode/opcode.yaml new file mode 100644 index 000000000..ec7db6e0a --- /dev/null +++ b/arch/inst_opcode/opcode.yaml @@ -0,0 +1,8 @@ +# yaml-language-server: $schema=../../schemas/inst_opcode_schema.json# + +$schema: inst_opcode_schema.json# +kind: instruction_opcode +name: opcode + +data: + location: 6-0 diff --git a/arch/inst_subtype/R/R-x.yaml b/arch/inst_subtype/R/R-x.yaml new file mode 100644 index 000000000..5a6efdb63 --- /dev/null +++ b/arch/inst_subtype/R/R-x.yaml @@ -0,0 +1,16 @@ +# yaml-language-server: $schema=../../../schemas/inst_subtype_schema.json + +$schema: inst_subtype_schema.json# +kind: instruction_subtype +name: R-x + +data: + type: { "$ref": "inst_type/R.yaml#" } + subtype: { "$ref": "inst_subtype/R/R-x.yaml#" } + $inherits: + - inst_opcode/funct7.yaml#/data + - inst_opcode/funct3.yaml#/data + - inst_opcode/opcode.yaml#/data + - inst_var/xs2.yaml#/data + - inst_var/xs1.yaml#/data + - inst_var/xd.yaml#/data diff --git a/arch/inst_type/R.yaml b/arch/inst_type/R.yaml new file mode 100644 index 000000000..9b93d07f9 --- /dev/null +++ b/arch/inst_type/R.yaml @@ -0,0 +1,20 @@ +# yaml-language-server: $schema=../../schemas/inst_type_schema.json# + +$schema: inst_type_schema.json# +kind: instruction_type +name: R +description: R-type instructions usually have two source registers and one destination registers +opcodes: + funct7: + location: 31-25 + funct3: + location: 14-12 + opcode: + location: 6-0 +variables: + rs2: + location: 24-20 + rs1: + location: 19-15 + rd: + location: 11-7 diff --git a/arch/inst_var/xd.yaml b/arch/inst_var/xd.yaml new file mode 100644 index 000000000..4009535d1 --- /dev/null +++ b/arch/inst_var/xd.yaml @@ -0,0 +1,9 @@ +# yaml-language-server: $schema=../../schemas/inst_var_schema.json# + +$schema: inst_var_schema.json# +kind: instruction_variable +name: xd + +data: + location: 11-7 + type: { "$ref": "inst_var_type/x_dst_reg.yaml#" } diff --git a/arch/inst_var/xs1.yaml b/arch/inst_var/xs1.yaml new file mode 100644 index 000000000..25205cc24 --- /dev/null +++ b/arch/inst_var/xs1.yaml @@ -0,0 +1,9 @@ +# yaml-language-server: $schema=../../schemas/inst_var_schema.json# + +$schema: inst_var_schema.json# +kind: instruction_variable +name: xs1 + +data: + location: 19-15 + type: { "$ref": "inst_var_type/x_src_reg.yaml#" } diff --git a/arch/inst_var/xs2.yaml b/arch/inst_var/xs2.yaml new file mode 100644 index 000000000..f83aaa816 --- /dev/null +++ b/arch/inst_var/xs2.yaml @@ -0,0 +1,9 @@ +# yaml-language-server: $schema=../../schemas/inst_var_schema.json# + +$schema: inst_var_schema.json# +kind: instruction_variable +name: xs2 + +data: + location: 24-20 + type: { "$ref": "inst_var_type/x_src_reg.yaml#" } diff --git a/arch/inst_var_type/x_dst_reg.yaml b/arch/inst_var_type/x_dst_reg.yaml new file mode 100644 index 000000000..7dfeb19a7 --- /dev/null +++ b/arch/inst_var_type/x_dst_reg.yaml @@ -0,0 +1,8 @@ +# yaml-language-server: $schema=../../schemas/inst_var_type_schema.json# + +$schema: inst_var_type_schema.json# +kind: instruction_variable_type +name: x_dst_reg +type: register_reference +register_file: X +access: W diff --git a/arch/inst_var_type/x_src_reg.yaml b/arch/inst_var_type/x_src_reg.yaml new file mode 100644 index 000000000..e24facc21 --- /dev/null +++ b/arch/inst_var_type/x_src_reg.yaml @@ -0,0 +1,8 @@ +# yaml-language-server: $schema=../../schemas/inst_var_type_schema.json# + +$schema: inst_var_type_schema.json# +kind: instruction_variable_type +name: x_src_reg +type: register_reference +register_file: X +access: R diff --git a/lib/arch_obj_models/instruction.rb b/lib/arch_obj_models/instruction.rb index 54c471a56..d561d946b 100644 --- a/lib/arch_obj_models/instruction.rb +++ b/lib/arch_obj_models/instruction.rb @@ -9,12 +9,130 @@ require_relative "../backend_helpers" require "awesome_print" +class InstructionType < DatabaseObject +end + +class InstructionSubtype < DatabaseObject + sig { returns(T::Array[Instruction::DecodeVariable]) } + def make_variables + if @data["fields"].key?("variables") + @data["fields"]["variables"].map { |var_data| Instruction::DecodeVariable.new(var_data) } + else + [] + end + end + + sig { returns(T::Array[Instruction::Encoding::Field]) } + def opcodes + @opcodes ||= + @data["fields"]["opcodes"].map do |opcode_data| + 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 + Instruction::Encoding::Field.new(opcode_data["name"], range) + end + end + + sig { params(for_inst: Instruction, base: Integer).returns(Instruction::Encoding) } + def make_encoding(for_inst, base) + Instruction::Encoding.new(for_inst.encoding_format(base), make_variables, opcodes) + end +end + # model of a specific instruction in a specific base (RV32/RV64) class Instruction < DatabaseObject # Add all methods in this module to this type of database object. include CertifiableObject include WavedromUtil + sig { returns(T::Boolean) } + def has_type? = @data.key?("format") + + sig { params(base: Integer).returns(InstructionType) } + 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 + + sig { params(base: Integer).returns(InstructionSubtype) } + 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 + + # @return [String] format, as a string of 0,1 and -, + # @example Format of `sd` + # sd.format #=> '-----------------011-----0100011' + sig { params(base: Integer).returns(String) } + def encoding_format(base) + @encoding_format ||= + if has_type? + if @data["format"].key?("RV32") + { + 32 => @data["format"]["RV32"]["encoding"], + 64 => @data["format"]["RV64"]["encoding"] + } + else + { + 32 => @data["format"]["encoding"], + 64 => @data["format"]["encoding"] + } + end + else + 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 + end + @encoding_format[base] + end + def processed_wavedrom_desc(base) data = wavedrom_desc(base) processed_data = process_wavedrom(data) @@ -37,7 +155,32 @@ def self.ary_from_location(location_str_or_int) bits end - def self.validate_encoding(encoding, inst_name) + sig { params(inst: Instruction, base: Integer).void } + def self.validate_encoding(inst, base) + match = inst.encoding_format(base) + raise "No match for instruction #{inst_name}?" if match.nil? + + encoding = inst.encoding(base) + variables = encoding.decode_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| variable.location_bits.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.none? { |variable| variable.location_bits.include?(i) } + raise ValidationError, "In instruction, #{inst_name}, bit #{i} is covered by both a variable and the match string" + end + end + end + end + + def self.deprecated_validate_encoding(encoding, inst_name) match = encoding["match"] raise "No match for instruction #{inst_name}?" if match.nil? @@ -65,11 +208,21 @@ def self.validate_encoding(encoding, inst_name) def validate super - if @data["encoding"]["RV32"].nil? - Instruction.validate_encoding(@data["encoding"], name) + if has_type? + if @data["format"]["RV32"].nil? + b = @data["base"].nil? ? 64 : T.cast(@data["base"], Integer) + Instruction.validate_encoding(self, b) + else + Instruction.validate_encoding(self, 32) + Instruction.validate_encoding(self, 64) + end else - Instruction.validate_encoding(@data["encoding"]["RV32"], name) - Instruction.validate_encoding(@data["encoding"]["RV64"], name) + 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 end @@ -314,6 +467,8 @@ def size # decode field constructions from YAML file, rather than riscv-opcodes # eventually, we will move so that all instructions use the YAML file, class DecodeVariable + extend T::Sig + # the name of the field attr_reader :name @@ -332,6 +487,15 @@ class DecodeVariable attr_reader :encoding_fields + sig { returns(String) } + attr_reader :location + + # @return [Array] Any array containing every encoding index covered by this variable + sig { returns(T::Array[Integer]) } + def location_bits + Instruction.ary_from_location(@location) + end + # @return [String] Name, along with any != constraints, # @example # pretty_name #=> "rd != 0" @@ -448,12 +612,12 @@ def grouped_encoding_fields end end - def initialize(inst, field_data) - @inst = inst + def initialize(field_data) @name = field_data["name"] @left_shift = field_data["left_shift"].nil? ? 0 : field_data["left_shift"] @sext = field_data["sign_extend"].nil? ? false : field_data["sign_extend"] @alias = field_data["alias"].nil? ? nil : field_data["alias"] + @location = field_data["location"] extract_location(field_data["location"]) @excludes = if field_data.key?("not") @@ -568,7 +732,7 @@ class Field attr_reader :range # @param name [#to_s] Either string of 0's and 1's or a bunch of dashes - # @param range [Range] Range of the field in the parent CSR + # @param range [Range] Range of the field in the encoding def initialize(name, range) @name = name.to_s @range = range @@ -634,10 +798,10 @@ def expand(exclusion_dvs, exclusion_dv_values, base, idx) # @param format [String] Format of the encoding, as 0's, 1's and -'s (for decode variables) # @param decode_vars [Array>] List of decode variable definitions from the arch spec - def initialize(format, decode_vars) + def initialize(format, decode_vars, opcode_fields = nil) @format = format - @opcode_fields = [] + @opcode_fields = opcode_fields.nil? ? [] : opcode_fields field_chars = [] @format.chars.each_with_index do |c, idx| if c == "-" @@ -646,7 +810,7 @@ def initialize(format, decode_vars) field_text = field_chars.join("") field_lsb = @format.size - idx field_msb = @format.size - idx - 1 + field_text.size - @opcode_fields << Field.new(field_text, field_lsb..field_msb) + @opcode_fields << Field.new(field_text, field_lsb..field_msb) if opcode_fields.nil? field_chars.clear next @@ -658,12 +822,16 @@ def initialize(format, decode_vars) # add the least significant field unless field_chars.empty? field_text = field_chars.join("") - @opcode_fields << Field.new(field_text, 0...field_text.size) + @opcode_fields << Field.new(field_text, 0...field_text.size) if opcode_fields.nil? end - @decode_variables = [] - decode_vars&.each do |var| - @decode_variables << DecodeVariable.new(self, var) + if decode_vars&.last.is_a?(DecodeVariable) + @decode_variables = decode_vars + else + @decode_variables = [] + decode_vars&.each do |var| + @decode_variables << DecodeVariable.new(var) + end end end @@ -675,22 +843,35 @@ def size def load_encoding @encodings = {} - if @data["encoding"].key?("RV32") - # there are different encodings for RV32/RV64 - @encodings[32] = Encoding.new(@data["encoding"]["RV32"]["match"], @data["encoding"]["RV32"]["variables"]) - @encodings[64] = Encoding.new(@data["encoding"]["RV64"]["match"], @data["encoding"]["RV64"]["variables"]) - elsif @data.key("base") - @encodings[@data["base"]] = Encoding.new(@data["encoding"]["match"], @data["encoding"]["variables"]) + if has_type? + if @data.key("base") + @encodings[@data["base"]] = subtype(@data["base"]).make_encoding(self, @data["base"]) + else + @encodings[32] = subtype(32).make_encoding(self, 32) + @encodings[64] = subtype(64).make_encoding(self, 64) + end else - @encodings[32] = Encoding.new(@data["encoding"]["match"], @data["encoding"]["variables"]) - @encodings[64] = Encoding.new(@data["encoding"]["match"], @data["encoding"]["variables"]) + if @data["encoding"].key?("RV32") + # there are different encodings for RV32/RV64 + @encodings[32] = Encoding.new(@data["encoding"]["RV32"]["match"], @data["encoding"]["RV32"]["variables"]) + @encodings[64] = Encoding.new(@data["encoding"]["RV64"]["match"], @data["encoding"]["RV64"]["variables"]) + elsif @data.key("base") + @encodings[@data["base"]] = Encoding.new(@data["encoding"]["match"], @data["encoding"]["variables"]) + else + @encodings[32] = Encoding.new(@data["encoding"]["match"], @data["encoding"]["variables"]) + @encodings[64] = Encoding.new(@data["encoding"]["match"], @data["encoding"]["variables"]) + end end end private :load_encoding # @return [Boolean] whether or not this instruction has different encodings depending on XLEN def multi_encoding? - @data.key?("encoding") && @data["encoding"].key?("RV32") + if has_type? + @data["format"].key?("RV32") + else + @data.key?("encoding") && @data["encoding"].key?("RV32") + end end # @return [Boolean] true if self and other_inst have indistinguishable encodings and can be simultaneously implemented in some design diff --git a/lib/architecture.rb b/lib/architecture.rb index c157a1c2e..e2e350e5d 100644 --- a/lib/architecture.rb +++ b/lib/architecture.rb @@ -143,6 +143,16 @@ def self.generate_obj_methods(fn_name, arch_dir, obj_class) arch_dir: "inst", klass: Instruction }, + { + fn_name: "instruction_type", + arch_dir: "inst_type", + klass: InstructionType + }, + { + fn_name: "instruction_subtype", + arch_dir: "inst_subtype", + klass: InstructionSubtype + }, { fn_name: "csr", arch_dir: "csr", @@ -321,7 +331,7 @@ def ref(uri) obj = case file_path when /^proc_cert_class.*/ - proc_cert_class_name = File.basename(file_path, ".yaml") + proc_cert_class_name = File.basename(file_path, ".yaml") proc_cert_class(proc_cert_class_name) when /^proc_cert_model.*/ proc_cert_model_name = File.basename(file_path, ".yaml") @@ -332,7 +342,7 @@ def ref(uri) when /^ext.*/ ext_name = File.basename(file_path, ".yaml") extension(ext_name) - when /^inst.*/ + when %r{^inst/.*} inst_name = File.basename(file_path, ".yaml") instruction(inst_name) when /^manual.*/ @@ -350,6 +360,13 @@ def ref(uri) when /^profile.*/ profile_name = File.basename(file_path, ".yaml") profile(profile_name) + when %r{^inst_subtype/.*/.*} + inst_subtype_name = File.basename(file_path, ".yaml") + instruction_subtype(inst_subtype_name) + when %r{^inst_type/[^/]+} + # type + inst_type_name = File.basename(file_path, ".yaml") + instruction_type(inst_type_name) else raise "Unhandled ref object: #{file_path}" end diff --git a/schemas/inst_opcode_schema.json b/schemas/inst_opcode_schema.json new file mode 100644 index 000000000..ffa4d56e9 --- /dev/null +++ b/schemas/inst_opcode_schema.json @@ -0,0 +1,34 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Instruction Opcode Schema", + "description": "Schema for instruction opcode definitions", + + "type": "object", + "required": ["$schema", "kind", "data"], + "properties": { + "$schema": { + "const": "inst_opcode_schema.json#" + }, + "kind": { + "const": "instruction_opcode" + }, + "name": { + "type": "string" + }, + "data": { + "type": "object", + "additionalProperties": false, + "properties": { + "location": { + "$ref": "schema_defs.json#/$defs/field_location" + }, + "value": { + "$ref": "schema_defs.json#/$defs/integer" + }, + "display_name": { + "type": "string" + } + } + } + } +} diff --git a/schemas/inst_schema.json b/schemas/inst_schema.json index f46b7649f..7784d2a63 100644 --- a/schemas/inst_schema.json +++ b/schemas/inst_schema.json @@ -2,24 +2,193 @@ "$schema": "http://json-schema.org/draft-07/schema#", "$defs": { - "field_location": { - "description": "Location specifier for a field", - "oneOf": [ - { + "fully_resolved_opcodes": { + "type": "object", + "required": ["location", "display_name", "value"], + "properties": { + "location": { + "type": "schema_defs.json#/$defs/field_location" + }, + "display_name": { "type": "string", - "pattern": "^(([0-9]+)|([0-9]+-[0-9]+))(\\|(([0-9]+)|([0-9]+-[0-9]+)))*$" + "description": "field name, displayed in encoding drawings" }, - { - "type": "integer", - "minimum": 0, - "maximum": 31 + "value": { + "oneOf": [ + { + "$ref": "schema_defs.json#/$defs/integer" + }, + { + "type": "object", + "required": ["$ref"], + "additionalProperties": false, + "properties": { + "$ref": { + "type": "string", + "pattern": "^inst_opcode/.*\\.yaml#$" + } + } + } + ] + }, + "$child_of": { + "oneOf": [ + { + "type": "string", + "pattern": "inst_opcode/[^/]+\\.yaml#/data" + }, + { + "type": "array", + "items": { + "type": "string", + "pattern": "inst_opcode/[^/]+\\.yaml#/data" + } + } + ] } - ] + }, + "additionalProperties": false + }, + "fully_resolved_variables": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": ["location", "type"], + "properties": { + "location": { + "$ref": "schema_defs.json#/$defs/possibly_split_field_location" + }, + "type": { + "type": "object", + "required": ["$ref"], + "additionalProperties": false, + "properties": { + "$ref": { + "type": "string", + "pattern": "inst_var_type/.+\\.json#" + } + } + } + } + } + }, + "fully_resolved_format": { + "$comment": "Fully resolved format; $child_of must exist, and all properties must be present", + "required": ["$child_of", "type", "subtype", "opcodes"], + "properties": { + "$child_of": { + "oneOf": [ + { + "$ref": "schema_defs.json#/$defs/reference" + }, + { + "type": "array", + "items": { + "$ref": "schema_defs.json#/$defs/reference" + } + } + ] + }, + "type": { + "type": "object", + "required": ["$ref"], + "additionalProperties": false, + "properties": { + "$ref": { + "type": "string", + "pattern": "inst_type/[A-Z][A-Za-z0-9\\-]*.yaml#" + } + } + }, + "subtype": { + "type": "object", + "required": ["$ref"], + "additionalProperties": false, + "properties": { + "$ref": { + "type": "string", + "pattern": "inst_subtype/[A-Z][A-Za-z0-9\\-]*/[A-Z][A-Za-z0-9\\-]*-[A-Za-z0-9\\-]*.yaml#" + } + } + }, + "opcodes": { + "type": "object", + "patternProperties": { + "$comment": "opcode names are lowercase with numbers", + "[a-z][a-z0-9]*": { + "$refs": "#/$defs/fully_resolved_opcodes" + } + } + }, + "variables": { + "$ref": "#/$defs/fully_resolved_variables" + } + }, + "additionalProperties": false + }, + "old_encoding": { + "type": "object", + "properties": { + "match": { + "oneOf": [ + { + "type": "string", + "pattern": "^[01-]{43}11111$", + "description": "48-bit encoding" + }, + { + "type": "string", + "pattern": "^[01-]{30}11$", + "description": "32-bit encoding" + }, + { + "type": "string", + "pattern": "^[01-]{14}((00)|(01)|(10))$", + "description": "16-bit encoding" + } + ] + }, + "variables": { + "type": "array", + "items": { + "$ref": "#/$defs/field" + } + }, + "additionalProperties": false + }, + "additionalProperties": false + }, + "type": { + "type": "object", + "properties": { + "$ref": { + "type": "string", + "format": "uri-reference", + "pattern": "^inst_type/[A-Z]+\\.yaml#.*$", + "description": "Reference to an instruction type definition" + } + }, + "required": ["$ref"], + "additionalProperties": false + }, + "subtype": { + "type": "object", + "properties": { + "$ref": { + "type": "string", + "format": "uri-reference", + "pattern": "^inst_type/[A-Z]+/[a-zA-Z0-9_]+\\.yaml#.*$", + "description": "Reference to an instruction subtype definition" + } + }, + "required": ["$ref"], + "additionalProperties": false }, "variable_metadata": { "properties": { "location": { - "$ref": "#/$defs/field_location" + "$ref": "schema_defs.json#/$defs/possibly_split_field_location" }, "sign_extend": { "type": "boolean", @@ -81,7 +250,7 @@ "description": "Cookie crumb of the reference to variable metadata" }, "location": { - "$ref": "#/$defs/field_location" + "$ref": "schema_defs.json#/$defs/possibly_split_field_location" }, "sign_extend": { "type": "boolean", @@ -117,38 +286,6 @@ } ] }, - "encoding": { - "type": "object", - "properties": { - "match": { - "oneOf": [ - { - "type": "string", - "pattern": "^[01-]{43}11111$", - "description": "48-bit encoding" - }, - { - "type": "string", - "pattern": "^[01-]{30}11$", - "description": "32-bit encoding" - }, - { - "type": "string", - "pattern": "^[01-]{14}((00)|(01)|(10))$", - "description": "16-bit encoding" - } - ] - }, - "variables": { - "type": "array", - "items": { - "$ref": "#/$defs/field" - } - }, - "additionalProperties": false - }, - "additionalProperties": false - }, "inst_data": { "type": "object", "required": [ @@ -191,27 +328,7 @@ "$ref": "schema_defs.json#/$defs/requires_entry", "description": "Extension(s) that defines the instruction" }, - "encoding": { - "description": "Instruction encoding and decode variables", - "oneOf": [ - { - "$ref": "#/$defs/encoding" - }, - { - "type": "object", - "properties": { - "RV32": { - "$ref": "#/$defs/encoding" - }, - "RV64": { - "$ref": "#/$defs/encoding" - } - }, - "required": ["RV32", "RV64"], - "additionalProperties": false - } - ] - }, + "hints": { "type": "array", "items": { @@ -307,6 +424,80 @@ "$source": { "description": "Path to the source file. Used by downstream tooling; not expected to be found in handwritten files", "type": "string" + }, + "format": { + "type": "object", + "oneOf": [ + { + "$ref": "#/$defs/fully_resolved_format" + }, + { + "$comment": "Unresolved version", + "required": ["$inherits", "opcodes"], + "properties": { + "$inherits": { + "oneOf": [ + { + "$ref": "schema_defs.json#/$defs/reference" + }, + { + "type": "array", + "items": { + "$ref": "schema_defs.json#/$defs/reference" + } + } + ] + }, + "opcodes": { + "type": "object", + "patternProperties": { + "$comment": "opcode names are lowercase with numbers", + "[a-z][a-z0-9]*": { + "type": "object", + "properties": { + "$comment": "location must come from inheritance", + "display_name": { + "type": "string", + "description": "field name, displayed in encoding drawings" + }, + "value": { + "$ref": "schema_defs.json#/$defs/integer" + }, + "$inherits": { + "type": "string", + "pattern": "inst_opcode/[^/]+\\.yaml#/data" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + }, + "encoding": { + "description": "Instruction encoding and decode variables", + "oneOf": [ + { + "$ref": "#/$defs/old_encoding" + }, + { + "type": "object", + "properties": { + "RV32": { + "$ref": "#/$defs/old_encoding" + }, + "RV64": { + "$ref": "#/$defs/old_encoding" + } + }, + "required": ["RV32", "RV64"], + "additionalProperties": false + } + ] } } } diff --git a/schemas/inst_subtype_schema.json b/schemas/inst_subtype_schema.json new file mode 100644 index 000000000..4095dd7c4 --- /dev/null +++ b/schemas/inst_subtype_schema.json @@ -0,0 +1,75 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Instruction Subtype Schema", + "description": "Schema for instruction subtype definitions", + + "type": "object", + "required": ["$schema", "kind", "data"], + "additionalProperties": false, + + "properties": { + "$schema": { + "const": "inst_subtype_schema.json#", + "description": "Pointer to schema" + }, + "kind": { + "const": "instruction_subtype", + "description": "Kind of the database object" + }, + "name": { + "type": "string", + "$ref": "schema_defs.json#/$defs/inst_subtype_name", + "description": "Name of the subtype; also serves as database key" + }, + "data": { + "type": "object", + "required": ["type", "$inherits"], + "additionalProperties": false, + "properties": { + "type": { + "type": "object", + "description": "Instruction format type (I-type, R-type, etc.)", + "additionalProperties": false, + "required": ["$ref"], + "properties": { + "$ref": { + "type": "string", + "pattern": "inst_type/[A-Z][A-Za-z0-9\\-]*\\.yaml#" + } + } + }, + "subtype": { + "type": "object", + "description": "Instruction format subtype (R-x-type, etc.)", + "additionalProperties": false, + "required": ["$ref"], + "properties": { + "$ref": { + "type": "string", + "pattern": "inst_subtype/[A-Z][A-Za-z0-9\\-]*/[A-Z][A-Za-z0-9\\-]*\\.yaml#" + } + } + }, + "$inherits": { + "oneOf": [ + { + "type": "string", + "pattern": "inst_((opcode)|(var))/.*\\.yaml#/data" + }, + { + "type": "array", + "items": { + "type": "string", + "pattern": "inst_((opcode)|(var))/.*\\.yaml#/data" + } + } + ] + } + } + }, + "$source": { + "description": "Path to the source file. Used by downstream tooling; not expected to be found in handwritten files", + "type": "string" + } + } +} diff --git a/schemas/inst_type_schema.json b/schemas/inst_type_schema.json new file mode 100644 index 000000000..d868d2abb --- /dev/null +++ b/schemas/inst_type_schema.json @@ -0,0 +1,49 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Instruction Type Schema", + "description": "Schema for instruction type definitions", + + "type": "object", + "properties": { + "$schema": { "const": "inst_type_schema.json#" }, + "kind": { "const": "instruction_type" }, + "name": { "$ref": "schema_defs.json#/$defs/inst_type_name" }, + "description": { "type": "string" }, + "opcodes": { + "type": "object", + "additionalProperties": false, + "patternProperties": { + "[a-z][A-Z0-9]*": { + "type": "object", + "required": ["location"], + "additionalProperties": false, + "properties": { + "location": { "$ref": "schema_defs.json#/$defs/field_location" } + } + } + } + }, + "variables": { + "type": "object", + "additionalProperties": false, + "patternProperties": { + "[a-z][A-Z0-9]*": { + "type": "object", + "required": ["location"], + "additionalProperties": false, + "properties": { + "location": { + "$ref": "schema_defs.json#/$defs/possibly_split_field_location" + } + } + } + } + }, + "$source": { + "description": "Path to the source file. Used by downstream tooling; not expected to be found in handwritten files", + "type": "string" + } + }, + "required": ["$schema", "kind", "name", "description"], + "additionalProperties": false +} diff --git a/schemas/inst_var_schema.json b/schemas/inst_var_schema.json new file mode 100644 index 000000000..78edd16bb --- /dev/null +++ b/schemas/inst_var_schema.json @@ -0,0 +1,40 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Instruction Variable Schema", + "description": "Schema for instruction decode variable definitions", + + "type": "object", + "required": ["$schema", "kind", "name", "data"], + "properties": { + "$schema": { + "const": "inst_var_schema.json#" + }, + "kind": { + "const": "instruction_variable" + }, + "name": { + "type": "string" + }, + "data": { + "type": "object", + "required": ["location", "type"], + "additionalProperties": false, + "properties": { + "location": { + "$ref": "schema_defs.json#/$defs/possibly_split_field_location" + }, + "type": { + "type": "object", + "required": ["$ref"], + "additionalProperties": false, + "properties": { + "$ref": { + "type": "string", + "pattern": "^inst_var_type/[^/]*\\.yaml#$" + } + } + } + } + } + } +} diff --git a/schemas/inst_var_type_schema.json b/schemas/inst_var_type_schema.json new file mode 100644 index 000000000..37f56176c --- /dev/null +++ b/schemas/inst_var_type_schema.json @@ -0,0 +1,45 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Instruction Variable Type Schema", + "description": "Schema for instruction variable type definitions", + + "type": "object", + "unevaluatedProperties": false, + "required": ["$schema", "kind", "name", "type"], + "properties": { + "$schema": { + "const": "inst_var_type_schema.json#" + }, + "kind": { + "const": "instruction_variable_type" + }, + "name": { + "type": "string" + }, + "type": { + "enum": ["register_reference"] + } + }, + "allOf": [ + { + "if": { + "properties": { + "type": { + "const": "register_reference" + } + } + }, + "then": { + "properties": { + "register_file": { + "enum": ["X", "F", "V"] + }, + "access": { + "enum": ["R", "W", "RW"], + "description": "Register access type: R - Read only (src) , W - Write only (dst), RW - Read/write (srcdst)" + } + } + } + } + ] +} diff --git a/schemas/schema_defs.json b/schemas/schema_defs.json index 8560534e6..8e3fcbbf8 100644 --- a/schemas/schema_defs.json +++ b/schemas/schema_defs.json @@ -34,7 +34,7 @@ }, "field_location": { "oneOf": [ - { "type": "number", "description": "Location of a single bit" }, + { "type": "integer", "description": "Location of a single bit" }, { "type": "string", "pattern": "^[0-9]+-[0-9]+$", @@ -43,6 +43,22 @@ ], "description": "Location of a field in a register" }, + "possibly_split_field_location": { + "description": "Location specifier for a field", + "oneOf": [ + { + "description": "bit range location, possibly split", + "type": "string", + "pattern": "^(([0-9]+)|([0-9]+-[0-9]+))(\\|(([0-9]+)|([0-9]+-[0-9]+)))*$" + }, + { + "description": "Single bit location", + "type": "integer", + "minimum": 0, + "maximum": 31 + } + ] + }, "revision_history_entry": { "type": "object", "properties": { @@ -374,6 +390,54 @@ }, "additionalProperties": false } + }, + "encoding_match": { + "oneOf": [ + { + "type": "string", + "pattern": "^[01-]{43}11111$", + "description": "48-bit encoding" + }, + { + "type": "string", + "pattern": "^[01-]{30}11$", + "description": "32-bit encoding" + }, + { + "type": "string", + "pattern": "^[01-]{14}((00)|(01)|(10))$", + "description": "16-bit encoding" + } + ] + }, + "inst_type_name": { + "type": "string", + "pattern": "[A-Z][A-Za-z0-9\\-]*" + }, + "inst_subtype_name": { + "type": "string", + "pattern": "[A-Z][A-Za-z0-9\\-]*-[A-Za-z0-9\\-]*" + }, + "reference": { + "type": "string", + "pattern": "^.+\\.yaml#(/.*)?$", + "description": "refrence to another database object, as a JSON Reference" + }, + "integer": { + "description": "An integer, either native to JSON or a number-like string", + "oneOf": [ + { + "type": "integer" + }, + { + "type": "string", + "pattern": "^0b[01]+$" + }, + { + "type": "string", + "pattern": "^0x[a-fA-F0-9]+$" + } + ] } } }