cse331-computer-organization
370 CHECKSA MIPS program written in assembly, and the processor built from gates to run it. Four Computer Organization assignments: a text analyzer, a gate-level datapath, a full single-cycle MIPS32 CPU, and a sequential multiplier — every one of them with a self-checking testbench that actually runs. Assembly ile yazılmış bir MIPS programı ve onu çalıştırmak için kapılardan kurulmuş işlemci. Dört Bilgisayar Organizasyonu ödevi: metin analizcisi, kapı seviyesi datapath, tam tek çevrimli MIPS32 işlemcisi ve sıralı çarpan — her birinin gerçekten çalışan, kendi kendini denetleyen testbench'i var.
Left: the counting routine from the assembly program. Right: the same algorithm's answers coming out of the processor, checked by a testbench. The two were written eight weeks apart for different assignments, and they agree. Solda: assembly programındaki sayma rutini. Sağda: aynı algoritmanın işlemciden çıkan cevapları, testbench tarafından doğrulanmış. İkisi farklı ödevler için sekiz hafta arayla yazıldı ve birbirini tutuyor.
Four assignments, and how they actually relate Dört ödev ve aralarındaki gerçek ilişki
They are not one codebase that grows. Assignments 2 and 3 build the same kind of machine at deliberately different levels, and their briefs are explicit about it — so the second does not reuse the first's code, because doing that would write it against its own brief. Bunlar büyüyen tek bir kod tabanı değil. Ödev 2 ve 3 aynı tür makineyi kasıtlı olarak farklı seviyelerde kuruyor ve brief'ler bunu açıkça söylüyor — bu yüzden ikincisi birincisinin kodunu kullanmıyor, kullanmak onu kendi brief'ine aykırı yazmak olurdu.
| Assignment 2 | Assignment 3 | |
|---|---|---|
| scopekapsam | Datapath only — no PC, no memory; the instruction is an input Sadece datapath — PC yok, bellek yok; komut bir girdi | Whole processor — fetch, memory, branches, jumps Tam işlemci — getirme, bellek, dallanma, atlama |
| instructionskomutlar | 11, all ALU11 tane, hepsi ALU | 36, including shifts, six branches and jal/jalr 36 tane; kaydırmalar, altı dallanma ve jal/jalr dahil |
| stylestil |
assign forbidden. A 32-bit adder is 32 full adders chained by their carries.
assign yasak. 32-bit toplayıcı, taşımalarıyla zincirlenmiş 32 tam toplayıcı.
|
assign preferred. The same adder is one line.
assign tercih ediliyor. Aynı toplayıcı tek satır.
|
The program and the machine that runs it Program ve onu çalıştıran makine
Assignment 1 is a MIPS program. Assignment 3 is a MIPS processor. So the processor runs the program — which is the one link across these four that is worth building rather than asserting. Ödev 1 bir MIPS programı. Ödev 3 bir MIPS işlemcisi. O hâlde işlemci programı çalıştırsın — bu dördü arasında iddia etmek yerine gerçekten kurulmaya değer tek bağ bu.
Not the same source. The assembly version walks a byte string with lb and talks to the user with syscall, and this CPU implements neither. The algorithm is identical; the string is stored one character per word and the counts are left in memory for the testbench to read.
Aynı kaynak değil. Assembly sürümü bir bayt dizisini lb ile geziyor ve kullanıcıyla syscall ile konuşuyor; bu işlemci ikisini de uygulamıyor. Algoritma birebir aynı; dizgi karakter başına bir word olarak saklanıyor ve sayımlar testbench'in okuması için bellekte bırakılıyor.
Every expected count in that testbench was produced by running the MARS version on the same string, not by counting by hand. On one row the two implementations agreed and the number I had written in the testbench was the thing that was wrong. O testbench'teki her beklenen sayı, MARS sürümünü aynı dizgiyle çalıştırarak üretildi; elle sayılarak değil. Bir satırda iki uygulama hemfikirdi ve testbench'e elimle yazdığım sayı yanlış olandı.
=== the assignment 1 algorithm,
on the assignment 3 CPU ===
"Hello World 123"
characters 15
vowels 3
consonants 7
digits 3
spaces 2
"MIPS Assembly 2025"
characters 18
vowels 3
consonants 9
digits 4
spaces 2
30 passed, 0 failed
RESULT: PASS
The bug that looked like correct code Doğru koda benzeyen hata
Two tests failed on the first run of the CPU: sra and srav returned 0x08000000 where 0xF8000000 was expected. An arithmetic right shift behaving as a logical one — the sign bit was not being copied in.
İşlemcinin ilk çalıştırmasında iki test patladı: sra ve srav, 0xF8000000 beklenen yerde 0x08000000 döndürdü. Aritmetik sağa kaydırma mantıksal gibi davranıyordu — işaret biti içeri kopyalanmıyordu.
The shifter did use $signed(a) >>> shamt, which is the advice everyone gives. It was still wrong. Signedness in Verilog is a property of the whole expression, not of one branch: the other arms of that conditional are unsigned, so the result type is unsigned and the $signed() is coerced straight back.
Kaydırıcıda zaten herkesin önerdiği $signed(a) >>> shamt yazıyordu. Yine de yanlıştı. Verilog'da işaretlilik tek bir dalın değil, ifadenin bütününün özelliği: o koşullu ifadenin diğer kolları işaretsiz olduğu için sonuç tipi işaretsiz oluyor ve $signed() anında geri dönüştürülüyor.
Moving the arithmetic shift into its own continuous assignment gives it a self-contained signed context. Two lines changed; nothing else in the design did. Aritmetik kaydırmayı kendi sürekli atamasına taşımak ona kendi içinde kapalı bir işaretli bağlam veriyor. İki satır değişti; tasarımda başka hiçbir şey değişmedi.
// wrong: the unsigned arms make the
// whole expression unsigned
assign y = (mode == 2'b00) ? (a << shamt)
: (mode == 2'b01) ? (a >> shamt)
: (mode == 2'b10) ? ($signed(a) >>> shamt)
: a;
// right: its own assignment, its own
// signed context
wire signed [31:0] a_signed = a;
wire [31:0] sra_y = a_signed >>> shamt;
assign y = (mode == 2'b00) ? (a << shamt)
: (mode == 2'b01) ? (a >> shamt)
: (mode == 2'b10) ? sra_y
: a;
Two briefs that cannot be implemented as written Yazıldığı gibi uygulanamayan iki brief
A field that is not there Var olmayan bir alan
The ALU-control table gives ALUOp=11 to ANDI, ORI and XORI together and says to tell them apart from funct. There is no funct in an I-type instruction — those six bits are the bottom of the immediate, so andi $1,$2,0xFF and ori $1,$2,0xFF are identical there.
ALU kontrol tablosu ANDI, ORI ve XORI'ye birlikte ALUOp=11 veriyor ve bunları funct'tan ayırmayı söylüyor. I-type bir komutta funct diye bir şey yok — o altı bit immediate'in alt ucu, dolayısıyla andi $1,$2,0xFF ile ori $1,$2,0xFF orada birebir aynı.
The ALU control takes the opcode as a third input instead, which keeps the brief's ALUOp table intact. The alternative was a wider ALUOp, which changes the control unit too. Bunun yerine ALU kontrolü opcode'u üçüncü girdi olarak alıyor; böylece brief'in ALUOp tablosu olduğu gibi kalıyor. Alternatif, kontrol birimini de değiştiren daha geniş bir ALUOp'tu.
500 characters in 200 bytes 200 bayta 500 karakter
The brief asks for a line "up to 500 characters" and a buffer declared .space 200. Both are requirements, and they do not fit together.
Brief hem "500 karaktere kadar" bir satır hem de .space 200 ile tanımlanmış bir tampon istiyor. İkisi de şart ve birbirine sığmıyor.
The buffer is 501 bytes. A buffer that cannot hold the input it is specified to read is a defect rather than a style choice, so the functional requirement wins — written down at the point of the decision rather than left for a reader to notice. Tampon 501 bayt. Okuması şart koşulan girdiyi tutamayan bir tampon, stil tercihi değil kusurdur; bu yüzden işlevsel gereksinim kazanıyor — kararın verildiği yere yazıldı, okuyanın fark etmesine bırakılmadı.
What is not claimed here Burada iddia edilmeyenler
No synthesis, no timing Sentez yok, zamanlama yok
Quartus is the synthesis target the briefs name, and the designs are written to be synthesisable — but nothing here was put through it, so there are no area or frequency numbers, because none were measured. Brief'lerin adını verdiği sentez hedefi Quartus ve tasarımlar sentezlenebilir olacak şekilde yazıldı — ama buradaki hiçbir şey oradan geçmedi; dolayısıyla alan ya da frekans sayısı yok, çünkü ölçülmedi.
The memory an FPGA would not give FPGA'nın vermeyeceği bellek
The data memory reads asynchronously, which is what single-cycle execution needs and what a block RAM does not do. On real hardware this forces either a second cycle or a registered path. It is documented in the source rather than discovered later. Veri belleği asenkron okuyor; tek çevrimli yürütmenin ihtiyacı bu, blok RAM'in yapmadığı da bu. Gerçek donanımda bu ya ikinci bir çevrim ya da kaydedilmiş bir yol gerektirir. Sonradan keşfedilmek yerine kaynağa yazıldı.
No delay slot, no overflow trap Gecikme yuvası yok, taşma tuzağı yok
A taken branch redirects the next fetch immediately, where real MIPS executes the following instruction first. And add wraps instead of trapping, because there is no exception mechanism to trap into. Both are deviations from MIPS32, and both are written down.
Alınan bir dallanma sonraki getirmeyi hemen yönlendiriyor; gerçek MIPS ise önce bir sonraki komutu çalıştırır. Ve add tuzağa düşmek yerine taşıyor, çünkü düşecek bir istisna mekanizması yok. İkisi de MIPS32'den sapma ve ikisi de yazılı.
Read further Devamını oku
The processor top level: fetch, decode, execute, memory, write-back and next-PC selection, with every documented choice at the head of the file. İşlemcinin en üst seviyesi: getirme, çözme, yürütme, bellek, geri yazma ve sonraki PC seçimi; her belgelenmiş karar dosyanın başında.
OPENAÇ arrow_forward build assemble.pyA MIPS assembler for exactly the instruction set the CPU implements. Hand-written hex was the alternative, and a mistyped nibble there looks exactly like a CPU bug. Tam olarak işlemcinin uyguladığı komut kümesi için bir MIPS assembler'ı. Alternatifi elle yazılmış hex'ti ve oradaki yanlış bir nibble, işlemci hatasına birebir benziyor.
OPENAÇ arrow_forward memory_alt slt32.vSigned set-less-than from gates. Subtracting and testing the sign bit is wrong when the subtraction overflows; the answer is sign XOR overflow. Kapılardan işaretli set-less-than. Çıkarıp işaret bitine bakmak, çıkarma taştığında yanlış sonuç verir; doğru cevap işaret XOR taşma.
OPENAÇ arrow_forward