[IAR for STM8] warning นี้ มีวิธีทำให้มันหายไปไหมครับ

  • 7 Replies
  • 268 Views
*

Offline TaoTao

  • ***
  • 231
    • View Profile
คือ ผมอ่านค่า  CNT of TIM1  มาเก็บไว้ที่  tim1_rec_[..] เพื่อดู machine cycle :

"tim1_rec_[0] = (( TIM1->CNTRH << 8 ) | (TIM1->CNTRL) );
.
tim1_rec_[1] = (( TIM1->CNTRH << 8 ) | (TIM1->CNTRL) );
.
tim1_rec_[2] = (( TIM1->CNTRH << 8 ) | (TIM1->CNTRL) );
.
"

ปรากฎว่า มันขึ้น warning แบบนี้ตลอดเลย

"Warning[Pa082]: undefined behavior: the order of volatile accesses is undefined in this  D:\Dropbox\__WP\_PJ_STM8S_IAR\Project\my_prj\stm8s_it.c 127"

โค้ดทุกอย่างก็ทำงานตามที่คิดไว้ ทั้งหมด
แต่ แอบโตะใจที่มันเตือนนี่แหล่ะครับ    ::)

มีทางทำให้มันไม่ warning ไหมครับ
แค่ส่องค่า เท่านั้นเอง ทำมาเป็นเตือน  ;D

*

Offline dec

  • **
  • 67
    • View Profile
เกิดจากใน 1 statement มีการ access ตัวแปร volatile มากกว่า 1 ตัวครับ

เดาว่าน่าจะเป็น CNTRH กับ CNTRL เพราะส่วนใหญ่ Register มักจะมีการนิยามให้เป็น volatile

การที่เรานิยามตัวแปรให้เป็น volatile คือการนิยามว่าตัวแปรตัวนั้นมีการเปลี่ยนแปลงได้ตลอดเวลา
คาดการณ์ไม่ได้ ดังนั้น compiler จึงไม่ optimize ตัวแปรที่นิยามว่าเป็น volatile เพื่อให้เวลา
cpu ทำการ access ตัวแปร volatile จะต้องไปอ่านค่าจาก memory จริง ๆ ของตัวแปร volatile ตัวนั้น

การ access ตัวแปร volatile มากกว่า 1 ตัว ใน 1 statement นั้นไม่ได้ผิด แต่จะทำให้เกิดการคาดเดาพฤติกรรม
ของโปรแกรมไม่ได้

เช่น statement นี้
Code: [Select]
tim1_rec_[0] = (( TIM1->CNTRH << 8 ) | (TIM1->CNTRL) );
ใน statement นี้มีการอ่านค่าจาก CNTRH และ CNTRL พร้อมกัน แต่ในความเป็นจริงแล้ว
การทำงานของ cpu มันจะอ่านทีละตัว จะอ่านตัวไหนก่อนก็ขึ้นอยู่กับ compiler ที่ใช้ในการ compile และ
ช่วงเวลาที่ cpu อ่านค่าจาก volatile ตัวแรกอยู่ ค่าของ volatile ตัวที่ 2 อาจเปลี่ยนแปลงไปแล้ว

เรื่องนี้ไม่ได้เป็นเรื่องร้ายแรงถ้าเรารู้ตัวว่าเรากำลังทำอะไรอยู่ เช่นกรณีนี้ เรารู้ว่าเราตัวว่าเราอ่านค่า counter ของ timer
ซึ่งมีการเพิ่มขึ้นเป็นเชิงเส้นเสมอ แต่ compiler มันไม่รู้ด้วย IAR มันเลยเตือนไปตาม ISO/ANSI standard

แต่ถ้าจะต้องการเลี่ยงไม่ให้เกิดการ warning เราก็ต้องทำให้ใน 1 statement มีการ access ตัวแปร volatile ตัวเดียว

เช่น
Code: [Select]
tim1_rec_[0] = TIM1->CNTRH;
tim1_rec_[0] = (( tim1_rec_[0] << 8 ) | (TIM1->CNTRL));

