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