การพัฒนาเฟิร์มแวร์สำหรับ RP2040 ด้วยภาษา C/C++ (ตอน 4 DSP)

Supachai Vorapojpisut
4 min readSep 26, 2022

--

จุดแข็งหนึ่งของสถาปัตยกรรม ARM Cortex-M ในตลาดไมโครคอนโทรลเลอร์ คือ CMSIS (Common Microcontroller Software Interface Standard) ซึ่งเป็นชุดของไลบรารีที่ทาง ARM ได้พัฒนาขึ้นสำหรับใช้ร่วมกันในทุกหน่วยประมวลผลที่อิงสถาปัตยกรรม ARM Cortex-M ไล่ตั้งแต่ Cortex-M0 จนถึง Cortex-M55 (ออกมาล่าสุดเมื่อ กพ. 2020) การมีไลบรารีที่ออกมาโดย ARM ที่เป็นบริษัทผู้ออกแบบสถาปัตยกรรม ไม่ขึ้นอยู่กับผู้ผลิตชิพที่ไลเซนส์มาผลิต องค์ประกอบของ CMSIS แบ่งออกเป็น CMSIS-CORE ที่กำหนดการอ้างอิงองค์ประกอบต่างๆ (รีจิสเตอร์ อินเตอร์รัพท์ …) ของหน่วยประมวลผล และกลุ่มไลบรารีเสริมสำหรับงานต่างๆ (RTOS, DSP, NN) ที่อิงอยู่บนหน่วยประมวลผล ไม่ขึ้นกับเพอริเฟอรัลบนชิพที่แต่ละผู้ผลิตจะเลือกไปพัฒนาต่อยอดเป็นจุดขายของตัวเอง

ว่าด้วย CMSIS-DSP

CMSIS-DSP เป็นไลบรารีสำหรับการประมวลผลทางคณิตศาสตร์ในส่วนที่เกี่ยวข้องกับการประมวลผลสัญญาณดิจิตอล ขอบเขตของไลบรารีแบ่งออกเป็น 3 ส่วนหลัก ได้แก่

  • ประเภทตัวแปรที่รองรับทั้งตัวเลข floating-point (เลขทศนิยม) เช่น float32_t แบบ 32 บิต และ fixed-point (เลขทศนิยมแบบจำกัดหลักทศนิยม) เช่น q31_t (เลขทศนิยมที่นิยามบนตัวเลขจำนวนเต็ม 32 บิตโดยใช้ 1 บิตเป็นเครื่องหมายและ 31 บิตเป็นเทอมส่วน) นอกจากนี้ยังรวมไปถึงข้อมูลแบบเวกเตอร์และเมตริกซ์ เช่น arm_matrix_instance_f32 คือเมตริกซ์ของตัวเลขทศนิยมแบบ float32_t
  • ฟังก์ชันสำหรับการประมวลผลทางคณิตศาสตร์รูปแบบต่างๆ ตั้งแต่การคำนวณเวกเตอร์และเมตริกซ์ เช่น arm_dot_prod_q31() การประมวลผลทางสถิติ เช่น arm_std_q15 วงจรกรองแบบดิจิตอล เช่น arm_fir_f16() และการควบคุมแบบป้อนกลับ เช่น arm_pid_f32()
  • โครงสร้างข้อมูลที่รองรับฟังก์ชันต่างๆ

ฟังก์ชันที่ไลบรารี CMSIS-DSP เตรียมไว้จะครอบคลุมทุกประเภทข้อมูลทั้งตัวเลขทศนิยม (float16_t float32_t float64_t) และตัวเลข fixed-point (q7_t q15_t q31_t q63_t) นักพัฒนาจึงสามารถ trade-off ระหว่างความถูกต้องในการประมวลผลและทรัพยากร (หน่วยความจำ/เวลา) ที่ใช้ นอกจากนี้การนำไลบรารี CMSIS-DSP ไปใช้กับสถาปัตยกรรม ARM Cortex-M รุ่นต่างๆจะมีกลไกในการเลือกตัวช่วยในการคำนวณให้โดยอัตโนมัติ เช่น ARM Cortex-M4 จะมี FPU (Floating-Point Unit) สำหรับช่วยคำนวณเลขทศนิยมแบบ single precision ทำให้การคำนวณตัวเลข float32_t จะถูกส่งต่อให้กับ FPU ทำให้คำนวณได้เร็วขึ้น

หน่วยประมวลผล RP2040 ใช้สถาปัตยกรรมแบบ ARM Cortex-M0+ แบบ dual core แต่ไม่รองรับ co-processor เช่น FPU และ DSP ทำให้การประมวลผลทั้งหมดอิงอยู่บนรหัสคำสั่งของ ARMv6-M เท่านั้น ตัวช่วยที่มีอยู่คือ ตัวคูณจำนวนเต็มแบบ 32 บิตภายในส่วนคอร์และตัวหารจำนวนเต็มที่อยู่ในส่วนบล็อก Single-cycle IO เท่านั้น ดังนั้น การเลือกประเภทข้อมูลจึงควรเน้นที่ตัวเลขแบบ fixed-point เช่น q31_t หากต้องการประสิทธิภาพในระดับดีหน่อย

การใช้งาน CMSIS-DSP กับ RP2040

ปัญหาหลักในการนำไลบรารี CMSIS มาใช้ในการพัฒนาเฟิร์มแวร์ของ RP2040 คือ ทีมพัฒนา Raspberry Pi Foundation เลือกจะไม่พัฒนาตามข้อกำหนดของ CMSIS เช่น การกำหนดชื่อรีจิสเตอร์และอินเตอร์รัพท์ต่างๆ ในรูป System View Description (SVD) ทำให้ไฟล์ header ไม่เข้ากันกับชุดเครื่องมือพัฒนาอื่น รวมทั้งไม่ได้รวมไลบรารี CMSIS มากับชุด C/C++ SDK การนำไลบรารี CMSIS มาพัฒนาเฟิร์มแวร์จึงเป็นภาระของนักพัฒนาที่ต้องดำเนินการเอง

รายละเอียดในการใช้งาน CMSIS ในเอกสารของ Pico C/C++ SDK

ชุดเครื่องมือพัฒนา Pico C/C++ SDK ล่าสุดได้สนับสนุนการนำไฟล์ header ของ CMSIS มาใช้ แต่รายละเอียดมีแค่ 2 บรรทัดเท่านั้น และก็ยังคงไม่รวมไลบรารี CMSIS มาในชุดเครื่องมือพัฒนาด้วย การค้นในอินเตอร์เน็ตพบว่ายังไม่มี solution ที่ชัดเจนในการนำใช้ไลบรารี CMSIS มาใช้กับ RP2040 โดยมี 2 กรณีที่เจอ

  • Edge Impulse รองรับการใช้ RP2040 โดยปล่อยเฟิร์มแวร์สำหรับ front-end เชื่อมต่อเซ็นเซอร์ในงานสาย machine learning โครงสร้างซอร์สโค้ดของเฟิร์มแวร์ Edge Impulse จะรวม CMSIS-DSP และ CMSIS-NN มาด้วย จึงเป็นหนึ่งในตัวเลือกของการตัดแต่งมาใช้งาน
  • โครงการ cmsis-pi-pico บน gitlab เป็น branch ที่แยกออกจาก main branch เพื่อมาพัฒนาแบบเจาะจง RP2040

การพัฒนาต่อยอดของทั้ง 2 แนวทางมีข้อดี/ข้อเสียต่างกัน การใช้เฟิร์มแวร์ของ Edge Impulse เป็นต้นแบบมีข้อดีที่เริ่มได้ง่ายโดยแค่ clone มาจาก github แล้วเปิดด้วย VS Code + CMake extension ก็สามารถ build ได้เลย รวมทั้งมาพร้อมกับส่วนโค้ดของไลบรารีจำนวนมากรวมถึงรองรับ TensorFlow Lite ส่วนข้อเสียคือ มาแบบจัดเต็มจึงต้องออกปรับแต่งพอสมควร ส่วนการใช้ไฟล์จากโครงการ cmsis-pi-pico เป็นต้นแบบ แม้จะดูเป็น minimal แต่เจอปัญหาที่เหมือนพัฒนาสำหรับ ARM Compiler และยังไม่รวมไลบรารี CMSIS-NN มาด้วย ดังนั้นบทความนี้เลยจะอธิบายทั้ง 2 แนวทางเพื่อให้เลือกวิธีที่เหมาะสมไปพัฒนาต่อได้เอง ฮาร์ดแวร์ที่จะใช้สาธิตคือ บอร์ด Maker Pi Pico

การพัฒนาต่อยอดจากเฟิร์มแวร์ Edge Impulse

Edge Impulse เป็นแพลตฟอร์มสำหรับสร้างโค้ด machine learning เพื่อให้ทำงานในบอร์ดไมโครคอนโทรลเลอร์ กลไกของ Edge Impulse จะทำงานด้วย model แบบ TensorFlow Lite ที่สามารถ convert และ export จาก model ที่ถูก train ด้วยโค้ด Python ที่ใช้ไลบรารี TensorFlow ได้ เครื่องมือของ Edge Impulse จะรองรับตลอดกระบวนการตั้งแต่นำเข้าหรือรวบรวม (มีโค้ดตัวอย่างสำหรับบอร์ดต่างๆเพื่อใช้อ่านและรายงานค่าจากเซ็นเซอร์) ข้อมูล, เตรียมชุดข้อมูล, train model และสร้างโค้ดสำหรับมาทำงานบนบอร์ดไมโครคอนโทรลเลอร์

แพลตฟอร์ม Edge Impulse

เฟิร์มแวร์ตัวอย่างของบอร์ด Raspberry Pi Pico จะรวมไลบรารีต่างๆที่จะใช้ในการประมวลผล โดยรวมไปถึงไลบรารี CMSIS-DSP ที่ใช้ในการคำนวณต่างๆ เช่น การประมวลผลเวกเตอร์/เมตริกส์ การแปลง FFT ดังนั้นเราสามารถนำส่วนโค้ดของเฟิร์มแวร์มาเป็นต้นแบบเพื่อเรียกใช้ชุดคำสั่งของไลบรารี CMSIS-DSP ได้ การเตรียมการเริ่มจากการใช้ git เพื่อดาวน์โหลดไฟล์จาก github

git clone https://github.com/edgeimpulse/firmware-pi-rp2040

การพัฒนาโค้ดใช้ VS Code + Pico SDK บน Windows ซึ่งสามารถไปดูขั้นตอนได้จาก การพัฒนาเฟิร์มแวร์สำหรับ RP2040 ด้วยภาษา C/C++ (ตอน 2 dual-core blink) ซึ่งจะใช้ CMake Extension ในการ build ดังนั้น โปรแกรม VS Code จะขึ้นมาถามตอนเรียก CMake: build ว่าจะใช้ toolchain ไหน ให้เลือกเป็น GCC 10.3.1 arm-none-eabi เพื่อให้ไปเรียกใช้ gcc มาทำการ build โค้ดตัวอย่าง เมื่อ build เสร็จ จะได้ไฟล์ ei_rp2040_firmware.elf เป็นเฟิร์มแวร์สำหรับติดตั้งต่อไป

การ build เฟิร์มแวร์ของ Edge Impulse

ตำแหน่งและไฟล์ของไลบรารี CMSIS-DSP ถูกประกาศไว้ในไฟล์ CMakeLists.txt ซึ่งจะสามารถเรียกใช้ในโค้ด main.cpp ในโฟลเดอร์ src ได้

การพัฒนาโดยใช้ cmsis-pi-pico

ข้อเสียของเฟิร์มแวร์ Edge Impulse อยู่ที่การรวมไลบรารีมาจำนวนมาก จึงอาจเสียเวลาในช่วงแรกเพื่อปรับแต่งไฟล์ CMakeLists.txt รวมทั้งตัดโฟลเดอร์ที่ไม่เกี่ยวข้องออกจาก project สำหรับนักพัฒนาที่ชอบแนวทาง build from scratch จึงอาจสนใจการใช้ไลบรารี cmsis-pi-pico ที่ปรับแต่งสำหรับ RP2040 โดยใช้ git ดาวน์โหลดมารวมในโฟลเดอร์ด้วยคำสั่ง

git clone https://gitlab.com/khockuba/cmsis-pi-pico

