바이너리 명령 디코더 튜토리얼

이 가이드의 목표는 다음과 같습니다.

  • 바이너리 형식 설명 파일의 구조와 구문을 알아봅니다.
  • 바이너리 형식 설명이 ISA 설명과 어떻게 일치하는지 알아봅니다.
  • RiscV RV32I 하위 집합 명령의 바이너리 설명을 작성합니다.

개요

RiscV 바이너리 명령 인코딩

바이너리 명령 인코딩은 마이크로프로세서에서 실행할 수 있습니다. 일반적으로 실행 파일에 저장되며 일반적으로 ELF 형식입니다 안내는 고정 너비일 수도 있고 가변일 수도 있습니다. 너비를 기준으로 합니다.

일반적으로 명령은 작은 수의 인코딩 형식 집합을 사용하며, 각 형식은 맞춤설정된 명령이 생성됩니다. 예: register-register 명령은 가용한 명령 코드 수를 최대화하는 하나의 형식을 사용할 수도 있고, 레지스터 즉시 명령의 경우 입력되는 숫자의 수를 상쇄하는 다른 명령을 사용합니다. 사용 가능한 명령 코드도 있습니다. 브랜치 및 점프 명령은 대부분의 경우 오프셋이 더 큰 브랜치를 지원하기 위해 즉시 객체를 사용합니다.

RiscV에서 디코딩하려는 명령에 사용되는 명령 형식 시뮬레이터는 다음과 같습니다.

레지스터-레지스터 명령에 사용되는 R-Type 형식:

2025년 31월 25일 2024년 2월 24일 19..15 2012년 14월 14일 11..7 6..0
7 5 5 3 5 7
func7 rs2 rs1 func3 번째 명령 코드

I-Type 형식은 레지스터 즉시 명령, 로드 명령 및 jalr 명령, 12비트 즉시.

2020년 31월 19..15 2012년 14월 14일 11..7 6..0
12 5 3 5 7
imm12 rs1 func3 번째 명령 코드

특수한 I-Type 형식, 즉각적인 명령으로 시프트에 사용, 5비트 즉시:

2025년 31월 25일 2024년 2월 24일 19..15 2012년 14월 14일 11..7 6..0
7 5 5 3 5 7
func7 uimm5 rs1 func3 번째 명령 코드

U-Type 형식, 긴 즉각적인 명령 (lui, auipc), 20비트에 사용됨 즉시:

2012년 12월 31일 11..7 6..0
20 5 7
uimm20 번째 명령 코드

B 유형 형식, 조건부 브랜치에 사용됨, 12비트 즉시.

31 2025년 30월 25일 2024년 2월 24일 19..15 2012년 14월 14일 11~8월 7 6..0
1 6 5 5 3 4 1 7
rs2 rs1 func3 명령 코드

jal 명령에 사용되는 J 유형 형식, 20비트 즉시.

31 21년 30월 30일 20 2012년 19월 12일 11..7 6..0
1 10 1 8 5 7
번째 명령 코드

S-유형 형식, 저장 명령에 사용됨, 12비트 즉시.

2025년 31월 25일 2024년 2월 24일 19..15 2012년 14월 14일 11..7 6..0
7 5 5 3 5 7
rs2 rs1 func3 명령 코드

이러한 형식에서 볼 수 있듯이 이 모든 명령은 32비트 길이입니다. 각 형식의 하위 7비트는 명령 코드 필드입니다. 또한 여러 형식이 동일한 크기 즉시 값을 가지므로 해당 비트는 여러 부분으로 나뉩니다. 앞으로 보게 되겠지만 바이너리 디코더는 사양 형식이 이를 표현할 수 있습니다.

바이너리 인코딩 설명

명령의 바이너리 인코딩은 바이너리 형식으로 표현됩니다. (.bin_fmt) 설명 파일 이 백도어는 문장의 바이너리 인코딩을 바이너리 형식 명령 디코더를 인코더 출력에서 생성됩니다. 생성된 디코더는 명령 코드를 결정하고 ISA에서 필요한 정보를 제공하기 위한 목적의 피연산자 및 즉시 필드 인코딩에 상관없이 디코더를 사용합니다.

이 튜토리얼에서는 하위 집합의 바이너리 인코딩 설명 파일을 작성합니다. 명령어를 시뮬레이션하기 위해 필요한 RiscV32I 명령의 작은 'Hello World' 있습니다. RiscV ISA에 대한 자세한 내용은 Risc-V 사양{.external}.

먼저 파일을 엽니다. riscv_bin_decoder/riscv32i.bin_fmt

파일의 콘텐츠는 여러 섹션으로 나뉩니다.

