elemcmp.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443
  1. # -*- coding: utf-8 -*-
  2. #
  3. # AWL simulator - FUP compiler - Compare operations
  4. #
  5. # Copyright 2017 Michael Buesch <m@bues.ch>
  6. #
  7. # This program is free software; you can redistribute it and/or modify
  8. # it under the terms of the GNU General Public License as published by
  9. # the Free Software Foundation; either version 2 of the License, or
  10. # (at your option) any later version.
  11. #
  12. # This program is distributed in the hope that it will be useful,
  13. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. # GNU General Public License for more details.
  16. #
  17. # You should have received a copy of the GNU General Public License along
  18. # with this program; if not, write to the Free Software Foundation, Inc.,
  19. # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  20. #
  21. from __future__ import division, absolute_import, print_function, unicode_literals
  22. from awlsim.common.compat import *
  23. from awlsim.fupcompiler.elem import *
  24. from awlsim.fupcompiler.elemoper import *
  25. from awlsim.fupcompiler.elembool import *
  26. from awlsim.fupcompiler.helpers import *
  27. from awlsim.core.operators import * #+cimport
  28. from awlsim.core.operatortypes import * #+cimport
  29. from awlsim.core.instructions.all_insns import * #+cimport
  30. class FupCompiler_ElemCmp(FupCompiler_Elem):
  31. """FUP compiler - Compare operation.
  32. """
  33. ELEM_NAME = "CMP"
  34. SUBTYPE = None # Override this in the subclass
  35. CMP_INSN_CLASS = None # Override this in the subclass
  36. EnumGen.start
  37. SUBTYPE_EQ_I = EnumGen.item
  38. SUBTYPE_NE_I = EnumGen.item
  39. SUBTYPE_LT_I = EnumGen.item
  40. SUBTYPE_GT_I = EnumGen.item
  41. SUBTYPE_LE_I = EnumGen.item
  42. SUBTYPE_GE_I = EnumGen.item
  43. SUBTYPE_EQ_D = EnumGen.item
  44. SUBTYPE_NE_D = EnumGen.item
  45. SUBTYPE_LT_D = EnumGen.item
  46. SUBTYPE_GT_D = EnumGen.item
  47. SUBTYPE_LE_D = EnumGen.item
  48. SUBTYPE_GE_D = EnumGen.item
  49. SUBTYPE_EQ_R = EnumGen.item
  50. SUBTYPE_NE_R = EnumGen.item
  51. SUBTYPE_LT_R = EnumGen.item
  52. SUBTYPE_GT_R = EnumGen.item
  53. SUBTYPE_LE_R = EnumGen.item
  54. SUBTYPE_GE_R = EnumGen.item
  55. EnumGen.end
  56. str2subtype = {
  57. "eq-int" : SUBTYPE_EQ_I,
  58. "ne-int" : SUBTYPE_NE_I,
  59. "lt-int" : SUBTYPE_LT_I,
  60. "gt-int" : SUBTYPE_GT_I,
  61. "le-int" : SUBTYPE_LE_I,
  62. "ge-int" : SUBTYPE_GE_I,
  63. "eq-dint" : SUBTYPE_EQ_D,
  64. "ne-dint" : SUBTYPE_NE_D,
  65. "lt-dint" : SUBTYPE_LT_D,
  66. "gt-dint" : SUBTYPE_GT_D,
  67. "le-dint" : SUBTYPE_LE_D,
  68. "ge-dint" : SUBTYPE_GE_D,
  69. "eq-real" : SUBTYPE_EQ_R,
  70. "ne-real" : SUBTYPE_NE_R,
  71. "lt-real" : SUBTYPE_LT_R,
  72. "gt-real" : SUBTYPE_GT_R,
  73. "le-real" : SUBTYPE_LE_R,
  74. "ge-real" : SUBTYPE_GE_R,
  75. }
  76. @classmethod
  77. def parse(cls, grid, x, y, subType, content):
  78. try:
  79. subType = cls.str2subtype[subType]
  80. type2class = {
  81. cls.SUBTYPE_EQ_I : FupCompiler_ElemCmpEQI,
  82. cls.SUBTYPE_NE_I : FupCompiler_ElemCmpNEI,
  83. cls.SUBTYPE_LT_I : FupCompiler_ElemCmpLTI,
  84. cls.SUBTYPE_GT_I : FupCompiler_ElemCmpGTI,
  85. cls.SUBTYPE_LE_I : FupCompiler_ElemCmpLEI,
  86. cls.SUBTYPE_GE_I : FupCompiler_ElemCmpGEI,
  87. cls.SUBTYPE_EQ_D : FupCompiler_ElemCmpEQD,
  88. cls.SUBTYPE_NE_D : FupCompiler_ElemCmpNED,
  89. cls.SUBTYPE_LT_D : FupCompiler_ElemCmpLTD,
  90. cls.SUBTYPE_GT_D : FupCompiler_ElemCmpGTD,
  91. cls.SUBTYPE_LE_D : FupCompiler_ElemCmpLED,
  92. cls.SUBTYPE_GE_D : FupCompiler_ElemCmpGED,
  93. cls.SUBTYPE_EQ_R : FupCompiler_ElemCmpEQR,
  94. cls.SUBTYPE_NE_R : FupCompiler_ElemCmpNER,
  95. cls.SUBTYPE_LT_R : FupCompiler_ElemCmpLTR,
  96. cls.SUBTYPE_GT_R : FupCompiler_ElemCmpGTR,
  97. cls.SUBTYPE_LE_R : FupCompiler_ElemCmpLER,
  98. cls.SUBTYPE_GE_R : FupCompiler_ElemCmpGER,
  99. }
  100. elemClass = None
  101. with contextlib.suppress(KeyError):
  102. elemClass = type2class[subType]
  103. if elemClass:
  104. return elemClass(grid=grid, x=x, y=y,
  105. content=content)
  106. except KeyError:
  107. pass
  108. return None
  109. def __init__(self, grid, x, y, content, **kwargs):
  110. FupCompiler_Elem.__init__(self, grid=grid, x=x, y=y,
  111. elemType=FupCompiler_Elem.TYPE_CMP,
  112. subType=self.SUBTYPE,
  113. content=content,
  114. **kwargs)
  115. def connIsOptional(self, conn):
  116. return conn.hasText({ "EN", "ENO", })
  117. def getConnType(self, conn, preferVKE=False):
  118. if conn in self.connections:
  119. if conn.textMatch(r"(OUT\d+)|(EN)|(ENO)"):
  120. return FupCompiler_Conn.TYPE_VKE
  121. return FupCompiler_Conn.TYPE_ACCU
  122. return FupCompiler_Conn.TYPE_UNKNOWN
  123. def __getConnsEN(self):
  124. """Get EN and ENO connections.
  125. """
  126. conn_EN = self.getUniqueConnByText("EN", searchInputs=True)
  127. conn_ENO = self.getUniqueConnByText("ENO", searchOutputs=True)
  128. if not conn_EN or not conn_ENO:
  129. raise FupElemError("Invalid EN or ENO connections "
  130. "in FUP compare %s." % (
  131. str(self)),
  132. self)
  133. return conn_EN, conn_ENO
  134. def __allConnsIN(self):
  135. """Get all INx connections.
  136. """
  137. conns = []
  138. for conn in FupCompiler_Conn.sorted(self.inConnections):
  139. if conn.textMatch(r"IN\d+"):
  140. conns.append(conn)
  141. if len(conns) != 2:
  142. raise FupElemError("Invalid number of input connections.", self)
  143. return conns
  144. def __allConnsOUT(self):
  145. """Get all OUTx connections.
  146. """
  147. for conn in FupCompiler_Conn.sorted(self.outConnections):
  148. if conn.textMatch(r"OUT\d+"):
  149. yield conn
  150. def compileConn(self, conn, desiredTarget, inverted=False):
  151. insns = []
  152. assert(conn in self.connections)
  153. awlInsnClass = FupCompiler_Conn.targetToInsnClass(desiredTarget,
  154. toLoad=True,
  155. inverted=inverted)
  156. if conn.textMatch(r"(ENO)|(OUT\d+)"):
  157. self._compileConn_checkTarget(conn, desiredTarget, inverted,
  158. targetExpectVKE=True,
  159. allowInversion=True)
  160. if self.needCompile:
  161. insns.extend(self.compile())
  162. if inverted:
  163. insns.append(self.newInsn(AwlInsn_NOT))
  164. else:
  165. insns.extend(conn.elem._loadFromTemp(awlInsnClass, conn))
  166. else:
  167. return FupCompiler_Elem.compileConn(self, conn, desiredTarget, inverted)
  168. return insns
  169. def _doPreprocess(self):
  170. # If the element connected to IN is not a LOAD operand, we must
  171. # take its ENO into account.
  172. # If we don't have a connection on EN, we implicitly connect
  173. # the IN-element's ENO to our EN here.
  174. # If we already have a connection on EN, we implicitly add an AND-element
  175. # between the IN-element's ENO and our EN.
  176. elemsA = []
  177. for conn in self.__allConnsIN():
  178. connectedElem = conn.getConnectedElem(viaOut=True)
  179. if not connectedElem.isType(FupCompiler_Elem.TYPE_OPERAND,
  180. FupCompiler_ElemOper.SUBTYPE_LOAD):
  181. elemsA.append(connectedElem)
  182. FupCompiler_Helpers.genIntermediateBool(
  183. parentElem=self,
  184. elemsA=elemsA,
  185. connNamesA=(["ENO"] * len(elemsA)),
  186. elemB=self,
  187. connNameB="EN",
  188. boolElemClass=FupCompiler_ElemBoolAnd)
  189. def _doCompile(self):
  190. insns = []
  191. conn_EN, conn_ENO = self.__getConnsEN()
  192. # Compile all elements connected to IN connections.
  193. for conn in self.__allConnsIN():
  194. connectedElem = conn.getConnectedElem(viaOut=True)
  195. # Only compile the element, if it is not a plain LOAD box.
  196. if connectedElem.needCompile and\
  197. not connectedElem.isType(FupCompiler_Elem.TYPE_OPERAND,
  198. FupCompiler_ElemOper.SUBTYPE_LOAD):
  199. insns.extend(connectedElem.compile())
  200. # If we have an ENO, store the EN state in BIE,
  201. # so that EN does not have to be evaluated twice.
  202. if conn_EN.isConnected and conn_ENO.isConnected:
  203. # Compile the element that drives EN.
  204. otherConn = conn_EN.getConnectedConn(getOutput=True)
  205. insns.extend(otherConn.compileConn(targetInsnClass=AwlInsn_U,
  206. inverted=False))
  207. # Save EN to BIE.
  208. insns.append(self.newInsn(AwlInsn_SAVE))
  209. # Compile the actual operation.
  210. for conn in self.__allConnsIN():
  211. # Compile the element connected to the input.
  212. otherConn = conn.getConnectedConn(getOutput=True)
  213. if otherConn.elem.needCompile:
  214. insns.extend(otherConn.elem.compile())
  215. else:
  216. insns.extend(otherConn.compileConn(targetInsnClass=AwlInsn_L))
  217. if conn.connType != FupCompiler_Conn.TYPE_ACCU:
  218. raise FupElemError("The IN connection "
  219. "of the FUP compare box %s must not be connected "
  220. "to a bit (VKE) wire." % (
  221. str(self)),
  222. self)
  223. # Add the arithmetic operation.
  224. insns.append(self.newInsn(self.CMP_INSN_CLASS))
  225. # If we have an EN input, add an AND operation between
  226. # the compare result and the EN input.
  227. if conn_EN.isConnected:
  228. # If we have an ENO, the EN state has been stored in BIE.
  229. # Use BIE so that EN does not have to be evaluated twice.
  230. if conn_ENO.isConnected:
  231. insns.append(self.newInsn_LOAD_BIE(AwlInsn_U))
  232. else:
  233. # Compile the element that drives this wire.
  234. otherConn = conn_EN.getConnectedConn(getOutput=True)
  235. insns.extend(otherConn.compileConn(targetInsnClass=AwlInsn_U,
  236. inverted=False))
  237. # Assign the outputs.
  238. storeToTempConns = set()
  239. for conn in self.__allConnsOUT():
  240. for otherElem in self.sorted(conn.getConnectedElems(viaIn=True)):
  241. if otherElem.isType(FupCompiler_Elem.TYPE_OPERAND,
  242. FupCompiler_ElemOper.SUBTYPE_ASSIGN):
  243. insns.extend(otherElem.emitStore_VKE())
  244. else:
  245. storeToTempConns.add(conn)
  246. if storeToTempConns:
  247. storeToTempConns.add(self.MAIN_RESULT)
  248. insns.extend(self._storeToTemp("BOOL", AwlInsn_ASSIGN, storeToTempConns))
  249. # Handle ENO output.
  250. if conn_ENO.isConnected:
  251. if conn_EN.isConnected:
  252. # Add instruction: U BIE
  253. insns.append(self.newInsn_LOAD_BIE(AwlInsn_U))
  254. else:
  255. # Add instruction to set VKE
  256. insns.append(self.newInsn(AwlInsn_SET))
  257. # Add VKE assignment instruction.
  258. storeToTempConns = set()
  259. for otherElem in self.sorted(conn_ENO.getConnectedElems(viaIn=True)):
  260. if otherElem.isType(FupCompiler_Elem.TYPE_OPERAND,
  261. FupCompiler_ElemOper.SUBTYPE_ASSIGN):
  262. insns.extend(otherElem.emitStore_VKE())
  263. else:
  264. storeToTempConns.add(conn_ENO)
  265. if storeToTempConns:
  266. insns.extend(self._storeToTemp("BOOL", AwlInsn_ASSIGN,
  267. storeToTempConns))
  268. return insns
  269. class FupCompiler_ElemCmpEQI(FupCompiler_ElemCmp):
  270. """FUP compiler - Compare operation - ==I
  271. """
  272. ELEM_NAME = "==I"
  273. SUBTYPE = FupCompiler_ElemCmp.SUBTYPE_EQ_I
  274. CMP_INSN_CLASS = AwlInsn_EQ_I
  275. class FupCompiler_ElemCmpNEI(FupCompiler_ElemCmp):
  276. """FUP compiler - Compare operation - <>I
  277. """
  278. ELEM_NAME = "<>I"
  279. SUBTYPE = FupCompiler_ElemCmp.SUBTYPE_NE_I
  280. CMP_INSN_CLASS = AwlInsn_NE_I
  281. class FupCompiler_ElemCmpGTI(FupCompiler_ElemCmp):
  282. """FUP compiler - Compare operation - >I
  283. """
  284. ELEM_NAME = ">I"
  285. SUBTYPE = FupCompiler_ElemCmp.SUBTYPE_GT_I
  286. CMP_INSN_CLASS = AwlInsn_GT_I
  287. class FupCompiler_ElemCmpLTI(FupCompiler_ElemCmp):
  288. """FUP compiler - Compare operation - <I
  289. """
  290. ELEM_NAME = "<I"
  291. SUBTYPE = FupCompiler_ElemCmp.SUBTYPE_LT_I
  292. CMP_INSN_CLASS = AwlInsn_LT_I
  293. class FupCompiler_ElemCmpGEI(FupCompiler_ElemCmp):
  294. """FUP compiler - Compare operation - >=I
  295. """
  296. ELEM_NAME = ">=I"
  297. SUBTYPE = FupCompiler_ElemCmp.SUBTYPE_GE_I
  298. CMP_INSN_CLASS = AwlInsn_GE_I
  299. class FupCompiler_ElemCmpLEI(FupCompiler_ElemCmp):
  300. """FUP compiler - Compare operation - <=I
  301. """
  302. ELEM_NAME = "<=I"
  303. SUBTYPE = FupCompiler_ElemCmp.SUBTYPE_LE_I
  304. CMP_INSN_CLASS = AwlInsn_LE_I
  305. class FupCompiler_ElemCmpEQD(FupCompiler_ElemCmp):
  306. """FUP compiler - Compare operation - ==D
  307. """
  308. ELEM_NAME = "==D"
  309. SUBTYPE = FupCompiler_ElemCmp.SUBTYPE_EQ_D
  310. CMP_INSN_CLASS = AwlInsn_EQ_D
  311. class FupCompiler_ElemCmpNED(FupCompiler_ElemCmp):
  312. """FUP compiler - Compare operation - <>D
  313. """
  314. ELEM_NAME = "<>D"
  315. SUBTYPE = FupCompiler_ElemCmp.SUBTYPE_NE_D
  316. CMP_INSN_CLASS = AwlInsn_NE_D
  317. class FupCompiler_ElemCmpGTD(FupCompiler_ElemCmp):
  318. """FUP compiler - Compare operation - >D
  319. """
  320. ELEM_NAME = ">D"
  321. SUBTYPE = FupCompiler_ElemCmp.SUBTYPE_GT_D
  322. CMP_INSN_CLASS = AwlInsn_GT_D
  323. class FupCompiler_ElemCmpLTD(FupCompiler_ElemCmp):
  324. """FUP compiler - Compare operation - <D
  325. """
  326. ELEM_NAME = "<D"
  327. SUBTYPE = FupCompiler_ElemCmp.SUBTYPE_LT_D
  328. CMP_INSN_CLASS = AwlInsn_LT_D
  329. class FupCompiler_ElemCmpGED(FupCompiler_ElemCmp):
  330. """FUP compiler - Compare operation - >=D
  331. """
  332. ELEM_NAME = ">=D"
  333. SUBTYPE = FupCompiler_ElemCmp.SUBTYPE_GE_D
  334. CMP_INSN_CLASS = AwlInsn_GE_D
  335. class FupCompiler_ElemCmpLED(FupCompiler_ElemCmp):
  336. """FUP compiler - Compare operation - <=D
  337. """
  338. ELEM_NAME = "<=D"
  339. SUBTYPE = FupCompiler_ElemCmp.SUBTYPE_LE_D
  340. CMP_INSN_CLASS = AwlInsn_LE_D
  341. class FupCompiler_ElemCmpEQR(FupCompiler_ElemCmp):
  342. """FUP compiler - Compare operation - ==R
  343. """
  344. ELEM_NAME = "==R"
  345. SUBTYPE = FupCompiler_ElemCmp.SUBTYPE_EQ_R
  346. CMP_INSN_CLASS = AwlInsn_EQ_R
  347. class FupCompiler_ElemCmpNER(FupCompiler_ElemCmp):
  348. """FUP compiler - Compare operation - <>R
  349. """
  350. ELEM_NAME = "<>R"
  351. SUBTYPE = FupCompiler_ElemCmp.SUBTYPE_NE_R
  352. CMP_INSN_CLASS = AwlInsn_NE_R
  353. class FupCompiler_ElemCmpGTR(FupCompiler_ElemCmp):
  354. """FUP compiler - Compare operation - >R
  355. """
  356. ELEM_NAME = ">R"
  357. SUBTYPE = FupCompiler_ElemCmp.SUBTYPE_GT_R
  358. CMP_INSN_CLASS = AwlInsn_GT_R
  359. class FupCompiler_ElemCmpLTR(FupCompiler_ElemCmp):
  360. """FUP compiler - Compare operation - <R
  361. """
  362. ELEM_NAME = "<R"
  363. SUBTYPE = FupCompiler_ElemCmp.SUBTYPE_LT_R
  364. CMP_INSN_CLASS = AwlInsn_LT_R
  365. class FupCompiler_ElemCmpGER(FupCompiler_ElemCmp):
  366. """FUP compiler - Compare operation - >=R
  367. """
  368. ELEM_NAME = ">=R"
  369. SUBTYPE = FupCompiler_ElemCmp.SUBTYPE_GE_R
  370. CMP_INSN_CLASS = AwlInsn_GE_R
  371. class FupCompiler_ElemCmpLER(FupCompiler_ElemCmp):
  372. """FUP compiler - Compare operation - <=R
  373. """
  374. ELEM_NAME = "<=R"
  375. SUBTYPE = FupCompiler_ElemCmp.SUBTYPE_LE_R
  376. CMP_INSN_CLASS = AwlInsn_LE_R