01076051Lecture 02

Computer Organization & Cortex-M Architecture

บทที่ 2: องค์กรของคอมพิวเตอร์และสถาปัตยกรรม Cortex-M

บทนี้เปิดฝาดูว่าข้างในชิปทำงานอย่างไร — ภาษาถูกแปลงเป็นบิต, หน่วยความจำจัดวางอย่างไร, register คืออะไร, สถาปัตยกรรม Cortex-M. ความเข้าใจนี้คือเหตุผลเบื้องหลังหลายอย่างใน Lab (เช่น ทำไมใช้ global).

ผลการเรียนรู้ (Learning Outcomes)

  • อธิบายลำดับภาษา (high→assembly→machine) และ toolchain (compiler/assembler/linker/loader)
  • อธิบาย program memory layout: Flash(code), RAM(Data/BSS, Stack, Heap) และ stack/heap ชนกัน
  • อธิบายทำไม global address คงที่จึง watch real-time ได้ แต่ local ไม่ได้
  • อธิบาย register Cortex-M (13 GPR + SP/LR/PC + PSR N Z C V)
  • เปรียบเทียบ Von Neumann vs Harvard; Cortex-M=Harvard
  • จำแนก Cortex-A/R/M และ M0/M3/M4/M7
  • เปรียบเทียบ SRAM/DRAM, ROM/PROM/EPROM/EEPROM/Flash

§2.1 ระดับภาษาและ Toolchain

ภาษาโปรแกรมมีหลายระดับไล่จากใกล้คนไปใกล้เครื่อง. ระดับบนสุดคือ High-level language (ภาษาระดับสูง เช่น C/C++) ที่เขียนใกล้ตัวปัญหาและคนอ่านเข้าใจง่าย. ถัดลงมาคือ Assembly ซึ่งเป็นรูปข้อความของคำสั่งเครื่องแต่ละคำสั่ง. ล่างสุดคือ Machine code ที่เป็นเลขฐานสอง จัดเป็น word ขนาด 8/16/32/64 บิต โดยแต่ละบิตแทนด้วยระดับแรงดันไฟฟ้า.

Toolchain คือชุดเครื่องมือที่แปลงภาษาระดับสูงให้เป็นไฟล์ที่ชิปรันได้. Compiler แปลงไฟล์ .c/.h เป็น assembly (.s). Assembler แปลง assembly เป็น object file โดยตั้งชื่อ symbol เป็นเลขลำดับภายใน. Linker รวม object หลายไฟล์เข้ากับ libraries แล้วจัด address ใหม่ให้เป็น executable (ELF/.bin/.hex) ซึ่ง loader ต้องรู้รูปแบบไฟล์จึงจะโหลดได้.

บนบอร์ด Nucleo เราข้ามขั้น loader ที่ซับซ้อนได้ เพราะลากไฟล์ .bin ไปวางลงไดรฟ์ของ ST-LINK ได้โดยตรง ไม่ต้องมี loader แยกต่างหาก.

VIZ-5Compile → Link Pipeline
Source.c / .h

โค้ดภาษา C/C++ (ใช้ bare-metal หรือ HAL)

คลิกแต่ละสเตจเพื่อดูว่าได้ไฟล์อะไรและทำอะไร. บน CubeIDE กด Build = รัน compiler→assembler→linker ให้อัตโนมัติ.

§2.2 Program Memory Layout

โค้ดโปรแกรมถูกเก็บใน Flash ซึ่งเป็นหน่วยความจำแบบ non-volatile (ข้อมูลไม่หายเมื่อปิดไฟ). ส่วนตัวแปรทั้งหลายอยู่ใน RAM ซึ่งถูกแบ่งเป็นหลายส่วน.

RAM แบ่งเป็นส่วนหลัก ๆ ดังนี้. Data segment เก็บตัวแปร global ที่มีค่าเริ่มต้น เช่น int A=5. BSS เก็บตัวแปร global ที่ไม่ได้กำหนดค่า ซึ่ง compiler จะตั้งค่าเริ่มต้นเป็น 0 ให้. Stack เก็บตัวแปร local และข้อมูลการเรียกฟังก์ชัน. Heap เก็บหน่วยความจำที่จองแบบ dynamic ด้วย malloc.

Stack มีพฤติกรรมพิเศษคือโตจาก address สูงลงไปหา address ต่ำ. เมื่อเข้าฟังก์ชันจะลดค่า SP (Stack Pointer) ลงเพื่อจองพื้นที่ เมื่อออกจากฟังก์ชันก็บวกค่า SP คืน ทั้งหมดนี้ compiler จัดการให้เอง. ส่วน Heap ใช้เมื่อเราไม่รู้ขนาดข้อมูลตอน compile เช่นการสร้าง linked list.