첫 번째는 decoder 정의입니다.

decoder RiscV32I {
  // The namespace in which code will be generated.
  namespace mpact::sim::codelab;
  // The name (including any namespace qualifiers) of the opcode enum type.
  opcode_enum = "OpcodeEnum";
  // Include files specific to this decoder.
  includes {
    #include "riscv_isa_decoder/solution/riscv32i_decoder.h"
  }
  // Instruction groups for which to generate decode functions.
  RiscVInst32;
};

디코더 정의는 디코더 RiscV32I의 이름과 네 개의 추가 정보가 있습니다. 첫 번째는 namespace로, 다음을 정의합니다. 생성된 코드가 배치될 네임스페이스입니다. 둘째, opcode_enum: 생성되는 명령 코드 열거 유형의 이름을 지정합니다. 참조되어야 합니다. 셋째, includes {}는 이 디코더를 생성합니다. 여기서는 ISA 디코더에 의해 생성된 파일이며 이전 튜토리얼에서 확인할 수 있습니다. 추가 include 파일은 전역 범위 includes {}에서 지정할 수 있습니다. 정의합니다. 이는 여러 디코더가 정의된 경우 유용하며 모두 포함할 수 있습니다. 네 번째는 실습에서 사용할 수 있는 디코더가 생성되는 명령을 구성하는 그룹입니다. Google의 하나뿐인 경우: RiscVInst32

다음으로 세 가지 형식 정의가 있습니다. 이는 서로 다른 요청 사항을 이미 정의된 명령에 의해 사용되는 32비트 명령 단어의 형식 있습니다.

// The generic RiscV 32 bit instruction format.
format Inst32Format[32] {
  fields:
    unsigned bits[25];
    unsigned opcode[7];
};

// RiscV 32 bit instruction format used by a number of instructions
// needing a 12 bit immediate, including CSR instructions.
format IType[32] : Inst32Format {
  fields:
    signed imm12[12];
    unsigned rs1[5];
    unsigned func3[3];
    unsigned rd[5];
    unsigned opcode[7];
};

// RiscV instruction format used by fence instructions.
format Fence[32] : Inst32Format {
  fields:
    unsigned fm[4];
    unsigned pred[4];
    unsigned succ[4];
    unsigned rs1[5];
    unsigned func3[3];
    unsigned rd[5];
    unsigned opcode[7];
};

첫 번째는 32비트 너비의 명령 형식 Inst32Format을 정의합니다. 두 필드: bits (25비트 너비) 및 opcode (7비트 너비) 각 필드는 unsigned: 값을 추출할 때 값이 0으로 확장됩니다. C++ 정수 유형에 배치됩니다. 비트필드 너비의 합계는 형식의 너비와 같습니다. 오류가 있는 경우 도구에서 있습니다. 이 형식은 다른 형식에서 파생되지 않으므로 최상위 형식으로 간주됩니다.

두 번째는 32비트 너비의 명령 형식을 정의합니다. IType Inst32Format의 형식을 사용하여 이 두 형식을 관련된로 만듭니다. 형식에는 필드: imm12, rs1, func3, rd, opcode imm12 필드는 다음과 같습니다. signed: 값이 추출되어 C++ 정수 유형에 배치됩니다. IType.opcode는 둘 다 동일한 부호 있는/부호 없는 속성이며 동일한 명령 단어 비트를 참조함 Inst32Format.opcode 형식으로 전송합니다.

세 번째 형식은 fence에서만 사용하는 맞춤 형식입니다. 명령, 이는 이미 지정된 명령이며 이 튜토리얼에서 다루겠습니다.

요점: 필드 이름을 다른 관련 형식으로 재사용하세요. 동일한 비트를 나타내며 동일한 부호 있는/부호 없는 속성을 갖습니다.

riscv32i.bin_fmt의 형식 정의 뒤에는 명령 그룹이 옵니다. 정의합니다. 안내 그룹의 모든 안내는 동일해야 합니다. 동일한 비트 길이로부터 (아마도 간접적으로) 파생되는 형식을 사용합니다. 최상위 명령 형식으로 되어 있습니다. ISA에 다른 지침이 있을 수 있는 경우 각 길이에 다른 명령 그룹이 사용됩니다. 또한 대상 ISA 디코딩이 ARM 또는 Thumb과 같은 실행 모드에 종속되는지 여부 명령어마다 별도의 안내 그룹이 필요합니다. 이 bin_fmt 파서는 각 명령 그룹의 바이너리 디코더를 생성합니다.

