İkili talimat kod çözücü eğiticisi

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şlevler i_type ad alanına yerleştirilir. Her ayıklayıcı işlevi inline olarak tanımlanır, biçimin genişliğini içeren en dar uint_t türünü alır ve ayıklanan alan genişliğini içeren en dar int_t (imzalı için) veya uint_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 dar uint_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.


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.