เมษายน 24, 2018, 07:17:23 pm *
ยินดีต้อนรับคุณ, บุคคลทั่วไป กรุณา เข้าสู่ระบบ หรือ ลงทะเบียน
ส่งอีเมล์ยืนยันการใช้งาน?

เข้าสู่ระบบด้วยชื่อผู้ใช้ รหัสผ่าน และระยะเวลาในเซสชั่น
   หน้าแรก   ช่วยเหลือ เข้าสู่ระบบ สมัครสมาชิก  
หน้า: [1]   ลงล่าง
  พิมพ์  
ผู้เขียน หัวข้อ: An Advanced Technique of Context Switching for RTOS (Keil C51)  (อ่าน 3848 ครั้ง)
0 สมาชิก และ 1 บุคคลทั่วไป กำลังดูหัวข้อนี้
ShadowMan
Administrator
Hero Member
*****
ออฟไลน์ ออฟไลน์

เพศ: ชาย
กระทู้: 8272


ShadowWares


| |
« เมื่อ: มกราคม 02, 2012, 06:43:44 pm »

ใครเคยใช้ RTOS ทุกตัวจะต้องรู้จักกับคำว่า "Context Switching" เป็นอย่างดี แต่คนที่ไม่เคยเจาะลึกลงไปยังตัว Kernel จะไม่มีทางรู้ได้เลยว่าเนื้อแท้ของมันเป็นอย่างไร?

การใช้งาน RTOS ทุกตัว ผู้ใช้ไม่จำเป็นต้องรู้เลยว่า Functions หรือ APIs ต่างๆ ถูกสร้างมาได้อย่างไร มีรูปร้างหน้าตาของโปรแกรมเป็นอย่างไร เพียงแค่ให้รู้ว่าใช้งานมันได้อย่างไร แบบไหนดี แบบไหนไม่ได้ แบบไหนควรใช้ แบบไหนควรหลีกเลี่ยง เท่านี้ถือว่าพอในระดับ RTOS User

มีคนสองกลุ่มหลักๆ ที่ต้องรู้ระดับแตกฉานคือ
1. บุคคลผู้ซึ่งต้องถ่ายทอดความรู้ให้คนอื่นนั่นหมายความว่า บุคคลนี้พร้อมจะรับกับคำถามทุกรูปแบบจากผู้เรียน และจะต้องแจกแจงทุกสิ่งอย่างระดับลึกสุดขั้วให้ได้
2. บุคคลผู้ที่พัฒนาตัว OS คนกลุ่มนี้ไม่ใช่แค่เพียงจะต้องรู้ลึกแย่างแตกฉาน แต่จะต้องตีความสิ่งที่รู้เป็นโปรแกรม ภายใต้สภาพแวดล้อมต่างๆ ไม่ว่าจะเป็นโครงสร้างทาง Hardware ของ CPU/MCU รวมไปถึงการใช้งาน IDE และ Compiler ด้วย นั่นหมายความคนกลุ่มนี้จะต้องมีความรู้ที่เรียกว่า ดุษฎี

กลับมาเข้าเรื่อง ว่าเราจะสร้าง Context Switching (เรื่องที่ยากต่อการทำความเข้าใจ และการเขียนโปรแกรม ลำดับต้นๆ ในโลกของ RTOS) ได้อย่างไร มาเริ่มกันที่ การ Save และ การ Load/Restore กันก่อน ดังนี้

Code: (c)
#define OS_SAVE_CONTEXT(){   \
   _push_(ACC);         \
   _push_(IE);            \
   _push_(B);            \
   _push_(DPL);         \
   _push_(DPH)   ;         \
   save_regs();         \
   _push_(PSW);         \
}
                     
#define OS_LOAD_CONTEXT(){   \
   _pop_(PSW);            \
   load_regs();         \
   _pop_(DPH);            \
   _pop_(DPL);            \
   _pop_(B);            \
   _pop_(IE);            \
   _pop_(ACC);            \
}


ไม่ทีอะไรแปลกใหม่สำหรับ Keil C51 นอกจาก save_regs() และ load_regs() สองตัวนี้คืออะไร มาดูกัน

Code: (c)
NAME   SDW_LLV

?PR?save_regs?SDW_LLV      SEGMENT CODE
?PR?load_regs?SDW_LLV      SEGMENT CODE

PUBLIC   save_regs
PUBLIC   load_regs

;-------------------------
   RSEG  ?PR?save_regs?SDW_LLV
   USING   0
save_regs:
   USING   0

   pop    acc
   pop    b
   push   ar2
   push   ar3
   push   ar4
   push   ar5
   push   ar6
   push   ar7
   push   ar0
   push   ar1
   push    b
   push    acc
   
   RET
;-------------------------
    RSEG  ?PR?load_regs?SDW_LLV
   USING   0
load_regs:
   USING   0

   pop acc
   pop b
   pop      ar1                                                            
   pop      ar0                                                            
   pop      ar7                                                            
   pop      ar6                                                            
   pop      ar5                                                            
   pop      ar4                                                            
   pop      ar3                                                            
   pop      ar2
   push b
   push acc   
   RET
;-------------------------
END

** code ข้างบนเป็นภาษา Assembly ที่ใช้แท็ก Code: (c) เพื่อให้อ่านง่าย เท่านั้น **

