แกะข้อสอบ A01
แกะข้อสอบ A01
198322 · สถาปัตยกรรมคอมพิวเตอร์ · แบบฝึกหัดครั้งที่ 1

เฉลย A01 แบบเล่นได้
ไม่ใช่แค่อ่าน

แยกอธิบายทีละข้อย่อย — แคปโจทย์จริงมาวางไว้ก่อน แล้วตามด้วยคำตอบ วิธีคิด และ “ถ้าอาจารย์เปลี่ยนโจทย์เป็นแบบนี้จะตอบยังไง” ส่วนรูปวงจรกับเครื่องมือทดลองอยู่ที่หัวข้อคำถามใหญ่ ไล่จากบนลงล่างได้เลย

6คำถาม · ข้อละ 10 คะแนน
4ข้อที่ต้องเขียนอธิบาย
7เครื่องมือทดลองในหน้านี้
38ข้อย่อย · แยกอธิบายทีละข้อ

ชีทพร้อมพิมพ์เข้าห้องสอบ

A4 · 4 หน้า · 2 แผ่น

สามไฟล์นี้เนื้อหาต่างกัน แต่สเปกเหมือนกันหมด — A4 · ขอบกระดาษ 4 มม. · ฟอนต์ 6.5pt · 3 คอลัมน์ · ยาว 4 หน้าพอดี พิมพ์สองหน้าได้ 2 แผ่นตามที่ห้องสอบอนุญาต เลือกเอาไปแค่ไฟล์เดียวก็พอ

4 หน้า ตัวอย่างหน้าแรกของชีทเฉลยพร้อมคำอธิบาย

เฉลย + อธิบาย + พลิกโจทย์

เฉลยครบทั้ง 6 ข้อพร้อมวิธีคิดทีละขั้น รูปวงจร 3 และ 4 แบบขยาย ตารางถอด address map และเคสพลิกโจทย์แยกตามข้อ

  • เฉลยทุกข้อ
  • มีรูปวงจร
  • 44 เคสพลิกโจทย์
4 หน้า ตัวอย่างหน้าแรกของชีทสรุปล้วน

สรุปล้วน — ไม่มีข้อสอบ

ทฤษฎี สูตร และวิธีคิดล้วนๆ ตั้งแต่บัสจนถึง MIPS ตาราง opcode/funct/register ครบ ปิดท้ายด้วย 25 คำถามชิงคะแนน

  • ไม่มีตัวข้อสอบ
  • ตารางอ้างอิงครบ
  • 25 ถาม–ตอบ
4 หน้า ตัวอย่างหน้าแรกของชีทสรุปรวมเฉลย

สรุป + เฉลยรวม

ครึ่งแรกเป็นสรุปทฤษฎีและสูตร ครึ่งหลังเป็นเฉลยแบบย่อ ปิดท้ายด้วยแม่แบบเปล่าไว้ร่าง address map ในห้องสอบ

  • สรุป + เฉลย
  • แม่แบบเปล่า
  • เคสพลิกโจทย์
ถ้าเลือกได้ไฟล์เดียว — เอา สรุป + เฉลยรวม เพราะอาจารย์ปรับตัวเลขแน่ๆ การมีสูตรและวิธีถอดวงจรติดไปด้วยช่วยได้มากกว่าคำตอบเดิมล้วนๆ
Previewเปิดอ่านทั้ง 4 หน้าได้เลย

ถ้าเบราว์เซอร์ไม่แสดง PDF ในกรอบนี้ ให้กดปุ่ม “เปิดดู” บนการ์ดด้านบนแทน

ตั้งค่าตอนพิมพ์ เลือก A4 · Scale = 100% (ถ้าเครื่องพิมพ์กินขอบให้ลดเป็น 97%) · เปิด Print backgrounds / พิมพ์กราฟิกพื้นหลัง ไม่งั้นสีหัวข้อจะหาย · เลือก สองหน้า (Two-sided) แบบพลิกด้านยาว จะได้ออกมา 2 แผ่นพอดี

12 กฎทอง

อ่านก่อนเข้าห้องสอบ

สายสัญญาณ

  1. Address / Read / Write = ทางเดียว CPU→Mem · Data bus = สองทาง
  2. read active → Memory ขับ data bus · write active → CPU ขับ
  3. address n บิต + data 8 บิต → อ้างได้ 2ⁿ ไบต์
  4. ค่าบน data bus ระบุไม่ได้ ถ้าโจทย์ไม่บอกเนื้อในหน่วยความจำ

บัสร่วมและ interrupt

  1. เขียน bus ได้ ทีละ 1 ตัว แต่ อ่านได้ทุกตัว
  2. เขียนพร้อมกัน = bus contention → ค่าขยะ + กระแสลัดวงจร
  3. interrupt ได้เฉพาะตัวที่ มีขา Int ต่อเข้า OR-gate ของ CPU
  4. จบ ISR → กลับที่ address เดิม (คำสั่งนั้นยังไม่ได้ทำ)

MIPS

  1. อุปกรณ์ไหนถูกเลือก = ดู CS มาจากสายไหน · วงกลม ○ = active-LOW
  2. beq/bne = offset สัมพัทธ์ · j = สัมบูรณ์
  3. Big Endian: ไบต์ address ต่ำสุด = ไบต์ ซ้ายสุด ของ word
  4. ใน lw rt,off(rs) เครื่องเรียง rs ก่อน rt

คำถาม 1 — Bus & ทิศทางสัญญาณ

10 คะแนน
หัวโจทย์ 1 จากข้อสอบ A01

ข้อนี้วัดสองเรื่อง: ทิศทางของสายแต่ละเส้น (ข้อ 1–4) และ ใครเป็นคนขับ data bus กับความจุของหน่วยความจำ (ข้อ 5–8) ลองกดสลับสถานะ read/write ในแล็บด้านบนดูก่อน แล้วค่อยไล่ทีละข้อ

ข้อย่อย1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8
รูปที่ 1 จากข้อสอบ A01
รูปที่ 1 — แผนภาพแสดงการต่อเชื่อมระหว่าง CPU และ Memory
Lab 01ไล่ทิศทางสัญญาณบนบัส
Memory CPU A address bus · 32 บิต · ทางเดียว data bus · 32 บิต · สองทาง read write
ไล่ทีละข้อย่อย
1.1

Address bus ทิศทางไหน

ตอบสั้น
โจทย์ข้อ 1.1 จากข้อสอบ A01
คำตอบCPU A ไป Memory — ทางเดียว
ทำไมCPU เป็นฝ่ายกำหนดว่าจะยุ่งกับช่องหน่วยความจำไหน ส่วน Memory เป็นฝ่ายถูกเลือกอย่างเดียว ไม่เคยเป็นคนบอก address เอง ⇒ ข้อมูลบนสายชุดนี้ไหลจาก CPU ไป Memory ทางเดียวเสมอ
ถ้าพลิกโจทย์เพิ่ม CPU B หรือ DMA ที่เป็น bus master ได้ → address bus กลายเป็น สองทางในเชิงระบบ (ยังทางเดียวเมื่อมองจาก master ตัวเดียว) และต้องมี arbiter มาตัดสินว่าใครได้ใช้
ถามว่า “ค่าบน address bus เป็นเท่าไร” → ตอบได้เสมอ เพราะ CPU เป็นคนใส่ค่าเอง ต่างจาก data bus ที่ตอบไม่ได้
1.2

Data bus ทิศทางไหน

ตอบสั้น
โจทย์ข้อ 1.2 จากข้อสอบ A01
คำตอบสองทาง (both)
ทำไมจังหวะ read Memory ส่งข้อมูลกลับมาให้ CPU · จังหวะ write CPU ส่งข้อมูลไปให้ Memory — ใช้ สายชุดเดียวกัน สลับกันตามจังหวะ จึงต้องมี tri-state buffer คอยคุมว่าใครได้ขับ ไม่งั้นชนกัน
ถ้าพลิกโจทย์ถ้าโจทย์แยกเป็น data-in กับ data-out คนละชุด → แต่ละชุดกลายเป็น ทางเดียว และไม่ต้องมี tri-state บนสายข้อมูล
ถามว่าทำไมไม่แยกสายไปเลย → เพื่อประหยัดจำนวนขาชิปและสายบนบอร์ด แลกกับการที่ใช้ได้ทีละทิศทาง
1.3

Read ทิศทางไหน

ตอบสั้น
โจทย์ข้อ 1.3 จากข้อสอบ A01
คำตอบCPU A ไป Memory — ทางเดียว
ทำไมread เป็น สายควบคุม ไม่ใช่สายข้อมูล — CPU เป็นคนสั่งว่า “ขออ่าน” แล้ว Memory ทำตาม ⇒ สัญญาณวิ่งจาก CPU ไป Memory ทางเดียว
อย่าสับสนกับ “ทิศทางของข้อมูลที่ถูกอ่าน” ซึ่งวิ่งสวนทางบน data bus
ถ้าพลิกโจทย์โจทย์เพิ่มสาย ready / wait / ack → สายพวกนี้อุปกรณ์เป็นคนส่งกลับ ⇒ ทิศทาง Memory ไป CPU
โจทย์เพิ่มสาย Int → อุปกรณ์เป็นคนส่ง ⇒ Memory/Device ไป CPU เช่นกัน
1.4

Write ทิศทางไหน

ตอบสั้น
โจทย์ข้อ 1.4 จากข้อสอบ A01
คำตอบCPU A ไป Memory — ทางเดียว
ทำไมเหตุผลเดียวกับ read ทุกประการ — เป็นสายควบคุมที่ CPU ใช้สั่ง Memory
สรุปกฎรวบยอด: ในรูปนี้มีแค่ data bus เส้นเดียวที่เป็นสองทาง นอกนั้น (address, read, write) เป็นทางเดียวจาก CPU ทั้งหมด
ถ้าพลิกโจทย์ถามว่าถ้าใช้สายเดียวแทน read กับ write ได้ไหม → ได้ ใช้สาย R/W̄ เส้นเดียว (1 = อ่าน, 0 = เขียน) แต่ต้องมีสาย enable เพิ่มเพื่อบอกว่าตอนนี้มี transaction จริงหรือเปล่า
1.5

