2009年6月24日 星期三

IEEE 802.11p日形完備 車載資通訊網路環環相扣

新通訊元件雜誌
▋技術前瞻
IEEE 802.11p日形完備 車載資通訊網路環環相扣
新通訊 2009 年 7 月號 101 期
文.鄭彥杰
由於資通訊技術的成熟與普及,讓原本處於閉塞而無法與外界進行單向或雙向資訊傳輸互動的汽車環境,增加了一個與外界溝通的通訊能力,也因此對車載資通訊(Telematics)系統產生了強烈的市場需求。
強化人車安全 DSRC演進踩油門

美國材料試驗協會(ASTM)為了發展專用短距通訊(DSRC)技術,因而針對北美地區不同廠商的電子收費系統(ETC)技術進行評估。電子收費系統特色在於採用915MHz頻段、利用分時多工存取(TDMA),以及配備主動式車載單元(OBU)作為通訊方式。如圖1所示,由於915MHz僅能支援0.5Mbit/s傳輸速率,且傳輸距離僅有30公尺;因此美國聯邦通訊委員會(FCC)決定將5.9GHz這個頻段資源應用在汽車通訊上,使DSRC規範可支援6M~25Mbit/s傳輸速率,傳輸距離更提升為數百公尺。

圖1 車用無線網路頻帶使用示意圖

當ASTM確定DSRC規範E2213-02時,便將5.9GHz採納為規格制定頻段,並決定採用IEEE 802.11a作為傳輸技術。而ASTM將E2213-03標準移往IEEE,也促成IEEE 802.11p的誕生。

IEEE 802.11p又稱為車用環境無線存取(WAVE)技術,目前規格制定進度為6.0版草案。在5.9GHz頻段上使用,此頻帶上有75MHz的頻寬,以10MHz為單位切割,將有七個頻道可供操作。其中一個頻道為控制頻道,其他則為服務頻道。

利用IEEE 802.11a規格作為實體層(Physical Layer)通訊技術,IEEE 802.11p相關應用以DSRC原先所規畫的方向為主,並加強車用安全,包括碰撞警示或道路危險警示等。為了在運輸方面無縫、可和諧操作的服務,WAVE網路服務提供車載裝置、管理服務以及各協定層之間的資料傳遞,其標準包括了IEEE Std 1609.1、IEEE Std 1609.2、IEEE Std 1609.3、IEEE Std 1609.4、及IEEE Std 802.11等。

WAVE系統中的1609.3為網路服務,以開放系統互連(OSI)通訊堆疊來看,是相對應於第三層與第四層架構,目的為提供WAVE系統的位址及路由服務,使得上層的應用服務能與下層通訊協定銜接。1609.4為多頻道操作(Multi-channel Operation),包括控制頻道(CCH)及服務頻道(SCH)的操作、優先權參數存取、頻道交換及路由、管理服務及多頻道選擇。1609.2則提供對應用程式及管理訊息的安全加密功能。

WAVE發展至關重要

IEEE 802.11p的發展具有兩項特性,一是相容於其他規格並與IEEE 1609系列規格相輔相成;二則是對IEEE 802.11a規格作部分的修正,讓設備可應用在高速移動上傳輸資料。

802.11p重視技術相容性

為了避免規格不一,使得市場接受度受到影響,IEEE 802.11p除了與ASTM E2213-03相容之外,也與ISO組織下制定車用規格的TC204(ITS)WG 16建立溝通管道,TC204 WG16也決議將支援最終的IEEE 802.11p版本。TC204 WG16制定的CALM M5,主要規範車輛在快速移動時,車對車通訊、不斷線通訊與多媒體影音下載等應用。

CALM M5採用5GHz頻段,因此也是利用IEEE 802.11a作為傳輸技術。當然,CALM還有其他版本,有利用2.5G與3G作為通訊技術的CALM Cellular等。