หรือเอาตัวแปรธรรมดามาช่วย
Code: [Select]
uint8_t tmp1, tmp2;
tmp1 = TIM1->CNTRH;
tmp2 = TIM1->CNTRL;
tim1_rec_[0] = ((tmp1  << 8 ) | (tmp2);

หรือถ้าเรามันใจว่าโปรแกรมเราจะทำงานได้ถูกต้องแน่นอน เราสามารถไปปิดการ warning สำหรับ case นี้ได้
โดยคลิกขวาที่ Project เลือก Option ไปที่ C/C++ Compiler เลือกแทบ Diagnostics
แล้วในช่อง Suppress these diagnostics: ให้พิมพ์ Pa082 แล้ว OK
เท่านี้มันจะไม่ warning case Pa082 อีกแล้ว

*

Offline TaoTao

  • ***
  • 231
    • View Profile
เกิดจากใน 1 statement มีการ access ตัวแปร volatile มากกว่า 1 ตัวครับ

เดาว่าน่าจะเป็น CNTRH กับ CNTRL เพราะส่วนใหญ่ Register มักจะมีการนิยามให้เป็น volatile

การที่เรานิยามตัวแปรให้เป็น volatile คือการนิยามว่าตัวแปรตัวนั้นมีการเปลี่ยนแปลงได้ตลอดเวลา
คาดการณ์ไม่ได้ ดังนั้น compiler จึงไม่ optimize ตัวแปรที่นิยามว่าเป็น volatile เพื่อให้เวลา
cpu ทำการ access ตัวแปร volatile จะต้องไปอ่านค่าจาก memory จริง ๆ ของตัวแปร volatile ตัวนั้น

การ access ตัวแปร volatile มากกว่า 1 ตัว ใน 1 statement นั้นไม่ได้ผิด แต่จะทำให้เกิดการคาดเดาพฤติกรรม
ของโปรแกรมไม่ได้

เช่น statement นี้
Code: [Select]
tim1_rec_[0] = (( TIM1->CNTRH << 8 ) | (TIM1->CNTRL) );
ใน statement นี้มีการอ่านค่าจาก CNTRH และ CNTRL พร้อมกัน แต่ในความเป็นจริงแล้ว
การทำงานของ cpu มันจะอ่านทีละตัว จะอ่านตัวไหนก่อนก็ขึ้นอยู่กับ compiler ที่ใช้ในการ compile และ
ช่วงเวลาที่ cpu อ่านค่าจาก volatile ตัวแรกอยู่ ค่าของ volatile ตัวที่ 2 อาจเปลี่ยนแปลงไปแล้ว

เรื่องนี้ไม่ได้เป็นเรื่องร้ายแรงถ้าเรารู้ตัวว่าเรากำลังทำอะไรอยู่ เช่นกรณีนี้ เรารู้ว่าเราตัวว่าเราอ่านค่า counter ของ timer
ซึ่งมีการเพิ่มขึ้นเป็นเชิงเส้นเสมอ แต่ compiler มันไม่รู้ด้วย IAR มันเลยเตือนไปตาม ISO/ANSI standard

แต่ถ้าจะต้องการเลี่ยงไม่ให้เกิดการ warning เราก็ต้องทำให้ใน 1 statement มีการ access ตัวแปร volatile ตัวเดียว

เช่น
Code: [Select]
tim1_rec_[0] = TIM1->CNTRH;
tim1_rec_[0] = (( tim1_rec_[0] << 8 ) | (TIM1->CNTRL));

หรือเอาตัวแปรธรรมดามาช่วย
Code: [Select]
uint8_t tmp1, tmp2;
tmp1 = TIM1->CNTRH;
tmp2 = TIM1->CNTRL;
tim1_rec_[0] = ((tmp1  << 8 ) | (tmp2);

หรือถ้าเรามันใจว่าโปรแกรมเราจะทำงานได้ถูกต้องแน่นอน เราสามารถไปปิดการ warning สำหรับ case นี้ได้
โดยคลิกขวาที่ Project เลือก Option ไปที่ C/C++ Compiler เลือกแทบ Diagnostics
แล้วในช่อง Suppress these diagnostics: ให้พิมพ์ Pa082 แล้ว OK
เท่านี้มันจะไม่ warning case Pa082 อีกแล้ว
ตอนแรก เคยเข้าใจว่า มันประมวลผล ตามลำดับที่เราโค้ดเลย
คือ ซ้ายไปขวา บนลงล่่าง  ::)

เข้าใจละ ต้องเข้าถึงทีละ volatile
ตอนนี้ หายแล้วๆ ผมทำแบบนี้อ่ะครับ
tim1_rec_[0]  = TIM1->CNTRH << 8;
tim1_rec_[0] |= TIM1->CNTRL;
อ่อ ต้องเก็บ High ก่อน ค่าจะได้ไม่ผิดเพี้ยนเยอะ  ;D

ขอบคุณค๊าบบบบ  ;D  ;D  ;D

*

Offline dec

  • **
  • 67
    • View Profile
ตอนแรก เคยเข้าใจว่า มันประมวลผล ตามลำดับที่เราโค้ดเลย
คือ ซ้ายไปขวา บนลงล่่าง  ::)