read active · addr 0x20004000

เขียนอธิบาย
โจทย์ข้อ 1.5 จากข้อสอบ A01
คำตอบ — ต้องตอบให้ครบ 2 ส่วน① ใครขับ data bus: Memory เป็นตัวเขียน (drive) ลง data bus — CPU เป็นฝ่ายอ่าน
② ค่าบน data bus เป็นเท่าไร: ระบุไม่ได้จากข้อมูลที่โจทย์ให้ เพราะมันคือ “เนื้อข้อมูล 32 บิตที่ถูกเก็บอยู่ที่ช่อง 0x20004000” ซึ่งโจทย์ไม่ได้บอกว่าในหน่วยความจำมีอะไรอยู่
ขยายความให้ได้เต็มคะแนนสาย read active แปลว่า CPU ร้องขอข้อมูล ⇒ Memory ถอดรหัส address 0x20004000 แล้ว ขับข้อมูล 32 บิต ออกมาบน data bus ให้ CPU latch เข้าไป · ในจังหวะนี้ CPU ต้องปล่อย data bus เป็น Hi-Z ไม่งั้นชนกัน
ค่าที่ปรากฏจึงเป็น “ค่าที่เก็บไว้ในหน่วยความจำ” ไม่ใช่ตัวเลขที่คำนวณจาก address ⇒ ตอบเป็นตัวเลขไม่ได้
กับดักตอบแค่ “Memory” แล้วจบ = ได้ครึ่งเดียว ต้องเขียนด้วยว่า ค่าระบุไม่ได้และเพราะอะไร · และอย่าเผลอตอบว่า data bus = 0x20004000 (นั่นคือค่าบน address bus)
ถ้าพลิกโจทย์read และ write inactive ทั้งคู่ → ไม่มีใครขับ ⇒ data bus เป็น Hi-Z / floating / ค่าไม่นิยาม และ Memory ไม่ถูกเข้าถึงเลย
read และ write active พร้อมกัน → ผิดกติกา ⇒ bus conflict ทั้ง CPU และ Memory ขับพร้อมกัน ค่าไม่นิยาม อาจเสียหาย
ถามค่าบน address bus0x2000 4000 ตอบได้เลย
ถามว่าอ่านได้กี่ไบต์ต่อครั้ง → data bus 32 บิต ⇒ 4 ไบต์
1.6

write active · addr 0x20008000

เขียนอธิบาย
โจทย์ข้อ 1.6 จากข้อสอบ A01
คำตอบ① CPU A เป็นตัวเขียนลง data bus — Memory เป็นฝ่ายรับไปบันทึกลงช่อง 0x20008000
② ค่า = ระบุไม่ได้ เพราะเป็นข้อมูล 32 บิตที่โปรแกรมสั่งให้เขียน ซึ่งโจทย์ไม่ได้ให้มา
ทำไมถึงตอบตรงข้ามกับข้อ 1.5ดูที่สายควบคุมอย่างเดียวพอ — write active แปลว่า CPU กำลัง “ยัดของ” ลง bus ⇒ CPU ขับ · ส่วน Memory เปลี่ยนเป็นฝ่าย latch เข้าไปเก็บ
ท่องให้ติด: read → Memory ขับ · write → CPU ขับ
ถ้าพลิกโจทย์โจทย์ให้ค่ามาด้วย เช่น “CPU เขียนค่า 0x1234ABCD” → คราวนี้ตอบตัวเลขได้ = 0x1234ABCD
ถามว่าเขียนลงที่ไหน → ช่องหน่วยความจำที่ address 0x20008000 (4 ไบต์ ตั้งแต่ 0x20008000 ถึง 0x20008003)
1.7

address 5 บิต · data 8 บิต อ้างได้กี่ไบต์

ตอบสั้น
โจทย์ข้อ 1.7 จากข้อสอบ A01
คำตอบ32 ไบต์
วิธีคิดaddress bus n บิต ⇒ อ้างได้ 2ⁿ ช่อง · ในที่นี้ 2⁵ = 32 ช่อง
data bus 8 บิต ⇒ ช่องละ 1 ไบต์ ⇒ 32 × 1 = 32 ไบต์
กับดักสาย read และ write ที่โจทย์บอกว่า “อย่างละ 1 bit” เป็น ตัวหลอก ไม่เกี่ยวกับความจุเลย · และสูตร ไม่ใช่ 2^addr × 2^data
ถ้าพลิกโจทย์data bus 16 บิต, address 10 บิต → ถ้า 1 ช่อง = 1 word 16 บิต ⇒ 2¹⁰ × 2 = 2048 ไบต์ · ถ้า byte-addressable ⇒ 2¹⁰ = 1024 ไบต์ (อ่านโจทย์ให้ขาด)
ถามกลับเป็น “ต้องใช้กี่บิตถึงจะอ้าง 1 MB” → 2ⁿ = 2²⁰ ⇒ 20 บิต
ถามเป็นบิตแทนไบต์ → 32 ไบต์ × 8 = 256 บิต
1.8

address 6 บิต · data 8 บิต อ้างได้กี่ไบต์

ตอบสั้น
โจทย์ข้อ 1.8 จากข้อสอบ A01
คำตอบ64 ไบต์
วิธีคิด2⁶ = 64 ช่อง × 1 ไบต์ = 64 ไบต์ — เพิ่ม address มา 1 บิต ความจุ เพิ่มเป็น 2 เท่า เสมอ
ตารางที่ควรจำ2⁵ = 32 · 2⁶ = 64 · 2¹⁰ = 1 K · 2¹² = 4 K · 2¹⁶ = 64 K · 2²⁰ = 1 M · 2²⁴ = 16 M · 2²⁸ = 256 M · 2³⁰ = 1 G · 2³² = 4 G
ถ้าพลิกโจทย์address 24 บิต → 16 MB · address 32 บิต → 4 GB · address 28 บิต → 256 MB
ถามย้อน “อยากได้ 1 GB ด้วย data bus 32 บิตแบบ word-addressable” → 1 GB ÷ 4 = 2²⁸ ช่อง ⇒ 28 บิต

คำถาม 2 — Bus Arbitration

10 คะแนน
หัวโจทย์ 2 จากข้อสอบ A01

ข้อ 1 นับเส้นจากรูป · ข้อ 2–4 ถามว่า “ใครเขียนได้” · ข้อ 5–7 ถามว่า “ใครอ่านได้” — สองกลุ่มนี้คำตอบต่างกันคนละขั้ว และข้อ 8 คือข้อเขียนอธิบายที่ต้องตอบให้ครบ 4 ชั้น

ข้อย่อย2.1 2.2 2.3 2.4 2.5 2.6 2.7 2.8
รูปที่ 2 จากข้อสอบ A01
รูปที่ 2 — แถวบนคือ bus ร่วม (นับเส้นได้ 8 เส้น) · สามเหลี่ยมคือ tri-state buffer · แถวล่างคือ dedicated lines ที่ใช้แจกสิทธิ
Lab 02ใครเขียนได้ ใครอ่านได้

กดเลือกอุปกรณ์ที่ได้สิทธิเขียน — หรือกด “ให้สิทธิทุกตัว” เพื่อดูว่าเกิดอะไรขึ้น

Device B ทำหน้าที่เป็น bus arbiter
ไล่ทีละข้อย่อย
2.1

ระบบ bus นี้กี่บิต

ตอบสั้น
โจทย์ข้อ 2.1 จากข้อสอบ A01
คำตอบ8 บิต
วิธีคิดนับเส้นสัญญาณของ bus ในรูปที่ 2 ตรงๆ — แถวบนที่ทุกอุปกรณ์ต่อร่วมกันมี 8 เส้น ⇒ ส่งข้อมูลได้ครั้งละ 8 บิต
อย่านับรวมสายแถวล่าง เพราะนั่นคือ dedicated lines ที่ใช้แจกสิทธิ ไม่ใช่ตัว bus
ถ้าพลิกโจทย์รูปวาดมา 16 เส้น → ตอบ 16 บิต (ข้อนี้วัดการนับเส้นล้วนๆ อย่าคิดเยอะ)
ถามว่า bus 8 บิต ส่งข้อมูล 32 บิตต้องใช้กี่รอบ → 32 ÷ 8 = 4 รอบ
ถาม bandwidth ที่ 50 MHz → 50 M × 1 ไบต์ = 50 MB/s
2.2

Device A ได้สิทธิ → ใครเขียนได้

ตอบสั้น
โจทย์ข้อ 2.2 จากข้อสอบ A01
คำตอบDevice A เท่านั้น
ทำไมการ “เขียนลง bus” คือการ ขับ (drive) สัญญาณ ⇒ ต้องมีตัวเดียวเท่านั้น ไม่งั้นชนกัน
ตัวที่ไม่ได้สิทธิจะถูก tri-state buffer บังคับให้อยู่สถานะ Hi-Z คือปล่อยสายลอย ไม่ยุ่งกับ bus
ถ้าพลิกโจทย์ถามว่าตัวที่ไม่ได้สิทธิทำอะไรได้บ้างอ่านได้ แต่เขียนไม่ได้
ถามว่าใครเป็นคนตัดสินว่า A ได้สิทธิbus arbiter ซึ่งในรูปนี้คือ Device B
2.3

Device B ถือสิทธิ → ใครเขียนได้

