,
(南京南瑞繼保電氣有限公司,南京 211102)
IEC61131是國際電工委員會(IEC)頒布的可編程控制器(PLC)國際標準,用于規范可編程控制器編程工具和應用程序的開發,目的是便于各廠家的應用程序移植,降低用戶的使用難度和維護成本[1-5]。其中IEC61131-3為軟件設計提供了標準化的編程概念和編程方法,定義了指令表IL、結構化文本ST、梯形圖LD、功能塊圖FBD、順序功能圖SFC幾種語言的規范,國內外工控廠家、研究機構已經開始提供基于該標準的產品[6-8]。參考文獻[9-10]介紹了嵌入式軟PLC系統的架構,參考文獻[11]提出一種將ST語言轉換為IL指令的方法,參考文獻[12]設計了ST的文法分析器,上述資料文獻推進了IEC61131在國內的研究進程。ST有標準的關鍵字和語法結構,可用于復雜邏輯設計實現,通常有將ST語句直接編譯為硬件相關的目標可執行文件或者將ST轉換為C語言后再調用第三方編譯的處理方法,這種方法執行效率較高,但無法滿足以程序組織單元(POU)為單位的在線無擾更新的需求。
在一些工控應用場合中,當裝置處于運行狀態時,需要能實現部分程序的下載更新和生效,不能將整個程序編譯為1個目標文件,而是需要編譯為與機器無關的虛擬機指令,在嵌入式裝置側的非實時任務中進行指令文件更新,在實時任務中解釋執行。針對上述需求,需要定義一套指令集,開發自主編譯器,本文介紹了指令集設計和對應的ST主要語句的處理表達模式、指令優化方法。
IEC61131定義的軟件模型包括配置、資源、任務、全局變量、訪問路徑,在任務中可運行程序實例。程序劃分為若干組織單元(POU)管理。ST語言是類似于PASCAL的高級語言,用文本編寫的代碼由語句和表達式組成,每行用分號結束,用ST語言編寫的POU包括變量聲明、賦值語句、流程控制語句(選擇、循環、返回),形成中間指令時需翻譯轉換這些語句。以CASE選擇語句為例,其示例代碼如下:
CASE PARA OF
1,2: a:=1;
3..5: a:=2;
ELSE a:=3;
END_CASE;
CASE語句中表達式的值必須是整數,并可采用連續符號..表示連續的多個整數,不同整數值用逗號分隔,用于執行相同的語句組。
為了滿足工控程序在線無擾更新的需求,將ST程序編譯為虛擬機解釋型指令。如圖1所示,編譯器分前端和后端,前端讀取ST源程序,借助于flex完成詞法分析,借助bison完成語法分析,其中flex是詞法分析工具、bison是語法分析工具[13]。自主開發的編譯器在語法分析過程中創建符號表保存數據,形成語法樹,遍歷語法樹進行語義分析,形成中間數據結構,并輸出中間指令。解釋器在初始化或指令文件更新后,讀取指令文件,形成指令數組,在周期或中斷任務中執行相關指令。

圖1 ST語言編譯解釋架構
在編譯后采用虛擬機指令表示,可以適當隔離不同機器平臺的特點,便于解釋器系統的移植。本文的中間指令以三地址碼為基礎,三地址碼包含一個操作符、源操作數和一個目的操作數。目的操作數用來存放源操作數經過操作符對應操作處理后產生的結果,或者對于轉移操作,目的操作數代表要轉移的目的地址。三地址碼中源操作數的數目可以小于兩個,三地址碼基于兩個基本概念:地址和指令[14]。地址可以為指向符號表條目的常量、編譯器生成的臨時變量。本文使用變量在數據區的序號作為地址標號,每個變量在數據區對應1個相同大小的結構體,記錄變量類型、初始值、是否保持、是否邊沿觸發等IEC61131定義的屬性。
本文在指令設計時參考了VCODE和CIL指令集的部分理念[15-16]:使執行頻繁的部分保持高效,使其它部分保持正確。根據ST語言的規范和特性,支持可變形參,可內置調用更豐富的庫函數接口。表1為指令集。