เข้าใจละ ต้องเข้าถึงทีละ volatile
ตอนนี้ หายแล้วๆ ผมทำแบบนี้อ่ะครับ
tim1_rec_[0]  = TIM1->CNTRH << 8;
tim1_rec_[0] |= TIM1->CNTRL;
อ่อ ต้องเก็บ High ก่อน ค่าจะได้ไม่ผิดเพี้ยนเยอะ  ;D

ขอบคุณค๊าบบบบ  ;D  ;D  ;D
จริงๆ มันก็ต้องซ้ายไปขวาแหละครับ อันนี้ผมสับสนเอง ต้องขอโทษด้วยครับ ยังงงตัวเองเหมือนกัน
ว่าทำไมถึงคิดออกมาได้ว่ามันอาจไม่ซ้ายไปขวา

จริงๆ ต้องบอกว่าใน 1 statement มันก็ต้องใช้คำสั่ง assembly มากกว่า 1 คำสั่ง
มันเสี่ยงที่จะเกิดเหตุการณ์ อ่านค่า volatile ตัวแรกมาแล้ว แล้วก่อนจะอ่านค่าอีกตัว มันเกิด interrupt
หรือเกิด context switch กรณีใช้ os ขึ้น แล้วทำให้ volatile อีกตัวเปลี่ยนค่าไปก่อนจะอ่านมันนั่นแหละครับ

เรื่องแบบนี้เกิดได้กับตัวแปรทุกตัว แต่ถ้านิยามเป็น volatile แล้ว compiler มันจะเอาใจใส่เป็นพิเศษกว่า

หลายคนยังเข้าใจผิดว่า 1 statement มันจะประมวลผลจนเสร็จ แล้วค่อยเกิด interrupt หรือ context switch
แต่ในความเป็นจริงมันเกิดในระดับภาษา assembly มันจึงมีความเสี่ยงที่ statement นั้น จะไม่ได้ผลลัพธ์ตามที่เขียนนั่นเอง

สุดท้ายแล้วถ้าแยกมาเขียน เป็นหลาย statement แทนมันจะต่างอะไร ก็ต้องบอกว่าไม่ต่างกันครับ แต่มันจะคาดเดาผลลัพธ์
ของแต่ละ statement ได้ชัดเจนกว่าแค่นั้นเอง

*

Offline ch25

  • ***
  • 150
    • View Profile
ขอถาม IAR ที่ใช้เป็นตัว crack หรือใช้ตัว limit ยังไงครับ กำลังหาเล่นอยู่

*

Offline dec

  • **
  • 67
    • View Profile
