3.1.1 Giới thiệu về trình biên dịch CodeWarrior
CodeWarrior là một công cụ phát triển phần mềm nhúng cho các dòng vi xử lý của FreeScale. Có nhiều phiên bản CodeWarrior vàởđâytôi dùng phiên bản 10.6. Có thể tải phiên bản này trên trang web của freescale[10].
Hình 3. 1: Giao diện CodeWarrior Các thành phần cơ bản của CodeWarrior:
Trình dịch (Compiler): Dùng để biên dịch mã nguồn (source code) sang mã đối tượng (object code).
Trình liên kết (Linker): Liên kết mã đối tượng được dịch ra từ trình dịch với các mã đối tượng trong thư viện và những tập tin khác.
Debugger: Được dùng để thực thi và chỉ rõ các hoạt động bên trong chương trình. Sử dụng debugger để phát hiện lỗi trong khi thực thi chương trình.
Editor: Dùng để soạn thảo và quản lý mã nguồn.
Hình 3. 2: Giao diện Debugger cho CodeWarrior
3.1.2 Giới thiệu về công cụ JTAG (Joint Test Action Group)
JTAG (Joint Test Action Group) là một phương pháp tích hợp để kiểm tra các kết nối trên PCB (printed circuit boards) dựa theo chuẩn IEEE 1149.1. Nhưng công cụ này cũng được dùng để kết nối với các chíp và gỡ lỗi (debug) phần mềm. [8,11]
JTAG được một nhóm kỹ sư Harry Wardrop đề xuất vào năm 1985. Sự ra đời của công cụ này xuất phát từ tính phức tạp về phần cứng trong các hệ thống ngày càng cao. Do vậy việc tích hợp JTAG vào các chip giúp cho việc kiểm thử, giải quyết các khó khăn về các lỗi thuận lợi, nhanh chóng hơn. Ngày nay các chíp hiệnđại hầu như đều có tích hợp JTAG bên trong ví dụ dòngARM, Coldfire,… Việc tích hợp JTAG vào chíp làm tăng chi phí nhưng nó lại rất hữu ích cho việc kiểm tra khi mà số lượng sản phẩm có giá trị từ hàng ngàn trở lên.
JTAG bao gồm các thành phần sau:[8]
TDI (Test Data In): Đường dữ liệu vào TDO (Test Data Out): Đường dữ liệu ra
TCK (Test Clock): Tín hiệu đồng bộ hóa tần số.
TRST (Test Reset) : Tuỳ chọn có thể có hoặc không dùngđể thực hiện khởiđộng lại JTAG và toàn hệ thống
Hình 3. 3: Sơ đồ kiến trúc JTAG
Ứng dụng điển hình của JTAG là để kiểm tra kết nối giữa các thiết bị (Interconnection testing between devices), kiểm tra logic core (Core logic testing), kiểm tra bộ nhớ (Memory testing), kiểm tra mức hệ thống (System level test).
Ứng dụng thực tế của JTAG là để truy cập vào các chíp, các mạch, các hệ thốngđể: Kiểm tra thiết kế/gỡ lỗi (Design verification/debug), Kiểm tra sản xuất (Manufacturing test), Kiểm tra tích hợp phần cứng/ phần mềm (Hardware/software integration), lập trình hệ thống (in-system programming).
3.1.3 Giới thiệu về chuẩn SWD (Serial Wire Debug)
SWD là cổng debug sử dụng 2 đường tín hiệu để truy cập vào giao diện debug ARM. Nó là một chuẩn debug của dòng ARM đang dần được thay thế cho JTAG. SWD gồm hai tín hiệu [9]:
SWDIO (Serial Wire Data Input/Output): Dòng dữ liệu hai chiều được điều khiển bởi cả máy chủ (host) và thiết bị (target).
SWCLK (Serial Wire Clock): Tín hiệu clock được điều khiển bởi máy chủ, đồng bộ hóa việc chuyển giao dữ liệu qua SWD.
Ngoài việc gỡ lỗi SWD có thể được sử dụng cho lập trình flash. Thông thường một thuật toán lập trình flash với các lệnh hỗ trợ “flash command” sẽ được nạp vào vi
điều khiển của chíp trên RAM qua cổng SWD, sau đó SWD sẽ được sử dụng để kiểm soát hoạt động ghi xóa xuống flash.
SWDvàJTAGlàhai giao diệnvật lý khác nhau, nhưng các lệnh và các thanh ghi truy cập được sử dụngcho lập trìnhflashđều giống nhau. Đối với dòng ARM Kinetis K được tích hợp cả hai chuẩn debug SWD và JTAG nhưng dòn Kinestis L chỉ tích hợp chuẩn SWD.[6]
3.2Tổng quan về mạch MKL46Z256 và phần mềm điều khiển chuẩn (Standard Software Driver - SSD)
3.2.1 Tổng quan về mạch MKL46Z256
MKL46Z256 là một vi điều khiển thuộc dòng ARM kinetis KL46 có một số đặc điểm cơ bản sau:[3]
Core: Arm cortex M0+, tần số CPU 48MHz, bus clock lên đến 24MHz Nguồn điện cần cung cấp trong khoảng 1.71 -3.6V
Memory: Bộ nhớ flash là 256KB,SRAM là 32KB
Nhiệt độ hoạt động xung quanh dao động từ-40 ° C đến105 °C.
Kết nối (Connection): Mạch này được tích hợp chuẩn SWD do vậy có thể sử dụng Jlink để debug và nạp phần mềm lên chip thông qua chuẩn SWD. Bộ nhớ Flash được chia làm 2 khối (block) P-Flash(Program Flash), với kích thước mỗi khối là 128KB. Trong mỗi khối lại được chia ra làm các vùng (sector) có kích thước 1KB. Sector là đơn vị nhỏ nhất có thể xoá của Flash. Bộ nhớ Flash và các thanh ghi của Flash được đặtở các vùng địa chỉ khác nhau như hình bên dưới. Địa chỉ bắt đầu của P-Flash là 0x00000000.
Hình 3. 4: Bản đồ bộ nhớ Flash
Trong bộ nhớ flash có cơ chế bảo vệ (protection) khi một số vùng cần được bảo vệ để tránh khỏi việc ghi và xóa vào các dữ liệu đã lưu trữ. Bộ nhớ flash được tích hợp
các giải thuật ghi, xóa cũng như là kiểm chứng việc ghi xóa xuống flash. Hỗ trợ cơ chế bảo mật (security) để ngăn chặn việc truy cập trái phép nội dung bộ nhớ flash.
Sơ đồ khối của bộ nhớ flash được hiển thị trong hình sau:
Hình 3. 5: Sơ đồ khối Flash
3.2.2 Phần mềm điều khiển chuẩn cho mô-đun Flash của mạch MKL46Z256 (Standar Software Driver – SSD) MKL46Z256 (Standar Software Driver – SSD)
SSD là một tập các giao diện chương trình ứng dụng cho phép người dùng có thể truy cập vào các khối bộ nhớ flash. Phần mềm điều khiển chuẩn này sẽ giúp thực hiện hoạt động ghi, xóa xuống flash với tốc độ cao.
Trình điều khiển này chứa các hàm riêng biệt ứng với mỗi lệnh của flash (flash command) để xử lý các hoạt động cụ thể của mô-đun flash. Có rất nhiều command nhưng trong ví dụ này tôi chỉ viết cho một số command cơ bản như: Erase All Block Command, Verify All Block Command, Erase Flash Sector Command, Verify sector command, Program Longword Command, Program Check Command [4].Từ đó tôi đã viết được phần mềm SSD gồm các hàm sau: FlashEraseAllBlock(), FlashVerifyAllBlock(), FlashEraseSector(), FlashVerifySection(), FlashProgramLongword(), FlashProgramCheck(), FlashCommandSequence(), PFlashGetProtection(), PflashSetProtection(). Ngôn ngữ được dùng để lập trình phần mềm này và thực hiện viết test code là ngôn ngữ lập trình C.
Thư mục chứa phần mềm này được đặt tên là SSD_FlashDriver gồm các thư mục như sau:
Docs (Documents): Thư mục chứa các tài liệu gồm có:
Tập tin “Tài liệu thiết kế chi tiết”: mô tả chi tiết chức năng của từng hàm, sơ đồ khối của từng hàm, các kết quả trả về của hàm đó. Program flash 0 Program flash 1 Programminga cceleration RAM Memory controller Control registers Status registers Interrupt Register access To MCU‟s Flash controller
Tập tin “Test Cases List and Report Results.excel”: chứa danh sách các tình huống kiểm thử của từng hàm nêu trên, mô tả chi tiết các bước của từng test case, dữ liệu đầu vào, kết quả đầu ra mong đợi, kết quả đầu ra thực tế của từng test case.
Drvsrc (Driver Source): Thư mục chứa mã nguồn chương trình được chia thành 2 thư mục nhỏ:
include: Chứa các tập tin có đuôi .h của chương trình
source: Chứa mã nguồn của từng hàm Test: Thư mục này có 2 thư mục nhỏ
Test_Code: Chứa mã nguồn của từng test case tương ứng với từng hàm nêu trên.
Test_MKL46Z256: Chứa file chạy chương trình thực thi kiểm thử của từng hàm.
3.2.3 Thiết kế tình huống kiểm thử cho phần mềm SSD
Chương 2 có trình bày các kỹ thuật thiết kế kiểm thử trong đó có kỹ thuật kiểm thử điều khiển luồng. Kiểm thử điều khiển luồng là một kỹ thuật thiết kế kiểm thửchủ yếu sử dụng trong kiểm thửđơn vị (Unit Test). Vì vậy tôi đã dùng kỹ thuật này để thiết kế các test case trong kiểm thử đơn vị cho phần mềm SSD.
Dựa theo kỹ thuật kiểm thử điều khiển luồng cùng tài liệu về mạch MKL46Z256 (tài liệu số 3 trong phần tài liệu tham khảo) tôi đã viết được “Tài liệu thiết kế chi tiết” của từng hàm. Chi tiết xin xem phần phụ lục A
Tôi sẽ trình bày chi tiết về việc thiết kế các tình huống kiểm thử cho hàm FlashEraseSector() – một hàm trong phần mềm điều khiển chuẩn SSD. Chức năng của hàm này là cho phép xoá tất cả địa chỉ của một hoặc nhiều sector trong bộ nhớ P-Flash và hàm này sẽ trả về lỗi nếu vùng được chọn để xoá ở trạng thái được bảo vệ (Protected).
Nhìn vào sơ đồ khối của hàm ta FlashEraseSector hình 3.6để xác định các đường đi kiểm thử dựa theo độ bao phủ cạnh. Tức là tôi sẽ tìm các đường đi bao phủ được các cạnh của hàm này ít nhất một lần.
BEGIN Call the FlashCommandSequence Check if target address is aligned? No END Return with returnCode Prepare passing parameters for FlashCommandSquence
for sector erase
FlashEraseSector()
Check if end address is within
valid range
No
Return code to state ‘invalid address’
Return code to state ‘invalid range’ Set Return code to
‘OK’ Check if size > 0 Check if return code is "OK" Yes Yes Yes No Check if size is aligned on sector size
Return code to state ‘invalid size’ Yes
Yes
No
Updated size and target address
No Check
if target address is within valid range
Yes
Return code to state ‘invalid range’ No
Đường đi số 1: Begin -> Set returnCode -> Check target address is valid range?-No -> Return error invalid range -> return returnCode -> End
Đường đi số 2: Begin -> Set returnCode -> Check target address is valid
range?-Yes -> Check target address is aligned?- No ->Return error invalid address - > return returnCode -> End
Đường đi số 3: Begin -> Set returnCode ->Check target address is valid
range?-Yes -> Check target address is aligned? -Yes -> Check size is aligned?- No -> Return error invalid size -> return returnCode -> End
Đường đi số 4: Begin -> Set returnCode -> Check target address is valid
range?-Yes -> Check target address is aligned? - Yes -> Check size is aligned?- Yes - > Check end address is valid range? - No -> Return error invalid range -> return returnCode -> End
Đường đi số 5: Begin -> Set returnCode -> Check target address is valid
range?-Yes -> Check target address is aligned? -Yes -> Check size is aligned?- Yes -> Check end address is valid range? - Yes -> Check size > 0? – No -> Return returnCode -> End
Đường đi số 6: Begin -> Set returnCode -> Check target address is valid
range?-Yes -> Check target address is aligned? -Yes -> Check size is aligned?- Yes -> Check end address is valid range? - Yes -> Check size > 0? - Yes -> Prepare passing parameters -> Call FlashCommandSequence -> Check returnCode is OK? - Yes -> Updated size and target address -> Check size>0? - Repeat or Not Repeat -> Return returnCode -> End
Đường đi số 7: Begin -> Set returnCode -> Check target address is valid
range?-Yes -> Check target address is aligned? -Yes -> Check size is aligned?- Yes -> Check end address is valid range? - Yes -> Check size>0?- Yes ->Prepare passing parameters -> Call FlashCommandSequence -> Check returnCode is OK? - No -> Return returnCode -> End
Sau khi đã xác định được các đường đi kiểm thử tôi tiến hành định rõ các tình huống kiểm thử từ các đường đi này. Ứng với 7 đường đi ở trên tôi có 7 trường hợp kiểm thử. Hàm FlashEraseSector() có prototype như sau:
UINT32 FlashEraseSector(PFLASH_SSD_CONFIG PSSDConfig, \ UINT32 destination, \
UINT32 size, \
pFLASHCOMMANDSEQUENCE FlashCommandSequence)
Để xác định các tham số đầu vào ta cần xác định các thông số: 1. Struct FLASH_SSD_CONFIG flashSSDConfig =
{
FTFx_REG_BASE, /* Địa chỉ bắt đầu của vùng thanh ghi : 0x40020000*/ PFLASH_BLOCK_BASE,/* Địa chỉ bắt đầu của PFlash: 0x00000000*/ PBLOCK_SIZE, /* Kích thước của PFlash: 0x00040000 – 256KB*/
NULL_CALLBACK /* Con trỏ null_callback */ };
2. „destination‟: /* Địa chỉ vùng PFlash mà người dùng muốn xóa */ 3. „size‟: /*kích thước vùng PFlash mà người dùng muốn xóa */
4. Call FlashCommandSequence: Thực hiện ghi chuỗi lệnh xuống Flash
Các thông số về flashSSDConfig, FlashCommandSequence là cố định nên tôi chỉ cần xác định đầu vào cho các tham số „destination‟ and „size‟ tương ứng với từng trường hợp kiểm thử.
Trƣờng hợp 1: FlashEraseSector() sẽ trả về lỗi nếu địa chỉ vùng PFlash được
truyền vào không hợp lệ tức là không nằm trong vùng PFlash.
Dữ liệu đầu vào:
'destination' = PFLASH_OUT_OF_RANGE (ví dụ: 0x00040004)
'size' =FTFx_PSECTOR_SIZE (0x400)
Dữ liệu đầu ra mong đợi: Trả về lỗi FTFx_ERR_RANGE
Trƣờng hợp 2: FlashEraseSector() sẽ trả về lỗi nếu địa chỉ vùng PFlash được
truyền vào không align theo sector size. Tức là địa chỉ đó không chia hết cho 0x400– kích thước của 1 sector.
Dữ liệu đầu vào:
'destination' = PFLASH_START_ADDR + 2*FTFx_PSECTOR_SIZE + 1 (ví dụ: 0x00000801 hoặc 0x00000802,…)
'size' = FTFx_PSECTOR_SIZE (0x400)
Dữ liệu đầu ra mong đợi: Trả về lỗi FTFx_ERR_ADDR
Trƣờng hợp 3: FlashEraseSector() sẽ trả về lỗi nếu kích thước vùng PFlash mà
người dùng muốn xóa được truyền vào không align theo sector size. Tức là kích thước (size) đó không chia hết cho 0x400 – kích thước của 1 sector.
Dữ liệu đầu vào:
'destination' = PFLASH_START_ADDR + 2*FTFx_PSECTOR_SIZE
'size' = FTFx_PSECTOR_SIZE + 1 (0x401) Dữ liệu đầu ra mong đợi: Trả về lỗi FTFx_ERR_SIZE
Trƣờng hợp 4: FlashEraseSector() sẽ trả về lỗi nếu kích thước vùng PFlash mà
người dùng muốn xóa được truyền vào là không hợp lệ. Tức là kích thước đó cộng với „destination‟ vượt quá giới hạn của vùng PFlash (kích thước vùng PFlash là 256KB – 0x40000)
Dữ liệu đầu vào:
'destination' = PFLASH_START_ADDR
'size' = PBLOCK_SIZE + FTFx_PSECTOR_SIZE (0x40400) Dữ liệu đầu ra mong đợi: Trả về lỗi FTFx_ERR_ RANGE
Trƣờng hợp 5: FlashEraseSector() sẽ trả về FTFx_OK nếu người dùng muốn
xóa vùng PFlash với kích thước = 0. Tức là truyền vào size = 0
'destination' = PFLASH_START_ADDR
'size' = 0x0
Dữ liệu đầu ra mong đợi: Trả về FTFx_OK
Trƣờng hợp 6: FlashEraseSector() sẽ trả về FTFx_OK nếu tất cả các tham số
truyền vào là hợp lệ và vùng PFlash mà người dùng muốn xóa ở trạng thái không bị bảo vệ - unprotected.
Dữ liệu đầu vào:
'destination' = PFLASH_START_ADDR + 2*FTFx_PSECTOR_SIZE
'size' = FTFx_PSECTOR_SIZE Dữ liệu đầu ra mong đợi: Trả về FTFx_OK
Trƣờng hợp 7: FlashEraseSector() sẽ trả về lỗi nếu vùng PFlash mà người
dùng muốn xóa ở trạng thái bảo vệ - protected.
Dữ liệu đầu vào:
'destination' = PFLASH_START_ADDR + 2*FTFx_PSECTOR_SIZE
'size' = 3*FTFx_PSECTOR_SIZE
Thiết lập trạng thái bảo vệ cho 1 số sector có thể dùng hàm PflashSetProtection()
Dữ liệu đầu ra mong đợi: Trả về lỗi FTFx_ERR_PVIOL
Vậy là tôi đã thiết kế được 7 trường hợp kiểm thử cho hàm FlashEraseSector, xác định được thông số đầu vào và dữ liệu đầu ra mong đợi cho từng trường hợp của hàm FlashEraseSector. Áp dụng như trên tôi tiến hành thiết kế các trường hợp kiểm thử cho các hàm khác của phần mềm SSD chi tiết các trường hợp kiểm thử của từng hàm xin xem phần phụ lục B. Trong đó hàm FlashCommandSequence() được các hàm khác gọi tới nên không cần xây dựng tập test case riêng cho hàm này nữa. Hai hàm PFlashGetProtection(), PflashSetProtection() được gộp chung trong một bộ test case.
3.3 Thiết lập môi trƣờng kiểm thử
Môi trường: Chương trình được chạy trên Windows XP hoặc Windows 7, dùng trình biên dịch CodeWarrior để debug.
Về phần cứng: Bảng mạch MKL46Z256VLL4, máy tính,cáp nối mạch với cổng USB (dây mini). Mạch này sẽ được cung cấp bởi nguồn 3.3V qua cổng USB.
Về phần mềm: Phần mềm SSD cho mô-đun flash được mô tả ở trên .
Về giao tiếp: Giao tiếp này được thực hiện thông qua cáp nối giữa cổng debug trên Mạch MKL46Z256VLL4 với cổng usb trên máy tính.
Hình 3. 7: Thiết lập môi trường kiểm thử
3.4 Demo chƣơng trình
Tôi sử dụng trình biên dịch CodeWarrior để biên dịch, nạp và debug chương trình kiểm thửcó tên là Test_MKL46Z256 trong thư mục Test/Test_MKL46Z256 lên mạch MKL46Z256. Việc thực hiện kiểm thử được tiến hành từng bước một theo danh sách các Test Case của từng hàm.
Dưới đây là hình ảnh chạy demo chương trình kiểm thử cho các hàm của phần mềm SSD. Hình 3.7 là giao diện chứa chương trình kiểm thử của phần mềm SSD
Hình 3. 8: Giao diện chứa chương trình kiểm thử của phần mềm SSD Evaluation
Board
PC
Cable connection
Giả sử để kiểm thử hàm FlashProgramLongWord() trong phần mềm SSD, tôi tiến hành biên dịch chương trình kiểm thử của hàm này(build project). Sau đó tiến hành nạp chương trình lên mạch để debug chương trình. Vào phần “Debug
Configurations”của trình biên dịch CodeWarrior để chọn kết nối (connection) thích hợp. Trong ứng dụng này tôi chọn loại kết nối sử dụng J-link dành cho ARM với cổng debug dùng chuẩn SWD như hình 3.9.
Hình 3. 9: Thiết lập kết nối để debug chương trình
Sau khi đã chọn được kết nối thích hợp tôi tiến hành chạy chương trình kiểm thử, debug chương trình.