จุดที่ต้องระวังคือ Stack (โตลง) กับ Heap (โตขึ้น) โตเข้าหากัน ถ้าทั้งคู่ใช้พื้นที่มากจนมาชนกันโปรแกรมจะพัง จึงอาจต้องกำหนดขนาด stack/heap ใน linker. ค่าคงที่เล็ก ๆ เก็บฝังไว้ในโค้ด (Flash) ได้ แต่ตัวแปรที่ต้องเปลี่ยนค่าต้องจองพื้นที่ใน RAM.

ASCII diagram: RAM layouttext
addr สูง ┌──────────┐
         │  Stack   │ ↓ โตลง (local, ฟังก์ชัน)
         │    ↓     │
         │  (ว่าง)  │ ← เสี่ยงชนกัน
         │    ↑     │
         │  Heap    │ ↑ โตขึ้น (malloc)
         ├──────────┤
         │ BSS (=0) │
         │Data(init)│
addr ต่ำ └──────────┘  (RAM)
Flash: [ code + ค่าคงที่ ] non-volatile
VIZ-7Program Memory Layout (RAM)
Stack ↓ (local, ฟังก์ชัน)
พื้นที่ว่าง (8 หน่วย) — เสี่ยงชนกัน
Heap ↑ (malloc)
BSS (=0)
Data (global init)

Flash (แยกต่างหาก, non-volatile): เก็บ code + ค่าคงที่

Stack โตจาก address สูงลงล่าง เมื่อเรียกฟังก์ชัน; Heap โตจาก address ต่ำขึ้นบนเมื่อ malloc. ถ้าโตเข้าหากันจนพื้นที่ว่างหมด จะเกิด stack/heap collision.

§2.3 Global vs Local Address

ตัวแปร global มี address ที่ compiler รู้แน่นอนตั้งแต่ตอน compile จึงอยู่ตำแหน่งคงที่. เพราะ address ไม่เปลี่ยน เราจึงอ่านค่าจาก address นั้นได้ตลอดเวลา ทำให้ watch หรือ monitor ค่าแบบ real-time ได้.

ตัวแปร local อยู่บน stack ซึ่ง address ขึ้นกับเส้นทางการเรียกฟังก์ชัน (call path) จึงไม่คงที่ แต่ละครั้งที่เรียกอาจอยู่คนละที่ จึง watch ค่าแบบ real-time ไม่ได้.

VIZ-8Global vs Local Address (Watch)
ความลึก call stack: 0
global: num
address
0x2000_0010
คงที่?
คงที่เสมอ
watch real-time
ทำได้

อยู่ Data/BSS — compiler รู้ address แน่นอน

local: tmp
address
ยังไม่ถูกสร้าง
คงที่?
—
watch real-time
N/A

อยู่บน stack — ลองกด call/return แล้วดู address ขยับ

Global มี address คงที่เสมอ จึง watch real-time ได้. Local อยู่บน stack — address เปลี่ยนตาม call path จึง watch real-time ไม่ได้.

§2.4 Registers ใน Cortex-M

Register คือหน่วยเก็บบิตชั่วคราวความเร็วสูงที่อยู่ภายใน CPU สร้างขึ้นจากวงจร D flip-flop. Cortex-M มี register ทั้งหมด 16 ตัว แบ่งเป็น general-purpose 13 ตัว (R0–R12) และ special register 3 ตัว.

special register 3 ตัวได้แก่ SP (Stack Pointer) ที่ชี้ยอด stack, LR (Link Register) ที่เก็บ address สำหรับกลับจากฟังก์ชัน, และ PC (Program Counter) ที่ชี้คำสั่งถัดไป. นอกจากนี้ยังมี PSR ที่เก็บ flag สถานะ 4 ตัว ได้แก่ N (Negative), Z (Zero), C (Carry) และ V (oVerflow).

ขั้นตอนการคำนวณของ ALU: โหลด operand จาก memory เข้า register → ให้ ALU คำนวณ → เก็บผลลัพธ์กลับ register แล้วจึงเขียนกลับ memory (หรือส่งตรงออก I/O).

คำสั่ง (instruction) ของ ARM ขนาด 32 บิต แบ่งบิตเป็นสองส่วน: บิตแรก ๆ เป็น opcode (บอกว่าทำอะไร) ส่วนที่เหลือเป็น operand (บอกว่าทำกับข้อมูลตัวไหน). วงจร decoder จะแปลง opcode ให้กลายเป็นสัญญาณควบคุมภายใน.