由於這些車用規格在制定時講究互通性,使車載設備與道路系統在未來布建與使用上,具有較經濟的效果。若部分區域已經先布建了其他規格的道路系統設備,即便使用者採用的是IEEE 802.11p車載設備也可以利用既有的路側設備(RSU)作DSRC應用。

此外,由於採用的是IEEE 802.11規格,因此也可以利用戶外的熱點(Hotspot)作為上網途徑。頻道178為控制頻道範圍,為任何車載設備皆可存取之頻帶,而其他頻道存取皆由服務提供設備來指派。IEEE 802.11p負責規範MAC/PHY標準制定,並與IEEE 1609系列規範車輛系統資訊的架構達到相輔相成。

802.11p規格與時俱進

接下來分別針對IEEE 802.11p實體層與媒體存取控制(MAC)層所增訂的部分作說明。

‧實體層
在實體層中,由於WAVE車載設備必須耐高溫與抗酷寒,所以在制定標準時,規格明確定義產品能運作的溫度範圍分別為辦公室環境(0~40℃)、工業環境(-20~+50℃、-30~+70℃)以及汽車與戶外環境(-40~+85℃)。使用正交分頻多工(OFDM)系統作為底層通訊媒介,並操作在5.850G~5.925GHz頻帶上,若頻道的頻寬為10MHz,所支援的傳輸速率分別為3、4、5、6、9、12、18、24及27Mbit/s;若頻道的頻寬為20MHz,所支援的傳輸速率分別為6、9、12、18、24、36、48、及54Mbit/s。

另有分別為BPSK、QPSK、16QAM及64QAM等四種調變方法,搭配二分之一及四分之三的編碼率作為底層傳輸的規格設計。如圖2所示,實體層於封包傳送過程中所附載的標頭訊框格式以及幫助系統同步並增加穩定可靠度的前置碼(Preamble)。前置碼包含十短兩長的訓練序列(Training Sequence),並於實體層標頭欄位記載傳送速率與傳送長度等資訊。

圖2 實體層匯聚協定前置碼與訊框格式示意圖
‧媒體存取控制層
圖3表示WAVE系統運作的環境,其中包含OBU以及RSU,而在RSU後端有著骨幹網路(Backbone Network)與後端伺服器連接,由後端伺服器觸發前端RSU發送廣播訊息,例如與交通安全相關訊息、通告RSU連線能力以及如何存取RSU後端伺服器資源。

圖3 WAVE系統運作示意圖

車輛行駛於道路上,可連接至網際網路,如圖4所示,道路設備可透過接口(Portal)連線至網際網路,而車輛形成一基本服務集合(BSS),此服務集合又稱車網服務集合(WAVE BSS),其中車載設備藉由道路設備提供具服務品質的媒體傳輸。設備中所包含的車用電子設備包含計算機(如電腦)、介面設備(如電子收費設備、螢幕輸入輸出設備),具有一無線通訊天線設備做為連接道路設備之用。而道路設備則藉由線路連線至網際網路,將車載設備所傳輸的資料傳至遠端伺服器。

圖4 車輛與網路連結示意圖
WAVE系統通訊管理機制層層把關

WAVE系統之通訊協定堆疊如圖5所示,IEEE 802.11p工作小組會議討論,各別將車用無線網路協定分為WAVE媒體存取層與WAVE實體層。為了解資料傳送的優先等級如何分類與如何進行傳送服務,針對其服務的服務品質(QoS)與WAVE管理實體(WME)的管理訊息資料庫(MIB)設定頻道切換(Channel-Switch),使媒體控制層將欲傳送的資料分配到有優先等級的佇列中並等待傳送,藉由競爭的方式獲得可以傳送的頻寬,再將資料透過實體層傳送出,以完成資料傳送的流程。

圖5 WAVE 通訊協定堆疊示意圖

