Một cách tổng quát, các tác vụ có thể được tạo hoặc hủy động, và được điều khiển thông qua các thuộc tính của tác vụ.
Các lời gọi hệ thống dành cho việc xử lý tác vụ:
Tên hàm Chức năng
t_create Tạo tác vụ mới t_ident Lấy mã ID của tác vụ t_start Khởi động tác vụ mới t_restart Tái khởi động một tác vụ t_delete Xóa một tác vụ
t_suspend Hoãn việc thực thi một tác vụ t_resume Tiếp tục việc thực thi một tác vụ t_setpri Thay đổi độ ưu tiên của một tác vụ
t_info Truy vấn thông tin của một đối tượng tác vụ
Bảng 2-1 - Bảng các lời gọi hệ thống cho việc quản lý tác vụ
2.7.3 Độ ƣu tiên của tác vụ
Độ ưu tiên của một tác vụ phải được gán cho nó ngay từ lúc tác vụ được tạo ra. Có tổng cộng 256 mức độ ưu tiên - mức cao nhất là 255 và thấp nhất là 0.
Một số mức độ ưu tiên được dành riêng cho các tác vụ đặc biệt của pSOSystem. Mức 0 được dùng cho tác vụ IDLE daemon, tác vụ được cung cấp bởi lõi pSOS+. Các mức từ 230 - 255 được dành riêng cho nhiều loại tác vụ có độ ưu tiên cao, bao gồm pSOS+ ROOT.
43 Độ ưu tiên của một tác vụ, bao gồm các tác vụ hệ thống, có thể được thay đổi trong lúc thực thi bằng cách dùng lời gọi hệ thống t_setpri.
2.7.4 Các ngăn xếp dành cho tác vụ
Mỗi tác vụ đều có một hoặc nhiều ngăn xếp riêng. Người dùng sẽ khai báo kích thước của ngăn xếp khi tạo tác vụ bằng hàm t_create().
2.7.5 Giao tiếp, đồng bộ hóa, thẻ độc quyền (mutex)
Một ứng dụng xây dựng dựa trên pSOS+ nói chung có thể được chia thành một tập các tác vụ và thủ tục xử lý ngắt (ISR). Một cách trừu tượng, mỗi tác vụ là một tiểu trình (thread) của các hành động độc lập nhau được thực thi cùng lúc với các tác vụ khác. Tuy nhiên, có nhiều tác vụ cần trao đổi dữ liệu, đồng bộ hóa hành động, hoặc cùng chia sẻ tài nguyên. Để phục vụ việc giao tiếp giữa tác vụ với tác vụ cũng như giữa ISR với tác vụ, việc đồng bộ hóa và quản lý các thẻ độc quyền (mutex), lõi pSOS+ cung cấp các tính năng như: hàng đợi thông điệp, sự kiện, semaphore, thẻ độc quyền và các biến điều kiện.
2.7.6 Hàng đợi thông điệp
Các hàng đợi thông điệp cung cấp một cơ chế có tính linh hoạt cao dành cho các mục đích thông thường dùng để triển khai việc giao tiếp và đồng bộ hóa.
Các lời gọi hệ thống dành cho hàng đợi thông điệp:
Tên hàm Chức năng
q_create Tạo một hàng đợi thông điệp
q_ident Lấy mã ID của một hàng đợi thông điệp q_delete Xóa một hàng đợi thông điệp
q_receive Nhận / đợi một thông điệp từ một hàng đợi q_send Gửi một thông điệp đến cuối hàng đợi q_urgent Chèn một thông điệp vào đầu hàng đợi
q_notify Đăng ký một tác vụ thông báo sự kiện một thông điệp vừa được gửi đến hàng đợi
44
Bảng 2-2 - Bảng các lời gọi xử lý hàng đợi thông điệp
Giống như tác vụ, hàng đợi thông điệp là một đối tượng tổng quát được tạo hoặc hủy động. Thứ tự xử lý các thông điệp có thể là first-in-first-out hay theo mức độ ưu tiên của tác vụ.
2.7.7 Sự kiện
Lõi pSOS+ cung cấp một tập các tính năng đồng bộ hóa bằng sự kiện. Mỗi tác vụ có 32 cờ sự kiện, được mã hóa nhị phân trong một word độ dài 32-bit. Mỗi bit trong 32 bit này được dùng cho một cờ sự kiện.
5 lời gọi hệ thống cung cấp khả năng đồng bộ hóa bằng sự kiện giữa các tác vụ với tác vụ, hay tác vụ với ISR:
Tên hàm Chức năng
ev_receive Nhận hoặc đợi các sự kiện ev_send Gửi các sự kiện cho một tác vụ
q_notify Đăng ký một tác vụ thông báo sự kiện một thông điệp vừa được gửi đến hàng đợi
q_vnotify Đăng ký một tác vụ thông báo sự kiện một thông điệp vừa được gửi đến một hàng đợi có độ dài tùy biến
sm_notify Đăng ký một tác vụ nhận thông báo sự có mặt của semaphore
Bảng 2-3 - Bảng các lời gọi hệ thống dành cho sự kiện
2.7.8 Semaphore
Lõi pSOS+ cung cấp các thao tác quen thuộc cho semaphore. Một cách tổng quát, chúng đóng vai trò là các thẻ tài nguyên (resource token) khi cài đặt thẻ độc quyền (mutex).
Các lời gọi hệ thống có liên quan đến semaphore là:
Tên hàm Chức năng
45 sm_ident Lấy mã ID của một semaphore
sm_delete Xóa một semaphore
sm_p Nhận / đợi một thẻ semaphore sm_v Trả về một thẻ semaphore
sm_notify Đăng ký một tác vụ thông báo sự có mặt có semaphore sm_info Truy vấn thông tin của một semaphore
Bảng 2-4 - Bảng các lời gọi hệ thống liên quan đến Semaphore
2.7.9 Các thẻ độc quyền
Lõi pSOS+ cung cấp một tập các thao tác với thẻ độc quyền (mutex). Thẻ độc quyền khá giống với semaphore. Tuy nhiên, chúng cung cấp thêm một số tính năng đặc biệc, như khả năng loại bỏ đảo ngược độ ưu tiên không giới hạn và loại bỏ khóa chết (deadlock).
Các lời gọi hệ thống liên quan đến thẻ độc quyền
Tên hàm Chức năng
mu_create Tạo một thẻ độc quyền
mu_ident Lấy mã ID của một thẻ độc quyền mu_delete Xóa một thẻ độc quyền
mu_lock Khóa một thẻ độc quyền mu_unlock Mở khóa một thẻ độc quyền
mu_setceil Lấy độ ưu tiên của một thẻ độc quyền mu_info Truy vấn thông tin của một thẻ độc quyền
Bảng 2-5 - Bảng các lời gọi hệ thống liên quan đến thẻ độc quyền
2.7.10 Quản lý thời gian
pSOS+ cung cấp các hàm sau đây:
Xác định lịch thời gian và ngày tháng
Thời gian không hoạt động (không nhất thiết phải có) của một tác vụ đang chờ thông điệp, semaphores, hay sự kiện.
46
Đánh thức hoặc gửi tín hiệu báo thức đến một tác vụ sau một khoảng thời gian hay tại một thời điểm được định sẵn
Truy vết thời gian một tác vụ đang chạy, và lập lịch round-robin.
Những hàm này phụ thuộc vào các ngắt thời gian theo chu kỳ (timer interrupt), và sẽ không hoạt động nếu không có đồng hồ thời gian thực hay timer của phần cứng.
Việc quản lý thời gian tường minh sẽ được thực hiện thông qua các lời gọi hệ thống sau:
Tên hàm Chức năng
tm_tick Thông tin về khoảng tick đồng hồ của lõi pSOS+ tm_set Thiết lập thời gian và ngày tháng
tm_get Lấy thông tin thời gian và ngày tháng tm_wkafter Đánh thức tác vụ sau một khoảng thời gian tm_wkwhen Đánh thức tác vụ tại một thời điểm đã định sẵn tm_evafter Gửi sự kiện đến tác vụ sau một khoảng thời gian tm_evevery Gửi sự kiện đến tác vụ theo chu kỳ thời gian tm_evwhen Gửi sự kiện đến tác vụ tại một thời điểm định sẵn tm_cancel Hủy một timer báo thức
tm_getticks Lấy tổng số tick đã trôi qua kể từ lúc hệ thống pSOS+ được khởi động
47
CHƢƠNG 3 : HỆ THỐNG ARM VỚI NỀN TẢNG EMBEDDED
LINUX
Giới thiệu về kiến trúc xử lý, nguyên lý biên dịch chương trình trên chip thuộc dòng ARM - kiến trúc hệ thống trên hệ điều hành Embedded Linux
3.1 Dòng ARM
3.1.1 Sơ lƣợc về tên gọi: 2
ARM lúc đầu được đặt tên theo công ty Acorn. ARM=Acorn RISC Machine (chiếc máy sử dụng tập lệnh đơn giản của công ty Acorn). Sau này, do có thêm nhiều công ty cùng phát triển và một số lý do khác, người ta thống nhất gọi ARM là Advance RISC Machine.
3.1.2 Sự kế thừa cấu trúc:
Với các mô hình RISC của Berkeley RISC I và II, Stanford MIPS, ARM kế thừa:
3.1.2.1 Cấu trúc cơ bản:
Cấu trúc load-store
Chỉ lệnh có chiều dài cố định <32bit> Cấu trúc chỉ lệnh có 3 địa chỉ.
Thay vì chỉ dùng 1 chu kì xung nhịp cho tất cả các chỉ lệnh, ARM thiết kế để sao cho tối giản số chu kì xung nhịp cho một chỉ lệnh, do đó tăng được sự phức tạp cho các chỉ lệnh đơn lẻ.
3.1.3 Mô hình thiết kế ARM (3): :
Để phục vụ mục đích của người dùng: r0-r14: 15 thanh ghi đa dụng, r15 là thanh ghi PC, thanh ghi trạng thái chương trình hiện tại (CPSR). Các thanh ghi khác được giữ lại cho hệ thống <thanh ghi chứa các ngắt chẳng hạn>
2
48
Hình 3-1: Các thanh ghi của ARM
3.1.3.1 Thanh ghi trạng thái chƣơng trình hiện tại(CPSR)
Hình 3-2: Cấu trúc của thanh ghi trạng thái chƣơng trình hiện tại
Thanh ghi CPSR được người dùng sử dụng chứa các bit điều kiện.
N: Negative- cờ này được bật khi bit cao nhất của kết quả xử lý ALU bằng 1. Z: Zero- cờ này được bật khi kết quả cuối cùng trong ALU bằng 0.
C: Carry- cờ này được bật khi kết quả cuối cùng trong ALU lớn hơn giá trị 32bit.
V: Overflow-cờ báo tràn sang bit dấu.
3.1.4 Cấu trúc load-store (3): :
Cũng như hầu hết các bộ xử lý dùng tập lệnh RISC khác, ARM cũng sử dụng cấu trúc load-store. Điều đó có nghĩa là: tất cả các chỉ lệnh <cộng, trừ…> đều được thực
49 hiện trên thanh ghi. Chỉ có lệnh copy giá trị từ bộ nhớ vào thanh ghi<load> hoặc chép lại giá trị từ thanh ghi vào bộ nhớ<store> mới có ảnh hưởng tới bộ nhớ.
Các bộ xử lý CISC cho phép giá trị trên thanh ghi có thể cộng với giá trị trong bộ nhớ, đôi khi còn cho phép giá trị trên bộ nhớ có thể cộng với giá trị trên thanh ghi. ARM không hỗ trợ cấu trúc lệnh dạng „từ bộ nhớ đến bộ nhớ‟. Vì thế, tất cả các lệnh của ARM có thể thuộc 1 trong 3 loại sau:
1. Chỉ lệnh xử lý dữ liệu: chỉ thay đổi giá trị trên thanh ghi.
2. Chỉ lệnh truyền dữ liệu: copy giá trị từ thanh ghi vào bộ nhớ và chép giá trị từ bộ nhớ vào thanh ghi<load-store>.
3. Chỉ lệnh điều khiển dòng lệnh: Bình thường, ta thực thi các chỉ lệnh chứa trong một vùng nhớ liên tiếp, chỉ lệnh điều khiển dòng lệnh cho phép chuyển sang các địa chỉ khác nhau khi thực thi lệnh, tới những nhánh cố định, <lệnh rẽ nhánh> hoặc là lưu và trở lại địa chỉ để phục hồi chuỗi lệnh ban đầu <chỉ lệnh rẽ nhánh và kết nối> hay là đè lên vùng code của hệ thống <gọi giám sát-ngắt phần mềm>.
3.1.5 Tập lệnh của ARM:
Tất cả lệnh của ARM đều là 32bit: Có cấu trúc dạng load-store.
Cấu trúc lệnh định dạng 3 địa chỉ (nghĩa là địa chỉ của 2 toán hạng nguồn và toán hạng đích đều là các địa chỉ riêng biệt)
Mỗi chỉ lệnh thực thi một điều kiện.
Có cả chỉ lệnh load-store nhiều thanh ghi đồng thời.
Có khả năng dịch bit kết hợp với thực thi lệnh ALU trong chỉ 1 chu kì máy. Thumb code.
3.1.6 ARM C-Compiler:
50
Hình 3-3: Cấu trúc của bộ công cụ hỗ trợ phát triển.
3.2 Kiến trúc Embedded Linux
3.2.1 Bootloader (vivi, u-boot), kernel, root filesystem
Có 3 thành phần chính trong một hệ thống Embedded Linux, đó là bootloader, kernel và root filesystem. Chương trình mã nguồn được viết xong, thứ tự tiếp theo là biên dịch (compile), sau đó sẽ được download các file kết quả lên một flash memory.
51
Hình 3-4: Sơ đồ bộ nhớ trên board HDR3 sử dụng MPC860 và Embedded Linux 3.2.1.1 Bootloader
Trong một hệ thống embedded, không tồn tại firmware giống như CMOS của PC. Vì vậy để khởi động một hệ thống embedded, chúng ta phải xây dựng bootloader. Bootloader là một phần rất quan trọng trong hệ thống embedded. Chức năng của bootloader như sau:
Copy kernel từ flash memory đến RAM và thực thi kernel Khởi tạo phần cứng
Bootloader cũng bao gồm chức năng ghi data đến flash memory (download kernel hoặc ram disk image đến flash memory bằng serial port hoặc network hardware)
52 Cung cấp giao tiếp để gửi lệnh đến target board hoặc nhận về trạng thái của target board
Vivi là bootloader được hãng MIZI research, Inc. (Korea) xây dựng riêng cho dòng xử lý ARM.
u-boot cũng là bootloader được xử dụng rộng rãi trong các hệ thống Embedded Linux (u-boot được xây dựng cho ARM hoặc u-boot được xây dựng cho PPC860)
Khi xây dựng u-boot cần phải viết chương trình khởi tạo, đọc/ghi flash memory, điều khiển các tín hiệu dùng để download file cấu hình cho FPGA (nếu dùng Xilinx FPGA trên board), cấu hình các thanh ghi cho MCU, … Thông thường, để xây dựng bootloader, người ta thường dùng các thư viện có sẵn, sau đó sửa đổi lại để phù hợp với hardware của target board.
Nếu là một board mới hoàn toàn (tức là vừa được lắp ráp xong và chưa có u-boot trong flash memory) cần phải dùng thiết bị BDM (Background Debug Mode) hoặc JTAG debugger để download bootloader tới flash memory. Trình điều khiển thiết bị BDM (JTAG debugger) có thể chạy trong Windows hoặc Linux nhưng thường trình điều khiển chạy trong Windows được ưa chuộng hơn. Thiết bị này thường được bán kèm target board. Một trong các thiết bị được ưa chuộng là DBI2000 (High-speed BDM and JTAG interfaces) của hãng Abatron
3.2.1.2 Kernel
Kernel là thành phần chính của hệ điều hành Linux, quản lý các tác vụ (task management), quản lý bộ nhớ (memory management), I/O (I/O management), timer (timer management), giao tiếp với các API (application programming interface…
53
Hình 3-5: Cấu trúc của hệ điều hành Linux
Trong hệ thống Embedded Linux, việc cấu hình kernel được thực hiện bằng cách dùng lệnh “make menuconfig”. Sau đó lựa chọn các module cần thiết cho kernel bởi hệ thống menu. Sau khi đã cấu hình kernel, thực hiện việc build kernel bằng lệnh “make zImage” (đối với ARM9) hoặc “make uImaze” (đối với MPC860). File cuối cùng nhận được sẽ là zImage hoặc uImage. File này sẽ được download tới flash memory.
54
Hình 3-6: Cấu hình kernel 3.2.1.3 Root Filesystem
Linux cung cấp nhiều loại filesystem hơn bất kỳ hệ điều hành nào. Filesystem được tách riêng thành 3 lớp cơ bản, bao gồm:
Filesystem được được đặt trên thiết bị nối trực tiếp đến hardware của hệ thống embedded (ví dụ như đĩa cứng, thẻ nhớ, v.v…)
Netword file system dùng protocol tiêu chuẩn như là NFS
Filesystem đặc biệt nằm trong bộ nhớ được biết đến như là initial RAM disk Root filesystem phải chứa mọi thứ cần thiết để hỗ trợ hệ điều hành Linux. Cấu trúc thư mục của root filesystem như sau:
/proc Proc filesystem.
/etc System configuration file /sbin System binaries
/bin Basic binaries /lib Shared Libraries /mnt Mount point
55 /usr Additional utilities và applications
Và một số thư mục khác như là: /dev /home /lost+found /opt /root /tmp
Có nhiều dạng initial RAM disk (initrd): Cramfs (Compressed ROM file system), Cramfs được thiết kế nhỏ gọn, dung lượng không vượt quá 256MB. JFFS (Journalling Flash File System), …Quá trình xây dựng một ram disk image ngay từ đầu tương đối phức tạp. Do vậy người ta thường dùng các thư viện có sẵn (được cho dưới dạng file nén, ví dụ: initrd.gz), sau đó giải nén, chép thêm các device driver và ứng dụng vào, thêm hoặc sửa đổi một số file, thư mục cần thiết. Cuối cùng, dùng các công cụ để nén và chuyển file nén thành dạng image để download tới flash memory.
3.2.2 Device driver
Device driver là software giao tiếp giữa hardware và Linux kernel. Linux device driver có thể được tích hợp vào trong kernel theo 2 phương pháp: compile trực tiếp vào kernel hoặc compile thành một object format (device_driver.o) để kernel có thể load khi có nhu cầu (sử dụng lệnh install module “insmod device_driver.o”).
Để compile device driver file phải xây dựng một Makefile. Kết quả thu được sẽ là file có phần mở rộng *.o
3.2.3 Application
Aplication là các ứng dụng riêng biệt của người sử dụng, với mục đích điều khiển hệ thống tùy theo mục đích sử dụng.
Dùng Makefile để compile ứng dụng. Kết quả thu được là một file có thể chạy được (tương tự *.exe trên hệ điều hành DOS)
56
3.2.4 Stand-alone mode
Thông thường hệ thống Embedded Linux phải có khả năng chạy độc lập. Thay vì mount root filesystem từ một remote server (NFS filesystem), ta dùng một ramdisk image. Ramdisk image chứa trong flash memory và được load vào RAM khi hệ thống khởi động. Đồng thời để chạy các ứng dụng một cách tự động, ta phải sửa đổi nội dung file /etc/init.d/rcS để hệ điều hành có thể load các device driver và chạy các ứng dụng.