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

Started by TaoTao, September 09, 2018, 03:32:52 PM

Previous topic - Next topic

TaoTao

คือ ผมอ่านค่า  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

dec

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

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

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

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

เช่น statement นี้
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 ตัวเดียว

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


หรือเอาตัวแปรธรรมดามาช่วย
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 อีกแล้ว

TaoTao

Quote from: dec on September 09, 2018, 10:26:58 PM
เกิดจากใน 1 statement มีการ access ตัวแปร volatile มากกว่า 1 ตัวครับ

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

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

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

เช่น statement นี้
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 ตัวเดียว

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


หรือเอาตัวแปรธรรมดามาช่วย
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


dec

Quote from: TaoTao on September 11, 2018, 02:38:22 AM
ตอนแรก เคยเข้าใจว่า มันประมวลผล ตามลำดับที่เราโค้ดเลย
คือ ซ้ายไปขวา บนลงล่่าง  ::)

เข้าใจละ ต้องเข้าถึงทีละ 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 ได้ชัดเจนกว่าแค่นั้นเอง

ch25

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

dec

Quote from: ch25 on September 13, 2018, 11:04:45 AM
ขอถาม 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 ได้ ลองดูนะครับ

TaoTao

Quote from: ch25 on September 13, 2018, 11:04:45 AM
ขอถาม IAR ที่ใช้เป็นตัว crack หรือใช้ตัว limit ยังไงครับ กำลังหาเล่นอยู่
ผมIAR เดิมๆ ครับ ตัว LIMIT 8kB ;D
เท่าที่ใช้มา ก็เพียงพอครับ
เพราะว่าโคดจึ๋งนึง ไม่ใหญ่โตอะไร
.
ถ้า อันลิมิต ชีวิตเกินร้อย น่าใช้อีกตัว ก็ COSMIC C เลยครับ เสียตรงที่ มันต้อง mail ไปขอ License เค้าทุกปีอ่ะ
.
แนะเริ่มจาก IAR ครับ
เพราะ UI ทันสมัย + ใช้ง่าย + เข้าใจง่าย + ยืดหยุ่น

TaoTao

Quote from: dec on September 11, 2018, 08:12:45 AM
Quote from: TaoTao on September 11, 2018, 02:38:22 AM
ตอนแรก เคยเข้าใจว่า มันประมวลผล ตามลำดับที่เราโค้ดเลย
คือ ซ้ายไปขวา บนลงล่่าง  ::)

เข้าใจละ ต้องเข้าถึงทีละ 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)