ตอบสั้น
โจทย์ข้อ 2.3 จากข้อสอบ A01
คำตอบDevice B เท่านั้น
กับดักของข้อนี้B เป็น arbiter ก็จริง แต่ arbiter คือ “คนแจกสิทธิ” ไม่ใช่ “คนที่เขียนได้ตลอดเวลา” — ตอนที่ B ถือสิทธิ B ก็เขียนได้ตัวเดียวเหมือนคนอื่น ⇒ ตอบ B ตัวเดียว ไม่ใช่ B รวมกับใครอีก
ถ้าพลิกโจทย์ถ้า arbiter (B) พัง → ไม่มีใครแจกสิทธิ ⇒ อาจไม่มีตัวไหนเขียนได้เลย (ระบบค้าง) หรือหลายตัวเขียนพร้อมกันจนเกิด contention
ถามว่า arbiter ต้องเป็น CPU เสมอไหม → ไม่จำเป็น เป็นวงจรแยกก็ได้ โจทย์เองก็เขียนว่า “อาจจะเป็น CPU”
2.4

Device C ได้สิทธิ → ใครเขียนได้

ตอบสั้น
โจทย์ข้อ 2.4 จากข้อสอบ A01
คำตอบDevice C เท่านั้น
สรุปข้อ 2.2–2.4คำตอบเป็นแพตเทิร์นเดียวกันหมด: ใครถือสิทธิ คนนั้นเขียนได้คนเดียว ไม่ว่าตัวนั้นจะเป็น arbiter หรือไม่
ถ้าพลิกโจทย์มีอุปกรณ์ 5 ตัว ต้องใช้ dedicated line กี่เส้น → แบบ 1 เส้นต่ออุปกรณ์ = 5 เส้น · ถ้าเข้ารหัสเป็นเลขฐานสอง = ⌈log₂5⌉ = 3 เส้น
ถามว่ามีวิธี arbitration แบบไหนบ้างDaisy chain (ส่งสิทธิเป็นทอดๆ ตัวใกล้ได้เปรียบ เกิด starvation) · Centralized / dedicated lines (แบบในรูปนี้) · Distributed · Round-robin (ยุติธรรม แก้ starvation)
2.5

Device A ได้สิทธิ → ใครอ่านได้

ตอบสั้น
โจทย์ข้อ 2.5 จากข้อสอบ A01
คำตอบDevice A, B และ C — ทุกตัว
ทำไมคำตอบพลิกจากข้อ 2.2การ “อ่าน” คือแค่ วัดแรงดันบนสาย (sense) ไม่ได้ขับสัญญาณ จึงไม่รบกวนใครและไม่ต้องขอสิทธิ
bus เป็น broadcast — ทุกตัวที่ต่ออยู่เห็นค่าเดียวกันหมดในเวลาเดียวกัน รวมถึง Device A ที่กำลังเขียนเองด้วย
กับดักอย่าตอบว่า “B กับ C” โดยตัด A ออก — ตัวที่เขียนก็อ่านค่าที่ตัวเองขับได้ และในทางปฏิบัติมักใช้อ่านกลับเพื่อตรวจสอบด้วยซ้ำ
2.6

Device B ถือสิทธิ → ใครอ่านได้

ตอบสั้น
โจทย์ข้อ 2.6 จากข้อสอบ A01
คำตอบDevice A, B และ C — ทุกตัว
ทำไมคำตอบไม่ขึ้นกับว่าใครถือสิทธิเลย เพราะสิทธิคุมแค่ “การเขียน” ไม่ได้คุม “การอ่าน”
2.7

Device C ได้สิทธิ → ใครอ่านได้

ตอบสั้น
โจทย์ข้อ 2.7 จากข้อสอบ A01
คำตอบDevice A, B และ C — ทุกตัว
จำแค่ประโยคเดียวจบทั้ง 6 ข้อเขียนได้ทีละตัว · อ่านได้ทุกตัวเสมอ
ถ้าพลิกโจทย์ถามว่าจะทำให้อุปกรณ์บางตัวอ่านไม่ได้ต้องทำยังไง → ต้องเพิ่มสาย chip-select / output-enable ฝั่งรับ หรือแยก bus ออกเป็นคนละเส้น — ตัว bus ร่วมเองทำไม่ได้
2.8

ถ้า A, B, C เขียนพร้อมกัน จะเกิดอะไรขึ้น

เขียนอธิบาย
โจทย์ข้อ 2.8 จากข้อสอบ A01
คำตอบ — เขียนให้ครบ 4 ชั้นนี้① ชื่อปรากฏการณ์: เกิด Bus contention / bus conflict — มี driver หลายตัวขับเส้นเดียวกันไปคนละระดับลอจิกในเวลาเดียวกัน
② ผลต่อข้อมูล: แรงดันบนเส้นถูกดึงไปค้างอยู่ระหว่าง 0 กับ 1 ⇒ เป็นค่า undefined ทุกตัวที่อ่านจะได้ข้อมูลผิด ระบบทำงานผิดพลาด
③ ผลต่อฮาร์ดแวร์: ตัวหนึ่งดันขึ้น VDD อีกตัวดึงลง GND ⇒ เกิดเส้นทาง ลัดวงจร กระแสสูงไหลผ่าน output driver ⇒ ร้อน กินไฟ และ ชิปเสียหายถาวรได้
④ วิธีป้องกัน: ใช้ arbiter คู่กับ tri-state buffer บังคับให้ตัวที่ไม่ได้สิทธิอยู่สถานะ Hi-Z (คือสามเหลี่ยมที่เห็นในรูปที่ 2) หรือใช้ open-drain + pull-up
เขียนสั้นๆ ก็ได้แบบนี้“เกิด bus contention — driver หลายตัวขับสายเดียวกันคนละลอจิก ทำให้ค่าบน bus ไม่นิยาม ทุกตัวอ่านได้ข้อมูลผิด และเกิดกระแสลัดวงจรผ่าน output driver จนชิปอาจเสียหาย ป้องกันด้วย arbiter + tri-state buffer ให้ตัวที่ไม่ได้สิทธิอยู่ Hi-Z”
ถ้าพลิกโจทย์ถ้าใช้ open-drain แทน tri-state → ไม่เกิดลัดวงจร (ไม่มีใครดัน HIGH) แต่ผลลัพธ์บนสายกลายเป็น wired-AND ของทุกตัว ⇒ ข้อมูลยังผิดอยู่ดี
ถ้ามีแค่ 2 ตัวเขียนพร้อมกันแต่ค่าตรงกัน → บังเอิญไม่เห็นข้อผิดพลาดบนข้อมูล แต่ยังเป็นการออกแบบที่ผิดและกระแสยังผิดปกติ
ถามว่า starvation คืออะไร → อุปกรณ์ที่ priority ต่ำไม่ได้สิทธิสักที แก้ด้วย round-robin หรือ aging

คำถาม 3 — Interrupt & Memory Map

10 คะแนน
หัวโจทย์ 3 จากข้อสอบ A01

ก่อนตอบข้อย่อยไหนก็ตาม ให้ ถอด address map จากรูปก่อนเสมอ — ทำครั้งเดียวใช้ตอบได้ทั้งข้อ 3.3 ถึง 3.5 ลองพิมพ์ address ลงหัววัดด้านบนเพื่อเช็คคำตอบได้

ข้อย่อย3.1 3.2 3.3 3.4 3.5
รูปที่ 3 จากข้อสอบ A01
รูปที่ 3 — Main Memory เป็น volatile · Boot Mem เป็น non-volatile · เส้นแดง = สาย interrupt · เส้นฟ้าบาง = chip-select จาก decoder

ถอด address map จากรูปที่ 3 — ทำก่อนตอบข้อย่อย

สูตร 4 ขั้น ใช้ได้กับทุกวงจรที่โจทย์ให้① ดูขา CS ของทุกกล่อง ว่ารับสายอะไร และมี วงกลม ○ คั่นไหม — มี ○ = ต้องเป็น 0 · ไม่มี ○ = ต้องเป็น 1 · หลายขา = ต้อง active ครบทุกขา
② Decoder ทำงานเมื่อ CS ครบ แล้ว X1X0 เลือก Y0–Y3 (00→Y0, 01→Y1, 10→Y2, 11→Y3)
③ ไล่เส้น Y ว่าไปเข้า CS ของกล่องไหน (ในรูปสายไขว้กัน ต้องไล่ทีละเส้น)
④ กล่องรับ A[k-0] กี่บิต ⇒ ขนาดของมัน = 2^(k+1) ไบต์
hex หลักแรกอุปกรณ์ช่วง addressเงื่อนไขจากรูป
0–7Main Memory0x00000000–0x7FFFFFFFCS1, CS0 มีวงกลม ○ ทั้งคู่และรับ A[31] ⇒ ต้อง A[31] = 0
8–BDMA0x80000000–0xBFFFFFFFCS1 ← A[31], CS0 ○← A[30] ⇒ A[31]=1 และ A[30]=0
CBoot Mem0xC0000000–0xCFFFFFFFDecoder เปิดเมื่อ A[31]=A[30]=1
X1X0 = A[29]A[28]
Y0→Boot · Y1→Dev0 · Y2→Dev1 · Y3→Dev2
ตัวละ 256 MB (A[27-0])
DDev 00xD0000000–0xDFFFFFFF
EDev 10xE0000000–0xEFFFFFFF
FDev 20xF0000000–0xFFFFFFFF
ทริกจำ 5 วินาทีดู hex หลักแรก ตัวเดียวพอ — 0-7 Main 8-B DMA C Boot D Dev0 E Dev1 F Dev2
Lab 03หัววัด address — วงจรรูปที่ 3 (ของคำถามนี้)

แผนที่นี้เป็นของ วงจรรูปที่ 3 เท่านั้น — คำถาม 6 ใช้วงจรคนละตัว (รูปที่ 4) และมี address map คนละแบบ

