Khi kiểm tra một hệ thống phần cứng/phần mềm, cần phải phân tách toàn bộ hệ thống. Một hướng dẫn thực tế là yêu cầu lập trình viên xem xét hệ thống. Thông thường thì thông số kỹ thuật phần mềm là tài liệu mô tả kỹ nhất về tổng thể hệ thống phần mềm/phần cứng. Trong luận văn này, phần mềm trong node chủ LIN là trình điều khiển thiết bị LIN.
Trong phần 1.2.4, chúng tôi đã thảo luận rằng các hành vi của node chủ LIN sẽ theo sau hoạt động của trình điều khiển thiết bị LIN. Do vậy, để kiểm tra node chủ LIN thì cần phải kiểm tra xem liệu trình điều khiển LIN có được thực hiện một cách chính xác bởi Aquarius như đã mô tả trong trình điều khiển LIN hay không.
Trong một hệ thống phần cứng/phần mềm, việc thực hiện đúng một đoạn phần mềm bao gồm việc kiểm tra các tình huống sau: các hàm trong chương trình có được gọi một cách chính xác không, liệu PC có nhảy đúng vị trí khi thủ tục quay lại hay không, liệu stack có được xửlý đúng hay không hay các biến có được gán đúng với các giá trị hay không,...
Nếu chúng ta muốn kiểm tra các giá trị của PC hoặc các biến, các địa chỉ bộ nhớ của chương trình và dữ liệu được phân bổ phải được lấy ra trước. Phân bổ bộ nhớ cho trình điều khiển thiết bị LIN được thực hiện trong giai đoạn liên kết của quá trình phiên dịch. Nói cách khác, các file đối tượng được sinh ra sau khi liên kết thông tin như các địa chỉ bộ nhớ dành cho chương trình hoặc dữ liệu. Do vậy trình điều khiển LIN phải được kiểm tra ở mức độ trừu tượng hóa thấp nơi hiển thị các địa chỉ bộ nhớ.
Bằng cách kiểm tra các file đối tượng thực hiện nhị phân lấy được trước đó (file .elf) của trình điều khiển thiết bị LIN đã được chuyển vào bộ nhớ, chúng ta có thể tìm thấy các thông tin cần thiết để kiểm tra việc thực hiện trình điều khiển LIN bởi Aquarius. Tuy nhiên, việc đọc code đối tượng nhị phân không hữu ích đối với các kỹ sư và đơn giản là quá tốn thời gian. Vì vậy, thay vì xem code các đối tượng nhị phân, chúng tôi đã kiểm tra chương trình tách rời có được do disassemble các code đối tượng của trình điều khiển thiết bị LIN. Disassembly là quá trình biên dịch một executable program thành các biểu diễn tương ứng. Một chương trình assembly có thể được phiên dịch để dễ đọc các code nhị phân, các code này sử dụng các mã bộ nhớ để tham chiếu đến lệnh mã máy, thay vì chỉ sử dụng các giá trị số của các lệnh. Thêm vào đó, chương trình disassembly có thể bao gồm các thông tin như địa chỉ bộ nhớ vì nó được lấy ra từ mức trừu tượng thấp nhất của quá trình biên dịch.
Hình 2.8 thể hiện quá trình từchương trình C đến chương trình disassembly.
Source C Program
Assembly Program
compiler
Binary Object files
assembler
Executable file
linker
Disassembled Program
disassembler (sh-elf-objdump)
Hình 2.8: Quá trình từchương trình C đến chương trình disassembly
Quy trình disassembly được thực hiện bằng một công cụ tên là “sh-elf- objdump”. Đây là một trong những tiện ích nhị phân (Binutil) của GCC cho họ SuperH. Công cụ có thể hiển thị thông tin từfile .elf và sinh ra chương trình tách rời (file .asm) của trình điều khiển thiết bị LIN.
Với các tùy chọn phù hợp cho sh-elf-objdump, công cụ có thể đưa ra các thông tin cần thiết để kiểm tra chính xác node chủLIN. File đầu ra có thể hiển thị bộ nhớ cho các lệnh máy từcác file đối tượng, các lệnh SuperH-2 trong hệhex cũng giống như trong các mẫu ký hiệu và các mục nhập bảng ký hiệu.
Mỗi file đối tượng nhị phân có một bảng ký hiệu sẽđược dùng bởi linker để giải quyết các tham chiếu không giải quyết được. Một bảng ký hiệu là một cấu trúc dữ liệu nơi mỗi định danh trong mã nguồn của một chương trình được liên kết thông tin tới khai báo hoặc sự xuất hiện của nó trong mã nguồn, chẳng hạn như loại, mức độ phạm vi và vị trí trong bộ nhớ. Một định danh trong chương trình C có thể là bất cứ biến toàn cục hoặc một hàm đã biết nào đó. Do vậy, như một quá trình của kỹ thuật nghịch đảo, việc tách rời các file thực hiện (file .elf) bằng sh-elf- ojbdump
có thể tham chiếu đến các bảng ký hiệu để có thể lấy được các địa chỉ đã được gán cho các biến toàn cục và các hàm đã biết.
Công cụ sh-elf-objdump trong một dự án được phát triển bởi nhóm của chúng tôi để cho ra các thông tin sau:
1. Chương trình C, 2. Các lệnh SuperH-2,
3. Các địa chỉ bộ nhớ của các lệnh SuperH-2, 4. Các mã máy nhị phân của các lệnh SuperH-2, 5. Các nhãn bổ sung.
Các nhãn bổ sung chỉ ra các thông tin khác nhau của chương trình tách rời:
1. Các hằng số như địa chỉ bộ nhớ của các hàm và các các biến toàn cục được phân bổ.
2. Nhảy lệnh và đích đến của các lệnh.
3. Các lệnh nhánh đã tạm dừng.
4. Gọi lệnh và các hàm được gọi.
Hình 2.9 thể hiện một chương trình C đơn giản. Hàm chính của chương trình C bao gồm một một vòng lặp vô hạn, ở đó chương trình gọi một hàm add ( ). Trong add ( ), giá trị của biến sum được tính. File thực hiện (file .efl) của chương trình C này được lấy ra bởi GCC dành cho SuperH-2, giống như quy trình chúng ta đã làm với trình điều khiển thiết bị LIN. Sau đó, chương trình tách rời tương ứng được lấy ra bằng cách tách rời file thực thi nhị phân với sh-elf-objdump. Một phần của file kết quảđược thể hiện trong hình 2.10.
Như đã mô tả trong hình 2,10, cột đầu tiên là địa chỉ bộ nhớ. Cột thứ hai tương ứng với mã máy của các lệnh SuperH-2. Cột thứ ba hiển thị các lệnh SuperH- 2 tương ứng. Các nhãn bổ sung được chèn vào bên trong chương trình tách rời. Ví dụ, label _31_label21_In_main_sh chỉ ra rằng vòng lặp vô hạn gọi hàm add ( ) và label _add tương ứng với địa điểm của hàm được gọi.
Ở phần đầu của hàm add ( ) được gọi trong chương trình tách rời, một số thanh ghi đa dụng, giá trị của thanh ghi SP (r14 có cùng nội dung với r15) và thanh
ghi thủ tục (PR) của bộ xử lý SuperH được đẩy vào stack (r15 là thanh ghi con trỏ stack của bộ xử lý SuperH). Việc vận hành stack này giúp các thanh ghi đa dụng được đẩy khỏi bị ghi đè trong hàm add ( ) được gọi. Bên cạnh đó, đẩy PR vào stack sẽ giúp PR không bịghi đè nếu hàm add ( ) gọi một hàm nhỏ và do đó, nó bảo đảm hàm add ( ) có thể trở lại đúng vị trí trong hàm được gọi. Nhìn chung, khi một hàm được gọi trong một hệ thống phần cứng/phần mềm dựa trên bộ xử lý SuperH, SP và PR phải được đẩy vào stack và một sốthanh ghi đa dụng của bộ xửlý được đẩy vào stack nếu cần. Mặt khác, khi đã gọi hàm xong, stack phải đẩy ra các thanh ghi theo thứ tựngược lại với thứ tựmà nó đã được đẩy vào.
void main_sh(void) {
000000b2 <_31_Label23_In_main_sh>:
while(1){
add();
b2: d0 07 mov.l d0, r0 ! 0x40 <_add>
000000b4 <_32_DelaySlot1_In_main_sh>:
b4: 40 0b jsr @r0
b6: 00 09 nop
00000040 <_add>:
unsigned long add(void){
40: 2f 86 mov.l r8,@-r15 42: 2f 96 mov.l r9,@-r15 44: 2f a6 mov.l r10,@-r15 46: 2f e6 mov.l r14,@-r15 48: 4f 22 sts.l pr,@-r15
4a: 6e f3 mov r15,r14
4c: d1 0a mov.l 78,r1 ! 0x2000 <__data_end>
4e: 69 12 mov.l @r1,r9
50: e8 01 mov #1,r8
52: da 0a mov.l 7c,r10 ! 0x34 <_addium>
00000054 <_5_Label4_In_add>:
static int sum=0;
int i;
for( i=1;i<=100;i++)
sum=sum+addium(i);
54: 4a 0b jsr @r10
...
00000034 <_addium>:
unsigned addium(unsigned i) {
34: 2f e6 mov.l r14,@-r15 ...
}
1 main { 2 while (1) {
3 add ( );
4 }
5 }
6 unsigned long add ( ) { 7 static int sum = 0;
8 int i;
9 for (i = 1; i < 100; i ++) 10 sum = sum + addium(i);
11 return (sum);
12 }
13 unsigned addium(unsigned i) 14 {
15 return i;
16 }
Fig. 2.9: A simple C program
Fig. 2.10: The assembly program of the C program
Hình 2.10: Chương trình assembly của chương trình C
Duy trì stack là cần thiết để đồng kiểm tra hình thức hệ thống phần cứng/phần mềm. Để kiểm tra việc thực hiện chương trình của bộ xử lý, cần phải chứng minh việc vận hành stack chính xác của từng lần gọi thủ tục trong chương trình nếu cần. Vận hành stack có mặt trong từng thủ tục được hiển thị trong chương trình tách rời của chương trình có mức trừu tượng hóa cao ban đầu.
Tóm lại, chương trình tách rời của trình điều khiển thiết bị LIN là một lựa chọn tốt để tìm thông tin như địa chỉ của các hàm hoặc các biến, duy trì stack để
Hình 2.9: Chương trình C
thểđược lấy ra bằng cách tách rời các file thực hiện (file .efl) mà chúng ta sử dụng để tạo ra bộ nhớ của mô hình cụ thể của node chủ LIN.
2.2.2 Biểu đồ luồng điều khiển của chương trình tách rời
Vì chúng ta đã lấy được chương trình tách rời của trình điều khiển thiết bị LIN bao gồm tất cảcác thông tin để kiểm tra, bước tiếp theo là tìm cách suy ra các tính chất cho IPC từchương trình tách rời.
Trong việc kiểm tra thực tế các logic tuần tự, chúng tôi thường kiểm tra một hành vi của thiết kếnơi các điều kiện nhất định của môi trường xuất hiện và thiết kế ở một số trạng thái nội nhất định. Các tính chất được định hình dựa trên FSM của thiết kế. Trong trường hợp node chủ LIN đề cập, chúng tôi cần kiểm tra một hành vi của trình điều khiển thiết bị LIN với các điều kiện nhánh nhất định xuất hiện và chương trình ở một vị trí nhất định trong bộ nhớ. Do đó, chúng tôi chọn Biểu đồ luồng điều khiển (CFG) của trình điều khiển thiết bị LIN để xuất ra các tính chất của hệ thống phần cứng/phần mềm. CFG là một đại diện, sử dụng các ký hiệu đồ họa của tất cảcác đường dẫn mà có thể đi qua một chương trình khi nó đang thực hiện.
Trong luận văn này, CFG của trình điều khiển thiết bị LIN được sử dụng để phân tích việc thực hiện trình điều khiển LIN và các tính chất của node chủ LIN được hình thành dựa trên CFG. Đối với những lý do mà chúng tôi nhắc đến ở trên, trình điều khiển thiết bị LIN phải được quan sát ở cấp độ trừu tượng thấp bao gồm tất cả các thông tin cho việc đồng kiểm tra hình thức. Vì vậy, chúng tôi chọn CFG của chương trình tách rời của trình điều khiển thiết bị LIN để xây dựng các tính chất của node chủ LIN. Biểu đồ luồng điều khiển của chương trình tách rời của trình điều khiển LIN có thểđược xây dựng theo định nghĩa sau.
Định nghĩa 2.1. (Biểu đồ luồng điều khiển của chương trình tách rời) Một biểu đồ luồng điều khiển của chương trình tách rời có 5 bộ (V, E, C, B, M), trong đó:
V là đỉnh. Các đỉnh tương ứng với các nhãn quan trọng trong chương trình tách rời mà chỉra bước chuyển hoặc đích chuyển tới,
E là cạnh giữa các đỉnh chỉ ra một chuỗi các lệnh liên tục giữa các đỉnh.
C là hàm dán nhãn điều kiện, map E vào với các điều kiện nhánh hoặc gián đoạn bên ngoài.
B là hàm nhãn gán khối, map E vào các biểu thức gán của các biến
M là hàm nhãn duy trì stack, map E với các hoạt động của stack như đẩy vào/đẩy ra các thanh ghi đa dụng, SP, PR.
Các đỉnh trong CFG tương ứng với các nhãn quan trọng trong chương trình tách rời. Như đã đánh dấu trong hình 2.10, các nhãn in đậm màu đỏ là các nhãn quan trọng trong chương trình tách rời chỉ ra các bước chuyển và các đích chuyển.
Từng cạnh giữa hai đỉnh chỉ ra đường thẳng của code trong chương trình tách rời.
Nó có thể bao gồm việc kiểm tra điều kiện đối với các điều kiện liên quan, các khối nhiệm vụvà duy trì stack. Đỉnh bắt đầu bằng một đường thẳng của code được gọi là đỉnh trước của cạnh và đỉnh kết thúc đường thẳng code được gọi là đỉnh đi sau của cạnh .
CFG của chương trình tách rời được thể hiện trong hình 2.10 được tạo nên theo định nghĩa 2.1. Nó được thể hiện trong hình 2.11.
_31_Label23_In_main_sh
_add
_5_Label4_In_add
i <= 100
sum = 0 i = 0 push stack(general
registers, SP, PR)
stack labeling function basic block assignment
labeling function condition labeling
function
_addium
push stack(general registers, SP, PR)
_2_Return_In_addium
pop stack(general registers, SP, PR)
sum = sum + addium(i) i ++
pop stack(general registers, SP, PR)
i > 100 sum = sum + addium(i)
i ++
_11_Return_In_add
pop stack(general registers, SP, PR)
Hình 2.11: CFG của chương trình tách rời của chương trình C đơn giản
2.2.3 Mẫu tính chất dựa trên CFG
Tương tựnhư quá trình hình thành lên các tính chất của các thiết kế theo thứ
tự dựa trên FSM, chúng ta xây dựng các tính chất của node chủ LIN dựa trên CFG của chương trình tách rời của trình điều khiển thiết bị LIN.
Mọi chuyển giao trên CFG tương ứng với một tính chất. Một tính chất dựa trên CFG mô tả việc truyền từ một đỉnh này sang đỉnh khác đã được hoàn tất sau một số khung thời gian nhất định, chỉ khi có các điều kiện được đáp ứng và trong khi các khối nhiệm vụ và các hoạt động stack được thực hiện. Tương tự như các biến trạng thái được dùng để thể hiện trạng thái của FSM, PC (thanh ghi bộ đếm chương trình của Aquarius) có thểđược sử dụng để thể hiện các đỉnh của CFG trong các tính chất của IPC, do các đỉnh tương ứng với các nhãn trong chương trình tách rời đã chỉ ra các vị trí quan trọng trong chương trình tách rời. Trong ví dụ trong hình 2.10, đỉnh tương ứng với label _31_Label_23_In_main_sh được thể hiện bằng PC = 'hb2 trong các tính chất của IPC.
Trong chương trình tách rời của chương trình C đơn giản thể hiện trong hình 2.10, có 7 bước chuyển tương ứng với 7 tính chất. Nhưng nếu chúng tôi coi kích thước của hệ thống phần cứng/phần mềm như trình điều khiển thiết bị LIN, các CFG có thể giới thiệu một sốlượng lớn các tính chất, có thể lên tới hàng trăm hoặc hàng nghìn. Rất may là, không cần thiết phải chứng minh từng chuyển đổi đơn lẻ trong CFG. Chúng tôi có thể cố gắng để kết hợp một số chuyển đổi đường thẳng thành một chuyển đổi, phục vụ cho mục đích giảm số tính chất. Trong ví dụ đơn giản trong hình 2.11, thay vì chứng minh riêng biệt chuyển đổi từ _31_Label_23_In_main_sh sang _add và chuyển đổi từ _add sang _5_Label4_In_add, chúng ta có thể ghép hai tính chất này vào thành một tính chất mô tả chuyển đổi bắt đầu từ _31_Label_23_In_main_sh và kết thúc bằng _5_Label4_In_add.
Giờ chúng ta xem xét một tính chất điển hình trong lĩnh vực kiểm tra phần cứng bằng IPC. Tính chất này được viết bằng Ngôn ngữ khoảng (ITL) và được chia thành 2 phần: giảđịnh và cam kết. Giảđịnh mô tả rằng thiết kếở một trạng thái bắt đầu nhất định dưới các điều kiện nhất định. Các điều kiện có thể bao gồm các thông tin như biểu thức đầu vào theo thời gian, biểu thức này sẽ bắt đầu chuyển đổi trạng
thái. Cam kết mô tả rằng thiết kế đặt đến trạng thái kết thúc nhất định sau một số khung thời gian nhất định và trạng thái của đầu ra theo thời gian.
Việc hình thành các tính chất đối với hệ thống phần cứng/phần mềm dựa trên CFG của chương trình tách rời của phần mềm tương tự như quy trình hình thành tính chất khi kiểm tra phần cứng. Đỉnh trước và các điều kiện của một chuyển đổi trong CFG được đặt trong phần giảđịnh của tính chất ITL, trong khi đỉnh sau, khối gán và vận hành stack thuộc về chuyển đổi được đặt trong phần cam kết của tính chất này.
Nếu chúng ta xem xét hệ thống phần cứng/phần mềm chạy chương trình C đơn giản đã thảo luận trước đó, dựa trên CFG được mô tả trong hình 2.11, một tính chất giả định mô tả chuyển đổi trong CFG từ _31_Label_23_In_main_sh sang _5_Label4_In_add được thể hiện trong hình 2.12.
property Predecessor_to_Successor;
assume:
//predecessor vertex at t: PC = 'hb2;
prove:
//successor vertex at t + k: PC = 'h54;
//block assignments at t + k: sum = = 0;
at t + k: i = = 0;
//stack maintenance at t + k: stack_operations;
end property;
eliminate gap between
software variables
and hardware
signals
macros
bit[] Label23_In_main_sh := 'hb2; end Label23_In_main_sh;
bit[] Label4_In_add := 'h54; end Label4_In_add;
unsigned PC:=CPU/DATAPATH/PC; end PC;
bit[] sum_1199 := meml('h2000); end sum_1199;
unsigned R8:=CPU/DATAPATH/REGISTER/REG[8]; end R8;
unsigned R9:=CPU/DATAPATH/REGISTER/REG[8]; end R9;
unsigned R15:=CPU/DATAPATH/REGISTER/REG[8]; end R15;
end macros;
macro bit cpu_at_Label26_In_main_sh :=
(
PC == Label23_In_main_sh &&
INSTRUCTION == 'h8bfb &&
CPU/DECODE/ID_STALL == 'h0 &&
CPU/DECODE/IR == 'h3122 &&
...
);
end macro;
macro bit cpu_at_Label4_In_add :=
(
PC == Label4_In_add &&
...
);
end macro;
property Label23_In_main_sh_to_Label4_In_add;
freeze:
R15_t = R15@t, R8_t = R8@t, R9_t = R9@t;
...
assume:
//predecessor vertex
at t: cpu_at_Label23_In_main_sh;
prove:
//successor vertex
at t + k: cpu_at_Label4_In_add;
//block assignments at t + k: sum_1199 = = 0;
at t + k: R8 = = 1;
//stack maintenance
at t + k: meml(unsigned(R15_t-4)) == R8_t;
at t + k: meml(unsigned(R15_t-8)) == R9_t;
...
end property;
Fig. 2.12: Peseudo property Fig. 2.13: The actual property
_31_Label23_In_main_sh (predecessor)
_add
_5_Label4_In_add (successor)
sum = 0 i = 0 push stack(general
registers, SP, PR)
Hình 2.12: Tính chất giả Hình 2.13: Tính chất thật
Trong tính chất giảđịnh, đỉnh trước và đỉnh sau được thay thế bởi các giá trị PC tương ứng (xem hình 2.10). PC cho đỉnh đi trước được đặt trong giả thiết khi PC cho đỉnh đi sau được đặt trong phần cam kết. Gán khối bao gồm phép gán của biến sum và i được thay thế trong cam kết của tính chất giảđịnh. Các hoạt động stack có trong chuyển đổi này không được xác định.