值得注意的是,IEEE 802.11p新增一個有別於信標訊框(Beacon Frame)的時間廣播訊框(圖6),作為通告OBU如何與RSU同步、RSU的連線能力以及QoS等參數資訊之用途。其型態為管理訊框,子型態為時間廣播訊框,而訊框內容為數個資訊單元,各別為時間標籤、連線能力、所支援的傳輸速率、國家頻率使用規範、加強型分散式通道存取參數設定、功率限制、時間廣播資訊、擴充連線能力以及製造商資訊。

點圖放大
圖6 時間廣播訊框結構

存在於WAVE環境中有進行兩種目的車載設備,但其使用方式必須決定於MIB中的dot11OCBEnabled(Outside the Context of a BSS)旗標。若旗標為真,則進行IP資料存取服務;若旗標為偽,則進行快速資料交換服務目的,如時間廣播封包或WAVE短訊息封包。

若以進行IP資料存取為目的,OBU必須為BSS的成員,才可進行IP資料存取,但在成為BSS成員前,需要進行認證服務程序,將是非常耗時的程序。若以進行快速資料交換服務目的,OBU不必為BSS的成員亦可進行資料存取,亦毋須進行認證與連結服務程序。

IEEE 802.11p定義了兩種操作頻道,一個是控制頻道,另一個為服務頻道。控制頻道是用來傳送廣播資訊與建立連線,支援短資訊的廣播與訊息傳送,而其他的資料交換則在服務頻道進行。服務頻道上相互溝通的可為路旁設備與車載設備亦或車載設備與車載設備。而頻道上的訊息交換必須根據優先等級的參數決定傳送的順序,表1為規格對於dot11OCBEnabled旗標致能後,所定義的QoS之參數設定,是為了達到高優先權資料能有較高的機會傳送之目的。其中Arbitrary Inter-Frame Space(AIFS)表示WAVE系統所採用的QoS設定中的任意訊框間隔數目。

表1 預設加強型分散式通道存取參數設定表

Telematics產業目前上層應用主要仍以安全議題為考量,透過結合先進資通訊技術來保障行車安全。IEEE 802.11p標準的成長與獲選為北美DSRC的規格選項是十分值得關注與投入的研究議題。

(本文作者任職於資策會新興智慧研究所)

參考資料

[1] ASTM E2213-03 Standard, Telecommunications and Information Exchange Between Roadside and Vehicle Systems, July 2003.
[2] IEEE P802.11p/D6. Draft Amendment for Wireless Access in Vehicular Environments (WAVE), March 2009.
[3] IEEE 1609.1-2006: IEEE Trial-Use Standard for Wireless Access in Vehicular Environments – (WAVE) Resource Manager
[4] IEEE 1609.2-2006: IEEE Trial-Use Standard for Wireless Access in Vehicular Environments – Security Service for Applications and Management Messages
[5] IEEE P1609.3 D1.0, Draft Standard for Wireless Access in Vehicular Environments (WAVE) - Networking Services, December 2008.
[6] IEEE P1609.4 D1.0, Draft Standard for Wireless Access in Vehicular Environments (WAVE) - Multi-channel Operation, December 2008.
[7] IEEE 802.11 Working Group, Part 11: Wireless LAN Medium Access Control (MAC) and Physical Layer (PHY) Specifications, June 2007.

http://www.2cm.com.tw

2009年6月10日 星期三

Makefile 語法簡介

有稍稍在 Linux 下碰過程式設計的開發者應該會知道,make 是用來將程式碼、函式庫、標頭檔及其它資源檔 build 成最終成果(即:最終的應用程式)的超強力輔助工具。

當然了,並不是非得動用到 make 才能 build 程式,或許有什麼程式設計魔人喜歡什麼都自己手動進行;但利用 make 及其參考檔(輸入檔案)Makefile 將會讓整個編譯工作輕鬆許多。若您曾經打包過 Debian Package,那麼應該會發現 debuan/rule 這個檔案的語法和 Makefile 幾乎是一模一樣,所以學習 Makefile 的語法對於 Debian Package Maintainer 而言也是一門必要的功課。