instruction group RiscV32I[32] "OpcodeEnum" : Inst32Format {
  fence   : Fence  : func3 == 0b000, opcode == 0b000'1111;
  csrs    : IType  : func3 == 0b010, rs1 != 0, opcode == 0b111'0011;
  csrw_nr : IType  : func3 == 0b001, rd == 0,  opcode == 0b111'0011;
  csrs_nw : IType  : func3 == 0b010, rs1 == 0, opcode == 0b111'0011;
};

명령 그룹은 이름 RiscV32I, 너비 [32], "OpcodeEnum"를 사용하기 위한 명령 코드 열거 유형 및 기본 명령어 형식으로 입력합니다. 명령 코드 열거 유형은 ISA에 대한 튜토리얼에서 다루는 형식 독립 명령 디코더 디코더로 구성됩니다.

각 명령 인코딩 설명은 다음 세 부분으로 구성됩니다.

  • 명령 코드 이름(명령어에서 사용된 것과 동일해야 함) 디코더 설명을 생성합니다.
  • 명령 코드에 사용할 명령 형식입니다. 이 형식은 마지막 부분에 있는 비트필드에 대한 참조를 충족시키는 데 사용됩니다.
  • 쉼표로 구분된 비트 필드 제약 조건 목록, ==, !=, <, <=, > >= 명령 코드가 모드와 성공적으로 매칭되려면 표시됩니다.

.bin_fmt 파서는 이 모든 정보를 사용하여 다음과 같은 디코더를 빌드합니다.

  • 각 비트에 적절한 추출 함수 (부호 있음/부호 없음) 제공 필드가 포함됩니다. 추출기 함수는 네임스페이스에 배치되며 형식 이름의 스네이크 케이스 버전으로 이름이 지정됩니다. 예를 들어 IType 형식의 추출기 함수는 i_type 네임스페이스에 배치됩니다. 각 추출기 함수는 inline로 선언되며 가장 좁은 uint_t을 사용합니다. 형식의 너비를 보유하고 가장 좁은 int_t을 반환하는 유형입니다. 추출된 필드를 보유하는 (부호 있는 경우), uint_t (부호 없는 경우) 유형 너비를 기준으로 합니다. 예:
inline uint8_t ExtractOpcode(uint32_t value) {
  return value & 0x7f;
}
  • 각 명령 그룹의 디코딩 함수입니다. 유형 값을 반환합니다. OpcodeEnum, 너비를 보유하는 가장 좁은 uint_t 유형을 사용합니다. 명령어 그룹 형식입니다.

초기 빌드 실행

디렉터리를 riscv_bin_decoder로 변경하고 다음을 사용하여 프로젝트를 빌드합니다. 다음 명령어를 실행합니다.

$ cd riscv_bin_decoder
$ bazel build :all

디렉터리를 다시 저장소 루트로 변경한 후 소스부터 검토됩니다 이를 위해 디렉터리를 bazel-out/k8-fastbuild/bin/riscv_bin_decoder (x86을 사용한다고 가정 호스트 - 다른 호스트의 경우 k8-fastbuild는 다른 문자열입니다).

$ cd ..
$ cd bazel-out/k8-fastbuild/bin/riscv_bin_decoder
  • riscv32i_bin_decoder.h
  • riscv32i_bin_decoder.cc

생성된 헤더 파일 (.h)

riscv32i_bin_decoder.h. 파일의 첫 번째 부분에는 표준 상용구 가드, include 파일, 네임스페이스 선언 등이 포함되어 있습니다. 그 이후에는 네임스페이스 internal에 템플릿 도우미 함수가 있습니다. 이 함수 는 64비트 C++에 맞추기에는 너무 긴 형식에서 비트 필드를 추출하는 데 사용됩니다. 정수입니다.

#ifndef RISCV32I_BIN_DECODER_H
#define RISCV32I_BIN_DECODER_H

#include <iostream>
#include <cstdint>

#include "third_party/absl/functional/any_invocable.h"


#include "learning/brain/research/mpact/sim/codelab/riscv_isa_decoder/solution/riscv32i_decoder.h"

