이 가이드의 목표는 다음과 같습니다.
- 바이너리 형식 설명 파일의 구조와 구문을 알아봅니다.
- 바이너리 형식 설명이 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
라는 형식을 추가합니다. 모든 비트필드
RType
은 unsigned
입니다. 이름, 비트 너비, 순서 (왼쪽에서 오른쪽 순)를 사용합니다.
형식을 정의합니다. 힌트가 필요하거나
완전한 솔루션인
여기를 클릭하세요.
다음으로 지침을 추가해야 합니다. 지침은 다음과 같습니다.
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
제약 조건은 대부분의 안내에서 다릅니다. add
및
sub
입니다.
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 형식입니다. 다른 필드를 추가하면 너비가 늘어나므로 필드를 추가할 수 없습니다.
오버레이를 추가할 수 있습니다. 오버레이는 오버레이의
형식으로 된 비트의 숫자로, 비트의 여러 하위 시퀀스를 결합하는 데
형식을 별도의 명명된 항목으로 변환합니다. 부작용은 생성된 코드가
이제 오버레이용 추출 함수도 포함됩니다.
설명하겠습니다. 이 경우 rs2
와 uimm5
모두 서명이 취소되면
필드가 사용된다는 점을 명시하는 것 외에는 큰 차이가 없습니다.
있습니다. uimm5
라는 오버레이를 R유형 형식에 추가하려면
마지막 필드 뒤에 다음 줄을 추가합니다.
overlays:
unsigned uimm5[5] = rs2;
추가해야 하는 유일한 새 형식은 U-Type 형식입니다. 이
이 형식을 사용하는 두 가지 명령어인 auipc
와
lui
입니다. 둘 다 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-유형의 func7
및 rd
필드를 사용하여 즉시 변위
인코딩합니다.
위에 지정된 필드가 포함된 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입니다.
이것으로 튜토리얼을 마칩니다. 도움이 되었기를 바랍니다.