PCI (1)

基本介紹

PCI(Peripheral Component Interconnect) 分為 2 種

1
2
1. 線路直接裝在主機板上
2. 插槽式介面

PCI 下一代為 PCIE,皆由 PCI-SIG 組織所制定

PCI/PCIE 主要的作用就是傳輸更大的資料量

以及對傳輸資料的品質有所提升,所以才會一直改進

歷史

1
2
3
4
5
6
7
Intel   1992 : PCI  1.0
PCI-SIG 1993 : PCI 2.0
PCI-SIG 1995 : PCI 2.1
PCI-SIG 1998 : PCI 2.2
PCI-SIG 2002 : PCI 2.3
PCI-SIG 2004 : PCI 3.0
PCI-SIG 2004 : PCIE 1.0 = 3GIO

中斷

PCI 4 種中斷

1
2
3
4
INTA#
INTB#
INTC#
INTD#

規格

1
2
3
4
5
6
7
33.33 MHz clock 的同步傳輸
133 megabytes per second for 32-bit bus width(33.33 MHz×32 bits ÷ 8 bits/byte = 133 MB/s)
32-bit 匯流排寬度
32/64位元的記憶體位址(4 gigabytes or 16 exabytes)
32 位元I/O port空間
256-byte(每個裝置)配置空間
5-volt 訊息

Spec

PCI 3.0

專有名詞

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
asserted / deasserted : 使(信號)生效/使(信號)失效
edge, clock edge : rising edge of the clock
# :
signal name 後面有 # = low voltage
signal name 後面沒有 # = high voltage
reserved : 此區塊只能由 PCI-SIG 修改

AD[31::00] :
AD = signal names
[31::00] = signal range/ 31 = msb, 00 = lsb


Interrupt有分好幾種 mode,可以分成
Edge Trigger / Level Trigger

Edge Trigger 分成 Rising and Falling Edge Trigger
Level Trigger 分成 High and Low Level

3
+-------+
| |
1 | | 2
| |
------+ +------
4

1: Rising
2: Falling
3: High
4: Low

Edge Trigger 主要是靠波形的變化
正緣觸發 (pos-edge) low -> high(Rising)
負緣觸發 (neg-edge) high -> low (Falling)
Level Trigger 則是看絕對的準位 -> 一定要到達某個值


主橋 (Host Bridge) = 北橋(North Bridge)
北橋晶片就是主板上離CPU最近的晶片

CPU --------- North Bridge ------- South Bridge
| | |
| AGP/PCIE/Memory PCI/Flash/IO
Clock


RC (Root Complex) : 其實就類似主橋的功能,只有在 X86 上面
局部總線(Local Bus) : 用來連接外部設備
IA (Intel Architecture) : 架構
帶寬(Bandwidth) : 在此指頻率,最高容忍度,數據傳輸率 (byte/s)

其他

PCI 其他分支

1
2
PCMCIA ->  PCI Card -> ExpressCard : 用在筆電上
Mini PCI : 插槽占用面積較小

PCISGI 還有規範

1
2
3
4
PCI-to-PCI 規範 : 體系基礎
電源管理
熱拔差
CompactPCI : 有背板的大型系統

PCI-X 也是 PCI 的分支

最後還是被 PCI-E 取代

1
支援 133/266/533 MHz

缺點

PCI/PCI-X 使用單端並行信號通訊 = 容易被干擾 = 頻率難提升

並行為同一時刻可傳輸多位數據

總線寬度 = 同時傳輸的位數,16 位總線 = 同時傳數據為 16 位

等於 2 byte ( 16 bit)

32 位總線 = 4 byte

64 位總線 = 8 byte

總線寬度越大資料可傳越多

總線帶寬表示在一定時間內傳輸的數據總量

64 位總線 800 MHz = (64bit*800MHz) / 8(byte) = 6.4 GB/s

Extensions C

參考

參考-1

基本介紹

在 C 語言中有一些 Extensions 的方法

要使用這些 Extensions 功能你可以先檢查 GNUC 是否有開

這些 Extensions 不是標準 C 的功能,是需要編譯器支援的

1
2
3
4
5
#if __GNUC__
/*
有定義 __GNUC__ 才可以使用 Extensions
*/
#endif

如果在 GCC 編譯時加上 -pedantic

而且你有用 Extensions 的話會發出警告

也就是說當你加上 -pedantic 代表你不想要用 Extensions

