,
(南京南瑞繼保電氣有限公司,南京 211102)
IEC61131是國(guó)際電工委員會(huì)(IEC)頒布的可編程控制器(PLC)國(guó)際標(biāo)準(zhǔn),用于規(guī)范可編程控制器編程工具和應(yīng)用程序的開(kāi)發(fā),目的是便于各廠家的應(yīng)用程序移植,降低用戶的使用難度和維護(hù)成本[1-5]。其中IEC61131-3為軟件設(shè)計(jì)提供了標(biāo)準(zhǔn)化的編程概念和編程方法,定義了指令表IL、結(jié)構(gòu)化文本ST、梯形圖LD、功能塊圖FBD、順序功能圖SFC幾種語(yǔ)言的規(guī)范,國(guó)內(nèi)外工控廠家、研究機(jī)構(gòu)已經(jīng)開(kāi)始提供基于該標(biāo)準(zhǔn)的產(chǎn)品[6-8]。參考文獻(xiàn)[9-10]介紹了嵌入式軟PLC系統(tǒng)的架構(gòu),參考文獻(xiàn)[11]提出一種將ST語(yǔ)言轉(zhuǎn)換為IL指令的方法,參考文獻(xiàn)[12]設(shè)計(jì)了ST的文法分析器,上述資料文獻(xiàn)推進(jìn)了IEC61131在國(guó)內(nèi)的研究進(jìn)程。ST有標(biāo)準(zhǔn)的關(guān)鍵字和語(yǔ)法結(jié)構(gòu),可用于復(fù)雜邏輯設(shè)計(jì)實(shí)現(xiàn),通常有將ST語(yǔ)句直接編譯為硬件相關(guān)的目標(biāo)可執(zhí)行文件或者將ST轉(zhuǎn)換為C語(yǔ)言后再調(diào)用第三方編譯的處理方法,這種方法執(zhí)行效率較高,但無(wú)法滿足以程序組織單元(POU)為單位的在線無(wú)擾更新的需求。
在一些工控應(yīng)用場(chǎng)合中,當(dāng)裝置處于運(yùn)行狀態(tài)時(shí),需要能實(shí)現(xiàn)部分程序的下載更新和生效,不能將整個(gè)程序編譯為1個(gè)目標(biāo)文件,而是需要編譯為與機(jī)器無(wú)關(guān)的虛擬機(jī)指令,在嵌入式裝置側(cè)的非實(shí)時(shí)任務(wù)中進(jìn)行指令文件更新,在實(shí)時(shí)任務(wù)中解釋執(zhí)行。針對(duì)上述需求,需要定義一套指令集,開(kāi)發(fā)自主編譯器,本文介紹了指令集設(shè)計(jì)和對(duì)應(yīng)的ST主要語(yǔ)句的處理表達(dá)模式、指令優(yōu)化方法。
IEC61131定義的軟件模型包括配置、資源、任務(wù)、全局變量、訪問(wèn)路徑,在任務(wù)中可運(yùn)行程序?qū)嵗?。程序劃分為若干組織單元(POU)管理。ST語(yǔ)言是類似于PASCAL的高級(jí)語(yǔ)言,用文本編寫的代碼由語(yǔ)句和表達(dá)式組成,每行用分號(hào)結(jié)束,用ST語(yǔ)言編寫的POU包括變量聲明、賦值語(yǔ)句、流程控制語(yǔ)句(選擇、循環(huán)、返回),形成中間指令時(shí)需翻譯轉(zhuǎn)換這些語(yǔ)句。以CASE選擇語(yǔ)句為例,其示例代碼如下:
CASE PARA OF
1,2: a:=1;
3..5: a:=2;
ELSE a:=3;
END_CASE;
CASE語(yǔ)句中表達(dá)式的值必須是整數(shù),并可采用連續(xù)符號(hào)..表示連續(xù)的多個(gè)整數(shù),不同整數(shù)值用逗號(hào)分隔,用于執(zhí)行相同的語(yǔ)句組。
為了滿足工控程序在線無(wú)擾更新的需求,將ST程序編譯為虛擬機(jī)解釋型指令。如圖1所示,編譯器分前端和后端,前端讀取ST源程序,借助于flex完成詞法分析,借助bison完成語(yǔ)法分析,其中flex是詞法分析工具、bison是語(yǔ)法分析工具[13]。自主開(kāi)發(fā)的編譯器在語(yǔ)法分析過(guò)程中創(chuàng)建符號(hào)表保存數(shù)據(jù),形成語(yǔ)法樹(shù),遍歷語(yǔ)法樹(shù)進(jìn)行語(yǔ)義分析,形成中間數(shù)據(jù)結(jié)構(gòu),并輸出中間指令。解釋器在初始化或指令文件更新后,讀取指令文件,形成指令數(shù)組,在周期或中斷任務(wù)中執(zhí)行相關(guān)指令。
圖1 ST語(yǔ)言編譯解釋架構(gòu)
在編譯后采用虛擬機(jī)指令表示,可以適當(dāng)隔離不同機(jī)器平臺(tái)的特點(diǎn),便于解釋器系統(tǒng)的移植。本文的中間指令以三地址碼為基礎(chǔ),三地址碼包含一個(gè)操作符、源操作數(shù)和一個(gè)目的操作數(shù)。目的操作數(shù)用來(lái)存放源操作數(shù)經(jīng)過(guò)操作符對(duì)應(yīng)操作處理后產(chǎn)生的結(jié)果,或者對(duì)于轉(zhuǎn)移操作,目的操作數(shù)代表要轉(zhuǎn)移的目的地址。三地址碼中源操作數(shù)的數(shù)目可以小于兩個(gè),三地址碼基于兩個(gè)基本概念:地址和指令[14]。地址可以為指向符號(hào)表?xiàng)l目的常量、編譯器生成的臨時(shí)變量。本文使用變量在數(shù)據(jù)區(qū)的序號(hào)作為地址標(biāo)號(hào),每個(gè)變量在數(shù)據(jù)區(qū)對(duì)應(yīng)1個(gè)相同大小的結(jié)構(gòu)體,記錄變量類型、初始值、是否保持、是否邊沿觸發(fā)等IEC61131定義的屬性。
本文在指令設(shè)計(jì)時(shí)參考了VCODE和CIL指令集的部分理念[15-16]:使執(zhí)行頻繁的部分保持高效,使其它部分保持正確。根據(jù)ST語(yǔ)言的規(guī)范和特性,支持可變形參,可內(nèi)置調(diào)用更豐富的庫(kù)函數(shù)接口。表1為指令集。
表1 ST虛擬機(jī)指令集
在存儲(chǔ)時(shí)指令類型占用1字節(jié),指令存在二元運(yùn)算(rd,rs1,rs2)、一元運(yùn)算(rd,rs)、跳轉(zhuǎn)指令jmp、函數(shù)調(diào)用scall等模式。采用緊湊型存儲(chǔ),根據(jù)指令類型動(dòng)態(tài)輸出形參個(gè)數(shù)。
當(dāng)定義指令集后,編譯器的主要工作是將ST語(yǔ)句編譯為指令序列。需要設(shè)計(jì)各語(yǔ)句對(duì)應(yīng)的編譯處理方案,這也是ST語(yǔ)言編譯的關(guān)鍵步驟。
2.1 IF語(yǔ)句的處理
IF語(yǔ)句帶有可選的ELSEIF、ELSE部分。這兩個(gè)可選部分的組合產(chǎn)生兩種形式。以帶有ELSEIF語(yǔ)句的為例,其語(yǔ)句結(jié)構(gòu)為:
IF
ELSEIF
…
ELSEIF
ELSE
END_IF;
對(duì)應(yīng)的編譯方案如下所示:
{Evaluate
(jz,
{ code for (jmp EndLabel) ELS1Label:(lab, ELS1Label) {Evaluate (jz, { code for jmp EndLabel) … ELSN-1Label:(lab, ELSN-1Label) {Evaluate (jz, { code for (jmp EndLabel) ELSNLabel:(lab, ELSNLabel) {code for EndLabel: (lab, EndLabel) IF語(yǔ)句翻譯處理的關(guān)鍵步驟為: ① 在每個(gè)布爾表達(dá)式后,需要根據(jù)表達(dá)式的值生成一個(gè)條件跳轉(zhuǎn); ② 在THEN部分后,如果有相應(yīng)的ELSEIF、ELSE部分,則需生成跳轉(zhuǎn)語(yǔ)句跳過(guò); ③ 標(biāo)識(shí) ELSEIF、ELSE部分的正確標(biāo)號(hào),以及IF語(yǔ)句的結(jié)尾須由構(gòu)造jmp元組的動(dòng)作例程產(chǎn)生,相應(yīng)的LABEL元組須放在元組序列的正確位置上?;跇?biāo)簽唯一分配機(jī)制可避免目標(biāo)元組序號(hào)回填。 ST語(yǔ)言的FOR語(yǔ)句的特點(diǎn)如下: ① 要求控制變量、初始值、終值是相同的整數(shù)類型(SINT、INT、DINT)的表達(dá)式,不能被任何重復(fù)的語(yǔ)句改變(在循環(huán)語(yǔ)句內(nèi)作為只讀加以保護(hù))。 ② FOR結(jié)構(gòu)中有兩個(gè)表達(dá)式,即“循環(huán)初始表達(dá)式”、“循環(huán)終止表達(dá)式”,這兩個(gè)表示的計(jì)算結(jié)果類型必須是有序類型。 ③ 標(biāo)準(zhǔn)ST規(guī)定FOR有2種形式,即FOR-TO-BY-DO、FOR-TO-DO。FOR 語(yǔ)句將控制變量從初始值向上或向下增加到終值,其增量由表達(dá)式的值決定。如果沒(méi)有BY關(guān)鍵字,則缺省值是1。終止的條件檢測(cè)在每個(gè)迭代開(kāi)始時(shí)進(jìn)行,初始值超過(guò)終值時(shí),退出語(yǔ)句序列。FOR語(yǔ)句的文法格式如下所示: FOR語(yǔ)句帶BY關(guān)鍵字 : FOR END_FOR; FOR語(yǔ)句帶未BY關(guān)鍵字自動(dòng)默認(rèn)增量為1 : FOR END_FOR; 其中expr1是循環(huán)索引賦值表達(dá)式,expr2是終止值表達(dá)式,expr3是增量表達(dá)式,實(shí)際應(yīng)用中,expr3多是常量表達(dá)式。為了提高運(yùn)行效率,當(dāng)expr3是常量值時(shí),可預(yù)先判定FOR語(yǔ)句是“向上計(jì)數(shù)”或“向下計(jì)數(shù)”類型。當(dāng)該常量值大于0時(shí),使用向上計(jì)數(shù)的元組序列,常量值小于0時(shí),使用向下計(jì)數(shù)的元組序列。通常的FOR語(yǔ)句代碼序列將會(huì)在循環(huán)地步增值索引變量,然后再跳轉(zhuǎn)回循環(huán)上部以測(cè)試新值是否上溢出。這種方法缺點(diǎn)是:如果上界是最大整數(shù)的話,這個(gè)增值可能在最后的測(cè)試前引起上溢[17]。為避免這個(gè)問(wèn)題,在參考文獻(xiàn)[17]的基礎(chǔ)上,本文設(shè)計(jì)了ST 語(yǔ)言的FOR語(yǔ)句的安全編譯方案,如圖2所示。 圖2 向上/下計(jì)數(shù)的FOR語(yǔ)句元組序列 圖2的語(yǔ)句序列可能看起來(lái)有些費(fèi)解,因?yàn)槊恳粋€(gè)序列都包括2個(gè)不停的終止測(cè)試。這是由于:如果迭代的上界是給定機(jī)器所能表示的最大整數(shù),則這種結(jié)構(gòu)是必要的,確保了向上計(jì)數(shù)的FOR的正確終止。在ST語(yǔ)言中,循環(huán)索引變量是一個(gè)從循環(huán)外可見(jiàn)的變量,當(dāng)索引變量是子界類型并且循環(huán)迭代在整個(gè)子界范圍時(shí),上述的代碼序列能確保該變量絕不被賦值為超出范圍的值,并保證該循環(huán)體代碼除非至少執(zhí)行一次,否則永不執(zhí)行。 當(dāng)expr3是常規(guī)表達(dá)式時(shí),不能預(yù)先判定是上行或下行,則先計(jì)算expr1、expr2,并判斷表達(dá)式值的大小,動(dòng)態(tài)確定上下限。如圖3所示,在循環(huán)執(zhí)行的指令段中,先執(zhí)行FOR中語(yǔ)句,之后計(jì)算expr3,更新索引變量,判斷是否大于上界或小于下界,若是,則跳出循環(huán)。 圖3 動(dòng)態(tài)確定上下限FOR語(yǔ)句元組序列 大部分情況下,應(yīng)用程序使用CASE語(yǔ)句時(shí),判斷變量都是單個(gè)常數(shù),故先檢測(cè)CASE是否為單變量-全常量分支,符合條件時(shí),形成基于跳轉(zhuǎn)表的優(yōu)化翻譯模式[18]。當(dāng)CASE的分支表達(dá)式是形如a,b,c..d的形式,可形成短路求值后跳轉(zhuǎn)指令,其編譯方案如圖4所示。 圖4 多分支CASE語(yǔ)句元組序列 在處理CASE分支的表達(dá)式時(shí),可直接輸出表達(dá)式對(duì)應(yīng)的三地址碼,不必臨時(shí)構(gòu)建boolean-expr表達(dá)式后再翻譯,例如: ① 對(duì)于CASE a單變量,輸出: (je, Var, a, LabelX); ② 對(duì)于CASE a,b多變量,輸出: (je, Var, a, LabelX) (je, Var, b, LabelX) ③ 對(duì)于 CASE a..b區(qū)間變量,輸出: (ge, Temp1, Var, a) (le, Temp2, Var, b) (and, Temp3, Temp1, Temp2) (jz, Temp3, LabelX) 當(dāng)CASE存在多個(gè)逗號(hào)時(shí),以逗號(hào)為間隔,形成上述的指令序列(其中..分配3個(gè)臨時(shí)變量),最后通過(guò)臨時(shí)變量的邏輯或運(yùn)算,由于已經(jīng)預(yù)先分配分支標(biāo)號(hào),可在OR運(yùn)算時(shí)加入短路求值跳轉(zhuǎn)的指令,提高運(yùn)行效率。 ST中的EXIT語(yǔ)句類似于C的break語(yǔ)句,在條件結(jié)束前終止循環(huán)。當(dāng)EXIT語(yǔ)句位于嵌套的循環(huán)結(jié)構(gòu)內(nèi)時(shí),從EXIT所在的最內(nèi)層循環(huán)突出,即在跟隨EXIT語(yǔ)句的第一循環(huán)的終止符后(END_FOR、END_WHILE、END_REPEAT),EXIT語(yǔ)句可以用無(wú)條件跳轉(zhuǎn)指令jmp來(lái)表示,它和IF/WHILE語(yǔ)句的區(qū)別在于跳轉(zhuǎn)目標(biāo)標(biāo)簽是由其它節(jié)點(diǎn)生成的。針對(duì)多層嵌套循環(huán),為了確定跳轉(zhuǎn)語(yǔ)句的跳轉(zhuǎn)目標(biāo),采用圖5所示的棧操作機(jī)制。 圖5 基于棧確定跳轉(zhuǎn)目標(biāo)標(biāo)簽 圖5的算法思路如下: ① 基于棧的數(shù)據(jù)結(jié)構(gòu)管理標(biāo)簽; ② 在遍歷語(yǔ)法樹(shù)時(shí)遇到可以用EXIT跳出的語(yǔ)句(例如WHILE),將跳轉(zhuǎn)目標(biāo)的標(biāo)簽壓入棧(EndLabel); ③ 將WHILE語(yǔ)句的本體轉(zhuǎn)換為中間代碼; ④ 如果在本體中發(fā)現(xiàn)EXIT語(yǔ)句,將棧頂?shù)臉?biāo)簽作為跳轉(zhuǎn)目標(biāo); ⑤ 本體的轉(zhuǎn)換結(jié)束后,將棧頂?shù)臉?biāo)簽彈出棧。 當(dāng)前EXIT語(yǔ)句的跳轉(zhuǎn)目標(biāo)的標(biāo)簽始終位于棧頂。 在嵌入式裝置解釋執(zhí)行二進(jìn)制指令時(shí),對(duì)實(shí)時(shí)性要求很高。常用的編譯器通常是在編譯階段進(jìn)行優(yōu)化,但由于局限于局部函數(shù)的優(yōu)化,欠缺整體的考慮,而且采用的優(yōu)化方法是個(gè)黑盒子,驗(yàn)證對(duì)比分析相對(duì)困難,所以需要一種安全、可靠的指令優(yōu)化方法,本文對(duì)編譯后的指令文件進(jìn)行常規(guī)優(yōu)化,該方法透明,可通過(guò)反匯編驗(yàn)證跟蹤優(yōu)化的正確性,優(yōu)化步驟如下: ① 解析指令文件,讀取文件頭,獲取數(shù)據(jù)區(qū)、指令區(qū)內(nèi)容。 ② 分析數(shù)據(jù)區(qū),獲取變量序號(hào)、變量類型、是否為常量、臨時(shí)變量等標(biāo)志屬性,其中臨時(shí)變量可進(jìn)行增加、刪除操作,常量變量可以直接運(yùn)算。 ③ 第1次遍歷指令區(qū),處理常量運(yùn)算,并優(yōu)化臨時(shí)變量賦值指令。其中對(duì)于右值變量都為常量類型的指令,則直接計(jì)算常量表達(dá)式,并將運(yùn)算指令修改為賦值指令。對(duì)于賦值指令,進(jìn)行臨時(shí)變量上下文的優(yōu)化,例如對(duì)于 “add tmp1, var2,var3;asgn var1, tmp1”的指令,可優(yōu)化為“add var1, var2, var3”。 ④ 第2次遍歷指令區(qū),進(jìn)行代數(shù)簡(jiǎn)化[19]。例如直接刪除與立即數(shù)0的加減法、與立即數(shù)1的乘法運(yùn)算,將與立即數(shù)0的乘法運(yùn)算轉(zhuǎn)化成0的賦值操作,簡(jiǎn)化與立即數(shù)TRUE、FALSE相關(guān)的邏輯運(yùn)算等。 ⑤ 第3次遍歷指令區(qū),進(jìn)行變量引用點(diǎn)分析,去除未使用變量和無(wú)效指令。統(tǒng)計(jì)各條指令的左值變量在后續(xù)指令中被作為右值變量的計(jì)數(shù),若引用計(jì)數(shù)為0,則刪除該條指令,若優(yōu)化后某臨時(shí)變量未被引用,則可從數(shù)據(jù)區(qū)刪除。 經(jīng)過(guò)100余個(gè)包含多層結(jié)構(gòu)體、數(shù)組變量的復(fù)雜語(yǔ)句測(cè)試用例統(tǒng)計(jì),指令優(yōu)化前后整體效率提升了10%~25%左右。 參考文獻(xiàn) [1] Karl-Heinz JohnMichael Tiegelkamp.IEC61131-3:Programming Industrial Automation System[M]. Berlin:Springer,2000. [2] InternationalElectrotechnical Commission.IEC61131-3 Programmable controllers-Part1:General information[S].2nd ed.2003. [3] InternationalElectrotechnical Commission.IEC61131-3 Programmable controllers-Part3:Programming language[S].2nd ed.2003. [4] InternationalElectrotechnical Commission.IEC61131-8 Guidelines for the application and implementation of programming languages[S].2003. [5] 中國(guó)國(guó)家標(biāo)準(zhǔn)管理委員會(huì).GB/T 15969.3-2006可編程控制器-第3部分:編程語(yǔ)言[S]. [6] 彭瑜,何衍慶.IEC61131-3編程語(yǔ)言及應(yīng)用基礎(chǔ) [M].北京:機(jī)械工業(yè)出版社,2008. [7] 任向陽(yáng).開(kāi)放式IEC61131控制系統(tǒng)設(shè)計(jì)[M].北京:機(jī)械工業(yè)出版社,2016. [8] Phonenix Contact Software GmbH.MULTIPROG help documents[EB/OL].[2018-02].http://www. phonenixcontact-software.com. [9] 未慶超,蔡啟仲,謝從澀,等.基于ARM的PLC編譯系統(tǒng)設(shè)計(jì)[J].計(jì)算機(jī)測(cè)量與控制,2014,22(4):1225-1229. [10] 姜娟,馮萍,康繼昌.嵌入式軟PLC開(kāi)發(fā)系統(tǒng)研究[J].科學(xué)技術(shù)與工程,2011,11(3):494-498. [11] 張玉嬌,卓懷忠,沈開(kāi)奎,等.基于IEC61131-3標(biāo)準(zhǔn)的ST轉(zhuǎn)化為IL語(yǔ)言的設(shè)計(jì)與實(shí)現(xiàn)[J].自動(dòng)化與儀表,2016(9):74-76. [12] 梁世武,李加恒,朱立國(guó),等.基于IEC61131-3標(biāo)準(zhǔn)的ST語(yǔ)言文法分析器的實(shí)現(xiàn)與應(yīng)用[J].儀器儀表標(biāo)準(zhǔn)化與計(jì)量,2015(5):26-29. [13] Jobn Levine.flex與bision[M].南京:東南大學(xué)出版社,2011. [14] Alfred V Aho,Monica S Lam,Ravi Sethi,et al.Compilers Principles,Techniques&Tools[M].龍書,譯.北京:機(jī)械工業(yè)出版社,2009. [15] 姜玲燕,梁阿磊,管海兵.動(dòng)態(tài)二進(jìn)制翻譯中的中間表示[J].計(jì)算機(jī)工程,2009,5(9):283-285. [16] Microsoft Corporation.Common Language Infrastructure(CIL),Partition III,CIL Instruction Set,2001. [17] Charles NFischer,Richard J LeBlanc.Crafting A Compiler with C[M].Boston:Addison Wesley,1991. [18] 裘魏.編譯器設(shè)計(jì)之路[M].北京:機(jī)械工業(yè)出版社,2011. [19] 青木峰郎.自制編譯器[M].北京:人民郵電出版社,2016.2.2 FOR語(yǔ)句的處理
2.3 CASE語(yǔ)句的處理
2.4 EXIT語(yǔ)句的處理
2.5 指令優(yōu)化
結(jié) 語(yǔ)