表1 ST虛擬機指令集
在存儲時指令類型占用1字節,指令存在二元運算(rd,rs1,rs2)、一元運算(rd,rs)、跳轉指令jmp、函數調用scall等模式。采用緊湊型存儲,根據指令類型動態輸出形參個數。
當定義指令集后,編譯器的主要工作是將ST語句編譯為指令序列。需要設計各語句對應的編譯處理方案,這也是ST語言編譯的關鍵步驟。
2.1 IF語句的處理
IF語句帶有可選的ELSEIF、ELSE部分。這兩個可選部分的組合產生兩種形式。以帶有ELSEIF語句的為例,其語句結構為:
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語句翻譯處理的關鍵步驟為: ① 在每個布爾表達式后,需要根據表達式的值生成一個條件跳轉; ② 在THEN部分后,如果有相應的ELSEIF、ELSE部分,則需生成跳轉語句跳過; ③ 標識 ELSEIF、ELSE部分的正確標號,以及IF語句的結尾須由構造jmp元組的動作例程產生,相應的LABEL元組須放在元組序列的正確位置上。基于標簽唯一分配機制可避免目標元組序號回填。 ST語言的FOR語句的特點如下: ① 要求控制變量、初始值、終值是相同的整數類型(SINT、INT、DINT)的表達式,不能被任何重復的語句改變(在循環語句內作為只讀加以保護)。 ② FOR結構中有兩個表達式,即“循環初始表達式”、“循環終止表達式”,這兩個表示的計算結果類型必須是有序類型。 ③ 標準ST規定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是循環索引賦值表達式,expr2是終止值表達式,expr3是增量表達式,實際應用中,expr3多是常量表達式。為了提高運行效率,當expr3是常量值時,可預先判定FOR語句是“向上計數”或“向下計數”類型。當該常量值大于0時,使用向上計數的元組序列,常量值小于0時,使用向下計數的元組序列。通常的FOR語句代碼序列將會在循環地步增值索引變量,然后再跳轉回循環上部以測試新值是否上溢出。這種方法缺點是:如果上界是最大整數的話,這個增值可能在最后的測試前引起上溢[17]。為避免這個問題,在參考文獻[17]的基礎上,本文設計了ST 語言的FOR語句的安全編譯方案,如圖2所示。 圖2 向上/下計數的FOR語句元組序列 圖2的語句序列可能看起來有些費解,因為每一個序列都包括2個不停的終止測試。這是由于:如果迭代的上界是給定機器所能表示的最大整數,則這種結構是必要的,確保了向上計數的FOR的正確終止。在ST語言中,循環索引變量是一個從循環外可見的變量,當索引變量是子界類型并且循環迭代在整個子界范圍時,上述的代碼序列能確保該變量絕不被賦值為超出范圍的值,并保證該循環體代碼除非至少執行一次,否則永不執行。 當expr3是常規表達式時,不能預先判定是上行或下行,則先計算expr1、expr2,并判斷表達式值的大小,動態確定上下限。如圖3所示,在循環執行的指令段中,先執行FOR中語句,之后計算expr3,更新索引變量,判斷是否大于上界或小于下界,若是,則跳出循環。 圖3 動態確定上下限FOR語句元組序列 大部分情況下,應用程序使用CASE語句時,判斷變量都是單個常數,故先檢測CASE是否為單變量-全常量分支,符合條件時,形成基于跳轉表的優化翻譯模式[18]。當CASE的分支表達式是形如a,b,c..d的形式,可形成短路求值后跳轉指令,其編譯方案如圖4所示。 圖4 多分支CASE語句元組序列 在處理CASE分支的表達式時,可直接輸出表達式對應的三地址碼,不必臨時構建boolean-expr表達式后再翻譯,例如: ① 對于CASE a單變量,輸出: (je, Var, a, LabelX); ② 對于CASE a,b多變量,輸出: (je, Var, a, LabelX) (je, Var, b, LabelX) ③ 對于 CASE a..b區間變量,輸出: (ge, Temp1, Var, a) (le, Temp2, Var, b) (and, Temp3, Temp1, Temp2) (jz, Temp3, LabelX) 當CASE存在多個逗號時,以逗號為間隔,形成上述的指令序列(其中..分配3個臨時變量),最后通過臨時變量的邏輯或運算,由于已經預先分配分支標號,可在OR運算時加入短路求值跳轉的指令,提高運行效率。 ST中的EXIT語句類似于C的break語句,在條件結束前終止循環。當EXIT語句位于嵌套的循環結構內時,從EXIT所在的最內層循環突出,即在跟隨EXIT語句的第一循環的終止符后(END_FOR、END_WHILE、END_REPEAT),EXIT語句可以用無條件跳轉指令jmp來表示,它和IF/WHILE語句的區別在于跳轉目標標簽是由其它節點生成的。針對多層嵌套循環,為了確定跳轉語句的跳轉目標,采用圖5所示的棧操作機制。 圖5 基于棧確定跳轉目標標簽 圖5的算法思路如下: ① 基于棧的數據結構管理標簽; ② 在遍歷語法樹時遇到可以用EXIT跳出的語句(例如WHILE),將跳轉目標的標簽壓入棧(EndLabel); ③ 將WHILE語句的本體轉換為中間代碼; ④ 如果在本體中發現EXIT語句,將棧頂的標簽作為跳轉目標; ⑤ 本體的轉換結束后,將棧頂的標簽彈出棧。 當前EXIT語句的跳轉目標的標簽始終位于棧頂。 在嵌入式裝置解釋執行二進制指令時,對實時性要求很高。常用的編譯器通常是在編譯階段進行優化,但由于局限于局部函數的優化,欠缺整體的考慮,而且采用的優化方法是個黑盒子,驗證對比分析相對困難,所以需要一種安全、可靠的指令優化方法,本文對編譯后的指令文件進行常規優化,該方法透明,可通過反匯編驗證跟蹤優化的正確性,優化步驟如下: ① 解析指令文件,讀取文件頭,獲取數據區、指令區內容。 ② 分析數據區,獲取變量序號、變量類型、是否為常量、臨時變量等標志屬性,其中臨時變量可進行增加、刪除操作,常量變量可以直接運算。 ③ 第1次遍歷指令區,處理常量運算,并優化臨時變量賦值指令。其中對于右值變量都為常量類型的指令,則直接計算常量表達式,并將運算指令修改為賦值指令。對于賦值指令,進行臨時變量上下文的優化,例如對于 “add tmp1, var2,var3;asgn var1, tmp1”的指令,可優化為“add var1, var2, var3”。 ④ 第2次遍歷指令區,進行代數簡化[19]。例如直接刪除與立即數0的加減法、與立即數1的乘法運算,將與立即數0的乘法運算轉化成0的賦值操作,簡化與立即數TRUE、FALSE相關的邏輯運算等。 ⑤ 第3次遍歷指令區,進行變量引用點分析,去除未使用變量和無效指令。統計各條指令的左值變量在后續指令中被作為右值變量的計數,若引用計數為0,則刪除該條指令,若優化后某臨時變量未被引用,則可從數據區刪除。 經過100余個包含多層結構體、數組變量的復雜語句測試用例統計,指令優化前后整體效率提升了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].北京:機械工業出版社,2008. [7] 任向陽.開放式IEC61131控制系統設計[M].北京:機械工業出版社,2016. [8] Phonenix Contact Software GmbH.MULTIPROG help documents[EB/OL].[2018-02].http://www. phonenixcontact-software.com. [9] 未慶超,蔡啟仲,謝從澀,等.基于ARM的PLC編譯系統設計[J].計算機測量與控制,2014,22(4):1225-1229. [10] 姜娟,馮萍,康繼昌.嵌入式軟PLC開發系統研究[J].科學技術與工程,2011,11(3):494-498. [11] 張玉嬌,卓懷忠,沈開奎,等.基于IEC61131-3標準的ST轉化為IL語言的設計與實現[J].自動化與儀表,2016(9):74-76. [12] 梁世武,李加恒,朱立國,等.基于IEC61131-3標準的ST語言文法分析器的實現與應用[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].龍書,譯.北京:機械工業出版社,2009. [15] 姜玲燕,梁阿磊,管海兵.動態二進制翻譯中的中間表示[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].北京:機械工業出版社,2011. [19] 青木峰郎.自制編譯器[M].北京:人民郵電出版社,2016.2.2 FOR語句的處理


2.3 CASE語句的處理

2.4 EXIT語句的處理

2.5 指令優化
結 語