Linux Kernel Marco(container_of)

介紹

此 marco 最主要的作用是

你只要傳給他某個 struct 中的 member 的 pointer

他會幫你算出此 struct 的起始位置

也就是說你就可以知道其他 member 的位置並存取

舉例

以下是還沒使用 container_of

先在 main 建立 person 變數,之後使用 print_info 印出 phone_num

如果你目前只能修改 print_info,要如何印出其他 member 的值?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
typedef struct person {
char name[16];
char phone_num[16];
int id;
int year;
}stPerson, *stPerson_ptr;

void print_info(char * phone)
{
printf("Phone num: %s \n",phone);
return;
}

int main(void)
{
stPerson_ptr pJason = malloc( sizeof(stPerson));
strncpy( pJason->name, "Jason", sizeof(pJason->name));
strncpy( pJason->phone_num, "0955123456", sizeof(pJason->phone_num));
pJason->id = 112233;
pJason->year = 28;

print_info(pJason->phone_num);
return 0;
}

使用 container_of

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})


typedef struct person {
char name[16];
char phone_num[16];
int id;
int year;
}stPerson, *stPerson_ptr;

void print_info(char * phone)
{
/* 關鍵是使用 container_of,需要傳入 3 個參數
1. struct 中的某個 member pointer
2. struct 名稱
3. 此 member 在 struct 中的名稱 */
stPerson_ptr pPerson = container_of( phone, stPerson, phone_num );

printf("Name : %s \n",pPerson->name);
printf("Phone num : %s \n",pPerson->phone_num);
printf("ID : %d \n",pPerson->id);
printf("Year : %d \n",pPerson->year);
return;
}

int main(void)
{
stPerson_ptr pJason = malloc( sizeof(stPerson));
strncpy( pJason->name, "Jason", sizeof(pJason->name));
strncpy( pJason->phone_num, "0955123456", sizeof(pJason->phone_num));
pJason->id = 112233;
pJason->year = 28;

print_info(pJason->phone_num);
return 0;
}

參考

非常詳細的介紹

GNU GCC __attribute__ 用法

參考

Function attributes

Variable attributes

Type attributes

簡介

attribute 其實就是一種檢查工具,他主要是讓 GCC 在編譯時看的

也就是說就算你把 attribute 拿掉也不會影響程式邏輯(目前所看到的)

attribute 可以檢查的有以下 3 種

1
2
3
Function   // 檢查函式
Variable // 檢查變數
Type // 檢查類型

要注意的是,有些檢查功能只能在特定平台( ARM / X86 / PowerPC etc)上才有作用

dlopen 和 dlsym 用法

參考

參考-1

參考-2

基本介紹

使用 dlopen 和 dlsym 可以使用 so 檔

將 so 檔裡面的函式取出來使用,達到動態替換函式的方法

1
2
3
4
#include <dlfcn.h>
void *dlopen (const char *filename, int flag);
void *dlsym (void *handle, const char *symbol);
int dlclose (void *handle);

首先 man dlopen 可以看到範例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
//Load the math library, and print the cosine of 2.0:

#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>

int
main(int argc, char **argv)
{
void *handle;

// 建立一個 function 名叫 cosine
// 需要傳入 double 類型參數並會回傳 double
double (*cosine)(double);
char *error;

// 打開 libm.so 動態函示庫
handle = dlopen("libm.so", RTLD_LAZY);
if (!handle) {
fprintf(stderr, "%s\n", dlerror());
exit(EXIT_FAILURE);
}

dlerror(); /* Clear any existing error */


/* Writing: cosine = (double (*)(double)) dlsym(handle, "cos");
would seem more natural, but the C99 standard leaves
casting from "void *" to a function pointer undefined.
The assignment used below is the POSIX.1-2003 (Technical
Corrigendum 1) workaround; see the Rationale for the
POSIX specification of dlsym(). */

// 將 libm.so 裡面的 cos 函式取出並指派給 cosine
//*(void **) (&cosine) = dlsym(handle, "cos");
// 可以改寫成以下這種寫法比較容易閱讀
cosine = dlsym(handle, "cos");

if ((error = dlerror()) != NULL) {
fprintf(stderr, "%s\n", error);
exit(EXIT_FAILURE);
}

printf("%f\n", (*cosine)(2.0));
dlclose(handle);
exit(EXIT_SUCCESS);
}