namespace mpact {
namespace sim {
namespace codelab {


namespace internal {

template <typename T>
static inline T ExtractBits(const uint8_t *data, int data_size,
                            int bit_index, int width) {
  if (width == 0) return 0;

  int byte_pos = bit_index >> 3;
  int end_byte = (bit_index + width - 1) >> 3;
  int start_bit = bit_index & 0x7;

  // If it is only from one byte, extract and return.
  if (byte_pos == end_byte) {
    uint8_t mask = 0xff >> start_bit;
    return (mask & data[byte_pos]) >> (8 - start_bit - width);
  }

  // Extract from the first byte.
  T val = 0;
  val = data[byte_pos++] & 0xff >> start_bit;
  int remainder = width - (8 - start_bit);
  while (remainder >= 8) {
    val = (val << 8) | data[byte_pos++];
    remainder -= 8;
  }

  // Extract any remaining bits.
  if (remainder > 0) {
    val <<= remainder;
    int shift = 8 - remainder;
    uint8_t mask = 0b1111'1111 << shift;
    val |= (data[byte_pos] & mask) >> shift;
  }
  return val;
}

}  // namespace internal

첫 번째 섹션 다음에는 각 네임스페이스에 하나씩 총 3개의 네임스페이스가 있습니다. riscv32i.bin_fmt 파일의 format 선언은 다음과 같습니다.


namespace fence {

...

}  // namespace fence

namespace i_type {

...

}  // namespace i_type

namespace inst32_format {

...

}  // namespace inst32_format

이러한 각 네임스페이스 내에서 inline 비트필드 추출 함수는 다음과 같습니다. 해당 형식의 각 비트 필드에 대한 정의가 정의되어 있습니다. 또한 기본 형식 다음 하위 형식에서 추출 함수를 복제합니다. 1) 필드 이름이 단일 필드 이름에서만 나오거나 2) 필드 이름은 동일한 유형 필드 (부호 있는/부호 없는 위치 및 비트 위치)를 나타냄 일종의 광고 소재입니다. 이렇게 하면 동일한 최상위 형식 네임스페이스에 있는 함수를 사용하여 추출할 비트입니다.

i_type 네임스페이스의 함수는 다음과 같습니다.

namespace i_type {

inline uint8_t ExtractFunc3(uint32_t value) {
  return  (value >> 12) & 0x7;
}

inline int16_t ExtractImm12(uint32_t value) {
  int16_t result = ( (value >> 20) & 0xfff) << 4;
  result = result >> 4;
  return result;
}

inline uint8_t ExtractOpcode(uint32_t value) {
  return value & 0x7f;
}

inline uint8_t ExtractRd(uint32_t value) {
  return  (value >> 7) & 0x1f;
}

inline uint8_t ExtractRs1(uint32_t value) {
  return  (value >> 15) & 0x1f;
}

}  // namespace i_type

마지막으로, 명령을 위한 디코더 함수의 함수 선언은 RiscVInst32 그룹이 선언되었습니다. 부호 없는 32비트를 명령 단어를 실행하고 OpcodeEnum 열거형 클래스 멤버를 반환합니다. 일치하는 항목, 또는 일치하는 항목이 없는 경우 OpcodeEnum::kNone입니다.

OpcodeEnum DecodeRiscVInst32(uint32_t inst_word);

생성된 소스 파일 (.cc)

이제 riscv32i_bin_decoder.cc를 엽니다. 파일의 첫 번째 부분에는 #include 및 네임스페이스 선언, 디코더 함수가 차례로 옵니다. 선언:

#include "riscv32i_bin_decoder.h"

namespace mpact {
namespace sim {
namespace codelab {

OpcodeEnum DecodeRiscVInst32None(uint32_t);
OpcodeEnum DecodeRiscVInst32_0(uint32_t inst_word);
OpcodeEnum DecodeRiscVInst32_0_3(uint32_t inst_word);
OpcodeEnum DecodeRiscVInst32_0_3c(uint32_t inst_word);
OpcodeEnum DecodeRiscVInst32_0_5c(uint32_t inst_word);

DecodeRiscVInst32None는 빈 디코딩 작업에 사용됩니다. OpcodeEnum::kNone를 반환하는 함수입니다. 나머지 세 가지 함수는 디코더를 생성합니다. 전체 디코더는 계층 구조로 작동합니다. 세트 디코더에 전달할 명령 단어의 비트 수가 계산되어 명령어 또는 명령어 그룹을 생성할 수 있습니다. 비트는 인접하지 않아야 합니다. 비트 수는 두 번째 수준의 디코더 함수로 채워집니다. 이 내용은 섹션에 다음 줄을 추가합니다.

absl::AnyInvocable<OpcodeEnum(uint32_t)> parse_group_RiscVInst32_0[kParseGroupRiscVInst32_0_Size] = {
    &DecodeRiscVInst32None, &DecodeRiscVInst32None,
    &DecodeRiscVInst32None, &DecodeRiscVInst32_0_3,
    &DecodeRiscVInst32None, &DecodeRiscVInst32None,

    ...

    &DecodeRiscVInst32None, &DecodeRiscVInst32None,
    &DecodeRiscVInst32None, &DecodeRiscVInst32None,
    &DecodeRiscVInst32_0_3c, &DecodeRiscVInst32None,

    ...
};

마지막으로 디코더 함수가 정의됩니다.

OpcodeEnum DecodeRiscVInst32None(uint32_t) {
  return OpcodeEnum::kNone;
}

OpcodeEnum DecodeRiscVInst32_0(uint32_t inst_word) {
  if ((inst_word & 0x4003) != 0x3) return OpcodeEnum::kNone;
  uint32_t index;
  index = (inst_word >> 2) & 0x1f;
  index |= (inst_word >> 7) & 0x60;
  return parse_group_RiscVInst32_0[index](inst_word);
}

OpcodeEnum DecodeRiscVInst32_0_3(uint32_t inst_word) {
  return OpcodeEnum::kFence;
}

OpcodeEnum DecodeRiscVInst32_0_3c(uint32_t inst_word) {
  if ((inst_word & 0xf80) != 0x0) return OpcodeEnum::kNone;
  return OpcodeEnum::kCsrwNr;
}

OpcodeEnum DecodeRiscVInst32_0_5c(uint32_t inst_word) {
  uint32_t rs1_value = (inst_word >> 15) & 0x1f;
  if (rs1_value != 0x0)
    return OpcodeEnum::kCsrs;
  if (rs1_value == 0x0)
    return OpcodeEnum::kCsrsNw;
  return OpcodeEnum::kNone;
}

OpcodeEnum DecodeRiscVInst32(uint32_t inst_word) {
  OpcodeEnum opcode;
  opcode = DecodeRiscVInst32_0(inst_word);
  return opcode;
}

이 경우 4개의 명령만 정의되어 있으면 매우 희소한 조회 테이블로 구성됩니다. 지침은 디코더 구조가 바뀌고 디코더의 레벨 수는 증가할 수도 있습니다


등록-등록 ALU 명령 추가

이제 riscv32i.bin_fmt 파일에 몇 가지 새로운 안내를 추가해 보겠습니다. 이 첫 번째 명령 그룹은 다음과 같은 레지스터-레지스터 ALU 명령입니다. add, and 등. RiscV32에서는 모두 R 유형 이진 명령을 사용합니다. 형식:

2025년 31월 25일 2024년 2월 24일 19..15 2012년 14월 14일 11..7 6..0
7 5 5 3 5 7
func7 rs2 rs1 func3 번째 명령 코드

가장 먼저 해야 할 일은 형식을 추가하는 것입니다. 이제 즐겨 사용하는 편집기에서 riscv32i.bin_fmt하세요. Inst32Format에서 Inst32Format에서 파생된 RType라는 형식을 추가합니다. 모든 비트필드 RTypeunsigned입니다. 이름, 비트 너비, 순서 (왼쪽에서 오른쪽 순)를 사용합니다. 형식을 정의합니다. 힌트가 필요하거나 완전한 솔루션인 여기를 클릭하세요.

다음으로 지침을 추가해야 합니다. 지침은 다음과 같습니다.

  • add - 정수 덧셈.
  • and - 비트 AND입니다.
  • or - 비트 OR입니다.
  • sll - 논리를 왼쪽으로 이동
  • sltu - 보다 작음, 부호 없음으로 설정합니다.
  • sub - 정수 뺄셈.
  • xor - 비트 xor.

인코딩은 다음과 같습니다.

2025년 31월 25일 2024년 2월 24일 19..15 2012년 14월 14일 11..7 6..0 명령 코드 이름
000 0000 rs2 rs1 000 번째 011 0011 추가
000 0000 rs2 rs1 111 번째 011 0011
000 0000 rs2 rs1 110 번째 011 0011 또는
000 0000 rs2 rs1 001 번째 011 0011 SLL
000 0000 rs2 rs1 011 번째 011 0011 슬투
010 0000 rs2 rs1 000 번째 011 0011 sub
000 0000 rs2 rs1 100 번째 011 0011 XOR
func7 func3 명령 코드

이 명령어 정의를 아래의 다른 안내 앞에 추가하세요. RiscVInst32 안내 그룹 바이너리 문자열은 접두사 0b입니다 (16진수의 경우 0x와 유사). 보다 간편하게 긴 이진수 문자열을 읽으려면 작은따옴표 '를 적절한 위치에 숫자 구분자를 입력합니다.

이러한 각 명령 정의에는 세 가지 제약조건, 즉 func7, func3, opcode sub를 제외한 모든 경우 func7 제약 조건은 다음과 같습니다. 다음과 같아야 합니다.

func7 == 0b000'0000

func3 제약 조건은 대부분의 안내에서 다릅니다. addsub입니다.

func3 == 0b000

opcode 제약 조건은 다음 각 안내에서 동일합니다.

opcode == 0b011'0011

각 줄은 세미콜론 ;로 끝나야 합니다.

완성된 솔루션은 여기에서 확인할 수 있습니다.

이제 이전과 마찬가지로 프로젝트를 빌드하고 생성된 riscv32i_bin_decoder.cc 파일. 추가 디코더 함수가 새 명령을 처리할 수 있도록 생성되었습니다. 대부분의 경우 이전에 생성된 것과 비슷하지만 add/sub 디코딩에 사용되는 DecodeRiscVInst32_0_c입니다.

OpcodeEnum DecodeRiscVInst32_0_c(uint32_t inst_word) {
  static constexpr OpcodeEnum opcodes[2] = {
    OpcodeEnum::kAdd,
    OpcodeEnum::kSub,
  };
  if ((inst_word & 0xbe000000) != 0x0) return OpcodeEnum::kNone;
  uint32_t index;
  index = (inst_word >> 30) & 0x1;
  return opcodes[index];
}

이 함수에는 정적 디코딩 테이블이 생성되며 조회 값은 적절한 색인을 선택합니다. 이렇게 하면 명령 디코더 계층 구조의 두 번째 계층에 있지만 명령 코드는 추가 비교 없이 표에서 직접 조회하는 경우 다른 함수 호출이 필요한 것과는 다릅니다.


즉시 실행 ALU 명령 추가

다음으로 추가할 안내 세트는 즉시 값을 표현해야 합니다. 이 같은 세 가지 그룹은 명령 (즉시 필드 기반): I-Type 즉시 지시 12비트 부호가 있는 즉각적인 명령으로, 특수 I-Type 즉각적인 명령은 시프트의 경우, U 유형 즉시(20비트 부호 없는 즉시 값 사용) 형식은 다음과 같습니다.

I-Type 즉시 형식:

2020년 31월 19..15 2012년 14월 14일 11..7 6..0
12 5 3 5 7
imm12 rs1 func3 번째 명령 코드

특수한 I-Type 즉시 형식:

2025년 31월 25일 2024년 2월 24일 19..15 2012년 14월 14일 11..7 6..0
7 5 5 3 5 7
func7 uimm5 rs1 func3 번째 명령 코드

U-Type 즉시 형식:

2012년 12월 31일 11..7 6..0
20 5 7
uimm20 번째 명령 코드

I-Type 형식이 이미 riscv32i.bin_fmt에 있으므로 추가하면 됩니다.

특수 I-Type 형식과 I-Type에서 정의한 R-Type 형식을 비교하면 이전 연습에서 유일한 차이점은 rs2 필드가 uimm5로 이름이 변경됩니다. 완전히 새로운 형식을 추가하는 대신 R-Type 형식입니다. 다른 필드를 추가하면 너비가 늘어나므로 필드를 추가할 수 없습니다. 오버레이를 추가할 수 있습니다. 오버레이는 오버레이의 형식으로 된 비트의 숫자로, 비트의 여러 하위 시퀀스를 결합하는 데 형식을 별도의 명명된 항목으로 변환합니다. 부작용은 생성된 코드가 이제 오버레이용 추출 함수도 포함됩니다. 설명하겠습니다. 이 경우 rs2uimm5 모두 서명이 취소되면 필드가 사용된다는 점을 명시하는 것 외에는 큰 차이가 없습니다. 있습니다. uimm5라는 오버레이를 R유형 형식에 추가하려면 마지막 필드 뒤에 다음 줄을 추가합니다.

