Bu eğitimin hedefleri şunlardır:
- İkili biçim açıklama dosyasının yapısını ve söz dizimini öğrenin.
- İkili biçim açıklamasının ISA açıklamasıyla nasıl eşleştiğini öğrenin.
- RiscV RV32I talimat alt kümesi için ikili açıklamaları yazın.
Genel Bakış
RiscV ikili talimat kodlaması
İkili talimat kodlaması, bir mikroişlemcide çalıştırılacak talimatları kodlamanın standart yoludur. Bunlar genellikle ELF biçiminde bir yürütülebilir dosyada depolanır. Talimatlar sabit genişlik veya değişken genişlik olabilir.
Talimatlarda genellikle küçük bir kodlama biçimi grubu kullanılır. Her biçim, kodlanan talimat türüne göre özelleştirilir. Örneğin, kayıt kaydı talimatları, kullanılabilir işlem kodlarının sayısını en üst düzeye çıkaran bir biçim kullanırken, hemen kaydolma talimatlarını kullanarak kodlanabilecek hemen boyutu artırmak için kullanılabilir işlem kodlarının sayısını dengeleyen bir başka biçim kullanabilir. Dal ve atlama talimatları, daha büyük ofsetlere sahip dalları desteklemek için neredeyse her zaman anında boyutunu en üst düzeye çıkaran biçimler kullanır.
RiscV simülatörümüzde kodunu çözmek istediğimiz talimatların kullandığı talimat biçimleri şunlardır:
Kayıt-kayıt talimatları için kullanılan R türü biçim:
31..25 | 24..20 | 19..15 | 14..12 | 11..7 | 6..0 |
---|---|---|---|---|---|
7 | 5 | 5 | 3 | 5 | 7 |
func7 | rs2 | rs1 | func3 | rd | işlem kodu |
I-Type biçimi, hemen kaydedilen talimatlar, yükleme talimatları ve jalr
talimatı için kullanılır, 12 bitlik hemen.
31..20 | 19..15 | 14..12 | 11..7 | 6..0 |
---|---|---|---|---|
12 | 5 | 3 | 5 | 7 |
imm12 | rs1 | func3 | rd | işlem kodu |
Anında talimatlarla kaydırma için kullanılan özel I-Type biçimi, 5 bit anında:
31..25 | 24..20 | 19..15 | 14..12 | 11..7 | 6..0 |
---|---|---|---|---|---|
7 | 5 | 5 | 3 | 5 | 7 |
func7 | uimm5 | rs1 | func3 | rd | işlem kodu |
Uzun anında talimatlar (lui
, auipc
) için kullanılan U-Type biçimi, 20 bit anında:
31.12 | 11..7 | 6..0 |
---|---|---|
20 | 5 | 7 |
uimm20 | . | işlem kodu |
Koşullu dallar için kullanılan B türü biçim, 12 bitlik anında.
31 | 30,25 | 24...20 | 19..15 | 14..12 | 11..8 | 7 | 6..0 |
---|---|---|---|---|---|---|---|
1 | 6 | 5 | 5 | 3 | 4 | 1 | 7 |
imm | imm | rs2 | rs1 | func3 | imm | imm | işlem kodu |
jal
talimatı için kullanılan J türü biçimi, 20 bit anında.
31 | 30...21 | 20 | 19...12 | 11.7 | 6..0 |
---|---|---|---|---|---|
1 | 10 | 1 | 8 | 5 | 7 |
imm | imm | imm | imm | rd | işlem kodu |
Mağaza talimatları için kullanılan S türü biçim, 12 bit anında.
31..25 | 24..20 | 19..15 | 14..12 | 11..7 | 6..0 |
---|---|---|---|---|---|
7 | 5 | 5 | 3 | 5 | 7 |
imm | rs2 | rs1 | func3 | imm | işlem kodu |
Bu biçimlerden de görebileceğiniz gibi, bu talimatların tümü 32 bit uzunluğundadır ve her biçimin alt 7 biti kod komutu alanıdır. Ayrıca, birkaç biçimde aynı boyuttaki anında değerlerin bitlerinin talimatın farklı bölümlerinden alındığını unutmayın. Göreceğimiz gibi, ikili kod çözücü spesifikasyon biçimi bunu ifade edebilir.
İkili kodlama açıklaması
Talimatın ikili kodlaması, ikili açıklama dosyasında (.bin_fmt
) ifade edilir. Bir ikili biçimli talimat kod çözücü oluşturulabilmesi için ISA'daki talimatların ikili kodlamasını açıklar. Oluşturulan kod çözücü, önceki eğitimde açıklanan ISA kodlamasından bağımsız kod çözücü tarafından ihtiyaç duyulan bilgileri sağlamak için işlem kodunu belirler, operand ve anında alanların değerini çıkarır.
Bu eğitimde, küçük bir "Merhaba Dünya" programında kullanılan talimatları simüle etmek için gereken RiscV32I talimatlarının bir alt kümesi için ikili kodlama açıklaması dosyası yazacağız. RiscV ISA hakkında daha fazla bilgi için Risc-V Spesifikasyonları{.external} sayfasına bakın.
Dosyayı açarak başlayın:
riscv_bin_decoder/riscv32i.bin_fmt
.
Dosyanın içeriği birkaç bölüme ayrılır.
İlk olarak decoder
tanımını inceleyelim.
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;
};
Kod çözücü tanımı, kod çözücümüzün adını RiscV32I
ve dört ek bilgi belirtir. Bunlardan ilki namespace
olup oluşturulan kodun yerleştirileceği ad alanını tanımlar. İkincisi, ISA kod çözücü tarafından oluşturulan kod yürütme talimatı numaralandırma türünün oluşturulan kodda nasıl referans alması gerektiğini belirten opcode_enum
. Üçüncü olarak, includes {}
bu kod çözücü için oluşturulan kod için gerekli dahil et dosyalarını belirtir.
Bizim durumumuzda bu, önceki eğitimdeki ISA kod çözücü tarafından oluşturulan dosyadır.
Ek dahil etme dosyaları, genel kapsamlı bir includes {}
tanımında belirtilebilir. Bu, birden fazla kod çözücü tanımlanmışsa ve bunların hepsinin aynı dosyalardan bazılarını içermesi gerekiyorsa faydalıdır. Dördüncüsü, kod çözücünün oluşturulduğu talimatları oluşturan talimat gruplarının adlarının listesidir. Bizim durumumuzda yalnızca bir tane var: RiscVInst32
.
Ardından üç biçim tanımı vardır. Bunlar, dosyadaki önceden tanımlanmış talimatlar tarafından kullanılan 32 bitlik bir talimat kelimesi için farklı talimat biçimlerini temsil eder.
// 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];
};
İlki, Inst32Format
adlı 32 bit genişliğinde bir talimat biçimi tanımlar. Bu biçimin iki alanı vardır: bits
(25 bit genişliğinde) ve opcode
(7 bit genişliğinde). Her alan unsigned
şeklindedir. Başka bir deyişle, değer ayıklanıp C++ tam sayı türüne yerleştirildiğinde değer sıfır olarak genişletilir. Bit alanlarının genişliklerinin toplamı, biçimin genişliğine eşit olmalıdır. Uyuşmazlık varsa araç hata üretir. Bu biçim başka bir biçimten türetilmediği için üst düzey biçim olarak kabul edilir.
İkincisi, Inst32Format
'ten türetilen IType
adlı 32 bit genişliğinde bir talimat biçimi tanımlar ve bu iki biçimi ilgili kılar. Biçim 5 alan içerir: imm12
, rs1
, func3
, rd
ve opcode
. imm12
alanı signed
değerine sahiptir. Bu, değer ayıklanıp C++ tam sayı türüne yerleştirildiğinde değerin işaretli olarak genişletileceği anlamına gelir. Her ikisinin de IType.opcode
ile aynı imzalı/imzasız özelliğe sahip olduğunu ve Inst32Format.opcode
ile aynı talimat kelime bitlerini belirttiğini unutmayın.
Üçüncü biçim, yalnızca fence
talimatıyla kullanılan özel bir biçimdir. Bu talimat halihazırda belirtilmiş bir talimattır ve bu eğiticide endişelenmemize gerek yoktur.
Önemli nokta: Aynı bitleri temsil ettikleri ve aynı imzalı/imzasız özelliğe sahip oldukları sürece alan adlarını farklı ilgili biçimlerde yeniden kullanın.
riscv32i.bin_fmt
içindeki biçim tanımlarından sonra bir talimat grubu tanımı gelir. Bir talimat grubundaki tüm talimatlar aynı bit uzunluğuna sahip olmalı ve aynı üst düzey talimat biçiminden türetilen (dolaylı olarak) bir biçim kullanmalıdır. Bir ISA'da farklı uzunluklara sahip talimatlar bulunabilir. Bu durumda her uzunluk için farklı bir talimat grubu kullanılır. Ayrıca, hedef ISA kod çözme işlemi Arm ve Thumb talimatları gibi bir yürütme moduna bağlıysa her mod için ayrı bir talimat grubu gerekir. bin_fmt
ayrıştırıcısı her talimat grubu için bir ikili kod çözücü oluşturur.
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;
};
Talimat grubu; RiscV32I
adını, genişliği [32]
, "OpcodeEnum"
kullanılacak işlem kodu numaralandırma türünü ve bir temel talimat biçimini tanımlar. Kod dizesi numaralandırma türü, ISA kod çözücüyle ilgili eğitimde açıklanan biçim bağımsız talimat kod çözücü tarafından üretilenle aynı olmalıdır.
Her talimat kodlama açıklaması 3 bölümden oluşur:
- İkisinin birlikte çalışabilmesi için talimat kod çözücü açıklamasında kullanılanla aynı olması gereken kod adı.
- İşletim kodu için kullanılacak talimat biçimi. Bu, son bölümdeki bit alanlarına yapılan referansları karşılamak için kullanılan biçimdir.
- Bit alanı kısıtlamalarının virgülle ayrılmış listesi (
==
,!=
,<
,<=
,>
ve>=
). İşlem kodunun talimat kelimesiyle başarılı bir şekilde eşleşmesi için tüm bu kısıtlamaların doğru olması gerekir.
.bin_fmt
ayrıştırıcısı, tüm bu bilgileri kullanarak aşağıdaki özelliklere sahip bir kod çözücü oluşturur:
- Her biçimdeki her bit alanına uygun şekilde ayıklama işlevleri (imzalı/imzasız) sağlar. Ayıklama işlevleri, biçim adının küçük harflerle yazılmış sürümüne göre adlandırılan ad alanlarında yer alır. Örneğin,
IType
biçimi için ayıklayıcı işlevleri_type
ad alanına yerleştirilir. Her ayıklayıcı işleviinline
olarak tanımlanır, biçimin genişliğini içeren en daruint_t
türünü alır ve ayıklanan alan genişliğini içeren en darint_t
(imzalı için) veyauint_t
(imzasız için) türünü döndürür. Örneğin:
inline uint8_t ExtractOpcode(uint32_t value) {
return value & 0x7f;
}
- Her talimat grubu için bir kod çözme işlevi.
OpcodeEnum
türündeki bir değer döndürür ve talimat grubu biçiminin genişliğini barındıran en daruint_t
türünü alır.
İlk derlemeyi gerçekleştirme
Dizini riscv_bin_decoder
olarak değiştirin ve aşağıdaki komutu kullanarak projeyi derleyin:
$ cd riscv_bin_decoder
$ bazel build :all
Şimdi dizininizi depo köküne geri değiştirin ve oluşturulan kaynaklara göz atalım. Bunun için dizini bazel-out/k8-fastbuild/bin/riscv_bin_decoder
olarak değiştirin (x86 ana makinede olduğunuz varsayılır. Diğer ana makinelerde k8-fastbuild başka bir dize olur).
$ cd ..
$ cd bazel-out/k8-fastbuild/bin/riscv_bin_decoder
riscv32i_bin_decoder.h
riscv32i_bin_decoder.cc
Oluşturulan başlık dosyası (.h)
riscv32i_bin_decoder.h
uygulamasını açın. Dosyanın ilk kısmı standart boilerplate korumaları, dahil edilen dosyaları ve ad alanı bildirimlerini içerir. Ardından, internal
ad alanında şablonlu bir yardımcı işlev vardır. Bu işlev, 64 bit C++ tam sayılarına sığmayacak kadar uzun biçimlerden bit alanları ayıklamak için kullanılır.
#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
İlk bölümün ardından, riscv32i.bin_fmt
dosyasındaki her format
beyanı için bir tane olmak üzere üç ad alanı grubu bulunur:
namespace fence {
...
} // namespace fence
namespace i_type {
...
} // namespace i_type
namespace inst32_format {
...
} // namespace inst32_format
Bu ad alanlarının her birinde, söz konusu biçimdeki her bit alanı için inline
bit alanı ayıklama işlevi tanımlanır. Buna ek olarak, temel biçim, ayıklama işlevlerinin 1) alan adlarının yalnızca tek bir alan adında yer aldığı veya 2) var oldukları her biçimde aynı tür alanına (imzalı/imzasız ve bit konumları) atıfta bulunduğu alt biçimlerden kopyasını oluşturur. Bu sayede, aynı bitleri tanımlayan bit alanlarının üst düzey biçim ad alanlarında işlevler kullanılarak ayıklanması sağlanır.
i_type
ad alanındaki işlevler aşağıda gösterilmiştir:
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
Son olarak, RiscVInst32
talimat grubu için kod çözücü işlevinin işlev tanımı açıklanır. Talimat kelimesinin değeri olarak 32 bitlik bir işaretsiz değer alır ve eşleşen OpcodeEnum
liste sınıfı üyesini veya eşleşme yoksa OpcodeEnum::kNone
değerini döndürür.
OpcodeEnum DecodeRiscVInst32(uint32_t inst_word);
Oluşturulan kaynak dosya (.cc)
Şimdi riscv32i_bin_decoder.cc
dosyasını açın. Dosyanın ilk bölümünde #include
ve ad alanı bildirimleri, ardından kod çözücü işlev bildirimleri yer alır:
#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
, boş kod çözme işlemleri (ör. OpcodeEnum::kNone
döndüren işlemler) için kullanılır. Diğer üç işlev, oluşturulan kod çözücüyü oluşturur. Genel kod çözücü hiyerarşik bir şekilde çalışır. Talimat kelimesindeki bir bit grubu, üst düzeydeki talimatları veya talimat gruplarını ayırt etmek için hesaplanır. Bitlerin bitişik olması gerekmez. Bit sayısı, ikinci düzey kod çözücü işlevleriyle doldurulan bir arama tablosunun boyutunu belirler. Bu, dosyanın bir sonraki bölümünde
görünür:
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,
...
};
Son olarak, kod çözücü işlevleri tanımlanır:
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;
}
Yalnızca 4 talimat tanımlanmış olduğunda, yalnızca tek bir kod çözme düzeyi ve çok seyrek bir arama tablosu bulunur. Talimatlar eklendikçe kod çözücünün yapısı değişir ve kod çözücü tablo hiyerarşisindeki düzey sayısı artabilir.
Kaydedici-kaydedici ALU talimatları ekleme
Şimdi riscv32i.bin_fmt
dosyasına yeni talimatlar ekleme zamanı. İlk talimat grubu, add
, and
gibi kaydedici-kaydedici ALU talimatlarıdır. RiscV32'de bunların tümü R türü ikili talimat biçimini kullanır:
31..25 | 24..20 | 19..15 | 14..12 | 11..7 | 6..0 |
---|---|---|---|---|---|
7 | 5 | 5 | 3 | 5 | 7 |
func7 | rs2 | rs1 | func3 | rd | işlem kodu |
İlk olarak biçimi eklememiz gerekir. riscv32i.bin_fmt
dosyasını favori düzenleyicinizde açın. Inst32Format
öğesinden hemen sonra, Inst32Format
kaynağından türetilen RType
adlı bir biçim eklenebilir. RType
öğesindeki tüm bit alanları unsigned
. Biçimi tanımlamak için yukarıdaki tablodan adları, bit genişliğini ve sırayı (soldan sağa) kullanın. İpucu almak veya çözümün tamamını görmek istiyorsanız burayı tıklayın.
Ardından talimatları eklememiz gerekir. Talimatlar:
add
: Tam sayı ekleme.and
: Bit tabanlı ve.or
: Bit tabanlı OR.sll
: Mantıksal sola kaydır.sltu
: Ayarlanan değerden küçüktür, işaretsiz.sub
: Tam sayı çıkarma.xor
: Bit tabanlı XOR.
Kodlamaları şunlardır:
31..25 | 24..20 | 19..15 | 14..12 | 11..7 | 6..0 | işlem kodu adı |
---|---|---|---|---|---|---|
000 0000 | rs2 | rs1 | 000 | rd | 011 0011 | add |
000 0000 | rs2 | rs1 | 111 | . | 011 0011 | ve |
000 0000 | rs2 | rs1 | 110 | rd | 011 0011 | veya |
000 0000 | rs2 | rs1 | 001 | . | 011 0011 | sll |
000 0000 | rs2 | rs1 | 011 | . | 011 0011 | sltu |
010 0000 | rs2 | rs1 | 000 | rd | 011 0011 | yerine girdi |
000 0000 | rs2 | rs1 | 100 | . | 011 0011 | xor |
func7 | func3 | işlem kodu |
Bu talimat tanımlarını, RiscVInst32
talimat grubundaki diğer talimatların önüne ekleyin. İkilik dizeler, 0b
ön ekiyle (on altılık sayılar için 0x
'e benzer) belirtilir. İkilik sayı içeren uzun dizelerin okunmasını kolaylaştırmak için tek tırnak işaretini '
, uygun gördüğünüz yerlerde rakam ayırıcı olarak da ekleyebilirsiniz.
Bu talimat tanımlarının her birinde func7
, func3
ve opcode
olmak üzere üç kısıtlama bulunur. sub
hariç tüm öğeler için func7
kısıtlaması şu şekilde olur:
func7 == 0b000'0000
func3
kısıtlaması, talimatların çoğunda değişiklik gösterir. add
ve sub
için:
func3 == 0b000
opcode
kısıtlaması, bu talimatların her biri için aynıdır:
opcode == 0b011'0011
Her satırı noktalı virgülle ;
bitirmeyi unutmayın.
Tamamlanmış çözüme buradan ulaşabilirsiniz.
Şimdi projenizi daha önce olduğu gibi derleyin ve oluşturulan riscv32i_bin_decoder.cc
dosyasını açın. Yeni talimatları uygulamak için ek kod çözücü işlevlerinin oluşturulduğunu göreceksiniz. Çoğunlukla daha önce oluşturulmuş olanlara benzerler ancak add
/sub
kod çözme işlemi için kullanılan DecodeRiscVInst32_0_c
öğesine bakın:
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];
}
Bu işlevde statik bir kod çözme tablosu oluşturulur ve uygun dizini seçmek için talimat kelimesinden bir arama değeri ayıklanır. Bu, talimat kod çözücü hiyerarşisinde ikinci bir katman ekler ancak kodlayıcı, başka karşılaştırmalar yapılmadan doğrudan bir tabloda aranabileceğinden başka bir işlev çağrısı gerektirmek yerine bu işleve yerleştirilir.
Anında ALU talimatları ekleme
Ekleyeceğimiz bir sonraki talimat grubu, kaydedicilerden biri yerine anında değer kullanan ALU talimatlarıdır. Bu talimatların üç grubu vardır (acil alana bağlı olarak): 12 bit imzalı anlık talimatlar, kaymalar için anında özel I-Type talimatları ve 20 bit imzasız anlık değere sahip U-Type. Biçimlerin listesi aşağıda verilmiştir:
I-Type anında biçimi:
31..20 | 19..15 | 14..12 | 11..7 | 6..0 |
---|---|---|---|---|
12 | 5 | 3 | 5 | 7 |
imm12 | rs1 | func3 | rd | işlem kodu |
Özel I-Type hızlı biçimi:
31..25 | 24..20 | 19..15 | 14..12 | 11..7 | 6..0 |
---|---|---|---|---|---|
7 | 5 | 5 | 3 | 5 | 7 |
func7 | uimm5 | rs1 | func3 | rd | işlem kodu |
U-Type hızlı biçimi:
31...12 | 11..7 | 6..0 |
---|---|---|
20 | 5 | 7 |
uimm20 | . | işlem kodu |
I-Type biçimi riscv32i.bin_fmt
'te zaten mevcut olduğundan bu biçimi eklemeniz gerekmez.
Özel I-Type biçimini önceki alıştırmada tanımladığımız R-Type biçimiyle karşılaştırırsak tek farkın, rs2
alanlarının uimm5
olarak yeniden adlandırılması olduğunu görürüz. Tamamen yeni bir biçim eklemek yerine R türü biçimi geliştirebiliriz. Biçimin genişliğini artıracağı için başka bir alan ekleyemiyoruz ancak yer paylaşımı ekleyebiliriz. Yer paylaşımı, biçimdeki bir bit kümesinin takma adıdır ve biçimin birden fazla alt dizilimini ayrı bir adlandırılmış öğede birleştirmek için kullanılabilir. Bunun bir yan etkisi olarak, oluşturulan kod artık alanlara ait işlevlerin yanı sıra yer paylaşımı için de bir ayıklama işlevi içerecek. Bu durumda, hem rs2
hem de uimm5
imzalanmamışsa alanın anında kullanıldığını açıkça belirtmek dışında pek bir fark olmaz. R türü biçimine uimm5
adlı bir yer paylaşımı eklemek için son alanın ardından şunları ekleyin:
overlays:
unsigned uimm5[5] = rs2;
Eklememiz gereken tek yeni biçim U tipi biçimdir. Biçimi eklemeden önce, bu biçimi kullanan iki talimatı (auipc
ve lui
) inceleyelim. Her ikisi de 20 bitlik anında değeri kullanmadan önce 12 bit sola kaydırır. Bu sayede PC'yi değere ekler (auipc
) veya doğrudan bir kaydediciye yazar (lui
). Bir yer paylaşımı kullanarak anında değerin önceden kaydırılmış bir sürümünü sağlayabilir, böylece hesaplamanın bir kısmını talimat yürütme yerine talimat kodu çözme işlemine kaydırabiliriz. Öncelikle biçimi yukarıdaki tabloda belirtilen alanlara göre ekleyin. Ardından aşağıdaki yer paylaşımını ekleyebiliriz:
overlays:
unsigned uimm32[32] = uimm20, 0b0000'0000'0000;
Yer paylaşımı söz dizimi, yalnızca alanları değil, değişmez değerleri de birleştirmemize olanak tanır. Bu durumda, 12 sıfırla birleştirerek 12 basamak sola kaydırırız.
Eklememiz gereken I-Type talimatları şunlardır:
addi
: Hemen ekleyin.andi
- Bit tabanlı ve anında.ori
: Bit tabanlı veya anında.xori
: Anlık değerle bit tabanlı XOR.
Kodlamaları şu şekildedir:
31..20 | 19..15 | 14..12 | 11..7 | 6..0 | opcode_name |
---|---|---|---|---|---|
imm12 | rs1 | 000 | . | 001 0011 | addi |
imm12 | rs1 | 111 | rd | 001 0011 | andi |
imm12 | rs1 | 110 | rd | 001 0011 | ori |
imm12 | rs1 | 100 | . | 001 0011 | xori |
func3 | işlem kodu |
Eklememiz gereken R-Type (özelleştirilmiş I-Type) talimatları şunlardır:
slli
- Mantıksal değeri sola kaydır (anında).srai
- Hemen değere göre sağa doğru aritmetik kaydır.srli
- Hemen sağa kaydırır.
Kodlamaları şunlardır:
31..25 | 24..20 | 19..15 | 14..12 | 11..7 | 6..0 | işlem kodu adı |
---|---|---|---|---|---|---|
000 0000 | uimm5 | rs1 | 001 | rd | 001 0011 | slli |
010 0000 | uimm5 | rs1 | 101 | . | 001 0011 | srai |
000 0000 | uimm5 | rs1 | 101 | . | 001 0011 | srli |
func7 | func3 | işlem kodu |
Eklememiz gereken U-Type talimatları şunlardır:
auipc
- Üst acil durumu PC'ye ekleyin.lui
- Üst kısmı hemen yükleyin.
Kodlamaları şu şekildedir:
31...12 | 11..7 | 6..0 | işlem kodu adı |
---|---|---|---|
uimm20 | rd | 001 0111 | auipc |
uimm20 | rd | 011 0111 | lui |
işlem kodu |
Değişiklikleri yapın ve ardından derleyin. Oluşturulan çıkışı kontrol edin. Daha önce olduğu gibi, çalışmanızı riscv32i.bin_fmt ile karşılaştırarak kontrol edebilirsiniz.
Dal ve atla ve bağlama talimatları ekleme
Tanımlanması gereken bir sonraki talimat grubu koşullu dal talimatları, atla ve bağla talimatı ve atla ve bağla kaydedici talimatıdır.
Eklediğimiz koşullu dalların tümü B türü kodlamayı kullanır.
31..25 | 24..20 | 19..15 | 14..12 | 11..7 | 6..0 |
---|---|---|---|---|---|
7 | 5 | 5 | 3 | 5 | 7 |
imm7 | rs2 | rs1 | func3 | imm5 | işlem kodu |
B türü kodlama, R türü kodlamayla aynı düzene sahiptir ancak RiscV dokümanlarıyla uyumlu olması için yeni bir biçim türü kullanmayı tercih ettik.
Ancak R türü kodlamanın func7
ve rd
alanlarını kullanarak uygun dal kaymasını hemen almak için bir yer paylaşımı da ekleyebilirsiniz.
Yukarıda belirtilen alanlarla BType
biçiminin eklenmesi gerekli olsa da yeterli değildir. Gördüğünüz gibi, anında iki talimat alanına bölünmüştür.
Ayrıca dal talimatları bunu iki alanın basit bir birleştirme işlemi olarak işlemez. Bunun yerine, her alan daha da bölümlere ayrılır ve bu bölümler farklı bir sırada birleştirilir. Son olarak, 16 bit hizalanmış bir ofset elde etmek için bu değer bir sola kaydırılır.
Anı oluşturmak için kullanılan talimat kelimesindeki bit dizisi şu şekildedir: 31, 7, 30..25, 11..8. Bu, aşağıdaki alt alan başvurularına karşılık gelir. Burada dizin veya aralık, alandaki bitleri sağdan sola doğru numaralandırılır (yani
imm7[6]
, imm7
değerinin en büyük basamağını, imm5[0]
ise imm5
değerinin en küçük basamağını ifade eder.
imm7[6], imm5[0], imm7[5..0], imm5[4..1]
Bu bit manipülasyonunu dal talimatlarının bir parçası haline getirmenin iki büyük dezavantajı vardır. Öncelikle, anlamsal işlevin uygulanmasını ikili talimat temsilindeki ayrıntılara bağlar. İkinci olarak, daha fazla çalışma
zamanı gider. Çözüm, soldaki kaydırmayı hesaba katmak için BType
biçimine bir yer paylaşımı eklemektir.
overlays:
signed b_imm[13] = imm7[6], imm5[0], imm7[5..0], imm5[4..1], 0b0;
Yer paylaşımının imzalandığına dikkat edin. Böylece, talimat kelimesinden çıkarıldığında otomatik olarak genişletilir.
Atla ve bağla (hemen) talimatı J türü kodlamayı kullanır:
31.12 | 11..7 | 6..0 |
---|---|---|
20 | 5 | 7 |
imm20 | rd | işlem kodu |
Bu da eklenmesi kolay bir biçimdir ancak talimat tarafından kullanılan immediate, göründüğü kadar basit değildir. Tam anında değeri oluşturmak için kullanılan bit dizileri şunlardır: 31, 19..12, 20, 30..21. Son anında değer, yarım kelime hizası için bir sola kaydırılır. Çözüm, biçime başka bir yer paylaşımı (sol kaydırma için 21 bit) eklemektir:
overlays:
signed j_imm[21] = imm20[19, 7..0, 8, 18..9], 0b0;
Gördüğünüz gibi, yer paylaşımlarının söz dizimi, bir alanda kısaltma biçiminde birden fazla aralık belirtmeyi destekler. Ayrıca, alan adı kullanılmazsa bit sayıları talimat kelimesinin kendisine atıfta bulunur. Bu nedenle, yukarıdaki ifade şu şekilde de yazılabilir:
signed j_imm[21] = [31, 19..12, 20, 30..21], 0b0;
Son olarak, atla ve bağla (register) işlevi daha önce kullanılan I türü biçimini kullanır.
I-Type'ın anlık biçimi:
31..20 | 19..15 | 14..12 | 11..7 | 6..0 |
---|---|---|---|---|
12 | 5 | 3 | 5 | 7 |
imm12 | rs1 | func3 | rd | işlem kodu |
Bu sefer, biçim üzerinde yapılması gereken bir değişiklik yok.
Eklememiz gereken şube talimatları:
beq
: Eşitse dal.bge
- Büyükse veya eşitse dal.bgeu
- Büyüktür veya eşittir imzalanmamışsa şube.blt
: Değer daha azsa dal.bltu
- İmzalanmamışsa şubelerden yararlanın.bne
: Eşit değilse dal.
Bunlar aşağıdaki şekilde kodlanır:
31..25 | 24..20 | 19..15 | 14..12 | 11..7 | 6..0 | işlem kodu adı |
---|---|---|---|---|---|---|
imm7 | rs2 | rs1 | 000 | imm5 | 110 0011 | beq |
imm7 | rs2 | rs1 | 101 | imm5 | 110 0011 | bge |
imm7 | rs2 | rs1 | 111 | imm5 | 110.0011 | Bgeu |
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 | işlem kodu |
jal
talimatı şu şekilde kodlanır:
31...12 | 11..7 | 6..0 | işlem kodu adı |
---|---|---|---|
imm20 | rd | 110 1111 | Jal |
işlem kodu |
jalr
talimatı şu şekilde kodlanır:
31..20 | 19..15 | 14..12 | 11..7 | 6..0 | opcode_name |
---|---|---|---|---|---|
imm12 | rs1 | 000 | . | 110 0111 | jalr |
func3 | işlem kodu |
Değişiklikleri yapın ve sonra oluşturun. Oluşturulan çıkışı kontrol edin. Daha önce olduğu gibi, çalışmanızı riscv32i.bin_fmt ile karşılaştırabilirsiniz.
Mağaza talimatları ekleyin
Depo talimatlarında, S-Type kodlaması kullanılır. Bu kodlama, hemen dosyanın bileşimi dışında dal talimatları tarafından kullanılan B Tipi kodlamayla aynıdır. RiscV dokümanlarıyla uyumlu kalmak için SType
biçimini eklemeyi tercih ettik.
31..25 | 24..20 | 19..15 | 14..12 | 11..7 | 6..0 |
---|---|---|---|---|---|
7 | 5 | 5 | 3 | 5 | 7 |
imm7 | rs2 | rs1 | func3 | imm5 | işlem kodu |
SType
biçiminde, anlık değer iki anlık alanın doğrudan birleştirmesidir. Bu nedenle, yer paylaşımı spesifikasyonu şu şekildedir:
overlays:
signed s_imm[12] = imm7, imm5;
Alanların tamamını birleştirirken bit aralığı belirteçlerinin gerekmediğini unutmayın.
Mağaza talimatları aşağıdaki şekilde kodlanır:
31..25 | 24..20 | 19..15 | 14..12 | 11..7 | 6..0 | işlem kodu adı |
---|---|---|---|---|---|---|
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 | işlem kodu |
Değişiklikleri yapın ve sonra oluşturun. Oluşturulan çıkışı kontrol edin. Daha önce olduğu gibi, çalışmanızı riscv32i.bin_fmt ile karşılaştırarak kontrol edebilirsiniz.
Yükleme talimatlarını ekleyin
Yükleme talimatlarında I-Type biçimi kullanılır. Orada herhangi bir değişiklik yapılması gerekmez.
Kodlamalar şunlardır:
31..20 | 19..15 | 14..12 | 11..7 | 6..0 | opcode_name |
---|---|---|---|---|---|
imm12 | rs1 | 000 | rd | 000 0011 | lb |
imm12 | rs1 | 100 | rd | 000 0011 | lbu |
imm12 | rs1 | 001 | rd | 000 0011 | lh |
imm12 | rs1 | 101 | . | 000 0011 | lhu |
imm12 | rs1 | 010 | rd | 000 0011 | lw |
func3 | işlem kodu |
Değişiklikleri yapın ve ardından derleyin. Oluşturulan çıkışı kontrol edin. Daha önce olduğu gibi, çalışmanızı riscv32i.bin_fmt ile karşılaştırarak kontrol edebilirsiniz.
Bu eğitim burada sona eriyor. Umarız faydalı olmuştur.