Makefile 語法:

以下為 Makefile 的基本語法:

註解:

以 # 開頭的即為註解。

變數宣告:(有人稱之為巨集)

語法:
MACRO = value

變數名稱為大小寫相異。在慣例上,Makefile 內部使用的變數名稱使用小寫;而使用者很可能從命令列自行另外指定數值的變數,像是 CFLAGS,則是使用大寫。利用 MACRO = 來取消該變數。

在 Makefile 中,可利用 $(MACRO) 或 ${MACRO} 來存取已定義的變數。例:

targets = foo
$(targets)
: common.h
gcc -o $(targets) foo.c

效果等同:

foo: common.h
gcc -o foo foo.c

:= 語法

注意到,make 會將整個 Makefile 展開後,再決定變數的值。也就是說,變數的值將會是整個 Mackfile 中最後被指定的值。例:

x = foo
y = $(x) bar
x = xyz
# y 的值為 xyz bar

在上例中,y 的值將會是 xyz bar,而不是 foo bar。

您可以利用 := 來避開這個問題。:= 表示變數的值決定於它在 Makefile 中的位置,而不是整個 Makefile 展開後最終的值。

x := foo
y := $(x) bar
x := xyz
# y 的值為 foo bar
在上例中,y 的值將會是 foo bar,而不是 xyz bar 了。

?= 語法:

?= 是一個簡化的語法:若變數未定義,則替它指定新的值。否則,採用原有的值。例:
FOO ?= bar
若 FOO 未定義,則 FOO = bar;若 FOO 已定義,則 FOO 的值維持不變。

+= 語法:

例:
CFLAGS = -Wall -g
CFLAGS += -O2

此時 CFLAGS 的值就變成 -Wall -g -O2 了。

define 語法:

使用 define 語法的唯一優點是它可以讓變數直接使用『斷行』。例:
define foo
uname -a
echo $$SHELL
endef

all:
$(foo)

上例可以視同於:
foo = uname -a; echo $$SHELL

all:
$(foo)

注意到在上例中使用了 $$,讓 '$' 能傳到 Shell 中。

在 target 裡另外指定變數的值

可以在 target 裡另外指定變數的值。例:
foo = abc

all: foo = xyz
all:
echo $(foo)
# 此時,foo 的值為 xyz

以下的語法提供了和上例相同的功能:

all: override foo = xyz

all: export foo = xyz

make 也可以存取環境變數。例:

all:
@echo $(CFLAGS)

在上例中,雖然在 Makefile 裡雖然沒有指定 CFLAGS 的值,但 make 會試圖以環境變數來代出 CFLAGS 的值。

可搭配 wildcard 指令在變數裡展開 * ? [...] 等萬用字元。例:
objects=$(wildcard *.o)

規則:(Rule)

指示 make 如何進行編譯。

主要語法:

target: dependencies
Commands

或

target: dependencies; Commands
Commands

Rule 指示了 make 如何建立 target;及何時要重新建立 target。

target:所要建立的檔案< /td>
dependencies:相依項目。 make 會據此決定是否要重新編譯 target。
Commands:建立 target 的指令。

在 Makefile 裡並沒有限定 Rule 的先後順序。但預設上,make 會參考 all 這個目標項目,並依據它的 dependencies 來決定要建立哪些項目。若沒有 all 項目,則會採用 Makefile 裡的第一個項目。

target:(目標項目)

這個項目所要建立的檔案,必須以 : 結尾。例:
foo.o: common.h
gcc -c foo.c
其中,foo.o 是這個項目要建立的檔案;common.h 是相依性的項目/檔案;而 gcc -c foo.c則為要產生這個項目所要執行的指令。

make 在編譯時,若發現 target 比較新,也就是 dependencies 都比 target 舊,那麼將不會重新建立 target,如此可以避免不必要的編譯動作。