ตัวอย่าง assembly: z = x + y (ย่อ เพื่อสื่อ load→compute→store)asm
LDR  R0, [x]      ; โหลด x เข้า R0
LDR  R1, [y]      ; โหลด y เข้า R1
ADD  R2, R0, R1   ; ALU: R2 = R0 + R1
STR  R2, [z]      ; เก็บผลกลับตัวแปร z
VIZ-9Register File — step z = x + y
ขั้นที่ 1/5
— เริ่มต้น —
register ว่าง (ค่าเริ่มต้น 0)
R0
0
R1
0
R2
0
R3
—
R4
—
R5
—
R6
—
R7
—
R8
—
R9
—
R10
—
R11
—
R12
—
SP
—
LR
—
PC
—
PSR flagsN = 0Z = 1C = 0V = 0

เดินทีละคำสั่ง: load operand เข้า register → ALU บวก → store กลับ memory. Register ที่เปลี่ยนจะถูกไฮไลต์.

§2.5 Von Neumann vs Harvard

สถาปัตยกรรมแบบ Von Neumann ใช้บัสเส้นเดียวร่วมกันทั้งการดึงคำสั่งและการเข้าถึงข้อมูล จึงดึงคำสั่งกับข้อมูลพร้อมกันไม่ได้.

สถาปัตยกรรมแบบ Harvard แยกบัสคำสั่งออกจากบัสข้อมูล จึงดึงทั้งสองอย่างพร้อมกันได้และทำงานเร็วกว่า แลกกับวงจรที่ซับซ้อนกว่า. Cortex-M เป็นแบบ Harvard (ภายใต้ ARMv7-M).

นอกจากนี้บัสยังแบ่งตามหน้าที่เป็น address bus, data bus และ control bus (ส่งสัญญาณ enable/read/write). สำหรับงานที่ต้องการราคาถูกและใช้พื้นที่น้อยก็มี ARMv6-M เป็นทางเลือก.

หัวข้อVon NeumannHarvard
บัสรวมแยก (คำสั่ง/ข้อมูล)
ดึงพร้อมกันไม่ได้ได้
ความเร็วช้ากว่าเร็วกว่า
ความซับซ้อนง่ายกว่าซับซ้อนกว่า
ใช้ในระบบทั่วไปบางแบบCortex-M (ARMv7-M)
VIZ-10Von Neumann vs Harvard Bus
CPUMemoryคำสั่ง+ข้อมูลบัสเดียว (shared)ผลัดกันวิ่งทีละตัว → คอขวด

Von Neumann ใช้บัสเดียวร่วมกัน คำสั่งและข้อมูลต้องผลัดกันวิ่ง (คอขวด). Harvard แยกบัส ดึงคำสั่งและข้อมูลพร้อมกันได้ จึงเร็วกว่า. Cortex-M เป็น Harvard.

§2.6 ตระกูล Cortex: A/R/M และ M0–M7

ARM แบ่งตระกูล Cortex เป็น 3 โปรไฟล์ตามลักษณะงาน: Cortex-A สำหรับงานสมรรถนะสูงที่รัน OS, Cortex-R สำหรับงาน real-time ที่ต้องตอบสนองแน่นอน และ Cortex-M สำหรับงานฝังตัวที่เน้นประหยัดพลังงาน. ภายในตระกูล M ยังแบ่งย่อยเป็นรุ่น M0, M3, M4, M7 ตามตารางด้านล่าง.

โปรไฟล์ชื่อเด่นตัวอย่างสถาปัตยกรรม
Cortex-AApplicationสมรรถนะสูง, รัน OSมือถือ, Raspberry PiARMv7-A
Cortex-RReal-timeตอบสนองเร็ว/แน่นอนABS/airbag (หลัก ms), หัวอ่าน HDDARMv7-R
Cortex-MMicrocontrollerประหยัดพลังงาน, งานฝังตัวบอร์ดเรา (STM32)ARMv7-M
โปรไฟล์ Cortex-A / R / M
รุ่น Mจุดเด่นสถาปัตยกรรม
M0gate น้อยมาก (<12K), 3µW/MHzARMv6-M
M3กระแสหลัก, pipeline 3-stageARMv7-M
M4DSP + FPUARMv7E-M
M7รุ่นของบอร์ดเราARMv7-M
รุ่น Cortex-M
VIZ-11Cortex Family Explorer
โปรไฟล์
ตระกูล Cortex-M
M7ARMv7-M

รุ่นของบอร์ดเรา (NUCLEO-F767ZI)

คลิกโหนดเพื่อดูการ์ดรายละเอียด. ไฮไลต์ M7 = แกนของบอร์ดในวิชานี้.

§2.7 เทคโนโลยีหน่วยความจำ

หน่วยความจำแบ่งเป็นสองกลุ่มใหญ่ตามการเก็บข้อมูล. RAM เป็นแบบ volatile เก็บข้อมูลชั่วคราว ข้อมูลหายเมื่อปิดไฟ. ส่วน ROM หรือหน่วยความจำ non-volatile เก็บ code และค่าคงที่ไว้ได้แม้ปิดไฟ.