  overlays:
    unsigned uimm5[5] = rs2;

추가해야 하는 유일한 새 형식은 U-Type 형식입니다. 이 이 형식을 사용하는 두 가지 명령어인 auipclui입니다. 둘 다 20비트 즉시 값을 사용하기 전에 12만큼 왼쪽으로 이동합니다. 레지스터에 pc를 추가하거나 (auipc) 레지스터에 직접 씁니다. (lui). 오버레이를 사용하면 즉시, 약간의 계산을 명령 실행에서 명령으로 이동 디코딩합니다. 먼저 표에 지정된 필드에 따라 형식을 추가하세요. 참조하세요. 그런 다음, 다음 오버레이를 추가할 수 있습니다.

  overlays:
    unsigned uimm32[32] = uimm20, 0b0000'0000'0000;

오버레이 구문을 사용하면 필드뿐 아니라 리터럴을 있습니다. 여기서는 12개의 0을 연결하여 실제로 왼쪽으로 이동 12로 높아졌습니다.

추가해야 하는 I-Type 안내는 다음과 같습니다.

  • addi - 즉시 추가.
  • andi - 비트 및 즉시
  • ori - 비트 또는 즉시입니다.
  • xori - 즉각적인 비트 xor입니다.

인코딩은 다음과 같습니다.

2020년 31월 19..15 2012년 14월 14일 11..7 6..0 opcode_name
imm12 rs1 000 번째 001 0011 Addi
imm12 rs1 111 번째 001 0011 안디
imm12 rs1 110 번째 001 0011 오리
imm12 rs1 100 번째 001 0011 소리
func3 명령 코드

추가해야 하는 R-유형 (특수 I-Type) 명령은 다음과 같습니다.