若該項目並非檔案,則為 fake 項目。如此一來將不會建立 target 檔案。但為了避免 make 有時會無去判斷 target 是否為檔案或 fake 項目,建議利用 .PHONY 來指定該項目為 fake 項目。例:
.PHONY: clean
clean:
rm *.o

在上例中,若不使用 .PHONY 來指定 clean 為 fake 項目的話,若目錄中同時存在了一個名為clean 的檔案,則 clean 這個項目將被視為要建立 clean 這個檔案,但 clean 這個項目卻又沒有任何的 dependencies,也因此,clean 項目將永遠被視為 up-to-date,永遠不會被執行。

因為利用了 .PHONY 來指定 clean 為 fake 項目,所以 make 不會去檢查目錄中是否存在了一個名為 clean 的檔案。如此也可以提昇 make 的執行效率。

其它類以 .PHONY 的語法請參考:

GNU `make': 4.9 Special Built-in Target Names
另外,如果某個非 fake 項目的 target 的 dependencies 包含了 fake 項目的話,因為 make 一定會執行 fake 項目,這樣一來,這個非 fake 項目的 target 一定也會被執行。這可能不是理想的做法。

dependencies:(相依性項目,以空白間隔)

dependencies 是指定在建立 target 之前,必須先檢查的項目。可以不指定。例:

foo.o: common.h
gcc -c foo.c

上例中是指:檢查 common.h。如果它的建立日期比 foo.o 新,就執行 gcc -c foo.c 來重新產生 foo.o。也就是說,可以依需求建立 dependencies,即使它和 target 一點關係也沒有。

相依性項目可以是 Makefile 中其它的 target。也因此,在建立該 target 之前,它會先檢查在 dependencies 裡所指定的所有 target。

Commands:(即為要執行的 Shell 指令)

必須以 開頭。使用 Shell Script 語法。在 Makefile 裡,只要以 開頭都將會被視為 Shell Script 執行。

每條法則必須寫在同一行。每條 Command 會啟動一個新的 Shell,預設為 /bin/sh。若執行完某條 Command 但傳回了錯誤值,make 就會中斷執行。

因為每條 Command 會啟動一個新的 Shell,所以有時執行的指令必須寫在同一行,像是使用if 來進行條件判斷,此時可以用 ; 來分隔指令。例:

all:
if [ -f foo ]; then rm foo; fi

而以下是錯誤示範:

all:
cd subdir; $(MAKE)

這時因為 make 只會檢查最後一個指令的傳回值,所以在以上指令中,即使 subdir 不存在,但 make 並不會因而中斷執行,並會繼續執行 $(MAKE) 指令,而產生了不可預期的結果。

為了避免這個問題,可以利用 && 來檢查其中某個指令是否成功執行,再決定是否執行下個指令。例:
all:
cd subdir && $(MAKE)

特別字元:

@:不要顯示執行的指令。

-:表示即使該行指令出錯,也不會中斷執行。

例:

.PHONY: clean
clean:
@echo "Clean..."
-rm *.o
因為 make 會一行一行將正在執行的 Commands 顯示在螢幕上,但您可以利用 @來暫時關閉這個功能。

而 make 只要遇到任何錯誤就會中斷執行。但像是在進行 clean 時,也許根本沒有任何檔案可以 clean,因而 rm 會傳回錯誤值,因而導致 make 中斷執行。我們可以利用 - 來關閉錯誤中斷功能,讓 make 不會因而中斷。

隱性法則:

在上例中的:

foo.o: common.h
gcc -c foo.c

由於產生 foo.o 的指令就是 gcc -c foo.c,因此在 Makefile 裡可以將其簡化為:

foo.o: common.h

此時 make 會依據 target 的副檔名來猜測該如何編譯 target。如此可以讓Makefile 更為簡潔。

您可以利用【空白指令】來避免 make 依據隱性法則而進行編譯。例:

foo.o: common.h

內部變數:

$?:代表已被更新的 dependencies 的值。
也就是 dependencies 中,比 targets 還新的值。
$@:代表 targets 的值。
$<:代表第一個 dependencies 的值。
$* :
代表 targets 所指定的檔案,但不包含副檔名。

例:
print: foo1.c foo2.c foo3.c
lpr -p $?
touch print

這樣會將 foo1.c foo2.c foo3.c 中已有更新的內容印至印表機。

內部函數:

您可以在 Makefile 使用 make 所支援的一些內部函數。詳情請參考:
GNU `make': 8 Functions for Transforming Text