ตัวอย่างการใช้งานจริง: DRAM ใช้เป็น main store หรือหน่วยความจำหลัก, SRAM ใช้เป็น cache ที่เร็วแต่แพง, ส่วน Flash ใช้เก็บ BIOS, firmware หรือเพลง MP3.

 SRAMDRAM
เซลล์MOS/MOSFETcapacitor (ต้อง refresh)
ความเร็วเร็ว (~2ns)ช้ากว่า (~10ns)
ใช้เป็นcache, local variablemain store
RAM (volatile)
ชนิดเขียน/ลบหมายเหตุ
Masked ROMกำหนดตอนผลิตแก้ไม่ได้
PROMเขียนครั้งเดียว—
EPROMลบด้วยแสง UV (~20 นาที)หน้าต่างควอตซ์
EEPROMลบด้วยไฟฟ้า ทีละไบต์ช้า ~10s/byte, 10k–100k รอบ, มัก external
Flashลบเป็นบล็อก, เร็วกว่า ~1s/byteเก็บ code ใน MCU/mass storage; NOR/NAND
ROM / non-volatile (เก็บ code+ค่าคงที่)
ตัวอย่างโครง main loop (Lab01 ย่อ)c
int main(void) {
    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();            // เปิด clock GPIOB, ตั้ง PB0 output push-pull
    while (1) {
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET);   // LD1 ติด (3.3V)
        HAL_Delay(500);
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET); // LD1 ดับ (0V)
        HAL_Delay(500);
    }
}
VIZ-12Memory Technology Comparison
Flashnon-volatile

ลบเป็นบล็อก, เร็วกว่า ~1s/byte; เก็บ code ใน MCU; NOR/NAND

Flash — ลบเป็นบล็อก
EEPROM — ลบทีละไบต์

กรองตามชนิด แล้วคลิกดูรายละเอียด. เดโมด้านล่างเทียบการลบ: Flash ลบทั้งบล็อกทีเดียว vs EEPROM ลบทีละไบต์.

VIZ-13GPIO Write Demo (PB0 / LD1)
clock พอร์ต GPIOB
__HAL_RCC_GPIOB_CLK_ENABLE()
LD1
0
PB0 bit
0V (ดับ)
สถานะ clock: ปิด

ลำดับการสั่ง GPIO: เปิด clock พอร์ต → ตั้งโหมด pin → เขียนค่า. ถ้ายังไม่เปิด clock การเขียนจะไม่ทำงาน. SET=logic 1 (3.3V, ไฟติด), RESET=logic 0 (0V, ไฟดับ).

แบบฝึกหัด

12 ข้อ — แต่ละข้อมีชนิดและระดับความยากกำกับ กดปุ่ม "ดูเฉลย" เพื่อดูคำตอบและคำอธิบาย

  1. 1.ปรนัยง่าย

    เรียงลำดับภาษาจากใกล้คนไปใกล้เครื่องได้อย่างไร

  2. 2.ปรนัยกลาง

    หน้าที่หลักของ linker คืออะไร

  3. 3.ปรนัยง่าย

    code ของโปรแกรมถูกเก็บไว้ที่ใด (ไม่หายเมื่อปิดไฟ)

  4. 4.ปรนัยกลาง

    ตัวแปร local อยู่ในส่วนใดของ RAM และโตไปทางใด

  5. 5.ถูก/ผิดกลาง

    ตัวแปร global มี address คงที่ จึง watch ค่าแบบ real-time ตอน debug ได้

  6. 6.ถูก/ผิดกลาง

    ตัวแปร local สามารถ watch ค่าแบบ real-time ได้เหมือนตัวแปร global

  7. 7.เติมคำง่าย

    Cortex-M มี register รวม 16 ตัว เป็น general-purpose ____ ตัว และ special ____ ตัว (SP, LR, PC)

  8. 8.เติมคำกลาง

    flag ใน PSR ของ Cortex-M มีสี่ตัวคือ N, Z, ____ และ ____

  9. 9.ปรนัยกลาง

    Cortex-M ใช้สถาปัตยกรรมบัสแบบใด

  10. 10.ปรนัยกลาง

    โปรไฟล์ Cortex ใดออกแบบมาสำหรับงาน real-time เช่น ABS/airbag

  11. 11.ปรนัยกลาง

    ข้อใดอธิบายความต่างของ EEPROM กับ Flash ได้ถูกต้อง

  12. 12.วิเคราะห์ยาก

    อธิบายว่าทำไม Lab01 ใช้ global num, led0 โดยโยงกับเรื่อง address และหน่วยความจำในบทนี้