Lab 07ไทม์ไลน์ interrupt — ดูว่าช่อง (a)(b)(c)(d) มาจากไหน
ไล่ทีละข้อย่อย
3.1

อุปกรณ์ไหน interrupt CPU ได้บ้าง

ตอบสั้น
โจทย์ข้อ 3.1 จากข้อสอบ A01
คำตอบDMA และ Dev 2 เท่านั้น
วิธีดูจากรูปไล่ เส้นสีแดง ในรูปที่ 3 — มันออกจากขา Int ของ Dev 2 (ซ้ายล่าง) และของ DMA (ขวาล่าง) วิ่งเข้า OR-gate แล้วเข้าขา Interrupt ของ CPU
กล่องที่เหลือ (Decoder, Main Memory, Boot Mem, Dev 0, Dev 1) ไม่มีขา Int เลย ⇒ ส่ง interrupt ไม่ได้
ตอบตามสายที่เห็นในรูปเท่านั้น อย่าเดาจากชื่ออุปกรณ์ว่าตัวไหน “น่าจะ” interrupt ได้
ถ้าพลิกโจทย์ถ้าโจทย์ย้ายสาย Int ของ Dev 0 มาต่อเพิ่ม → คำตอบเปลี่ยนเป็น DMA, Dev 2, Dev 0 ทันที — ข้อนี้วัดการอ่านรูปล้วนๆ
ถามว่าทำไมต้อง OR-gate → CPU มีขา Interrupt ขาเดียว จึงต้องรวมสัญญาณจากทุกตัวเข้าด้วยกัน
ถามว่าถ้า Dev 2 กับ DMA เรียกพร้อมกัน CPU รู้ไหมว่าใครเรียกไม่รู้ OR-gate บอกได้แค่ “มีคนเรียก” ⇒ ISR ต้องไล่ poll status ของแต่ละตัว หรือใช้ vectored interrupt / priority encoder แยกสาย
3.2

ถ้าไม่ใช้ interrupt CPU จะรู้ได้ยังไงว่าอุปกรณ์ทำเสร็จ

เขียนอธิบาย
โจทย์ข้อ 3.2 จากข้อสอบ A01
คำตอบใช้ Polling (programmed I/O หรือ busy-waiting): CPU วนลูปอ่าน status register ของอุปกรณ์ผ่าน memory-mapped I/O ซ้ำๆ แล้วตรวจบิต ready/done ว่าขึ้นหรือยัง ถ้ายังก็วนอ่านใหม่ ถ้าขึ้นแล้วจึงไปอ่านข้อมูลผลลัพธ์
โครงโปรแกรม polling
poll: lw   $t0, 0($s0)   # $s0 = addr ของ status register
      andi $t0, $t0, 1   # แยกเอาบิต DONE
      beq  $t0, $zero, poll  # ยังไม่เสร็จ → วนใหม่
      lw   $t1, 4($s0)   # เสร็จแล้ว → อ่านข้อมูล
ต้องเขียนข้อดีข้อเสียด้วยถึงจะเต็มข้อเสีย: เปลือง CPU cycle ไปกับการถามซ้ำ (busy-waiting) ทำงานอื่นไม่ได้ · ถ้าเว้นช่วง poll ห่างก็ตอบสนองช้า (latency สูง) · อุปกรณ์ยิ่งเยอะยิ่งไม่ scale
ข้อดี: ฮาร์ดแวร์ง่ายมาก ไม่ต้องมีสาย Int, ISR หรือ vector table และเวลาที่ใช้คาดเดาได้
ถ้าพลิกโจทย์ถามว่า interrupt ต่างจาก polling ยังไง → ต่างที่ ใครเป็นคนเริ่ม: polling = CPU ถามเอง · interrupt = อุปกรณ์เรียก CPU เมื่อพร้อม
ถามว่า DMA ต่างจากทั้งสองยังไง → DMA ย้ายข้อมูลเองโดยไม่ผ่าน CPU แล้วค่อย interrupt บอกตอนจบ
ถามว่าเมื่อไรควรใช้ polling → เมื่ออุปกรณ์เร็วมากหรือมีตัวเดียว และต้องการความแน่นอนของเวลา (real-time ง่ายๆ)
3.3

interrupt ก่อนโหลดคำสั่งที่ 0x00001000 · vector 0xCFFF0000

4 ช่องย่อย
โจทย์ข้อ 3.3 จากข้อสอบ A01
คำตอบ(a) 0xCFFF0000  ·  (b) 0xCFFF0000  ·  (c) Boot Memory  ·  (d) 0x00001000
กติกา 4 ช่อง จำแค่นี้ใช้ได้ทั้ง 3 ข้อ(a) คำสั่งถัดไป = ค่า interrupt vector เพราะ CPU กระโดดไป ISR ทันที
(b) ISR อยู่ที่ = ค่า vector เดียวกัน (โจทย์ชุดนี้ vector ชี้ตรงไปที่ ISR เลย)
(c) เอา vector ไป เทียบ address map — 0xCFFF0000 หลักแรกเป็น C ⇒ Boot Mem
(d) จบ ISR กลับไปทำ คำสั่งเดิมที่ยังไม่ได้ทำ = 0x00001000 เพราะโจทย์เขียนว่า “ก่อนจะโหลดคำสั่งที่ …” ⇒ คำสั่งนั้นยังไม่ถูก execute จึงไม่ต้อง +4
ถ้าพลิกโจทย์เปลี่ยนเป็น “หลังจากทำคำสั่งที่ 0x00001000 เสร็จแล้วจึงได้ interrupt” → (d) = 0x00001004 (บวก 4)
ถ้า CPU ปิดรับ interrupt (masked / disabled) → ไม่กระโดดเลย (a) = 0x00001000 และไม่มี ISR
vector = 0x90000000 → หลักแรก 9 ⇒ DMA ⇒ ผิดปกติ เพราะ ISR ต้องอยู่ในหน่วยความจำ ไม่ใช่ในอุปกรณ์ I/O
3.4

interrupt ก่อนโหลดคำสั่งที่ 0xCFFE1000 · vector 0x00010000

4 ช่องย่อย
โจทย์ข้อ 3.4 จากข้อสอบ A01
คำตอบ(a) 0x00010000  ·  (b) 0x00010000  ·  (c) Main Memory  ·  (d) 0xCFFE1000
วิธีคิดใช้กติกา 4 ช่องเดิมทุกประการ — สิ่งที่เปลี่ยนคือ vector อยู่ที่ 0x00010000 ซึ่ง หลักแรกเป็น 0 ⇒ A[31] = 0 ⇒ ตกใน Main Memory
ส่วนคำสั่งที่ค้างอยู่ 0xCFFE1000 อยู่ใน Boot Mem แต่ไม่เกี่ยว — ช่อง (c) ถามถึงที่อยู่ของ ISR ไม่ใช่ที่อยู่ของคำสั่งเดิม
เฉลยของอาจารย์ข้อนี้พิมพ์ผิดในไฟล์เฉลย ช่อง (a) พิมพ์ไว้ว่า 0x00001000 ซึ่งน่าจะก๊อปมาจากข้อ 3.3 — เพราะ (a) ต้องเท่ากับ vector เสมอ อย่างที่เห็นชัดในข้อ 3.3 และ 3.5 ที่ (a) = (b)
⇒ ค่าที่ถูกต้องคือ 0x00010000 ถ้าเจอในห้องสอบให้ตอบค่านี้แล้วเขียนกำกับว่า “= interrupt vector”
ถ้าพลิกโจทย์สลับให้ vector = 0xE0000100 → หลักแรก E ⇒ Dev 1 · vector = 0xD0000000Dev 0
ถามว่าทำไม ISR ไม่ควรอยู่ในอุปกรณ์ I/O → เพราะ I/O ไม่ได้เก็บคำสั่งไว้ให้ fetch ได้ตามปกติ และมักอ่านแล้วค่าเปลี่ยน
3.5

interrupt ก่อนโหลดคำสั่งที่ 0x00410080 · vector 0x05000000

4 ช่องย่อย
โจทย์ข้อ 3.5 จากข้อสอบ A01
คำตอบ(a) 0x05000000  ·  (b) 0x05000000  ·  (c) Main Memory  ·  (d) 0x00410080
วิธีคิดvector = 0x05000000 หลักแรกเป็น 0 ⇒ A[31] = 0 ⇒ Main Memory
คำสั่งที่ค้าง 0x00410080 ก็อยู่ใน Main Memory เหมือนกัน แต่ไม่ได้ทำให้คำตอบเปลี่ยน — (d) ยังเป็น address เดิมอยู่ดี
สรุปแพตเทิร์นทั้ง 3 ข้อ(a) และ (b) = vector เสมอ · (c) = เอา vector ไปเทียบ map · (d) = address เดิมที่ค้างอยู่ — ไม่มีข้อไหนหลุดจากแพตเทิร์นนี้เลย
ถ้าพลิกโจทย์โจทย์ถามเพิ่มว่า CPU ต้องเก็บอะไรไว้ก่อนกระโดด → เก็บ PC เดิม (ที่จะกลับมา) พร้อม status register และ register ที่ ISR จะใช้ ลง stack หรือ EPC
ถามลำดับการทำงานเต็มๆ → ① ทำคำสั่งปัจจุบันให้จบ ② เช็คว่าเปิดรับ interrupt ③ เก็บ PC + สถานะ ④ กระโดดไป vector ⑤ ทำ ISR ⑥ เคลียร์ flag ที่อุปกรณ์ ⑦ คืนค่าแล้วกลับ PC เดิม

คำถาม 4 — เขียน MIPS Assembly แทน C

10 คะแนน
หัวโจทย์ 4 จากข้อสอบ A01