條件判斷:

可以在 Makefile 中使用以下的條件判斷語法。但由於它們不是 rule,所以不可以 開頭;但其後要執行的指令則必須以 開頭,make 才會視其為 Shell 指令。

ifeq:(檢查 value1, value2 是否相等)
ifeq (value1, value2)
...
else
...
endif
ifneq:(提供和 ifeq 相反的功能)
ifneq (value1, value2)
...
else
...
endif
ifdef:(檢查 variable 變數是否為空的)
ifdef variable
...
else
...
endif

ifndef:
(提供和 ifdef 相反的功能)
ifdef variable
...
else
....
endif

引入檔案:

將外部檔案引入 Makefile 中。可以視為直接在此將該檔案內容全數插入 Makefile 中。

例:

include foo.in
將 foo.in 的內容全數引入 Makefile 裡。

可以同時引入多個檔案、使用變數 $(MACRO) 或是使用萬用字元(* ? 或 [...])。例:
include foo.in common*.in $(MAKEINCS)

子目錄:

如果該專案有多個目錄,且每一個目錄中都有 Makefile,則利用以下指令來進入子目錄並進行編譯:
cd dir && $(MAKE)
例:
SUBDIRS = dir1 dir2 dir3

all:
for i in $(SUBDIRS); do
(cd $$i && make);
done

clean:
for i in $(SUBDIRS); do
(cd $$i && make clean);
done

install:
for i in $(SUBDIRS); do
(cd $$i && make install);
done

make 參數:

可以用 make 的參數來蓋過 Makefile 裡,用變數所指定的參數。例:

make CFLAGS="-g -O2"
您可以在 Makefile 裡使用 override 來避免變數的值被 make 的參數所取代。例:
override CFLAGS = -Wall -g
可以在 make 後指定要重新建立的 target。例:
make clean
以上會執行 Makefile 中的 clean 區段。

參考資訊:

GNU `make' 說明手冊(英文版)

2009年6月5日 星期五

如何使用C開發Verilog System Task/Function? (SOC) (Verilog) (Verilog PLI)

如何使用C開發Verilog System Task/Function? (SOC) (Verilog) (Verilog PLI)


Abstract
本文介紹使用C開發Verilog System task/function,以彌補Verilog功能的不足。

Introduction
使用環境 : Cadense NC-Verilog 5.4 + Visual C++ 6.0

Verilog PLI(Programming Language Interface)是Verilog所提供的機制,我們可以使用C語言開發自己的system task/function,以彌補在Verilog撰寫testbench的不足。

在此文件,將學習到:
1.如何在Verilog呼叫C function?
2.如何撰寫簡單的calltf routine與register function?
3.如何在Windows平台使用Cadence NC-Verilog編譯與連結?

如下圖所示,當simulator執行自己開發的system task時,會轉而執行C的function,執行完再回到Verilog。

pli_flow

使用C開發Verilog system task的流程如下圖所示:

pli_hello_world01

Step 1:
建立C function

Step 2:
建立C function與Verilog system task的連結資料

hello_world.c / C

1 #include <stdio.h> // define NULL, printf()
2 #include "vpi_user.h" // required by VPI application
3
4 // my own C function for Verilog
5 PLI_INT32 hello_world(PLI_BYTE8 *user_data) {
6 vpi_printf("Hello World from C!!\n");
7
8 return 0;
9 }
10
11 // defined in vpi_user.h
12 /*
13 typedef struct t_vpi_systf_data {
14 PLI_INT32 type; // vpiSysTask, vpiSysFunc
15 PLI_INT32 sysfunctype; // vpiSysTask, vpi[Int,Real,Time,Sized, SizedSigned]Func
16 PLI_BYTE8 *tfname; // first character must be `$'
17 PLI_INT32 (*calltf)(PLI_BYTE8 *);
18 PLI_INT32 (*compiletf)(PLI_BYTE8 *);
19 PLI_INT32 (*sizetf)(PLI_BYTE8 *); // for sized function callbacks only
20 PLI_BYTE8 *user_data;
21 } s_vpi_systf_data, *p_vpi_systf_data;
22 */
23
24 // associating C function with new verilog system task
25 // you can use your favorite function name
26 void register_my_systfs() {
27 s_vpi_systf_data tf_data; // defined in vpi_user.h
28
29 tf_data.type = vpiSysTask; // system task
30 tf_data.tfname = "$hello_world"; // name of system task
31 tf_data.calltf = hello_world; // C function name
32 tf_data.compiletf = NULL; // no compiletf routine
33
34 vpi_register_systf(&tf_data); // register system task to verilog
35 }


第2行

#include "vpi_user.h" // required by VPI application


寫PLI一定要include。

第4行

// my own C function for Verilog
PLI_INT32 hello_world(PLI_BYTE8 *user_data) {
vpi_printf(
"Hello World from C!!\n");

return 0;
}


自己的C function,僅簡單的顯示Hello World。由於struct s_vpi_systf_data規定自訂的C function的signature必須為PLI_INT32 (*calltf)(PLI_BYTE8 *),所以依照其規定宣告hello_world型別。

24行

// associating C function with new verilog system task
// you can use your favorite function name
void register_my_systfs() {
s_vpi_systf_data tf_data;
// defined in vpi_user.h

tf_data.type
= vpiSysTask; // system task
tf_data.tfname = "$hello_world"; // name of system task
tf_data.calltf = hello_world; // C function name
tf_data.compiletf = NULL; // no compiletf routine

vpi_register_systf(
&tf_data); // register system task to verilog
}


建立C function與Verilog system task的連結資料,s_vpi_systf_data定義在vpi_user.h,其完整定義如下:

typedef struct t_vpi_systf_data {
PLI_INT32 type;
// vpiSysTask, vpiSysFunc
PLI_INT32 sysfunctype; // vpiSysTask, vpi[Int,Real,Time,Sized, SizedSigned]Func
PLI_BYTE8 *tfname; // first character must be `$'
PLI_INT32 (*calltf)(PLI_BYTE8 *);
PLI_INT32 (
*compiletf)(PLI_BYTE8 *);
PLI_INT32 (
*sizetf)(PLI_BYTE8 *); // for sized function callbacks only
PLI_BYTE8 *user_data;
} s_vpi_systf_data,
*p_vpi_systf_data;


register_my_systfs() function主要的目的在於將s_vpi_systf_data struct填滿, 名稱不一定要取register_my_systfs,可以自行命名。

29行

tf_data.type = vpiSysTask; // system task


定義為System Task。

30行

tf_data.tfname = "$hello_world"; // name of system task


定義Verilog system task名稱

31行

tf_data.calltf = hello_world; // C function name


指定自己寫的C function名稱,為function pointer,為了callback使用。

32行

tf_data.compiletf = NULL; // no compiletf routine


由於沒用到compiletf,所以設定為NULL。

34行

vpi_register_systf(&tf_data); // register system task to verilog


註冊新的system task。

Step 3:
在simulator註冊新Verilog system task

