이 가이드의 목표는 다음과 같습니다.
- MPACT-Sim 시뮬레이터에서 명령이 어떻게 나타나는지 알아보세요.
- ISA 설명 파일의 구조와 구문에 대해 알아보십시오.
- RiscV RV32I 하위 집합 명령의 ISA 설명 작성
개요
MPACT-Sim에서 대상 명령은 정보를 더 많이 사용할 수 있도록 하는 표현을 사용하고 의미 체계를 더 빠르게 실행할 수 있습니다. 이러한 명령 인스턴스는 캐시에 저장된 데이터를 캐시하여 자주 실행되는 명령이 실행됩니다
안내 클래스
시작하기 전에 안내가 어떤 방식으로 작동하는지 살펴보면
MPACT-Sim으로 표현됩니다. Instruction
클래스는 다음에 정의되어 있습니다.
mpact-sim/mpact/sim/generic/instruction.h.
Instruction 클래스 인스턴스에는 명령이 '실행'될 때 다음과 같이 시뮬레이션합니다.
- 안내 주소, 시뮬레이션된 안내 크기(예: .text 형식 크기)
- 명령 명령 코드.
- 조건자 피연산자 인터페이스 포인터 (해당하는 경우)
- 소스 피연산자 인터페이스 포인터의 벡터입니다.
- 대상 피연산자 인터페이스 포인터의 벡터입니다.
- 시맨틱 함수를 호출할 수 있습니다.
- 아키텍처 상태 객체를 가리키는 포인터
- 컨텍스트 객체를 가리키는 포인터입니다.
- 하위 및 다음 명령 인스턴스를 가리키는 포인터입니다.
- 분해 문자열입니다.
이러한 인스턴스는 일반적으로 명령어 (인스턴스) 캐시에 저장되며, 명령이 재실행될 때마다 재사용됩니다. 이렇게 하면 성능이 향상됩니다. 실행할 수 있습니다
컨텍스트 객체에 대한 포인터를 제외하고 모두 컨텍스트 객체에 의해 명령 디코더로 구성됩니다. 이 작업 튜토리얼에서 이러한 항목의 세부사항을 알 필요는 없습니다. 사용할 수 있습니다 대신 그것이 어떻게 사용되는지 개괄적으로 이해하면 충분합니다
호출 가능한 시맨틱 함수는 C++ 함수/메서드/함수 객체입니다.
(람다 포함) - 명령어의 의미 체계를 구현합니다. 대상
예를 들어 add
명령어의 경우 각 소스 피연산자를 로드하고 두 피연산자를 더합니다.
하나의 대상 피연산자에 결과를 기록합니다. 이번 주제는
시맨틱 함수에 대해서는 시맨틱 함수 가이드에서 자세히 다룹니다.
명령 피연산자
명령 클래스에는 다음과 같은 세 가지 유형의 피연산자 인터페이스에 대한 포인터가 포함됩니다. 조건자, 소스, 대상입니다. 이러한 인터페이스를 사용하면 시맨틱 함수가 기본 명령어의 실제 유형과 별도로 작성되어야 하는 경우 피연산자입니다. 예를 들어, 레지스터와 즉시의 값에 액세스하는 것은 동일한 인터페이스를 통해 사용할 수 있습니다. 즉, 동일한 작업을 수행하는 명령이 다른 피연산자 (예: 레지스터와 즉시)에 관한 연산이 동일한 시맨틱 함수를 사용하여 구현됩니다
조건자 피연산자 인터페이스: 조건자 피연산자 명령 실행 (다른 ISA의 경우 null임)은 주어진 명령은 조건자의 불리언 값에 기반하여 실행되어야 합니다.
// The predicte operand interface is intended primarily as the interface to
// read the value of instruction predicates. It is separated from source
// predicates to avoid mixing it in with the source operands needed for modeling
// the instruction semantics.
class PredicateOperandInterface {
public:
virtual bool Value() = 0;
// Return a string representation of the operand suitable for display in
// disassembly.
virtual std::string AsString() const = 0;
virtual ~PredicateOperandInterface() = default;
};
명령어 시맨틱 함수가 피연산자 입력을 읽을 수 있도록 하는 소스 피연산자 인터페이스 기본 피연산자와 관계없이 명령어 피연산자의 값 반환 있습니다. 인터페이스 메서드는 스칼라값 피연산자와 벡터 값 피연산자를 모두 지원합니다.
// The source operand interface provides an interface to access input values
// to instructions in a way that is agnostic about the underlying implementation
// of those values (eg., register, fifo, immediate, predicate, etc).
class SourceOperandInterface {
public:
// Methods for accessing the nth value element.
virtual bool AsBool(int index) = 0;
virtual int8_t AsInt8(int index) = 0;
virtual uint8_t AsUint8(int index) = 0;
virtual int16_t AsInt16(int index) = 0;
virtual uint16_t AsUint16(int) = 0;
virtual int32_t AsInt32(int index) = 0;
virtual uint32_t AsUint32(int index) = 0;
virtual int64_t AsInt64(int index) = 0;
virtual uint64_t AsUint64(int index) = 0;
// Return a pointer to the object instance that implements the state in
// question (or nullptr) if no such object "makes sense". This is used if
// the object requires additional manipulation - such as a fifo that needs
// to be pop'ed. If no such manipulation is required, nullptr should be
// returned.
virtual std::any GetObject() const = 0;
// Return the shape of the operand (the number of elements in each dimension).
// For instance {1} indicates a scalar quantity, whereas {128} indicates an
// 128 element vector quantity.
virtual std::vector<int> shape() const = 0;
// Return a string representation of the operand suitable for display in
// disassembly.
virtual std::string AsString() const = 0;
virtual ~SourceOperandInterface() = default;
};
대상 피연산자 인터페이스는
DataBuffer
인스턴스 (레지스터 값을 저장하는 데 사용되는 내부 데이터 유형) 가
대상 피연산자에도 지연 시간이 있으며, 이는 피연산자의
명령에 의해 할당되는 데이터 버퍼 인스턴스가
시맨틱 함수가 타겟 레지스터의 값을 업데이트하는 데 사용됩니다. 대상
예를 들어 add
명령어의 지연 시간은 1인 반면, mpy
명령어의 경우
명령은 4일 수 있습니다. 이 내용은
시맨틱 함수에 대한 튜토리얼에서 확인할 수 있습니다.
// The destination operand interface is used by instruction semantic functions
// to get a writable DataBuffer associated with a piece of simulated state to
// which the new value can be written, and then used to update the value of
// the piece of state with a given latency.
class DestinationOperandInterface {
public:
virtual ~DestinationOperandInterface() = default;
// Allocates a data buffer with ownership, latency and delay line set up.
virtual DataBuffer *AllocateDataBuffer() = 0;
// Takes an existing data buffer, and initializes it for the destination
// as if AllocateDataBuffer had been called.
virtual void InitializeDataBuffer(DataBuffer *db) = 0;
// Allocates and initializes data buffer as if AllocateDataBuffer had been
// called, but also copies in the value from the current value of the
// destination.
virtual DataBuffer *CopyDataBuffer() = 0;
// Returns the latency associated with the destination operand.
virtual int latency() const = 0;
// Return a pointer to the object instance that implmements the state in
// question (or nullptr if no such object "makes sense").
virtual std::any GetObject() const = 0;
// Returns the order of the destination operand (size in each dimension).
virtual std::vector<int> shape() const = 0;
// Return a string representation of the operand suitable for display in
// disassembly.
virtual std::string AsString() const = 0;
};
ISA 설명
프로세서의 ISA (명령 집합 아키텍처)는 추상 모델을 정의합니다. 상호작용하는 방식을 규정합니다. 이는 포드의 데이터 유형, 레지스터 및 기타 머신 상태가 그 동작 (시맨틱스)을 파악할 수 있습니다. 목적 ISA에는 명령의 실제 인코딩이 포함되지 않습니다. 이는 별도로 취급됩니다.
프로세서 ISA는 추상적이고 인코딩에 관계없이 설정된 명령 집합으로 사용됩니다. 설명 파일 사용 가능한 명령 집합을 열거합니다. 각 명령에 대해 피연산자의 이름, 피연산자의 수와 이름 및 피연산자의 의미 체계를 구현하는 C++ 함수/호출 가능 항목에 바인딩합니다. 또한 디스어셈블리 서식 문자열과 명령의 사용법을 지정할 수 있습니다. 리소스 이름을 입력합니다 전자는 텍스트 분류를 생성하는 데 유용합니다. 디버그, 추적 또는 대화형 사용을 위한 명령 표현입니다. 이 후자를 사용하여 시뮬레이션에서 주기 정확도를 높일 수 있습니다.
ISA 설명 파일은 다음에 대한 코드를 생성하는 isa-파서에 의해 파싱됩니다. 표현에 의존하지 않는 명령 디코더를 사용합니다. 이 디코더는 명령어 객체의 필드를 채웁니다. 구체적인 값(예: 대상 레지스터 번호는 형식별 명령에서 얻어집니다. 디코더로 구성됩니다. 이러한 디코더 중 하나가 바로 바이너리 디코더로, 다음 튜토리얼에서 확인할 수 있습니다.
이 가이드에서는 간단한 스칼라에 대한 ISA 설명 파일을 작성하는 방법을 설명합니다. 살펴봤습니다 RiscV RV32I 명령의 하위 집합을 사용하여 이를 설명하고 다른 튜토리얼과 함께 인코더-디코더 아키텍처를 'Hello World'를 시뮬레이션하는 있습니다. RiscV ISA에 대한 자세한 내용은 Risc-V 사양.
먼저 파일을 엽니다.
riscv_isa_decoder/riscv32i.isa
파일의 콘텐츠는 여러 섹션으로 나뉩니다. 첫 번째는 ISA가 선언:
isa RiscV32I {
namespace mpact::sim::codelab;
slots { riscv32; }
}
이렇게 하면 RiscV32I
가 ISA의 이름으로 선언되고 코드 생성기는
다음과 같은 인터페이스를 정의하는 RiscV32IEncodingBase
라는 클래스를 만듭니다.
생성된 디코더가 명령 코드와 피연산자 정보를 얻는 데 사용합니다. 이름
이 클래스는 ISA 이름을 파스칼 표기법으로 변환하여 생성되며,
EncodingBase
와 연결합니다. 선언 slots { riscv32; }
RiscV32I에 단일 명령 슬롯 riscv32
만 있음을 지정합니다.
ISA (VLIW 명령의 다중 슬롯과 대조)를 사용하고 있고
유효한 명령어는 riscv32
에서 실행하도록 정의된 명령어입니다.
// First disasm fragment is 15 char wide and left justified.
disasm widths = {-15};
이는 디스어셈블리의 첫 번째 분해 프래그먼트가 사양 (자세한 내용은 아래 참조)에서 15자 이내로 와이드 필드입니다. 이후의 모든 프래그먼트는 작업 없이 이 필드에 추가됩니다. 공백을 추가할 수 있습니다.
그 아래에는 세 개의 슬롯 선언인 riscv32i
, zicsr
, riscv32
가 있습니다.
위의 isa
정의에 따라 riscv32
에 정의된 명령어만
슬롯은 RiscV32I
isa의 일부가 됩니다. 다른 두 슬롯은 어떤 용도인가요?
슬롯을 사용하여 명령을 개별 그룹으로 분해할 수 있으며, 그 다음에는
끝에 단일 슬롯으로 결합됩니다. : riscv32i, zicsr
표기법에 유의하세요.
riscv32
슬롯 선언에 포함되어야 합니다. 이는 슬롯 riscv32
가
zicsr
및 riscv32i
슬롯에 정의된 모든 명령을 사용합니다. RiscV 32비트 ISA
RV32I라는 기본 ISA로 구성되며, 선택적 확장 프로그램 집합은
추가할 수 있습니다. 슬롯 메커니즘을 사용하면 이러한 확장 프로그램의 명령을
필요에 따라 결합하여 지정한 다음
전체 ISA 이 경우 RiscV 'I' 그룹이 정의됨
'zicsr'에 포함된 것과 별도로 그룹 추가 그룹을 정의할 수 있음
검색어: 'M' (곱하기/나누기), 'F' (단정밀도 부동 소수점), 'D'
(배정밀도 부동 소수점), 'C' (컴팩트 16비트 명령) 등
.
// The RiscV 'I' instructions.
slot riscv32i {
...
}
// RiscV32 CSR manipulation instructions.
slot zicsr {
...
}
// The final instruction set combines riscv32i and zicsr.
slot riscv32 : riscv32i, zicsr {
...
}
zicsr
및 riscv32
슬롯 정의는 변경할 필요가 없습니다. 하지만
이 튜토리얼에서는 riscv32i
에 필요한 정의를 추가하는 데 중점을 둡니다.
있습니다. 현재 이 슬롯에 정의된 내용을 자세히 살펴보겠습니다.
// The RiscV 'I' instructions.
slot riscv32i {
// Include file that contains the declarations of the semantic functions for
// the 'I' instructions.
includes {
#include "learning/brain/research/mpact/sim/codelab/riscv_semantic_functions/solution/rv32i_instructions.h"
}
// These are all 32 bit instructions, so set default size to 4.
default size = 4;
// Model these with 0 latency to avoid buffering the result. Since RiscV
// instructions have sequential semantics this is fine.
default latency = 0;
// The opcodes.
opcodes {
fence{: imm12 : },
semfunc: "&RV32IFence"c
disasm: "fence";
ebreak{},
semfunc: "&RV32IEbreak",
disasm: "ebreak";
}
}
첫째, includes {}
섹션이 있는데 이 섹션에는
이 슬롯이 참조될 때 생성된 코드에 포함될 수 있습니다.
최종 ISA에서 간접적으로
공격을 받을 수 있습니다 포함 파일은 전역적으로
범위가 지정된 includes {}
섹션. 이 경우 항상 포함됩니다. 이렇게 하면
동일한 include 파일을 각 슬롯에 추가해야 하는 경우 편리합니다.
정의합니다.
default size
및 default latency
선언에서는 다음을 정의합니다.
달리 지정하지 않으면 명령의 크기는 4이고 명령의 지연 시간은
대상 피연산자 쓰기의 주기는 0회입니다. 참고: 명령어 크기는
는
시뮬레이션된 명령 프롬프트에서 실행할 다음 순차적 명령의
프로세서 이 값은
명령 표현을 사용합니다.
슬롯 정의의 중심에는 명령 코드 섹션이 있습니다. 보시다시피
명령 코드 (명령) fence
및 ebreak
는 지금까지
riscv32i
입니다. fence
명령 코드는 이름 (fence
)을 지정하여 정의됩니다.
피연산자 사양 ({: imm12 : }
), 선택적 디스어셈블리
형식 ("fence"
)으로, 시맨틱으로 바인딩될 호출 가능 함수
함수 ("&RV32IFence"
)를 호출합니다.
명령 피연산자는 삼중으로 지정되며, 각 구성요소는
세미콜론으로 구분, 조건자 ':' 소스 피연산자 목록 ':'
대상 피연산자 목록. 소스 및 대상 피연산자 목록은 쉼표입니다.
피연산자 이름의 목록이 포함되어 있습니다. 보시다시피 명령 피연산자는
fence
명령어에 조건자 피연산자 없음, 단일 소스만 포함
피연산자 이름이 imm12
이고 대상 피연산자가 없습니다. RiscV RV32I 하위 집합은
조건자 실행을 지원하지 않으므로 조건자 피연산자가 항상 비어 있습니다.
이 튜토리얼에서 확인할 수 있습니다.
의미론적 함수는 C++
함수 또는 호출 가능 함수를 호출하여 시맨틱 함수를 호출하는 데 사용할 수 있습니다.
시맨틱 함수/호출 가능 항목은 void(Instruction *)
입니다.
분해 사양은 쉼표로 구분된 문자열 목록으로 구성됩니다.
일반적으로 두 개의 문자열만 사용됩니다. 하나는 명령 코드용이고 하나는
피연산자. 형식이 지정되면 (명령에서 AsString()
호출 사용) 각각
문자열은 disasm widths
에 따라 필드 내에서 형식이 지정됩니다.
사양을 참조하세요.
다음 연습을 통해 riscv32i.isa
파일에 안내를 추가할 수 있습니다.
'Hello World'를 시뮬레이션하기에 충분한 있습니다. 급한 사용자를 위해
솔루션에서 찾을 수 있습니다.
riscv32i.isa
및
rv32i_instructions.h
초기 빌드 실행
디렉터리를 riscv_isa_decoder
로 변경하지 않았다면 지금 변경합니다. 그런 다음
프로젝트를 다음과 같이 빌드합니다. 이 빌드가 성공해야 합니다.
$ cd riscv_isa_decoder
$ bazel build :all
디렉터리를 다시 저장소 루트로 변경한 후
소스부터 검토됩니다 이를 위해 디렉터리를
bazel-out/k8-fastbuild/bin/riscv_isa_decoder
(x86을 사용한다고 가정
호스트 - 다른 호스트의 경우 k8-fastbuild는 다른 문자열입니다).
$ cd ..
$ cd bazel-out/k8-fastbuild/bin/riscv_isa_decoder
이 디렉터리에는 다음과 같은 파일이 있습니다. 생성된 C++ 파일을 제공합니다.
riscv32i_decoder.h
riscv32i_decoder.cc
riscv32i_enums.h
riscv32i_enums.cc
브라우저에서 riscv32i_enums.h
를 클릭하여 살펴보겠습니다. 해야 할 일
다음과 같은 내용이 포함되어 있음을 알 수 있습니다.
#ifndef RISCV32I_ENUMS_H
#define RISCV32I_ENUMS_H
namespace mpact {
namespace sim {
namespace codelab {
enum class SlotEnum {
kNone = 0,
kRiscv32,
};
enum class PredOpEnum {
kNone = 0,
kPastMaxValue = 1,
};
enum class SourceOpEnum {
kNone = 0,
kCsr = 1,
kImm12 = 2,
kRs1 = 3,
kPastMaxValue = 4,
};
enum class DestOpEnum {
kNone = 0,
kCsr = 1,
kRd = 2,
kPastMaxValue = 3,
};
enum class OpcodeEnum {
kNone = 0,
kCsrs = 1,
kCsrsNw = 2,
kCsrwNr = 3,
kEbreak = 4,
kFence = 5,
kPastMaxValue = 6
};
constexpr char kNoneName[] = "none";
constexpr char kCsrsName[] = "Csrs";
constexpr char kCsrsNwName[] = "CsrsNw";
constexpr char kCsrwNrName[] = "CsrwNr";
constexpr char kEbreakName[] = "Ebreak";
constexpr char kFenceName[] = "Fence";
extern const char *kOpcodeNames[static_cast<int>(
OpcodeEnum::kPastMaxValue)];
enum class SimpleResourceEnum {
kNone = 0,
kPastMaxValue = 1
};
enum class ComplexResourceEnum {
kNone = 0,
kPastMaxValue = 1
};
enum class AttributeEnum {
kPastMaxValue = 0
};
} // namespace codelab
} // namespace sim
} // namespace mpact
#endif // RISCV32I_ENUMS_H
보시다시피
riscv32i.isa
파일은 열거형 중 하나로 정의됩니다. 또한
명령 코드의 모든 이름을 저장하는 OpcodeNames
배열이 있습니다.
riscv32i_enums.cc
에 정의되어 있음). 다른 파일에는 생성된 디코더,
이에 대해서는 다른 튜토리얼에서 더 자세히 다루겠습니다.
Bazel 빌드 규칙
Bazel의 ISA 디코더 대상은
mpact/sim/decoder/mpact_sim_isa.bzl
에서 로드된 mpact_isa_decoder
(mpact-sim
저장소에 있음) 이 가이드에서는
riscv_isa_decoder/BUILD
는 다음과 같습니다.
mpact_isa_decoder(
name = "riscv32i_isa",
src = "riscv32i.isa",
includes = [],
isa_name = "RiscV32I",
deps = [
"//riscv_semantic_functions:riscv32i",
],
)
이 규칙은 ISA 파서 툴과 생성기를 호출하여 C++ 코드를 생성하고,
그런 다음 생성된 를
//riscv_isa_decoder:riscv32i_isa
라벨 includes
섹션은 사용됩니다.
소스 파일에 포함될 수 있는 추가 .isa
파일을 지정합니다. 이
isa_name
는 특정 isa를 지정하는 데 사용되며, 2개 이상인 경우 필수입니다.
디코더를 생성할 소스 파일에 포함됩니다.
레지스터-레지스터 ALU 명령 추가
이제 riscv32i.isa
파일에 몇 가지 새로운 안내를 추가해 보겠습니다. 첫 번째
명령 그룹은 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 | 번째 | 명령 코드 |
.isa
파일은 형식에 구애받지 않는 디코더를 생성하는 데 사용되지만
바이너리 형식과 레이아웃을 고려하는 것이 좋습니다. 사용자로서
디코더를 채우는 디코더와 관련이 있는 세 개의 필드가 있는데,
명령어 객체: rs2
, rs1
, rd
. 이 시점에서
같은 방식으로 인코딩된 정수 레지스터 (비트 시퀀스)에 대한 이러한 이름
모든 명령에서 동일한 명령 필드에 사용할 수 있습니다.
추가할 안내는 다음과 같습니다.
add
- 정수 덧셈.and
- 비트 AND입니다.or
- 비트 OR입니다.sll
- 논리를 왼쪽으로 이동sltu
- 보다 작음, 부호 없음으로 설정합니다.sub
- 정수 뺄셈.xor
- 비트 xor.
이러한 각 안내는 opcodes
섹션의
riscv32i
슬롯 정의. 이름, 명령 코드,
디스어셈블리 및 의미적 함수입니다. 이름은 간단하지만
위의 명령 코드 이름을 사용하겠습니다. 또한 모두 동일한 피연산자를 사용하므로
피연산자 사양에 { : rs1, rs2 : rd}
를 사용할 수 있습니다. 즉,
rs1에 의해 지정된 레지스터 소스 피연산자는 소스의 색인 0을 갖습니다.
명령 객체의 피연산자 벡터, 지정된 레지스터 소스 피연산자
rs2는 색인 1을 가지며 rd로 지정된 레지스터 대상 피연산자는
대상 피연산자 벡터 (색인 0)의 유일한 요소가 됩니다.
다음은 시맨틱 함수 사양입니다. 이 작업은 키워드
semfunc
및 할당에 사용할 수 있는 callable 함수를 지정하는 C++ 문자열
std::function
로 변경합니다. 이 튜토리얼에서는 함수를 사용하므로 호출 가능 함수
문자열은 "&MyFunctionName"
입니다.
fence
명령이며 "&RV32IAdd"
, "&RV32IAnd"
등이어야 합니다.
마지막은 분해 사양입니다. 키워드 disasm
로 시작하고
이어서 쉼표로 구분된 문자열 목록이 옵니다. 이 문자열은
명령은 문자열로 출력되어야 합니다. 앞에 %
기호를 사용하면
피연산자 이름은
확인할 수 있습니다. add
명령어의 경우 disasm: "add", "%rd,
%rs1,%rs2"
입니다. 즉, add
명령어의 항목이 다음과 같이 표시됩니다.
예를 들면 다음과 같습니다.
add{ : rs1, rs2 : rd},
semfunc: "&RV32IAdd",
disasm: "add", "%rd, %rs1, %rs2";
계속해서 riscv32i.isa
파일을 수정하고 다음 안내를 모두
.isa
설명 도움이 필요하거나 작업을 확인하려면
설명 파일이
여기에서 확인할 수 있습니다.
안내를 riscv32i.isa
파일에 추가한 후에는
이전에 작성한 새 시맨틱 함수 각각에 대한 함수 선언을
다음 위치에 있는 rv32i_instructions.h
파일을 참조합니다.
`../semantic_functions/. 도움이 필요하거나 작업을 확인하려면
정답은
여기에서 확인할 수 있습니다.
이 모든 작업이 완료되면 다시 riscv_isa_decoder
로 변경합니다.
다시 빌드하면 됩니다 생성된 소스 파일을 자유롭게 검사하세요.
즉시 입력으로 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 | 번째 | 명령 코드 |
보시다시피 피연산자 이름 rs1
와 rd
은
정수 레지스터를 나타내는 데 사용되므로 이러한 이름은
유지됨. 즉각적인 값 필드의 길이와 위치가 다릅니다.
2개 (uimm5
및 uimm20
)는 부호가 없는 반면 imm12
는 부호가 있습니다. 각각의
자체 이름을 사용합니다
따라서 I-Type 명령어의 피연산자는 { : rs1, imm12 :rd
}
여야 합니다. 특수한 I-Type 명령어의 경우 { : rs1, uimm5 : rd}
이어야 합니다.
U-Type 명령 피연산자 사양은 { : uimm20 : rd }
여야 합니다.
추가해야 하는 I-Type 안내는 다음과 같습니다.
addi
- 즉시 추가.andi
- 비트 및 즉시ori
- 비트 또는 즉시입니다.xori
- 즉각적인 비트 xor입니다.
추가해야 하는 특수 I-Type 명령은 다음과 같습니다.
slli
- 즉시 왼쪽 논리를 기준으로 이동.srai
- 즉시 오른쪽 산술을 이동합니다.srli
- 즉시 오른쪽으로 오른쪽으로 이동합니다.
추가해야 하는 U-Type 안내는 다음과 같습니다.
auipc
- PC에 상한 즉시를 추가합니다.lui
- 상한 즉시 로드.
명령 코드에 사용할 이름은 명령 이름에서 자연스럽게 따릅니다.
모두 고유하므로 새로 만들 필요가 없습니다. 예를 들어
의미론적 함수를 지정하면, 명령 객체가
기본 피연산자에 구속되지 않는 소스 피연산자에 대한 인터페이스
있습니다. 이것은 동일한 연산을 가지지만
피연산자 유형이 다를 수 있고 동일한 시맨틱 함수를 공유할 수 있음 예를 들면 다음과 같습니다.
addi
명령어는 다음과 같은 경우 add
명령어와 동일한 연산을 수행합니다.
피연산자 유형을 무시하므로 동일한 시맨틱 함수를 사용할 수 있음
사양 "&RV32IAdd"
. andi
, ori
, xori
, slli
도 마찬가지입니다.
다른 명령에서는 새로운 시맨틱 함수를 사용하지만 이름을 지정해야 합니다.
피연산자가 아닌 연산을 기반으로 하므로 srai
의 경우 "&RV32ISra"
를 사용합니다. 이
U 유형 명령어 auipc
와 lui
에는 상응하는 등록 항목이 없으므로 괜찮습니다.
"&RV32IAuipc"
및 "&RV32ILui"
를 사용합니다.
분해 문자열은 이전 연습의 문자열과 매우 비슷하지만
예상대로 %rs2
에 대한 참조가 %imm12
, %uimm5
로 대체됩니다.
또는 %uimm20
를 적절하게 사용하세요.
계속 진행하여 변경하고 빌드합니다. 생성된 출력을 확인합니다. 인코더-디코더 아키텍처를 이전 버전과 비교해 봤을 때 riscv32i.isa 및 rv32i_instructions.h
지점 추가 및 점프 앤 링크 지침 추가
추가해야 하는 브랜치와 이동 및 링크 명령어는 대상을 사용합니다.
명령 자체에만 암시되는 피연산자, 즉 다음 pc
값으로 사용됩니다. 이 단계에서는 이를
next_pc
이후 튜토리얼에서 자세히 정의됩니다.
브랜치 안내
우리가 추가하는 브랜치는 모두 B 유형 인코딩을 사용합니다.
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 | 임 | 임 | 명령 코드 |
서로 다른 직접 필드는 12비트 부호 있는 즉시 필드로 연결됩니다.
값으로 사용됩니다. 실제로 관련이 없는 형식이므로 이를 즉시
bimm12
: 12비트 브랜치 즉시 단편화는
바이너리 디코더 만들기에 대한 다음 튜토리얼에서 확인하시기 바랍니다. 모든
브랜치 명령은 rs1과 rs2에 의해 지정된 정수 레지스터를 비교합니다.
조건이 true이면 현재 값이 현재 pc 값에 추가됩니다.
실행할 다음 명령의 주소를 생성합니다. 이
따라서 브랜치 명령어는 { : rs1, rs2, bimm12 : next_pc }
이어야 합니다.
추가해야 하는 브랜치 명령어는 다음과 같습니다.
beq
- 같으면 분기합니다.bge
- 이상인 경우 분기합니다.bgeu
- 부호가 없는 경우보다 크거나 같은 경우 분기합니다.blt
- 미만이면 분기합니다.bltu
- 부호 없는 경우보다 작은 경우 분기합니다.bne
- 같지 않으면 분기합니다.
이러한 명령 코드 이름은 모두 고유하므로 .isa
에서 재사용할 수 있습니다.
설명 물론 새로운 시맨틱 함수 이름을 추가해야 합니다(예:
"&RV32IBeq"
등
분해 사양은 이제 좀 더 까다롭습니다.
명령을 사용하여 목적지를 계산하는 데 사용되며
명령어 피연산자입니다. 그러나 이 데이터는
명령어 객체를 사용할 수 있습니다. 해결책은
표현식 문법을 읽을 수 있습니다. '%' 대신 다음에
피연산자 이름에 %(expression: print format)을 입력하실 수 있습니다. 매우 간단함
표현식이 지원되지만 주소와 오프셋도 그중 하나이며 @
현재 명령 주소에 사용되는 기호입니다. 인쇄 형식은 다음과 유사합니다.
C 스타일 printf 형식이지만 선행 %
가 없음 디스어셈블리 형식은
그러면 beq
명령어는 다음과 같이 됩니다.
disasm: "beq", "%rs1, %rs2, %(@+bimm12:08x)"
점프 앤 링크 안내
점프 및 링크 명령 두 개만 추가하면 됩니다. jal
(점프 앤 링크) 및
jalr
(간접 점프 앤 링크).
jal
명령어는 J 유형 인코딩을 사용합니다.
31 | 21년 30월 30일 | 20 | 2012년 19월 12일 | 11..7 | 6..0 |
---|---|---|---|---|---|
1 | 10 | 1 | 8 | 5 | 7 |
임 | 임 | 임 | 임 | 번째 | 명령 코드 |
브랜치 명령과 마찬가지로, 20 비트 즉각적 인은 여러 개의
필드가 여러 개이므로 이름을 jimm20
로 지정합니다. 단편화는 중요하지 않음
지금쯤 다루지만 다음 동영상에서 다룰 예정입니다.
바이너리 디코더 만들기 튜토리얼을 참조하세요. 피연산자
사양은 { : jimm20 : next_pc, rd }
가 됩니다. 두 가지
대상 피연산자, 다음 pc 값 및 링크 레지스터 등
지시사항입니다.
위의 브랜치 안내와 마찬가지로 분해 형식은 다음과 같습니다.
disasm: "jal", "%rd, %(@+jimm20:08x)"
간접 점프 및 링크는 12비트 즉시 입력과 함께 I-Type 형식을 사용합니다. 그것은
부호 확장 즉시 값을
rs1
: 타겟 안내 주소를 생성합니다. 링크 레지스터는
rd
로 지정된 정수 레지스터
2020년 31월 | 19..15 | 2012년 14월 14일 | 11..7 | 6..0 |
---|---|---|---|---|
12 | 5 | 3 | 5 | 7 |
imm12 | rs1 | func3 | 번째 | 명령 코드 |
패턴을 보셨다면 이제 피연산자 사양이
jalr
의 경우 { : rs1, imm12 : next_pc, rd }
여야 하며 디스어셈블리는
사양:
disasm: "jalr", "%rd, %rs1, %imm12"
변경한 다음 빌드하세요. 생성된 출력을 확인합니다. 단 이전처럼 작업한 내용을 riscv32i.isa 및 rv32i_instructions.h
매장 안내 추가
매장 안내는 매우 간단합니다. 모두 S유형 형식을 사용합니다.
2025년 31월 25일 | 2024년 2월 24일 | 19..15 | 2012년 14월 14일 | 11..7 | 6..0 |
---|---|---|---|---|---|
7 | 5 | 5 | 3 | 5 | 7 |
임 | rs2 | rs1 | func3 | 임 | 명령 코드 |
보시다시피, 이것은 단편화된 12비트 즉시의 또 다른 사례입니다.
simm12
로 지정합니다. 저장 명령은 모두 정수의 값을 저장합니다.
rs2에 의해 지정된 레지스터를
rs1로 지정된 정수 레지스터의 값을
12 비트 즉시입니다. 피연산자 형식은 { : rs1, simm12, rs2 }
이어야 합니다.
확인하시기 바랍니다.
구현해야 하는 매장 안내는 다음과 같습니다.
sb
- 저장 바이트입니다.sh
- 반단어를 저장합니다.sw
- 단어 저장.
sb
의 분해 사양은 다음과 같습니다.
disasm: "sb", "%rs2, %simm12(%rs1)"
시맨틱 함수 사양도 예상과 같습니다. "&RV32ISb"
,
기타
변경한 다음 빌드하세요. 생성된 출력을 확인합니다. 단 이전처럼 작업한 내용을 riscv32i.isa 및 rv32i_instructions.h
로드 안내 추가
로드 명령어는 시뮬레이터에 입력합니다. 로드 지연 시간이 불확실함, 로드 명령은 두 개의 개별 작업으로 나뉩니다. 1) 효과적 주소 계산 및 메모리 액세스, 2) 결과 다시 쓰기를 지원합니다. 시뮬레이터는 로드의 의미적 작업을 별도의 안내, 기본 안내 및 하위 명령이 있습니다. 게다가 피연산자를 지정할 때는 기본 피연산자와 피연산자를 모두 지정해야 합니다. child 명령어와 함께 사용할 수 있습니다. 이는 피연산자 사양을 셋잇단 음표의 목록입니다. 구문은 다음과 같습니다.
{(predicate : sources : destinations),
(predicate : sources : destinations), ... }
로드 명령은 모두 이전 코드와 마찬가지로 I-Type 형식을 다음 안내를 따르세요.
2020년 31월 | 19..15 | 2012년 14월 14일 | 11..7 | 6..0 |
---|---|---|---|---|
12 | 5 | 3 | 5 | 7 |
imm12 | rs1 | func3 | 번째 | 명령 코드 |
피연산자 사양은 주소를 계산하는 데 필요한 피연산자를 분할함
로드 데이터의 레지스터 대상에서 메모리 액세스를 시작합니다.
{( : rs1, imm12 : ), ( : : rd) }
시맨틱 작업이 두 개의 명령어로 분할되므로 시맨틱 함수는
마찬가지로 호출 가능 함수 두 개를 지정해야 합니다. lw
(로드 단어)의 경우 다음과 같습니다.
작성:
semfunc: "&RV32ILw", "&RV32ILwChild"
분해 사양이 좀 더 일반적입니다. 이 항목은
제공합니다. lw
의 경우 다음과 같아야 합니다.
disasm: "lw", "%rd, %imm12(%rs1)"
구현해야 하는 로드 명령은 다음과 같습니다.
lb
- 로드 바이트입니다.lbu
- 서명되지 않은 로드 바이트입니다.lh
- 하프워드 로드.lhu
- 서명되지 않은 하프워드를 로드합니다.lw
- 단어를 로드합니다.
변경한 다음 빌드하세요. 생성된 출력을 확인합니다. 단 이전처럼 작업한 내용을 riscv32i.isa 및 rv32i_instructions.h
여기까지 와 주셔서 감사합니다. 이 정보가 도움이 되었기를 바랍니다.