ขอถาม IAR ที่ใช้เป็นตัว crack หรือใช้ตัว limit ยังไงครับ กำลังหาเล่นอยู่

ผมใช้ IAR for ARM แบบ crack ครับ โหลดตัว Free-Trials จากเว็บ IAR ส่วนตัว crack โหลดจากกระทู้นี้ http://www.electoday.com/index.php/topic,15428.0.html

crack มันสามารถเลือก product เป็น iar for stm8 ได้ ลองดูนะครับ

*

Offline TaoTao

  • ***
  • 231
    • View Profile
ขอถาม IAR ที่ใช้เป็นตัว crack หรือใช้ตัว limit ยังไงครับ กำลังหาเล่นอยู่
ผมIAR เดิมๆ ครับ ตัว LIMIT 8kB ;D
เท่าที่ใช้มา ก็เพียงพอครับ
เพราะว่าโคดจึ๋งนึง ไม่ใหญ่โตอะไร
.
ถ้า อันลิมิต ชีวิตเกินร้อย น่าใช้อีกตัว ก็ COSMIC C เลยครับ เสียตรงที่ มันต้อง mail ไปขอ License เค้าทุกปีอ่ะ
.
แนะเริ่มจาก IAR ครับ
เพราะ UI ทันสมัย + ใช้ง่าย + เข้าใจง่าย + ยืดหยุ่น

*

Offline TaoTao

  • ***
  • 231
    • View Profile
ตอนแรก เคยเข้าใจว่า มันประมวลผล ตามลำดับที่เราโค้ดเลย
คือ ซ้ายไปขวา บนลงล่่าง  ::)

เข้าใจละ ต้องเข้าถึงทีละ volatile
ตอนนี้ หายแล้วๆ ผมทำแบบนี้อ่ะครับ
tim1_rec_[0]  = TIM1->CNTRH << 8;
tim1_rec_[0] |= TIM1->CNTRL;
อ่อ ต้องเก็บ High ก่อน ค่าจะได้ไม่ผิดเพี้ยนเยอะ  ;D

ขอบคุณค๊าบบบบ  ;D  ;D  ;D
จริงๆ มันก็ต้องซ้ายไปขวาแหละครับ อันนี้ผมสับสนเอง ต้องขอโทษด้วยครับ ยังงงตัวเองเหมือนกัน
ว่าทำไมถึงคิดออกมาได้ว่ามันอาจไม่ซ้ายไปขวา

จริงๆ ต้องบอกว่าใน 1 statement มันก็ต้องใช้คำสั่ง assembly มากกว่า 1 คำสั่ง
มันเสี่ยงที่จะเกิดเหตุการณ์ อ่านค่า volatile ตัวแรกมาแล้ว แล้วก่อนจะอ่านค่าอีกตัว มันเกิด interrupt
หรือเกิด context switch กรณีใช้ os ขึ้น แล้วทำให้ volatile อีกตัวเปลี่ยนค่าไปก่อนจะอ่านมันนั่นแหละครับ

เรื่องแบบนี้เกิดได้กับตัวแปรทุกตัว แต่ถ้านิยามเป็น volatile แล้ว compiler มันจะเอาใจใส่เป็นพิเศษกว่า

หลายคนยังเข้าใจผิดว่า 1 statement มันจะประมวลผลจนเสร็จ แล้วค่อยเกิด interrupt หรือ context switch
แต่ในความเป็นจริงมันเกิดในระดับภาษา assembly มันจึงมีความเสี่ยงที่ statement นั้น จะไม่ได้ผลลัพธ์ตามที่เขียนนั่นเอง

สุดท้ายแล้วถ้าแยกมาเขียน เป็นหลาย statement แทนมันจะต่างอะไร ก็ต้องบอกว่าไม่ต่างกันครับ แต่มันจะคาดเดาผลลัพธ์
ของแต่ละ statement ได้ชัดเจนกว่าแค่นั้นเอง
ขอบคุณคร๊าบบบ  8)