โจทย์กำหนด $s0=a · $s1=b · $s2=i · $s3 = base address ของ array A (int = 4 ไบต์) — ข้อ (a)–(c) เป็นคำสั่งเดี่ยวๆ ส่วน (d) และ (e) ต้องประกอบเงื่อนไขด้วย slt เอง

ข้อย่อย4 (a) 4 (b) 4 (c) 4 (d) 4 (e)

ตารางแปลง pseudo → คำสั่งจริง — ต้องท่องก่อนทำข้อนี้

โจทย์บอกไว้ชัดใช้ pseudo-instruction หัก 1 คะแนนต่อข้อ ⇒ ห้ามใช้ li, move, la, blt, bgt, ble, bge, beqz, bnez
Pseudoเขียนแทนด้วยPseudoเขียนแทนด้วย
li $s0, Naddi $s0,$zero,Nblt $a,$b,Lslt $t0,$a,$b ; bne $t0,$zero,L
move $s0,$s1add $s0,$s1,$zerobgt $a,$b,Lslt $t0,$b,$a ; bne $t0,$zero,L
la $s0, LBLlui + orible $a,$b,Lslt $t0,$b,$a ; beq $t0,$zero,L
not $s0,$s1nor $s0,$s1,$zerobge $a,$b,Lslt $t0,$a,$b ; beq $t0,$zero,L
สูตรจำ sltslt rd, X, Y อ่านว่า “X น้อยกว่า Y ไหม” — เงื่อนไข ≤ กับ ≥ ไม่มีคำสั่งตรงๆ ต้อง พลิกเป็นนิเสธ เสมอ เช่น a ≤ bไม่ใช่ (b < a)
หลักคิดรวมif / while ให้ตรวจนิเสธของเงื่อนไข แล้ว branch ข้ามออกไป ② array A[k] ของ int ⇒ offset = k × 4 ③ index เป็นตัวแปรต้อง sll $t0,$s2,2 ก่อนบวกเข้ากับ base
ไล่ทีละข้อย่อย
4 (a)

a = A[0] + 32;

โค้ด
โจทย์ข้อ 4(a) จากข้อสอบ A01
คำตอบ
lw   $t0, 0($s3)      # $t0 = A[0]  · offset = 0*4 = 0
addi $s0, $t0, 32     # a = A[0] + 32
ทำไมต้อง 2 คำสั่งMIPS เป็น load–store architecture — คำสั่งคำนวณทำงานกับ register เท่านั้น แตะหน่วยความจำโดยตรงไม่ได้ ⇒ ต้อง lw เอาค่าเข้ามาก่อน แล้วค่อยบวก
ใช้ $t0 เป็นตัวพักได้ เพราะโจทย์ไม่ได้ห้าม (จะ lw $s0,0($s3) แล้ว addi $s0,$s0,32 ก็ถูกเหมือนกัน)
ถ้าพลิกโจทย์a = A[2] - b;lw $t0, 8($s3) ; sub $s0, $t0, $s1
a = A[0] + A[1];lw $t0,0($s3) ; lw $t1,4($s3) ; add $s0,$t0,$t1
ถ้า A เป็น char → offset ไม่ต้องคูณ และใช้ lb แทน lw · ถ้าเป็น double → offset = index × 8
4 (b)

A[1] = a;

โค้ด
โจทย์ข้อ 4(b) จากข้อสอบ A01
คำตอบ
sw   $s0, 4($s3)      # offset = 1*4 = 4
จุดที่ต้องระวังA[1] ไม่ใช่ offset 1 แต่เป็น 1 × 4 = 4 เพราะ int กว้าง 4 ไบต์
และลำดับการเขียนคือ sw ค่าที่จะเก็บ, offset(base) — เอา $s0 ไปเก็บที่ $s3 + 4
ถ้าพลิกโจทย์A[3] = a;sw $s0, 12($s3)
A[i] = a; (index เป็นตัวแปร) → ต้องคูณเอง:
sll  $t0, $s2, 2      # $t0 = i*4
add  $t0, $t0, $s3    # $t0 = &A[i]
sw   $s0, 0($t0)
A[i+1] = a;addi $t0,$s2,1 ก่อน แล้วค่อย sll
4 (c)

a = -4;

โค้ด
โจทย์ข้อ 4(c) จากข้อสอบ A01
คำตอบ
addi $s0, $zero, -4   # ห้ามใช้ li $s0,-4 เพราะเป็น pseudo
ทำไมใช้ addi กับ $zero$zero เป็น register ที่มีค่า 0 ตลอดเวลา ⇒ 0 + (-4) = -4 ได้ค่าคงที่ลง register โดยไม่ต้องใช้ pseudo-instruction
immediate เป็น 16 บิตมีเครื่องหมาย ใส่ค่า −32768 ถึง 32767 ได้ ⇒ −4 ใส่ตรงๆ ได้เลย
ถ้าพลิกโจทย์a = 0;add $s0, $zero, $zero (หรือ addi $s0,$zero,0)
a = 100000; → เกิน 16 บิต ต้องใช้ 2 คำสั่ง: lui $s0, 0x0001 ; ori $s0, $s0, 0x86A0
b = a;add $s1, $s0, $zero (แทน move)
4 (d)

if (a < b) { a = b; } a++;

โค้ด
โจทย์ข้อ 4(d) จากข้อสอบ A01
คำตอบ
      slt  $t0, $s0, $s1     # $t0 = (a < b) ? 1 : 0
      beq  $t0, $zero, ENDIF # ไม่จริง → ข้าม body
      add  $s0, $s1, $zero   # a = b   (แทน move)
ENDIF:
      addi $s0, $s0, 1       # a++  (อยู่นอก if ทำเสมอ)
วิธีคิดทีละบรรทัด① เงื่อนไข a < b ตรงกับ slt พอดี ⇒ ได้ 1 เมื่อจริง
② ต้องการ “ข้าม body เมื่อไม่จริง” ⇒ ใช้ beq $t0,$zero กระโดดไป ENDIF
a = b เขียนด้วย add $s0,$s1,$zero เพราะ move เป็น pseudo
a++ อยู่นอก if ⇒ ต้องวางหลัง label ENDIF ไม่ใช่ในบล็อก
กับดักถ้าเอา addi $s0,$s0,1 ไปไว้ก่อน ENDIF: จะกลายเป็น a++ เฉพาะตอนเงื่อนไขจริง ⇒ ผิดความหมายของ C
ถ้าพลิกโจทย์if (a <= b) { a = b; } → พลิกเป็นนิเสธ: slt $t0,$s1,$s0 (b < a) ; bne $t0,$zero,ENDIF
if(a==b){a=0;}else{a=1;}bne $s0,$s1,ELSE / add $s0,$zero,$zero / j ENDIF / ELSE: addi $s0,$zero,1 / ENDIF:
if (a > b) { b = a; }slt $t0,$s1,$s0 ; beq $t0,$zero,ENDIF ; add $s1,$s0,$zero
4 (e)

while (a <= b) { a += i; } A[0] = a;

โค้ด
โจทย์ข้อ 4(e) จากข้อสอบ A01
คำตอบ
LOOP: slt  $t0, $s1, $s0     # $t0 = (b < a) = นิเสธของ a<=b
      bne  $t0, $zero, ENDLOOP  # b<a จริง → ออกจากลูป
      add  $s0, $s0, $s2     # a += i
      j    LOOP
ENDLOOP:
      sw   $s0, 0($s3)       # A[0] = a
จุดตัดสินของข้อนี้เงื่อนไขคือ a ≤ b ซึ่ง slt ทำตรงๆ ไม่ได้ ⇒ ต้องพลิกเป็น ออกจากลูปเมื่อ b < a
โครง while มาตรฐาน: LOOP: ตรวจเงื่อนไข → j LOOP ท้าย body → ENDLOOP: ล่างสุด
คำสั่ง sw อยู่นอกลูป ตรงกับ C ที่ A[0] = a; อยู่หลังปีกกาปิด
กับดัก① เขียน slt $t0,$s0,$s1 (a < b) แล้วใช้ beq — จะกลายเป็น while(a < b) ตกกรณี a = b ไป
② ลืม j LOOP ⇒ ลูปทำรอบเดียวจบ
③ เอา sw ไปไว้ในลูป ⇒ เขียนซ้ำทุกรอบ ผลลัพธ์สุดท้ายเท่ากันแต่ผิดความหมาย
ถ้าพลิกโจทย์do { a += i; } while (a < b); → ไม่ต้องตรวจก่อนเข้า:
LOOP: add  $s0,$s0,$s2
      slt  $t0,$s0,$s1
      bne  $t0,$zero,LOOP
for(i=0;i<10;i++) a+=A[i]; → init add $s2,$zero,$zero → ตรวจด้วย slt → body ใช้ sll/add/lw/add → update addi $s2,$s2,1j กลับ
a = a*8;sll $s0,$s0,3 · a = a/4; (a ≥ 0) → srl $s0,$s0,2

คำถาม 5 — Assembly เป็น Machine Code

10 คะแนน
หัวโจทย์ 5 จากข้อสอบ A01

ข้อ 1–2 เป็น I-type ตรงๆ · ข้อ 3–4 คือหัวใจของข้อนี้ — โปรแกรมชุดเดียวกันแต่วางไว้คนละที่ แล้วถามว่า machine code เปลี่ยนตัวไหนบ้าง ลองพิมพ์คำสั่งลงเครื่องแปลงด้านบนเทียบคำตอบได้

ข้อย่อย5.1 5.2 5.3 5.4

รูปแบบคำสั่งและสูตร — ใช้ตอบทั้ง 4 ข้อย่อย

op31–26
rs25–21
rt20–16
rd15–11
shamt10–6
funct5–0

R-typeadd rd, rs, rt · op = 000000 เสมอ แยกชนิดด้วย funct