คำถามคือทำไมต้องทำการ define และทำไมต้องแทรก Assembly ?
อย่างที่ทราบกันเป็นอย่างดีว่า Task แต่ละ Task จะมี Stack, Status, Working Register และอื่นๆ (รวมๆ เรียกว่า Context) ที่จำเป็นเป็นของตัวเอง และการ Save/Load Context จะส่งผลโดยตรงกับค่าของ Context ดังนั้นจำเป็นจะต้องหลีกเลี่ยงคำสั่งว่าด้วยการ CALL ทั้งหลาย เพราะคำสั่งกลุ่มนี้จะส่งผลโดยตรงกับ SP (Stack Pointer) ซึ่งมีความเสี่ยงสูงมากที่จะก่อให้เกิดการผิดพลาด
และด้วยข้อจำกัด/ข้อกำหนดของ Keil C51/A51 ไม่ยอมให้ใช้ pragma asm/pragma endasm ในบล๊อกของ define ได้ ก็เลยต้องโยกในส่วนของการ push/pop Working Register ทั้งหลายไว้ในอีกฟังก์ชั่น และแน่นอนฟังก์ชั่นดังกล่าวคือ save_regs(), และ load_regs() สองฟังก์ชั่นนี้ มีการใช้คำสั่ง CALL เปลี่ยน SP ไปสองตำแหน่ง แต่ SP จะกลับมาเป็นค่าเดิมเมื่อเจอคำสั่ง RET ในฟังก์ชั่นดังกล่าว

เมื่อสังเกตุให้ดีๆ จะเห็นว่าผมได้ใช้ pop ไว้สองบรรทัดแรก ละ push ไว้สองบรรทัดหลัง (ก่อน RET) ของทังสองฟังก์ชั่น ความหมายคือ เนื่องจากฟังก์ชั่นทั้งสองนี้ถูกเรียกด้วย LCALL (หรือ CALL) CPU จึงทำการเก็บค่า Return address ไว้ใน Stack จำนวนสองไบต์ จึงต้องทำการ pop ออกมาเก็บไว้ก่อน การ pop จะทำให้ Stack Pointer ลดค่าลงสองตพแหน่ง ทำให้ Stack Pointer กลับมาจำแหน่งที่ต้องการพอดี จากนั้นเมื่อการกระทำต่างๆ เสร็จ จึงทำการ push ค่าที่เก็บไว้ใน acc และ ผลคือค่าของ return address จะอยู่ด้านบนสุดของ Stack เมื่อเจอคำสั่ง RET ใน load_regs()  CPU จะทำการโหลดค่าจาก Stack กลับไปยัง Program Counter เพื่อกระโดดกลับไปยังตำแหน่งที่ฟังก์ชั่นนี้ถูกเรียก ผลคือค่าของ Stack Pointer ลดลง 2 ตำแหน่ง การลดลง 2 ตำแหน่งดังกล่าว จะส่งผลให้คำสั่ง RET โหลดค่าจาก SP ไปยัง PC (Program Counter) ส่งผลให้ CPU/MCU ไปทำงานต่อยัง Scheduler ได้ทันที ไม่ใช้ฟังก์ชั่นที่เรียก (Caller) ความหมายตรงนี้คือศิลปะที่น่าทึ่งของ RTOS เข้าใจส่วนนี้ เขียน OS ใช้ได้เองอย่างไม่ต้องสงสัย
 
หลายๆ กรณีในส่วนของการ save context จะใส่ code ในส่วนของการ disable global interrupt ลงไปยังคำสั่งแรกของ save_regs() และ ในส่วนของการ load/restore context จะใส่ code ในส่วนของการ enable global interrupt ลงไปยังคำสั่งหลังสุด (ก่อน return) ไม่เช่นนั้นหากมี interrupt ซ้อนขึ้นมาในจังหวะนี้ ระบบจะผิดพลาดทันที ในที่นี้ไม่ได้รวมไว้ เนื่องจากทำการแยกการ enable/disable interrupt ไว้ต่างหาก
การเขียนโปรแกรมสำหรับ Save/Reload มีวิธีการแตกต่างกันออกไปแล้วแต่จุดประสงค์ และความต้องการของ Programmer แต่สำหรับ MCS-51 แบบเดิมๆ ส่วนที่จำเป็นอีกส่วนหนึ่งก็คือการเลือกเปลี่ยน Context ระหว่าง System Stack (idata) และ Task Stack (นิยมให้เป็น xdata) ส่วนนี้จะมาเล่าให้ฟังในตอนต่อไป
 smiley
บันทึกการเข้า

By SDW: Do No Wrong Is Do Nothing
          If you want to increase your success rate, double your failure rate
Kamikiyoko
Newbie
*
ออฟไลน์ ออฟไลน์

กระทู้: 2


| |
« ตอบ #1 เมื่อ: สิงหาคม 26, 2015, 11:51:42 am »

เป็นบท ความ รีวิว การสอนที่ดีมากเลยครับผม
บันทึกการเข้า

ggaa
Newbie
*
ออฟไลน์ ออฟไลน์

กระทู้: 1


| |
« ตอบ #2 เมื่อ: ตุลาคม 18, 2017, 03:38:45 pm »

ขอบคุณครับ
บันทึกการเข้า
หน้า: [1]   ขึ้นบน
  พิมพ์  
 
กระโดดไป: