Tiền xử lý pragma section trong c

SỬ DỤNG TỪ KHÓA #pragma TRONG LẬP TRÌNH

Nguyên là trong một lần tìm cách lập trình truy xuất DMA giữa khối SPI và bộ nhớ của vi điều khiển Concerto F28M36, mình bắt gặp dòng khai báo #pragma DATA_ALIGN (ucDMAControlTable, 1024) trong project mẫu của hãng Texas Instrument. Lên mạng tìm hiểu về khai báo này mình thu thập được khá nhiều kiến thức hay ho về bộ tiền xử lý #pragma, vấn đề Data Alignment, nên mình viết bài này để tổng hợp lại kiến thức học hỏi được và chia sẻ với mọi người.

Nào, bây giờ bắt đầu nhé! J

Từ khóa #pragma là một chỉ thị tiền xử lý trong ngôn ngữ C/C++, cũng tương tự như #include, #define Tức là những đoạn mã có vai trò định hướng cho trình dịch và sẽ được trình biên dịch xử lý đầu tiên, trước khi bắt đầu biên dịch chương trình. [1][2][3][4]

Tiền xử lý pragma section trong c
Hình 1: Quá trình biên dịch chương trình bằng ngôn ngữ C trên PC (Ảnh: fresh2refresh.com)

1    #pragma là gì?

#pragma là một bộ tiền xử lý có nhiệm vụ chỉ dẫn cho trình biên dịch thực hiện một chức năng, thiết lập một chế độ, hoặc tiến hành một thao tác cụ thể nào đó. Nếu như  #include được dùng để chèn mã thư viện nguồn vào đoạn code [1] [3], #define được dùng để thay thế các hằng số, các đoạn mã cố định trong code [2] [3], thì #pragma không có một chuẩn chức năng cụ thể nào. Mỗi trình dịch sẽ có quy định về các chức năng của #pragma riêng. Điều đó có nghĩa, nếu muốn tìm hiểu một pragma cụ thể nào, ta phải tìm kiếm trong tài liệu của trình biên dịch sử dụngpragma đó. Một số pragma mà bạn có thể đã gặp như:

ü  #pragma once : được dùng để chỉ dẫn trình biên dịch tránh include lại file nhiều lần [5][6]

ü   #pragma DATA_ALIGN : được dùng để cài đặt địa chỉ dữ liệu thỏa mãn ‘Data alignment’ .

Tiền xử lý pragma section trong c
Hình 2: Các nhóm tiền xử lý trong ngôn ngữ C (Ảnh: cquestions.com)

Cú pháp của bộ tiền xử lý#pragma như sau:

#pragma Yêu_cầu_cụ_thể_cho_trình_dịch

Phần Yêu_cầu_cụ_thể_cho_trình_dịch có thể chỉ là tên của pragma (ví dụ: #pragma One ) hoặc sẽ có đối số đi kèm (ví dụ: #pragma DATA_ALIGN ( ucDMAControlTable, 1024), đối số ở đây là biếnucDMAControlTable và hằng số 1024). Các đối số, hoặc hàm được sử dụng với pragma phải được khai báo, định nghĩa bên ngoài tất cả các hàm. Đồng thời,pragma cũng phải được khai báo trước khi các đối số hoặc các hàm đó được khai báo, định nghĩa. Nếu yêu cầu này không được đảm bảo, trình biên dịch sẽ đưa ra cảnh báo và có thể sẽ bỏ qua chỉ dẫn của pragma [11]. Việc khai báo này có thể được minh họa thông qua ví dụ sau:

//*****************************************************************************

// The control table used by the uDMA controller.  This table must be aligned

// to a 1024 byte boundary.

//*****************************************************************************

#pragma DATA_ALIGN(ucDMAControlTable, 1024)

unsigned char ucDMAControlTable[1024];

int main(void)

{

       volatile unsigned int TableIndex;

       volatile unsigned short Match = 0x00;

       unsigned long dummy_read;

#pragma DATA_ALIGN được khai báo ngoài hàm main, và trước khi đối số ucDMAControlTable  được khai báo.

2    #pragma DATA_ALIGN

Trong mục này, mình sẽ trình bày chi tiết một chút về #pragma DATA_ALIGN. Đây là bộ tiền xử lý mà mình gặp trong một chương trình mẫu về truy xuất DMA như đã dẫn ở trên. #pragma DATA_ALIGN là bộ tiền xử lý có nhiệm vụ chỉ dẫn cho trình biên dịch lưu dữ liệu được chỉ định vào một địa chỉ nào đó trong bộ nhớ để có thể truy xuất một cách tối ưu nhất. Việc lưu biến như vậy được gọi là Data Alignment. Bây giờ mình sẽ giới thiệu một chút về Data Alignment.

2.1  Data Alignment

Thoạt tiên khi nghe cụm từ “Data Alignment” ta sẽ nghĩ tới một phương pháp tổ chức dữ liệu nào đó trong bộ nhớ, giống như việc căn trái, phải các ký tự trong Microsoft Word. Nhưng thực tế thì không phải như vậy, Data Alignment KHÔNG tham gia bất cứ hoạt động nào vào việc tổ chức dữ liệu. Data Alignment đơn thuần chỉ là XÁC ĐỊNH ĐỊA CHỈ LƯU DỮ LIỆU. Nội dung chi tiết của Data Alignment đã được trình bày khá cụ thể ở tài liệu [8] [9] [10], ở đây mình chỉ tổng hợp lại mà thôi.

Sự xuất hiện của Data Alignment có liên quan tới quá trình truy xuất dữ liệu trong bộ nhớ của CPU. Hiện nay, CPU không truy xuất dữ liệu bằng từng byte riêng lẻ mà theo từng khối dữ liệu có dung lượng bằng 2^N byte như 2, 4, 8, 16, và 32 byte tùy theo từng hệ thống. Lý do là vì tốc độ truy xuất dữ liệu theo khối nhanh hơn rất nhiều so với bằng từng byte.

Tiền xử lý pragma section trong c
Hình 3: Truy nhập bộ nhớ theo từng byte và theo khối

Việc truy nhập theo từng khối dẫn đến quá trình thao tác dữ liệu trong bộ nhớ sẽ diễn ra ở các vùng địa chỉ có giá trị bằng nguyên lần dung lượng khối dữ liệu truy nhập. Ví dụ như truy xuất khối dữ liệu có dung lượng 4 byte, các địa chỉ dữ liệu có thể thao tác là 0x0000, 0x0004, 0x0008, … 0x0004*n như hình 3.

Từ đây người ta đưa ra khái niệm Data Alignment như một phương pháp ghi dữ liệu vào bộ nhớ thỏa mãn điều kiện địa chỉ của dữ liệu là một số ‘chẵn’ – tức địa chỉ dữ liệu chia hết cho dung lượng khối dữ liệu có thể truy xuất (2, 4, 8, 16, … 2^n byte). Cùng với Data Alignment ta có khái niệm Alignment Boundary hay N-byte Alignment chỉ số byte cao nhất mà dữ liệu có thể truy nhập theo khối – tức số N lớn nhất mà địa chỉ dữ liệu chia hết (N thỏa mãn hàm mũ cơ số 2). Ở đây mình xin lấy lại ví dụ ở tài liệu [10], dữ liệu có địa chỉ 0x12FEEC  (tương ứng 1244908 hệ cơ số 10) thỏa mãn 4-byte alignment hay có giá trị Alignment Boundary bằng 4 vì địa chỉ của dữ liệu chia hết cho 4 (đương nhiên là địa chỉ này chia hết cho 2 nhưng 4 là số lớn nhất).

Tiền xử lý pragma section trong c
Hình 4: CPU truy xuất 4 byte dữ liệu từ bộ nhớ: (trái) dữ liệu được ghi thỏa mãn 4-byte alignment, (phải) dữ liệu được ghi không thỏa mãn 4-byte alignment (Ảnh: songho.ca)

Như vậy điều gì sẽ xảy ra khi địa chỉ dữ liệu không phải là một số chẵn (hay không chia hết cho dung lượng khối dữ liệu). Ở đây mình xin dịch lại ví dụ ở tài liệu [10] trên trang sonhho.ca để trả lời. Như môt tả phía bên phải ở hình trên, ta thấy trường hợp 4 byte dữ liệu được ghi vào địa chỉ lẻ (0x0001 chẳng hạn). Việc ghi dữ liệu như thế này sẽ làm chậm quá trình truy nhập dữ liệu bởi CPU phải thực hiện thêm nhiều thao tác. Vấn đề này được minh họa ở hình dưới, CPU sẽ phải thực hiện 2 quá trình đọc dữ liệu ở 4 byte trên và 4 byte dưới, sau đó phải dùng thuật toán dịch bit để dịch 4 byte trên đi 1 byte và 4 byte dưới đi 3 byte, rồi thực hiện phép OR logic mới thu được dữ liệu cần truy xuất. Rõ ràng là chậm hơn nhiều khi đọc dữ liệu ở địa chỉ thỏa mãn 4-byte alignment, quá trình đọc chỉ diễn ra một lần duy nhất.

Tiền xử lý pragma section trong c
Hình 5: Quá trình truy suất 4 byte dữ liệu khi địa chỉ không thỏa mãn điều kiện Data Alignment (Ảnh: songho.ca)

Do đó có thể khái quát Data Alignment là một phương pháp chọn địa chỉ trong bộ nhớ để ghi dữ liệu sao cho quá trình truy suất bởi CPU được tối ưu nhất. Trong các tài liệu [8][9][10] tác giả có trình bày thêm về Data Padding và quá trình ghi dữ liệu của một biến cấu trúc có nhiều thành phần ở các định dạng dữ liệu khác nhau. Trong bài viết này mình xin dừng lại việc giới thiệu ở đây.

Đối với các biến thông thường thì ta không cần quan tâm địa chỉ lưu dữ liệu để thỏa mãn điều kiện data alignment. Việc đó sẽ được tự động thực hiện trong quá trình khai báo biến. Tại thời điểm khai báo, biến sẽ được cấp phát địa chỉ phù hợp với dung lượng của kiểu khai báo. Chỉ trong một vài trường hợp đặc biệt ta mới phải chỉ định địa chỉ lưu dữ liệu, ví dụ như khi sử dụng các bộ DMA. Trong trường hợp đó thì ta phải sử dụng đến bộ tiền xử lý #pragma DATA_ALIGN.

2.2  Sử dụng #pragma DATA_ALIGN

Trong mục này mình sẽ giới thiệu về việc sử dụng #pragma DATA_ALIGN trong việc khai báo một vùng bộ nhớ để lưu thông tin của bộ DMA. Đây là một đoạn code được viết cho nhân ARM của vi điều khiển Concerto F28M36 trên phần mềm Code Composer Studio V6.

Trước hết ta hãy xem sét yêu cầu của bộ DMA này thông qua một đoạn trích trong tài liệu Technical Reference Manual:

Tiền xử lý pragma section trong c

Như vậy, qua tài liệu chúng ta thấy bộ DMA của F28M36 yêu cầu một vùng nhớ trong bộ nhớ để lưu thông tin điều khiển dưới dạng một biến cấu trúc. Yêu cầu đặt ra cho vùng dữ liệu này là phải liên tục và được align với dung lượng 1024-byte.

Tiền xử lý pragma section trong c
Hình 6: Sơ đồ bộ nhớ dữ liệu điều khiển của bộ DMA

Như ở hình 6, ta thấy dữ liệu điều khiển được lưu trong một vùng nhớ có dung lượng 1024 byte với địa chỉ offset từ 0x0 tới 0x3F0 (kết thúc bộ nhớ ở địa chỉ 0x3FF tương ứng 1023d). Bộ nhớ này lưu dữ liệu của 32 kênh DMA với 2 mục Primary và Alternate. Mỗi kênh ở một mục sẽ có dung lượng 16 byte với địa chỉ offset từ 0x0 đến 0xF.

Quay trở lại việc lập trình giao tiếp DMA, ta thấy cần phải khai báo một vùng nhớ để lưu thông tin cho DMA thỏa mãn align 1024 byte. Tức dữ liệu phải bắt đầu từ một vị trí có địa chỉ bằng số nguyên lần giá trị 1024. Khi tham khảo project mẫu viết cho DMA của hãng Texas Instrument, ta thấy hai đoạn code mà ta phán đoán được sử dụng để khai báo vùng nhớ này:

Khai báo biến mảng ucDMAControlTable với mục đích lưu thông tin DMA

//*****************************************************************************

// The control table used by the uDMA controller.  This table must be aligned

// to a 1024 byte boundary.

//*****************************************************************************

#pragma DATA_ALIGN(ucDMAControlTable, 1024)

unsigned char ucDMAControlTable[1024];

 Gán địa chỉ mảng dữ liệu cho bộ DMA

// Set the base for the channel control table.

uDMAControlBaseSet(ucDMAControlTable);

 Để đảm bảo đoạn code trên đáp ứng việc khai báo bộ nhớ cho uDMA. Ta tìm hiểu dòng khai báo#pragma DATA_ALIGN. Do chỉ thị tiền xử lý #pragma không có chuẩn chung, nên ta hãy tìm thông tin từ nhà cung cấp trình biên dịch. Trong tài liệu spnu151n – ARM Optimizing C/C++ Compiler v16.12.0.STS User’s Guide, ta xác định được chức năng của bộ tiền xử lý#pragma DATA_ALIGN như sau:

Tiền xử lý pragma section trong c

 Từ tài liệu ta thấy bộ tiền xử lý #pragma DATA_ALIGN có định dạng:

 #pragma DATA_ALIGN(symbol, const)

#pragma DATA_ALIGN  được sử dụng để align dữ liệu với giá trị alignment boundary được khai báo ở đối số thứ 2 (constant) của bộ tiền xử lý theo đơn vị byte. Như vậy, khai báo #pragma DATA_ALIGN(ucDMAControlTable, 1024) cho biết đối số ucDMAControlTable sẽ được cấp phát vùng nhớ thỏa mãn data alignment với alignment boundary bằng 1024 byte.

Tiền xử lý pragma section trong c

Hình trên thể hiện màn hình debug của chương trình có sử dụng khai báo báo #pragma DATA_ALIGN như đã dẫn, ta thấy mảng ucDMAControlTable được khai báo ở địa chỉ 0x20001000h  tương ứng 536875008d  chia hết cho 1024, cho thấy mảng ucDMAControlTableđược aligned với dữ liệu 1024 byte, đáp ứng yêu cầu của khối uDMA.

Đến đây thì mình đã hoàn thành xong bài viết với mục đích giới thiệu về bộ tiền xử lý #pragma , cũng như quá trình tìm hiểu một pragma cụ thể (ở đây là #pragma DATA_ALIGN), những vấn đề về Data Alignment. Do sự hạn chế về hiểu biết và kinh nghiệm của người viết trong lĩnh vực lập trình mà bài viết có thể có nhiều sai sót, vì vậy nếu có vấn đề gì mong các bạn đóng góp ý kiến. Tại hạ vẫn như cũ – xin được rửa tai lắng nghe J!

 V1.0 – 05/01/2017     

Thanh Phong tổng hợp

Tài liệu tham khảo và các thông tin liên quan

Về bộ tiền xử lý

Tiếng Việt

[1] Phạm Hoài Nguyên, Chỉ Thị Tiền Xử Lý Trong C/C++

https://www.stdio.vn/articles/read/512/chi-thi-tien-xu-ly-trong-cc

[2] daynhauhoc.com, Ngôn Ngữ C – 23 – Tiền xử lý #define

https://www.youtube.com/channel/UCDFU6Q8PQJgbJhxIgnDs7_A

Tiếng Anh

[3] Brian W. Kernighan, Dennis M. Ritchie, The C Programming Language – 2nd Edition: Chapter 4.11 The C Preprocessor,  Prentice Hall PTR

[4] fresh2refresh.com, C – Preprocessor directives

http://fresh2refresh.com/c-programming/c-preprocessor-directives/

Về bộ tiền xử lý #pragma

Tiếng Việt

[5] daynhauhoc.com, Hỏi về #pragma trong khai báo thư viện c++

https://daynhauhoc.com/t/hoi-ve-pragma-trong-khai-bao-thu-vien-c/817

Tiếng Anh

[6] cprogramming.com, #pragma

http://www.cprogramming.com/reference/preprocessor/pragma.html

[7] stackoverflow.com, Use of  `#pragma` in c

http://stackoverflow.com/questions/232785/use-of-pragma-in-c

Về Data Alignment

Tiếng Việt

[8] daynhauhoc.com, Data structure alignment & padding

https://daynhauhoc.com/t/chia-se-data-structure-alignment-padding/1123

[9] Nguyễn Hữu Phương, Lý Do Tồn Tại Data Alignment

https://www.stdio.vn/articles/read/529/ly-do-ton-tai-data-alignment-struct-alignment-memory-alignment

Tiếng Anh

[10] songho.ca, Data Alignment

http://www.songho.ca/misc/alignment/dataalign.html

Về #pragma DATA_ALIGN

[11] Texas Instrument, spnu151n.pdf – ARM Optimizing C/C++ Compiler v16.12.0.STS User’s Guide – mục 5.10 Pragma Directives

[12] electronics.stackexchange.com, DATA_ALIGN Pragma – Tiva

http://electronics.stackexchange.com/questions/178102/data-align-pragma-tiva

[13] stackoverflow.com, What is pragma align in C?

http://stackoverflow.com/questions/13621386/what-is-pragma-align-in-c