  • slli - 즉시 왼쪽 논리를 기준으로 이동.
  • srai - 즉시 오른쪽 산술을 이동합니다.
  • srli - 즉시 오른쪽으로 오른쪽으로 이동합니다.

인코딩은 다음과 같습니다.

2025년 31월 25일 2024년 2월 24일 19..15 2012년 14월 14일 11..7 6..0 명령 코드 이름
000 0000 uimm5 rs1 001 번째 001 0011 SLI
010 0000 uimm5 rs1 101 번째 001 0011 스라이
000 0000 uimm5 rs1 101 번째 001 0011 Srli
func7 func3 명령 코드

추가해야 하는 U-Type 안내는 다음과 같습니다.

  • auipc - PC에 상한 즉시를 추가합니다.
  • lui - 상한 즉시 로드.

인코딩은 다음과 같습니다.

2012년 12월 31일 11..7 6..0 명령 코드 이름
uimm20 번째 001 0111 auipc
uimm20 번째 011 0111 lui
명령 코드

변경한 다음 빌드하세요. 생성된 출력을 확인합니다. 단 이전처럼 작업한 내용을 riscv32i.bin_fmt입니다.


정의해야 하는 다음 명령 세트는 조건부 브랜치입니다. 명령, 점프 및 링크 명령, 점프 및 링크 레지스터 지시사항입니다.

추가하는 조건부 브랜치는 모두 B 유형 인코딩을 사용합니다.

2025년 31월 25일 2024년 2월 24일 19..15 2012년 14월 14일 11..7 6..0
7 5 5 3 5 7
imm7 rs2 rs1 func3 imm5 명령 코드

B-Type 인코딩은 레이아웃이 R-Type 인코딩과 동일하지만 RiscV 문서에 맞게 새로운 형식 유형을 사용하기로 선택합니다. 하지만 오버레이를 추가하여 적절한 브랜치를 가져올 수도 있습니다. R-유형의 func7rd 필드를 사용하여 즉시 변위 인코딩합니다.

위에 지정된 필드가 포함된 BType 형식을 추가해야 하지만 추가할 수는 없습니다. 충분합니다 보시다시피 즉시 입력은 두 개의 명령 필드로 나뉩니다. 또한 브랜치 명령은 이것을 살펴보겠습니다. 대신 각 필드가 추가로 파티션이 나뉘며 다른 순서로 연결됩니다. 마지막으로, 그 값은 하나는 16비트 정렬 오프셋을 얻는 것입니다.

즉시를 형성하는 데 사용되는 명령 단어의 비트 시퀀스는 31, 7, 30..25, 11..8. 여기에 해당하는 하위 필드 참조는 다음과 같습니다. 색인 또는 범위는 오른쪽에서 왼쪽으로 번호가 매겨진 필드의 비트를 지정합니다. 예: imm7[6]imm7의 msb를 나타내고 imm5[0]imm5입니다.

imm7[6], imm5[0], imm7[5..0], imm5[4..1]

이 비트 조작 부분을 브랜치 명령의 일부로 만드는 것은 두 가지입니다. 큰 단점이 있습니다 첫째, 시맨틱 함수의 구현을 자세히 살펴 보겠습니다. 둘째, 실행 시간을 늘리기 위해 발생할 수 있습니다 정답은 다음과 같은 오버레이를 BType 형식에 추가하는 것입니다. '0' 후행 왼쪽 시프트를 고려합니다.

  overlays:
    signed b_imm[13] = imm7[6], imm5[0], imm7[5..0], imm5[4..1], 0b0;

오버레이는 서명되어 있으므로 자동으로 부호 확장됩니다. 추출해야 합니다.

점프 및 링크 (즉시) 명령은 다음과 같이 J 유형 인코딩을 사용합니다.

2012년 12월 31일 11..7 6..0
20 5 7
imm20 번째 명령 코드

이것은 쉽게 추가할 수 있는 형식이지만, 마찬가지로 명령은 보이는 것만큼 간단하지 않습니다. 비트 시퀀스는 31, 19..12, 20, 30..21이고 마지막 직계는 반단어 정렬을 위해 왼쪽으로 1씩 이동 해결 방법은 오버레이 (왼쪽 시프트를 고려하여 21비트)를 형식으로 업데이트합니다.