op31–26
rs25–21
rt20–16
immediate / offset15–0

I-typelw rt, imm(rs) · addi rt, rs, imm · beq rs, rt, offset

op31–26
target address25–0

J-typej target · jal target

ลำดับฟิลด์สลับกับที่เขียนadd rd, rs, rt แต่ในเครื่องเรียง rs · rt · rd  ·  lw rt, off(rs) แต่ในเครื่อง rs มาก่อน rt เสมอ (ตัวในวงเล็บขึ้นก่อน!)
สูตรที่ต้องท่อง Branch offset = ( addr(label) − addr(branch) − 4 ) ÷ 4  → เก็บ 16 บิต signed
Branch ปลายทาง = addr(branch) + 4 + offset × 4
Jump field = addr(target) ÷ 4  → เก็บ 26 บิตล่าง
Jump ปลายทาง = (PC+4)[31:28] ‖ field ‖ 00
2's complement 16 บิต = 0x10000 − |n| ⇒ −1 = 0xFFFF · −4 = 0xFFFC · −5 = 0xFFFB
R-typefunctI / JopRegisterเลข
add0x20lw100011$zero0
sub0x22sw101011$t0–$t78–15
and / or0x24 / 0x25addi001000$s016
slt0x2Abeq / bne000100 / 000101$s117
sll / srl0x00 / 0x02lui / ori001111 / 001101$s218
jr0x08j / jal000010 / 000011$s521
ท่องแค่นี้พอ$sN = 16 + N · $tN = 8 + N · $ra = 31 · $sp = 29
Lab 04เครื่องแปลง MIPS ↔ machine code
ไล่ทีละข้อย่อย
5.1

lw $s0, 4($s1)

แปลงเป็น hex
โจทย์ข้อ 5.1 จากข้อสอบ A01
คำตอบ
0x8E30 0004
กางบิตทีละฟิลด์
100011op = lw
10001rs = $s1 = 17
10000rt = $s0 = 16
0000 0000 0000 0100offset = 4
รวมเป็น 1000 1110 0011 0000 0000 0000 0000 0100 ⇒ จับกลุ่มละ 4 บิตแปลงเป็น hex ได้ 0x8E300004
กับดักlw $s0, 4($s1)ตัวในวงเล็บ ($s1) คือ rs ต้องใส่ก่อน ส่วน $s0 คือ rt ใส่ทีหลัง · สลับที่กันเมื่อไรได้ 0x8E2E0004 ผิดทันที
ถ้าพลิกโจทย์lw $t1, 8($s2) → 100011·10010·01001·0008 = 0x8E490008
เปลี่ยนแค่ offset → เปลี่ยนแค่ 4 หลักหลัง ของ hex เท่านั้น (4 หลักแรกยังเป็น 0x8E30)
เปลี่ยนเป็น lb → op เปลี่ยนเป็น 100000 ⇒ 0x8230 0004
5.2

sw $s0, -4($s1)

แปลงเป็น hex
โจทย์ข้อ 5.2 จากข้อสอบ A01
คำตอบ
0xAE30 FFFC
กางบิตทีละฟิลด์
101011op = sw
10001rs = $s1
10000rt = $s0
1111 1111 1111 1100offset = −4
offset ติดลบต้องทำ 2’s complement 16 บิต: 0x10000 − 4 = 0xFFFC
สังเกตว่า 4 หลักแรกเปลี่ยนจาก 8E เป็น AE เพราะ op เปลี่ยนจาก lw เป็น sw เท่านั้น
ตารางค่าติดลบที่ใช้บ่อย−1 = 0xFFFF · −2 = 0xFFFE · −3 = 0xFFFD · −4 = 0xFFFC · −5 = 0xFFFB · −8 = 0xFFF8 · −16 = 0xFFF0 · −32 = 0xFFE0
ถ้าพลิกโจทย์sw $s0, -8($s1) → 0xAE30FFF8
ให้ hex มาแล้วถอดกลับ → กาง 32 บิต ดู 6 บิตแรก: 101011 = sw ⇒ อ่าน rs, rt แล้วแปลง imm กลับเป็นเลขมีเครื่องหมาย
5.3

โปรแกรม 4 บรรทัด เริ่มที่ 0x00400054

4 คะแนน
โจทย์ข้อ 5.3 จากข้อสอบ A01
คำตอบทั้ง 4 บรรทัด
addressAssemblymachine code
0x00400054lab0: beq $t0, $s1, lab10x11110002
0x00400058addi $s1, $s1, 10x22310001
0x0040005Cj lab00x08100015
0x00400060lab1: add $s2, $s1, $zero0x02209020
วิธีทดที่ต้องเขียนลงกระดาษ① ไล่ address ก่อน — ทุกคำสั่งยาว 4 ไบต์ ⇒ …054, …058, …05C, …060 และ lab1 อยู่ที่ 0x00400060
beq: offset = ( 0x400060 − ( 0x400054 + 4 ) ) ÷ 4 = 8 ÷ 4 = 2
   000100 · 01000($t0) · 10001($s1) · 0x0002 ⇒ 0x11110002
addi: 001000 · 10001 · 10001 · 0x0001 ⇒ 0x22310001
j: field = 0x00400054 ÷ 4 = 0x0100015 ⇒ 000010 ‖ field ⇒ 0x08100015
add: 000000 · 10001($s1) · 00000($zero) · 10010($s2) · 00000 · 100000 ⇒ 0x02209020
กับดัก① ลืมลบ 4 (PC+4) ตอนคิด branch offset
② ลืมหาร 4 ทั้งใน branch และ jump
add $s2,$s1,$zero เรียงในเครื่องเป็น rs=$s1, rt=$zero, rd=$s2 — ไม่ใช่ตามลำดับที่เขียน
ถ้าพลิกโจทย์สลับให้ j กระโดดไป lab1 แทน → field = 0x00400060 ÷ 4 = 0x0100018 ⇒ 0x08100018
เปลี่ยน beq เป็น bne → op เป็น 000101 ⇒ 0x15110002
ถ้า label อยู่ก่อน branch (ลูปย้อนกลับ) → offset ติดลบ เช่นย้อน 3 คำสั่ง ⇒ −4 = 0xFFFC
5.4

โปรแกรมชุดเดิม แต่เริ่มที่ 0x00408054

4 คะแนน
โจทย์ข้อ 5.4 จากข้อสอบ A01
คำตอบ — เปลี่ยนแค่บรรทัดเดียว
Assembly5.3 · เริ่ม 0x004000545.4 · เริ่ม 0x00408054
lab0: beq $t0,$s1,lab10x111100020x11110002
addi $s1,$s1,10x223100010x22310001
j lab00x081000150x08102015
lab1: add $s2,$s1,$zero0x022090200x02209020
นี่คือประเด็นที่อาจารย์ตั้งใจวัดbeq ไม่เปลี่ยน เพราะเก็บ offset สัมพัทธ์ — ระยะจาก branch ถึง label ยังเท่าเดิม (2 คำสั่ง) ไม่ว่าโปรแกรมจะไปวางที่ไหน
j เปลี่ยน เพราะเก็บ address สัมบูรณ์ — 0x00408054 ÷ 4 = 0x0102015 ⇒ 0x08102015
addi และ add ไม่เปลี่ยน เพราะไม่เกี่ยวกับ address เลย
นี่คือเหตุผลที่โค้ดซึ่งใช้แต่ branch เป็น relocatable — ย้ายไปวางที่ไหนก็ยังทำงานถูก
ถ้าพลิกโจทย์ย้ายไป 0x10400054j: 0x10400054 ÷ 4 = 0x04100015 แต่เก็บได้แค่ 26 บิตล่าง = 0x0100015 ⇒ machine code ยังเป็น 0x08100015 และทำงานถูกเพราะ PC อยู่ region 256 MB เดียวกัน
ถามว่า j ยิงได้ไกลแค่ไหน → 26 บิต × 4 = 2²⁸ = 256 MB ภายใน region เดียวกัน (บิต 31–28 มาจาก PC+4) ⇒ ข้าม region ต้องใช้ jr ที่ไปได้ทั้ง 4 GB
ถามว่า beq ยิงได้ไกลแค่ไหน → ±2¹⁵ คำสั่ง = −32768 … +32767 คำสั่ง (≈ ±128 KB)
ถามว่าทำไมต้องหาร 4 → คำสั่ง MIPS ยาว 4 ไบต์เสมอ address จึงลงท้ายด้วย 00 ตลอด ⇒ ไม่ต้องเก็บ 2 บิตนั้น ประหยัดบิตและยิงได้ไกลขึ้น 4 เท่า

คำถาม 6 — Bootstrapping COE 2019

10 คะแนน
หัวโจทย์ 6 จากข้อสอบ A01

ข้อนี้ยาวแต่เป็นงานเดียวซ้ำๆ — ถอด address map จากรูปที่ 4 หนึ่งครั้ง แล้วอ่าน Boot Mem จากรูปที่ 5 ทีละ 4 ไบต์ ก็ตอบได้ครบทั้ง 8 ข้อย่อย กดปุ่มในแล็บด้านบนเพื่อเดินทีละคำสั่งดูก่อนได้

ข้อย่อย6.1 6.2 6.3 6.4 6.5 6.6 6.7 6.8
รูปที่ 4 จากข้อสอบ A01
รูปที่ 4 — โครงสร้างคอมพิวเตอร์ COE 2019 · Gate A1 รับ 20 บิต ออก 1 บิต · Big Endian · Bootstrapping Address = 0x00000000
รูปที่ 5 จากข้อสอบ A01
รูปที่ 5 — ข้อมูลภายใน Boot Mem · อ่านแบบ address 0x00000000 มีข้อมูล 0x3C, address 0x00000001 มีข้อมูล 0x15