複製C:\Program Files\Cadence Design Systems\IUS\tools\src\vpi_user.c到目前目錄,將vpi_user.c修改如下:

1 /*
2 * |-----------------------------------------------------------------------|
3 * | |
4 * | Copyright Cadence Design Systems, Inc. 1985, 1988. |
5 * | All Rights Reserved. Licensed Software. |
6 * | |
7 * | |
8 * | THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF CADENCE DESIGN SYSTEMS |
9 * | The copyright notice above does not evidence any actual or intended |
10 * | publication of such source code. |
11 * | |
12 * |-----------------------------------------------------------------------|
13 */
14
15 /*
16 * |-------------------------------------------------------------|
17 * | |
18 * | PROPRIETARY INFORMATION, PROPERTY OF CADENCE DESIGN SYSTEMS |
19 * | |
20 * |-------------------------------------------------------------|
21 */
22
23 #include <stdarg.h>
24 #include "vpi_user.h"
25 #include "vpi_user_cds.h"
26
27
28 /* extern void setup_test_callbacks();*/
29
30 /* ----------------------------------------------------------------
31 The following is an example of what should be included in this file:
32
33 extern void setup_my_callbacks(); <-- Add a declaration for your routine.
34
35 void (*vlog_startup_routines[])() =
36 {
37 $*** add user entries here ***$
38
39 setup_my_callbacks, <-- Add your routine to the table.
40
41 0 $*** final entry must be 0 ***$
42
43 };
44 ------------------------------------------------------------------ */
45
46 extern void register_my_systfs();
47
48 void (*vlog_startup_routines[VPI_MAXARRAY])() =
49 {
50 register_my_systfs,
51 0 /*** final entry must be 0 ***/
52 };


46行

extern void register_my_systfs();


使用extern宣告在hello_world.c的register_my_systfs。

48行

void (*vlog_startup_routines[VPI_MAXARRAY])() =
{
register_my_systfs,
0 /*** final entry must be 0 ***/
};


設定simulator啟動時,載入PLI的array,注意最後一個element必須為0。

Step 4:
使用PLI Wizard產生Dynamic Link Library (libvpi.dll)

啟動PLI Wizard

開始->程式集->Cadence Design Systems->Design & Verification->PLI Wizard

File -> New Session

pli_hello_world02

選擇VPI Application與libvp

pli_hello_world03

加入hello_world.c,vpi_user.c會自動抓進來

pli_hello_world04

加入VC6的include path: C:\Program Files\Microsoft Visual Studio\VC98\Include

選擇C語言

pli_hello_world05

按Finish完成

pli_hello_world06

選(Y)

pli_hello_world07

按Close離開

pli_hello_world08

File -> Exit離開PLI Wizard

Step 5:
設定libvpi.dll路徑

在Path環境變數加上libvpi.dll的路徑,注意必須加在C:\Program Files\Cadence Design Systems\IUS\tools\bin;C:\Program Files\Cadence Design Systems\IUS\tools\lib;C:\Novas\Debussy\bin;之前,否則PLI Wizard啟動會有問題。

設定好後須登出在登入,path才會生效。

若只是為了測試,可將libvpi.dll放在與*.v同目錄下即可。

Step 6:

在Verilog使用新的system task

hello_world.v / Verilog

1 module hello_world;
2
3 initial begin
4 $hello_world;
5 #10 $finish;
6 end
7
8 endmodule


Step 7:
執行NC-Verilog

ncverilog +access+r hello_world.v


pli_hello_world09

完整程式碼下載
pli_hello_world.7z

Conclusion
PLI在Verilog的IEEE標準中有明確定義,但如何compiling與linking方面,就與simulator與OS有關,IEEE標準並沒有加以定義。本文雖然只是一個小小的Hello World,已經展示了Verilog PLI如何在Windows平台與Cadence NC-Verilog compiling與linking,並看到Verilog如何呼叫一個C的function。