  overlays:
    signed j_imm[21] = imm20[19, 7..0, 8, 18..9], 0b0;

보시다시피 오버레이의 구문은 필드를 약식 형식으로 입력합니다. 또한 필드 이름을 사용하지 않는 경우 비트 숫자는 지시어 자체를 나타내므로 위의 내용도 작성:

    signed j_imm[21] = [31, 19..12, 20, 30..21], 0b0;

마지막으로 점프 앤 링크 (레지스터)는 확인할 수 있습니다

I-Type 즉시 형식:

2020년 31월 19..15 2012년 14월 14일 11..7 6..0
12 5 3 5 7
imm12 rs1 func3 번째 명령 코드

이번에는 형식을 변경하지 않아도 됩니다.

추가해야 하는 브랜치 명령어는 다음과 같습니다.

  • beq - 같으면 분기합니다.
  • bge - 이상인 경우 분기합니다.
  • bgeu - 부호가 없는 경우보다 크거나 같은 경우 분기합니다.
  • blt - 미만이면 분기합니다.
  • bltu - 부호 없는 경우보다 작은 경우 분기합니다.
  • bne - 같지 않으면 분기합니다.

이들은 다음과 같이 인코딩됩니다.

2025년 31월 25일 2024년 2월 24일 19..15 2012년 14월 14일 11..7 6..0 명령 코드 이름
imm7 rs2 rs1 000 imm5 110 0011 Beq
imm7 rs2 rs1 101 imm5 110 0011 Bge
imm7 rs2 rs1 111 imm5 110 0011 브게우
imm7 rs2 rs1 100 imm5 110 0011 blt
imm7 rs2 rs1 110 imm5 110 0011 bltu
imm7 rs2 rs1 001 imm5 110 0011 bne
func3 명령 코드

jal 명령어는 다음과 같이 인코딩됩니다.

2012년 12월 31일 11..7 6..0 명령 코드 이름
imm20 번째 110 1111 Jal
명령 코드

jalr 명령어는 다음과 같이 인코딩됩니다.

2020년 31월 19..15 2012년 14월 14일 11..7 6..0 opcode_name
imm12 rs1 000 번째 110 0111 Jalr
func3 명령 코드

변경한 다음 빌드하세요. 생성된 출력을 확인합니다. 단 이전처럼 작업한 내용을 riscv32i.bin_fmt입니다.


매장 안내 추가

스토어 명령은 B-유형과 동일한 S-유형 인코딩을 사용합니다. 브랜치 명령에 의해 사용되는 인코딩(인코딩과 유사하지만 있습니다. RiscV와 일치하도록 SType 형식을 추가합니다. 문서를 참조하세요.

2025년 31월 25일 2024년 2월 24일 19..15 2012년 14월 14일 11..7 6..0
7 5 5 3 5 7
imm7 rs2 rs1 func3 imm5 명령 코드

SType 형식의 경우 즉시는 다행히 스트레이트입니다. 두 개의 직접적인 필드를 정방향으로 연결하므로 오버레이 사양은 다음과 같습니다.

  overlays:
    signed s_imm[12] = imm7, imm5;

전체 필드를 연결할 때는 비트 범위 지정자가 필요하지 않습니다.

저장 명령은 다음과 같이 인코딩됩니다.

2025년 31월 25일 2024년 2월 24일 19..15 2012년 14월 14일 11..7 6..0 명령 코드 이름
imm7 rs2 rs1 000 imm5 010 0011 sb
imm7 rs2 rs1 001 imm5 010 0011 sh
imm7 rs2 rs1 010 imm5 010 0011 sw
func3 명령 코드

변경한 다음 빌드하세요. 생성된 출력을 확인합니다. 단 이전처럼 작업한 내용을 riscv32i.bin_fmt입니다.


로드 안내 추가

로드 안내에서는 I-Type 형식을 사용합니다. 여기서 변경할 사항이 없습니다.

인코딩은 다음과 같습니다.

2020년 31월 19..15 2012년 14월 14일 11..7 6..0 opcode_name
imm12 rs1 000 번째 000 0011 lb
imm12 rs1 100 번째 000 0011 Lbu
imm12 rs1 001 번째 000 0011
imm12 rs1 101 번째 000 0011
imm12 rs1 010 번째 000 0011 lw
func3 명령 코드

변경한 다음 빌드하세요. 생성된 출력을 확인합니다. 단 이전처럼 작업한 내용을 riscv32i.bin_fmt입니다.

이것으로 튜토리얼을 마칩니다. 도움이 되었기를 바랍니다.