ขั้นที่ 1 — ถอด address map ของ COE 2019 จากรูปที่ 4

Gate A1 คิดยังไง โจทย์ให้ YA1 = Ā₁₂ · Ā₁₃ · … · Ā₃₁ ⇒ เป็น AND ของบิตที่กลับค่าทั้งหมด ⇒ จะเป็น 1 ก็ต่อเมื่อ A[31:12] เป็น 0 ทั้งหมด
นั่นคือครอบคลุม address ตั้งแต่ 0 ถึง 2¹²−1 ⇒ 4 KB แรกเท่านั้น
กล่องเงื่อนไข CS จากรูปช่วง addressขนาด
Boot MemCS1 = CS0 = YA1 (ไม่มี ○ = active-HIGH) ⇒ A[31:12] = 00x00000000–0x00000FFF4 KB
A[11-0]
Main MemoryCS1 ○← YA1 และ CS0 ○← A[31] ⇒ ต้องเป็น 0 ทั้งคู่0x00001000–0x7FFFFFFF≈2 GB
A[30-0]
Dev 0Decoder เปิดเมื่อ CS1 = CS0 = A[31] = 1
X1 = A[30], X0 = A[29]
Y0→Dev0 · Y1→Dev1 · Y2→Dev2 · Y3→Dev3
0x80000000–0x9FFFFFFF512 MB
ต่อตัว
A[28-0]
Dev 10xA0000000–0xBFFFFFFF
Dev 20xC0000000–0xDFFFFFFF
Dev 30xE0000000–0xFFFFFFFF
ทริกจำ 5 วินาที0x0–0xFFF = Boot (4 KB) หลักแรก 0-7 ที่เหลือ = Main 8,9 = Dev0 A,B = Dev1 C,D = Dev2 E,F = Dev3
อย่าจำสลับกับรูปที่ 3รูปที่ 3 ใช้ 0-7 Main · 8-B DMA · C Boot · D Dev0 · E Dev1 · F Dev2 — คนละวงจร คนละ map สิ่งที่ควรท่องคือ วิธีถอด ไม่ใช่ตัวช่วง

ขั้นที่ 2 — อ่าน Boot Mem ทีละ 4 ไบต์ (Big Endian)

Big Endianไบต์ที่ address ต่ำสุด คือไบต์ที่ มีนัยสำคัญสูงสุด (ซ้ายสุดของ word) ⇒ ไบต์ 3C 15 A0 00 ที่ 0x0–0x3 รวมเป็น word 0x3C15A000
ถ้าเป็น Little Endian จะกลายเป็น 0x00A0153C แล้วคำสั่งเปลี่ยนไปทั้งหมด
Address4 ไบต์Machine codeop 6 บิตแรกMIPS Assembly
0x000000003C 15 A0 000x3C15A000001111 = luilui $s5, 0xA000
0x0000000434 10 00 270x34100027001101 = oriori $s0, $zero, 0x0027
0x00000008AE B0 00 000xAEB00000101011 = swsw $s0, 0($s5)
0x0000000C8E B1 00 040x8EB10004100011 = lwlw $s1, 4($s5)
0x0000001008 10 00 140x08100014000010 = jj 0x00400050
Lab 08หัววัด address — วงจรรูปที่ 4 COE 2019 (ของคำถามนี้)

แผนที่นี้เป็นของ วงจรรูปที่ 4 (COE 2019) เท่านั้น — สังเกตว่า Boot Mem กินแค่ 4 KB แรก ต่างจากวงจรรูปที่ 3 อย่างสิ้นเชิง

Lab 05เดินโปรแกรม boot ทีละคำสั่ง
PC เริ่มที่ Bootstrapping Address = 0x00000000 · Big Endian
Address4 ไบต์Machine codeMIPS Assemblyผล
ไล่ทีละข้อย่อย
6.1

คำสั่งแรกหลังเปิดเครื่อง

3 ช่องย่อย
โจทย์ข้อ 6.1 จากข้อสอบ A01
คำตอบAddress: 0x00000000
Machine code: 0x3C15A000
MIPS Assembly: lui $s5, 0xA000
วิธีคิดโจทย์บอกว่า Bootstrapping Address = 0x00000000 ⇒ PC เริ่มที่นั่น และ address นี้ตกใน Boot Mem (4 KB แรก) ซึ่งเป็น non-volatile จึงมีคำสั่งค้างอยู่จริงตอนเปิดเครื่อง
อ่าน 4 ไบต์แรกจากรูปที่ 5 แบบ Big Endian: 3C 15 A0 000x3C15A000
กางบิต: 001111(op = 0x0F = lui) · 00000 · 10101(rt = 21 = $s5) · 0xA000
ผลลัพธ์: $s5 = 0xA0000000 เพราะ lui ยกค่า 16 บิตขึ้นไปไว้ครึ่งบน ครึ่งล่างเป็น 0
ถ้าพลิกโจทย์เปลี่ยน Bootstrapping Address เป็น 0x00000010 → คำสั่งแรกกลายเป็น j 0x00400050 ทันที
เปลี่ยนเป็น Little Endian → 4 ไบต์แรกกลายเป็น 0x00A0153C ⇒ op = 000000 = R-type ⇒ ต้องถอดใหม่หมด
ถามว่าทำไม Boot Mem ต้อง non-volatile → ตอนเปิดเครื่อง RAM ยังว่างเปล่า ต้องมีโปรแกรมค้างอยู่ในชิปจริงถึงจะเริ่มระบบได้
6.2

คำสั่งที่สอง

3 ช่องย่อย
โจทย์ข้อ 6.2 จากข้อสอบ A01
คำตอบAddress: 0x00000004
Machine code: 0x34100027
MIPS Assembly: ori $s0, $zero, 0x0027
วิธีคิดคำสั่งถัดไปอยู่ที่ PC + 4 = 0x00000004 · อ่าน 34 10 00 27 ⇒ 0x34100027
กางบิต: 001101(op = 0x0D = ori) · 00000(rs = $zero) · 10000(rt = 16 = $s0) · 0x0027
ผลลัพธ์: $s0 = 0 OR 0x27 = 0x00000027 ⇒ ใช้ ori กับ $zero เพื่อยัดค่าคงที่ลง register
ถ้าพลิกโจทย์ถามว่าทำไมใช้ ori ไม่ใช่ addi → ได้ผลเหมือนกันในกรณีนี้ แต่ ori ใช้ zero-extend ส่วน addi ใช้ sign-extend ⇒ ถ้าค่าเกิน 0x7FFF ผลจะต่างกัน
เปลี่ยนค่าเป็น 0x0041 → $s0 = 0x00000041 และคำสั่งกลายเป็น 0x34100041
6.3

คำสั่งที่สาม

3 ช่องย่อย
โจทย์ข้อ 6.3 จากข้อสอบ A01
คำตอบAddress: 0x00000008
Machine code: 0xAEB00000
MIPS Assembly: sw $s0, 0($s5)
วิธีคิดและผลที่เกิดขึ้นอ่าน AE B0 00 00 ⇒ 0xAEB00000 · กางบิต: 101011(op = 0x2B = sw) · 10101(rs = $s5) · 10000(rt = $s0) · offset 0
ปลายทางจริง = $s5 + 0 = 0xA0000000 ⇒ hex หลักแรกเป็น A ⇒ ตกในช่วง Dev 1
⇒ คำสั่งนี้คือการ ส่งค่า 0x27 ออกไปให้อุปกรณ์ Dev 1 ผ่าน memory-mapped I/O — จำไว้ใช้ตอบข้อ 6.8
ถ้าพลิกโจทย์ถ้าคำสั่งแรกเป็น lui $s5, 0xC000 → ปลายทาง 0xC0000000 ⇒ หลักแรก C ⇒ Dev 2
ถ้าเป็น 0x8000Dev 0 · ถ้าเป็น 0x0040 → 0x00400000 ⇒ Main Memory (ไม่ใช่ I/O แล้ว)
ถามว่า memory-mapped I/O คืออะไร → อุปกรณ์ถูกจัดสรร address ปนอยู่กับหน่วยความจำ ⇒ ติดต่อด้วย lw/sw ธรรมดา ไม่ต้องมีคำสั่งพิเศษ
6.4

คำสั่งที่สี่

3 ช่องย่อย
โจทย์ข้อ 6.4 จากข้อสอบ A01
คำตอบAddress: 0x0000000C
Machine code: 0x8EB10004
MIPS Assembly: lw $s1, 4($s5)
วิธีคิดอ่าน 8E B1 00 04 ⇒ 0x8EB10004 · 100011(op = lw) · 10101(rs = $s5) · 10001(rt = 17 = $s1) · offset 4
ต้นทางจริง = 0xA0000000 + 4 = 0xA0000004Dev 1 อีกครั้ง
⇒ อ่านค่าตอบกลับจากอุปกรณ์เข้ามาเก็บใน $s1
จุดที่มักโดนถามต่อถามว่า $s1 มีค่าเท่าไรบอกไม่ได้ เพราะเป็นค่าที่อ่านมาจาก Dev 1 ซึ่งโจทย์ไม่ได้ให้ — เหมือนกับข้อ 1.5 ที่ค่าบน data bus ระบุไม่ได้
ถ้าพลิกโจทย์เปลี่ยน offset เป็น 8 → อ่านจาก 0xA0000008 ยังเป็น Dev 1 อยู่ดี (ช่วงของ Dev 1 กว้าง 512 MB)
เปลี่ยน rt เป็น $s0 → machine code เป็น 0x8EB00004 และ $s0 จะถูกทับ ⇒ คำตอบข้อ 6.6 เปลี่ยนเป็น “บอกไม่ได้” ทันที
6.5

คำสั่งที่ห้า

