Datapath yang sudah terakit tetap diam tanpa perintah tentang jalur mana yang harus dibuka pada setiap instruksi. Sinyal kontrol adalah kelompok bit keluaran decoder, yaitu rangkaian yang menerjemahkan kode instruksi menjadi pilihan jalur, dengan tugas mengaktifkan penulisan register, memilih masukan mux, menetapkan operasi ALU, dan membuka pintu memori. Topik ini memperkenalkan tujuh sinyal pada datapath siklus tunggal, menyusun tabel sinyalnya untuk empat instruksi fiktif, lalu membangun decoder yang menerjemahkan opcode 4 bit menjadi tabel tersebut secara otomatis.
Tujuh sinyal dan tugasnya#
Setiap sinyal kontrol berupa satu keputusan biner, kecuali ALUOp yang memilih operasi. RegWrite membuka pintu tulis register file, dan tanpa sinyal ini hasil apa pun tidak pernah sampai ke register. ALUSrc memilih masukan kedua untuk ALU pada mux di depannya, antara register kedua dan offset yang dibawa instruksi. ALUOp menetapkan operasi yang dihitung, misalnya tambah untuk membentuk alamat maupun kurang untuk menguji kesamaan pada beq. MemRead dan MemWrite membuka pintu baca dan tulis memori data, sedangkan MemToReg memilih isi mux tulis balik antara hasil ALU dan data memori. Terakhir, Branch mengizinkan mux PC meninggalkan PC berikutnya dan mengambil alamat cabang.
Pembagian tugasnya mengikuti letak komponen di datapath. Sinyal yang mengatur register file dan mux bekerja di ujung masuk dan ujung keluar jalur, sedangkan sinyal memori bekerja di tengahnya. Semuanya muncul bersamaan pada setiap clock, karena decoder hanya membaca opcode instruksi yang sedang berjalan tanpa menunggu hasil apa pun. Sifat kombinasional ini membuat seluruh keputusan siklus sudah tersedia sebelum nilai data sempat mengalir dari satu komponen ke komponen berikutnya.
Tabel sinyal per instruksi#
Resep lengkap sebuah instruksi adalah baris sinyalnya, sehingga empat instruksi pada datapath dapat diringkas menjadi tabel berikut.
| Instruksi | RegWrite | ALUSrc | ALUOp | MemRead | MemWrite | MemToReg | Branch |
|---|---|---|---|---|---|---|---|
| add R1, R2, R3 | 1 | 0 | tambah | 0 | 0 | 0 | 0 |
| load R4, R1, 2 | 1 | 1 | tambah | 1 | 0 | 1 | 0 |
| store R4, R1, 3 | 0 | 1 | tambah | 0 | 1 | - | 0 |
| beq R1, R4, 2 | 0 | 0 | kurang | 0 | 0 | - | 1 |
Tabel di atas dapat dibaca baris demi baris. Baris add hanya menyalakan RegWrite, karena operasinya selesai di ALU dan hasilnya ditulis ke register. Baris load menggunakan ALUSrc agar offset ikut dijumlahkan, MemRead agar memori data membaca, MemToReg agar data memori itulah yang masuk ke register, dan RegWrite agar tulisannya terjadi. Baris store membalik arah data, yaitu MemWrite menyala tanpa RegWrite sama sekali, sehingga tidak ada register yang tersentuh. Baris beq hanya menyalakan Branch, dengan ALUOp kurang untuk menguji kesamaan melalui selisih nol.
Kolom MemToReg pada baris store dan beq bertanda minus. Tanda itu berarti nilai sinyal tidak berpengaruh, atau don’t care dalam istilah perancangan, karena mux tulis balik memang tidak digunakan oleh kedua instruksi tersebut. Decoder tetap mengeluarkan sebuah nilai untuknya, tetapi tidak ada komponen yang membacanya. Menuliskan minus alih-alih mengarang nilai 0 atau 1 membuat tabel sesuai dengan perilaku perangkat keras.
Decoder dari opcode menjadi sinyal#
Decoder kontrol adalah rangkaian kombinasional yang menerjemahkan opcode menjadi ketujuh sinyal sekaligus. Opcode 4 bit memberi kombinasi masukan, sehingga decoder lengkap berisi enam belas baris resep, dan empat barisnya saja yang digunakan instruksi pada topik ini. Karena tidak memiliki elemen penyimpan, decoder tidak mengingat instruksi sebelumnya dan jawabannya murni fungsi dari opcode saat itu juga. Bentuk paling sederhananya adalah tabel pencarian, persis seperti pada berkas berikut.
"""Decoder sinyal kontrol untuk empat instruksi ISA fiktif 16 bit.
Decoder dimodelkan sebagai tabel pencarian: opcode 4 bit masuk,
tujuh sinyal kontrol keluar tanpa elemen penyimpan apa pun.
"""
KONTROL = {
1: dict(RegWrite=1, ALUSrc=0, ALUOp="tambah", MemRead=0, MemWrite=0, MemToReg=0, Branch=0),
3: dict(RegWrite=1, ALUSrc=1, ALUOp="tambah", MemRead=1, MemWrite=0, MemToReg=1, Branch=0),
4: dict(RegWrite=0, ALUSrc=1, ALUOp="tambah", MemRead=0, MemWrite=1, MemToReg="-", Branch=0),
5: dict(RegWrite=0, ALUSrc=0, ALUOp="kurang", MemRead=0, MemWrite=0, MemToReg="-", Branch=1),
}
CONTOH = [
("add R1, R2, R3", 1 << 12),
("load R4, R1, 2", (3 << 12) | (4 << 9) | (1 << 6) | 2),
("store R4, R1, 3", (4 << 12) | (4 << 9) | (1 << 6) | 3),
("beq R1, R4, 2", (5 << 12) | (4 << 9) | (1 << 6) | 2),
]
KUNCI = ("RegWrite", "ALUSrc", "ALUOp", "MemRead", "MemWrite", "MemToReg", "Branch")
lebar = {k: len(k) + 2 for k in KUNCI}
print(f"{'Instruksi':<16}{'opcode':>6}" + "".join(f"{k:>{lebar[k]}}" for k in KUNCI))
for nama, instr in CONTOH:
op = (instr >> 12) & 0xF
sinyal = KONTROL[op]
baris = f"{nama:<16}{format(op, '04b'):>6}"
for k in KUNCI:
baris += f"{str(sinyal[k]):>{lebar[k]}}"
print(baris)Instruksi opcode RegWrite ALUSrc ALUOp MemRead MemWrite MemToReg Branch
add R1, R2, R3 0001 1 0 tambah 0 0 0 0
load R4, R1, 2 0011 1 1 tambah 1 0 1 0
store R4, R1, 3 0100 0 1 tambah 0 1 - 0
beq R1, R4, 2 0101 0 0 kurang 0 0 - 1Kamus KONTROL pada berkas itu adalah decoder itu sendiri, yaitu pemetaan langsung dari opcode ke ketujuh sinyal tanpa logika tambahan. Bagian utama program membaca 4 bit tertinggi dari setiap kata instruksi contoh, memanggil baris resepnya, lalu mencetaknya berdampingan dengan opcode binernya. Keluarannya cocok dengan tabel pada bagian sebelumnya, dan kecocokan itu memperlihatkan bahwa tabel resep dan decoder adalah dua sisi dari satu benda yang sama. Pada perangkat keras sungguhan, tabel pencarian ini diwujudkan sebagai gerbang yang menyala hanya pada satu kombinasi opcode, sehingga setiap baris resep menjadi satu rangkaian pengenalan kode.
Kesalahan umum#
- Mencampur ALUOp dengan ALUSrc. ALUOp memilih operasi yang dihitung, sedangkan ALUSrc memilih asal operand kedua pada mux. Instruksi load menggunakan keduanya sekaligus, yaitu operasi tambah dengan operand berupa offset.
- Mengira Branch saja cukup untuk melompat. Sinyal Branch hanya mengizinkan mux PC, sedangkan keputusan lompatnya tetap bergantung pada hasil uji nol dari ALU. Keduanya harus menyala bersama agar cabang benar-benar diambil.
- Mengisi kolom minus dengan 0 lalu mengira perilakunya berubah. Nilai don’t care memang boleh 0 atau 1, tetapi menuliskannya sebagai 0 menyembunyikan fakta bahwa sinyal itu tidak digunakan.
- Mengira decoder membutuhkan clock tersendiri sebelum instruksi berjalan. Decoder bersifat kombinasional tanpa elemen penyimpan, sehingga jawabannya sudah tersedia pada clock yang sama dengan eksekusi. Menambahkan tahap penyimpanan hanya menunda seluruh datapath tanpa manfaat apa pun pada desain ini.
Ringkasan#
- Sinyal kontrol adalah keluaran decoder kombinasional yang mengarahkan register file, mux, ALU, dan memori data pada setiap clock.
- Setiap instruksi memiliki resep sinyalnya sendiri, dan resep store paling mencolok karena menulis memori tanpa menulis register sama sekali.
- Tanda minus menandai sinyal don’t care yang nilainya tidak memengaruhi perilaku perangkat keras.
- Decoder berbentuk tabel pencarian dari opcode 4 bit, tanpa elemen penyimpan dan tanpa ingatan tentang instruksi sebelumnya.
Referensi#
- Computation Structures — MIT OpenCourseWare — Membahas unit kontrol dan cara opcode diterjemahkan menjadi sinyal pengendali datapath.
- RISC-V Instruction Set Manual — RISC-V International — Menetapkan pengodean opcode pada ISA nyata yang menjadi rujukan pola decoder prosesor sungguhan.