จากนั้นจึงปรับแต่งไฟล์ CMakeLists.txt ให้รู้ path และไฟล์ของไลบรารี CMSIS-DSP

add_subdirectory(cmsis/)# Include path
target_include_directories(${NAME} INTERFACE
./cmsis/Core/Include
./cmsis/DSP/Include
)
# Include required libraries
target_link_libraries(${NAME}
pico_stdlib
cmsis_core
cmsis_dsp
pico_multicore
pico_sync
pico_util
)

โค้ดที่ใช้ทดสอบคือ การทำ FFT แบบ fixed-point (q15 หมายถึงใช้ 15 บิตในการแทนตัวเลขทศนิยมด้วย 1/ค่าจำนวนเต็ม และใช้ 1 บิตเป็นเครื่องหมาย) ซึ่งดัดแปลงมาจากโค้ดตัวอย่างของไลบรารี CMSIS-DSP เอง ภายในส่วนโค้ดจะมีข้อมูล time vector เตรียมไว้แล้ว ซึ่งโค้ดส่วน DSP จะประมวลผลใน core1 โดยควบคุมจังหวะด้วย core0 ผ่านทาง Mutex

void core1_dsp_task() {
uint32_t core_id;
int fft_size = 1024;
arm_status status = ARM_MATH_SUCCESS;
q15_t max_value;
float32_t max_fvalue;
uint32_t test_index;
// init hardware
gpio_init(LED1);
gpio_set_dir(LED1, GPIO_OUT);
// init software
status = arm_cfft_init_q15(&cfftInstQ15, fft_size);
while(1) {
if (dataReady) {
// FFT operation
mutex_enter_blocking(&bufMtx);
gpio_put(LED1, 1);
arm_cfft_q15(&cfftInstQ15, inDataBuf, 0, 1); // forward transform with bit reverse
arm_cmplx_mag_q15(inDataBuf, outDataBuf, fft_size);
arm_max_q15(outDataBuf, fft_size, &max_value, &test_index);
gpio_put(LED1, 0);
mutex_exit(&bufMtx);
dataReady = false;
// report
arm_q15_to_float(&max_value, &max_fvalue, 1);
arm_q15_to_float(outDataBuf, printBuf, DATA_SIZE/2);
printf(“[%f”, printBuf[0]);
for (int i=1; i < DATA_SIZE/2; i++) {
printf(“,%f”, printBuf[i]);
}
printf(“]\n”);
}
sleep_ms(10);
}
}

การทดสอบความถูกต้องในการคำนวณ FFT (1024 จุด) จะใช้การรายงานค่าผ่านทาง serial ทั้งค่า time vector และ frequency spectrum มายังโปรแกรม MATLAB ผลที่แสดงในหน้าต่างของ MATLAB app จะเห็นว่า frequency spectrum ที่ได้จากการคำนวณ FFT ในบอร์ด Raspberry Pi Pico แบบ fixed-point มีความสอดคล้องกับค่า frequency spectrum ของ MATLAB ที่ใช้คำสั่ง fft()

การทดสอบ FFT เทียบระหว่าง CMSIS-DSP และ MATLAB

終わりに (ในตอนท้าย)

บทความนี้ได้แนะนำการผนวกไลบรารี CMSIS-DSP มาใช้กับหน่วยประมวลผล RP2040 โดยอธิบายมา 2 รูปแบบแล้วแต่ผู้ที่สนใจ ตัวไลบรารี CMSIS-DSP มีประโยชน์หลายด้านไม่ได้จำกัดเฉพาะการประมวลผล DSP เนื่องจากโค้ดจะมีประเภทข้อมูล fixed-point มาด้วยทั้ง q7 q15 q31 จึงสามารถใช้เร่งการประมวลผลเพื่อทดแทนการคำนวณแบบทศนิยมที่หน่วยประมวลผลไมโครคอนโทรลเลอร์ไม่ค่อยถนัดนัก

อ่านเพิ่มเติม

  1. End-to-end tinyML audio classification with the Raspberry Pi RP2040
  2. คลิปที่บันทึกจากกิจกรรม TESA Tech Update บน YouTube

--

--

Supachai Vorapojpisut
Supachai Vorapojpisut

Written by Supachai Vorapojpisut

Assistant Professor at Thammasat University

No responses yet