3 ช่องย่อย
โจทย์ข้อ 6.5 จากข้อสอบ A01
คำตอบAddress: 0x00000010
Machine code: 0x08100014
MIPS Assembly: j 0x00400050
วิธีคำนวณปลายทางของ jอ่าน 08 10 00 14 ⇒ 0x08100014 · 000010 = op ของ j
field 26 บิต = 0 0001 0000 0000 0000 0001 0100 = 0x0100014
ปลายทาง = (PC+4)[31:28] ‖ field ‖ 00 = 0x0 ‖ (0x0100014 × 4) = 0x00400050
address นี้หลักแรกเป็น 0 และไม่อยู่ใน 4 KB แรก ⇒ ตกใน Main Memory — คือกระโดดไปรันโปรแกรมหลัก
ถ้าพลิกโจทย์ถ้าไม่มีคำสั่ง j นี้ → คำสั่งที่ 6 จะอยู่ที่ 0x14 = 27 A5 00 04 = 0x27A50004 ⇒ addiu $a1, $sp, 4 · คำสั่งที่ 7 ที่ 0x18 = 0x8FA40000 ⇒ lw $a0, 0($sp)
ถามว่า j ข้าม region ได้ไหม → ไม่ได้ เพราะบิต 31–28 ยึดมาจาก PC+4 ⇒ ยิงได้แค่ 256 MB ใน region เดียวกัน ต้องใช้ jr ถ้าจะข้าม
6.6

ค่าของ $s0 หลัง 5 คำสั่งแรก

1 ช่อง
โจทย์ข้อ 6.6 จากข้อสอบ A01
คำตอบ$s0 = 0x00000027
ไล่ทีละคำสั่งว่าใครแตะ $s0 บ้างlui $s5, 0xA000 → เขียนลง $s5
ori $s0, $zero, 0x27 → เขียนลง $s0 = 0x27 ✓
sw $s0, 0($s5)อ่าน $s0 ไปเขียนหน่วยความจำ ไม่ได้เปลี่ยนค่า
lw $s1, 4($s5) → เขียนลง $s1
j → ไม่แตะ register ใดเลย
⇒ มีคำสั่งเดียวที่เขียนลง $s0 ⇒ ค่ายังเป็น 0x00000027
ถ้าพลิกโจทย์ถามค่า $s50xA0000000
ถามค่า $s1บอกไม่ได้ เพราะอ่านมาจาก Dev 1
ถ้าคำสั่งที่ 4 เป็น lw $s0, 4($s5) → $s0 ถูกทับด้วยค่าจากอุปกรณ์ ⇒ ตอบว่า บอกไม่ได้
6.7

คำสั่งที่หกอยู่ที่ address ใด

1 ช่อง
โจทย์ข้อ 6.7 จากข้อสอบ A01
คำตอบ0x00400050
ทำไมไม่ใช่ 0x00000014เพราะคำสั่งที่ 5 คือ j 0x00400050 ซึ่ง เปลี่ยน PC ⇒ คำสั่งถัดไปไม่ได้อยู่ที่ 0x10 + 4 = 0x14 แต่ไปที่ปลายทางของ jump แทน
กฎ: PC ← PC + 4 ทุกคำสั่ง ยกเว้น branch/jump ที่เป็นจริง
ถ้าพลิกโจทย์ถ้าคำสั่งที่ 5 เป็น beq ที่เงื่อนไขไม่จริง → PC ไปที่ 0x00000014 ตามปกติ
ถ้าเป็น jal แทน j → ไปที่ 0x00400050 เหมือนกัน แต่ $ra จะถูกตั้งเป็น 0x00000014 ไว้กลับ
6.8

คำสั่งไหนติดต่อ I/O และกับอุปกรณ์ใด

เขียนอธิบาย
โจทย์ข้อ 6.8 จากข้อสอบ A01
คำตอบ0x00000008 → Dev 1  (คำสั่ง sw $s0, 0($s5) เขียนไปที่ 0xA0000000)
0x0000000C → Dev 1  (คำสั่ง lw $s1, 4($s5) อ่านจาก 0xA0000004)
วิธีหาแบบเป็นระบบ① หา ทุกคำสั่ง lw และ sw ในโปรแกรม ⇒ มี 2 ตัวคือที่ 0x08 กับ 0x0C
② คำนวณ address จริง = ค่าใน base register + offset ⇒ 0xA0000000 และ 0xA0000004
③ เอาไป เทียบ address map ⇒ hex หลักแรกเป็น A ⇒ ตกช่วง Dev 1 (0xA0000000–0xBFFFFFFF)
กับดักของข้อนี้คำสั่งอีก 3 ตัวไม่นับlui, ori, j เป็นการคำนวณ/กระโดดล้วนๆ ไม่แตะ data bus ของอุปกรณ์เลย
การ fetch คำสั่งเองไม่นับเป็น I/O — ถึงแม้ทุกคำสั่งจะถูกอ่านมาจาก Boot Mem ก็ตาม เพราะโจทย์ถามถึงการ “ติดต่อ I/O” ผ่าน memory-mapped I/O
ถ้าพลิกโจทย์ถามว่าคำสั่งไหนติดต่อ Main Memoryไม่มีเลย ใน 5 คำสั่งแรก
ถ้า lui ใส่ 0xC000 → ทั้งสองคำสั่งไปคุยกับ Dev 2 แทน
ถามว่าโปรแกรม boot ชุดนี้ทำอะไร → ตั้ง base address ของ Dev 1 → ส่งค่า 0x27 ไปให้อุปกรณ์ → อ่านค่าตอบกลับ → กระโดดไปรันโปรแกรมหลักที่ 0x00400050 ใน Main Memory
เปลี่ยน Gate A1 เป็นรับ A[31-16] → Boot Mem โตเป็น 64 KB และ Main Memory เริ่มที่ 0x00010000

ตารางอ้างอิง & สูตร

เปิดตอนทำโจทย์
Lab 06คิด branch offset / jump field

รูปแบบคำสั่ง

op31–26
rs25–21
rt20–16
rd15–11
shamt10–6
funct5–0

Radd rd, rs, rt · op = 000000 เสมอ แยกด้วย funct

op31–26
rs25–21
rt20–16
immediate / offset15–0

Ilw rt, imm(rs) · addi rt, rs, imm · beq rs, rt, imm

op31–26
target address25–0

Jj target · jal target

สูตรที่ต้องท่อง Branch imm = ( addr(label) − addr(branch) − 4 ) ÷ 4  (16 บิต signed)
Branch ปลายทาง = addr(branch) + 4 + imm × 4
Jump field = addr(target) ÷ 4  (26 บิตล่าง)
Jump ปลายทาง = (PC+4)[31:28] ‖ field ‖ 00
2's complement 16 บิต = 0x10000 − |n| ⇒ −1 = 0xFFFF · −4 = 0xFFFC · −5 = 0xFFFB
ระยะยิงj = 256 MB ใน region เดียว · beq = ±32768 คำสั่ง · jr = 4 GB

opcode · funct · register

R-typefunctI / Jop
add / addu0x20 / 0x21lw / sw0x23 / 0x2B
sub / subu0x22 / 0x23lb / sb0x20 / 0x28
and / or0x24 / 0x25addi / addiu0x08 / 0x09
xor / nor0x26 / 0x27andi / ori0x0C / 0x0D
slt / sltu0x2A / 0x2Blui / slti0x0F / 0x0A
sll / srl0x00 / 0x02beq / bne0x04 / 0x05
jr / jalr0x08 / 0x09j / jal0x02 / 0x03
RegisterเลขbinaryRegisterเลขbinary
$zero000000$s01610000
$v0–$v12–300010–00011$s11710001
$a0–$a34–700100–00111$s21810010
$t0–$t78–1501000–01111$s31910011
$t8–$t924–2511000–11001$s52110101
$sp / $ra29 / 3111101 / 11111$s72310111
ท่องแค่นี้พอ$sN = 16 + N · $tN = 8 + N · op ที่ต้องจำ: lw 100011 · sw 101011 · addi 001000 · beq 000100 · j 000010 · R-type 000000

กับดักที่ทำให้เสียคะแนนมากที่สุด

  1. ข้อ 1.5/1.6 ตอบแค่ “CPU” หรือ “Memory” แต่ไม่บอกว่าค่าบน data bus ระบุไม่ได้
  2. คิดว่า arbiter เขียน bus ได้ตลอด (ข้อ 2.3)
  3. ข้อ 2.5–2.7 ตอบว่าอ่านได้เฉพาะตัวที่ไม่ได้เขียน — ที่ถูกคือ ทุกตัว
  4. ข้อ 3 ช่อง (d) ตอบ address + 4 ทั้งที่โจทย์ว่า “ก่อนจะโหลด
  5. ลืมว่า lw/sw เรียง rs ก่อน rt ในเครื่อง
  6. คิด branch offset เป็นไบต์ (ลืมหาร 4) หรือลืมบวก PC+4
  1. j ลืมหาร 4 ก่อนใส่ field 26 บิต
  2. ใช้ li / move / blt ในข้อ 4 ⇒ หักข้อละ 1 คะแนน
  3. อ่าน Big Endian กลับด้าน
  4. ลืมว่า Boot Mem ในรูป 4 มีแค่ 4 KB (0x0–0xFFF)
  5. จำ address map ของรูป 3 กับรูป 4 สลับกัน
  6. ลืมคูณ index ด้วย 4 สำหรับ array int
3 นาทีแรกในห้องสอบ① เปิดดูรูปวงจรทุกรูปแล้ว ร่าง address map ลงกระดาษทดทันที ② เขียนตาราง register และ opcode ไว้มุมกระดาษ ③ ค่อยเริ่มตอบ — เรียงข้อ 5 → 6 → 1 → 2 → 3 → 4 (คำนวณล้วนก่อน เขียนโค้ดทีหลัง)