main.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444
  1. # -*- coding: utf-8 -*-
  2. #
  3. # AWL simulator - LinuxCNC HAL interface
  4. #
  5. # Copyright 2013-2020 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.cython_support cimport * #@cy
  23. from awlsim.common.compat import *
  24. from awlsim.common.util import *
  25. from awlsim.common.exceptions import *
  26. #from awlsimhw_linuxcnc.main cimport * #@cy
  27. from awlsim.common.datatypehelpers import * #+cimport
  28. from awlsim.core.hardware_params import *
  29. from awlsim.core.hardware import * #+cimport
  30. from awlsim.core.operators import * #+cimport
  31. from awlsim.core.offset import * #+cimport
  32. from awlsim.core.cpu import * #+cimport
  33. class Sig(object):
  34. def __init__(self, hal, halName, address, offset):
  35. self.hal = hal
  36. self.halName = halName
  37. self.address = address
  38. self.offset = offset
  39. def __str__(self):
  40. return "awlsim.%s" % self.halName
  41. class SigBit(Sig):
  42. width = 1
  43. def __init__(self, hal, halName, address, offset, bitOffset):
  44. Sig.__init__(self, hal, halName, address, offset)
  45. self.bitOffset = bitOffset
  46. self.setMask = 1 << bitOffset
  47. self.clrMask = (1 << bitOffset) ^ 0xFF
  48. def readInput(self, destBuf, toOffset):
  49. if self.hal[self.halName]:
  50. destBuf[toOffset] |= self.setMask
  51. else:
  52. destBuf[toOffset] &= self.clrMask
  53. def writeOutput(self, srcBuf, fromOffset):
  54. self.hal[self.halName] = (srcBuf[fromOffset] >> self.bitOffset) & 1
  55. class SigU8(Sig):
  56. width = 8
  57. def readInput(self, destBuf, toOffset):
  58. destBuf[toOffset] = self.hal[self.halName] & 0xFF
  59. def writeOutput(self, srcBuf, fromOffset):
  60. self.hal[self.halName] = srcBuf[fromOffset] & 0xFF
  61. class SigU16(Sig):
  62. width = 16
  63. def readInput(self, destBuf, toOffset):
  64. word = self.hal[self.halName] & 0xFFFF
  65. destBuf[toOffset] = (word >> 8) & 0xFF
  66. destBuf[toOffset + 1] = word & 0xFF
  67. def writeOutput(self, srcBuf, fromOffset):
  68. word = ((srcBuf[fromOffset] << 8) |
  69. srcBuf[fromOffset + 1])
  70. self.hal[self.halName] = word & 0xFFFF
  71. class SigS16(Sig):
  72. width = 16
  73. def readInput(self, destBuf, toOffset):
  74. word = self.hal[self.halName] & 0xFFFF
  75. destBuf[toOffset] = (word >> 8) & 0xFF
  76. destBuf[toOffset + 1] = word & 0xFF
  77. def writeOutput(self, srcBuf, fromOffset):
  78. word = ((srcBuf[fromOffset] << 8) |
  79. srcBuf[fromOffset + 1])
  80. self.hal[self.halName] = wordToSignedPyInt(word)
  81. class SigU31(Sig):
  82. width = 32 # U31 memory width is 32 bit
  83. def readInput(self, destBuf, toOffset):
  84. dword = self.hal[self.halName] & 0x7FFFFFFF
  85. destBuf[toOffset] = (dword >> 24) & 0xFF
  86. destBuf[toOffset + 1] = (dword >> 16) & 0xFF
  87. destBuf[toOffset + 2] = (dword >> 8) & 0xFF
  88. destBuf[toOffset + 3] = dword & 0xFF
  89. def writeOutput(self, srcBuf, fromOffset):
  90. dword = ((srcBuf[fromOffset] << 24) |
  91. (srcBuf[fromOffset + 1] << 16) |
  92. (srcBuf[fromOffset + 2] << 8) |
  93. srcBuf[fromOffset + 3])
  94. self.hal[self.halName] = dword & 0x7FFFFFFF
  95. class SigS32(Sig):
  96. width = 32
  97. def readInput(self, destBuf, toOffset):
  98. dword = self.hal[self.halName] & 0xFFFFFFFF
  99. destBuf[toOffset] = (dword >> 24) & 0xFF
  100. destBuf[toOffset + 1] = (dword >> 16) & 0xFF
  101. destBuf[toOffset + 2] = (dword >> 8) & 0xFF
  102. destBuf[toOffset + 3] = dword & 0xFF
  103. def writeOutput(self, srcBuf, fromOffset):
  104. dword = ((srcBuf[fromOffset] << 24) |
  105. (srcBuf[fromOffset + 1] << 16) |
  106. (srcBuf[fromOffset + 2] << 8) |
  107. srcBuf[fromOffset + 3])
  108. self.hal[self.halName] = dwordToSignedPyInt(dword)
  109. class SigFloat(Sig):
  110. width = 32
  111. def readInput(self, destBuf, toOffset):
  112. dword = pyFloatToDWord(self.hal[self.halName])
  113. destBuf[toOffset] = (dword >> 24) & 0xFF
  114. destBuf[toOffset + 1] = (dword >> 16) & 0xFF
  115. destBuf[toOffset + 2] = (dword >> 8) & 0xFF
  116. destBuf[toOffset + 3] = dword & 0xFF
  117. def writeOutput(self, srcBuf, fromOffset):
  118. dword = ((srcBuf[fromOffset] << 24) |
  119. (srcBuf[fromOffset + 1] << 16) |
  120. (srcBuf[fromOffset + 2] << 8) |
  121. srcBuf[fromOffset + 3])
  122. self.hal[self.halName] = dwordToPyFloat(dword)
  123. class HardwareInterface_LinuxCNC(AbstractHardwareInterface): #+cdef
  124. name = "LinuxCNC"
  125. description = "LinuxCNC hardware support.\nhttp://linuxcnc.org/"
  126. paramDescs = [
  127. HwParamDesc_int("inputSize",
  128. description="Input area size",
  129. defaultValue=32,
  130. mandatory=True),
  131. HwParamDesc_int("outputSize",
  132. description="Output area size",
  133. defaultValue=32,
  134. mandatory=True),
  135. ]
  136. def __init__(self, sim, parameters={}):
  137. AbstractHardwareInterface.__init__(self,
  138. sim=sim,
  139. parameters=parameters)
  140. self.linuxCNC_initialized = False
  141. def __createHalPins(self):
  142. """Create the LinuxCNC HAL pins.
  143. """
  144. HAL_BIT, HAL_U32, HAL_S32, HAL_FLOAT = (
  145. linuxCNCHal.HAL_BIT, linuxCNCHal.HAL_U32,
  146. linuxCNCHal.HAL_S32, linuxCNCHal.HAL_FLOAT)
  147. HAL_IN, HAL_OUT, HAL_RO, HAL_RW = (
  148. linuxCNCHal.HAL_IN, linuxCNCHal.HAL_OUT,
  149. linuxCNCHal.HAL_RO, linuxCNCHal.HAL_RW)
  150. def newpin(name, *args):
  151. try:
  152. try:
  153. self.halComponent[name]
  154. except AttributeError:
  155. self.halComponent.newpin(name, *args)
  156. except linuxCNCHal.error as e:
  157. printWarning("Failed to create HAL pin '%s'. "
  158. "Please restart LinuxCNC." % name)
  159. def newparam(name, *args):
  160. try:
  161. try:
  162. self.halComponent[name]
  163. except AttributeError:
  164. self.halComponent.newparam(name, *args)
  165. except linuxCNCHal.error as e:
  166. printWarning("Failed to create HAL param '%s'. "
  167. "Please restart LinuxCNC." % name)
  168. # Create the input pins
  169. for i in range(self.inputAddressBase, self.inputAddressBase + self.inputSize):
  170. offset = i - self.inputAddressBase
  171. for bit in range(8):
  172. newpin("input.bit.%d.%d" % (i, bit), HAL_BIT, HAL_IN)
  173. newparam("input.bit.%d.%d.active" % (i, bit), HAL_BIT, HAL_RW)
  174. newpin("input.u8.%d" % i, HAL_U32, HAL_IN)
  175. newparam("input.u8.%d.active" % i, HAL_BIT, HAL_RW)
  176. if i % 2:
  177. continue
  178. if self.inputSize - offset < 2:
  179. continue
  180. newpin("input.u16.%d" % i, HAL_U32, HAL_IN)
  181. newparam("input.u16.%d.active" % i, HAL_BIT, HAL_RW)
  182. newpin("input.s16.%d" % i, HAL_S32, HAL_IN)
  183. newparam("input.s16.%d.active" % i, HAL_BIT, HAL_RW)
  184. if self.inputSize - offset < 4:
  185. continue
  186. newpin("input.u31.%d" % i, HAL_U32, HAL_IN)
  187. newparam("input.u31.%d.active" % i, HAL_BIT, HAL_RW)
  188. newpin("input.s32.%d" % i, HAL_S32, HAL_IN)
  189. newparam("input.s32.%d.active" % i, HAL_BIT, HAL_RW)
  190. newpin("input.float.%d" % i, HAL_FLOAT, HAL_IN)
  191. newparam("input.float.%d.active" % i, HAL_BIT, HAL_RW)
  192. # Create the output pins
  193. for i in range(self.outputAddressBase, self.outputAddressBase + self.outputSize):
  194. offset = i - self.outputAddressBase
  195. for bit in range(8):
  196. newpin("output.bit.%d.%d" % (i, bit), HAL_BIT, HAL_OUT)
  197. newparam("output.bit.%d.%d.active" % (i, bit), HAL_BIT, HAL_RW)
  198. newpin("output.u8.%d" % i, HAL_U32, HAL_OUT)
  199. newparam("output.u8.%d.active" % i, HAL_BIT, HAL_RW)
  200. if i % 2:
  201. continue
  202. if self.outputSize - offset < 2:
  203. continue
  204. newpin("output.u16.%d" % i, HAL_U32, HAL_OUT)
  205. newparam("output.u16.%d.active" % i, HAL_BIT, HAL_RW)
  206. newpin("output.s16.%d" % i, HAL_S32, HAL_OUT)
  207. newparam("output.s16.%d.active" % i, HAL_BIT, HAL_RW)
  208. if self.outputSize - offset < 4:
  209. continue
  210. newpin("output.u31.%d" % i, HAL_U32, HAL_OUT)
  211. newparam("output.u31.%d.active" % i, HAL_BIT, HAL_RW)
  212. newpin("output.s32.%d" % i, HAL_S32, HAL_OUT)
  213. newparam("output.s32.%d.active" % i, HAL_BIT, HAL_RW)
  214. newpin("output.float.%d" % i, HAL_FLOAT, HAL_OUT)
  215. newparam("output.float.%d.active" % i, HAL_BIT, HAL_RW)
  216. newparam("config.ready", HAL_BIT, HAL_RW)
  217. printInfo("Mapped AWL/STL input area: P#E %d.0 BYTE %d" % (
  218. self.inputAddressBase, self.inputSize))
  219. printInfo("Mapped AWL/STL output area: P#A %d.0 BYTE %d" % (
  220. self.outputAddressBase, self.outputSize))
  221. def doStartup(self):
  222. global linuxCNCHalComponent
  223. global linuxCNCHalComponentReady
  224. if not self.linuxCNC_initialized:
  225. if linuxCNCHalComponent is None:
  226. self.raiseException("LinuxCNC HAL component not set.")
  227. self.halComponent = linuxCNCHalComponent
  228. # Get parameters
  229. self.inputSize = self.getParamValueByName("inputSize")
  230. self.outputSize = self.getParamValueByName("outputSize")
  231. self.__createHalPins()
  232. self.__configDone = False
  233. # Signal LinuxCNC that we are ready.
  234. if not linuxCNCHalComponentReady:
  235. self.halComponent.ready()
  236. linuxCNCHalComponentReady = True
  237. self.linuxCNC_initialized = True
  238. def doShutdown(self):
  239. pass
  240. #TODO find overlappings
  241. def __buildTable(self, baseName, addressBase, size):
  242. tab = []
  243. addr2sig = {}
  244. def isActive(name):
  245. activeName = "%s.active" % name
  246. try:
  247. return self.halComponent[activeName]
  248. except AttributeError:
  249. printWarning("Pin '%s' cannot be used without restart. "
  250. "Please restart LinuxCNC." % name)
  251. return False
  252. def add(sig):
  253. tab.append(sig)
  254. if not isinstance(sig, SigBit):
  255. addr2sig[sig.address] = sig
  256. printInfo("Active HAL pin: %s" % str(sig))
  257. for address in range(addressBase, addressBase + size):
  258. offset = address - addressBase
  259. for bitNr in range(8):
  260. if isActive("%s.bit.%d.%d" % (baseName, address, bitNr)):
  261. add(SigBit(self.halComponent,
  262. "%s.bit.%d.%d" % (baseName, address, bitNr),
  263. address, offset, bitNr))
  264. if isActive("%s.u8.%d" % (baseName, address)):
  265. add(SigU8(self.halComponent,
  266. "%s.u8.%d" % (baseName, address),
  267. address, offset))
  268. if address % 2:
  269. continue
  270. if size - offset < 2:
  271. continue
  272. if isActive("%s.u16.%d" % (baseName, address)):
  273. add(SigU16(self.halComponent,
  274. "%s.u16.%d" % (baseName, address),
  275. address, offset))
  276. if isActive("%s.s16.%d" % (baseName, address)):
  277. add(SigS16(self.halComponent,
  278. "%s.s16.%d" % (baseName, address),
  279. address, offset))
  280. if size - offset < 4:
  281. continue
  282. if isActive("%s.u31.%d" % (baseName, address)):
  283. add(SigU31(self.halComponent,
  284. "%s.u31.%d" % (baseName, address),
  285. address, offset))
  286. if isActive("%s.s32.%d" % (baseName, address)):
  287. add(SigS32(self.halComponent,
  288. "%s.s32.%d" % (baseName, address),
  289. address, offset))
  290. if isActive("%s.float.%d" % (baseName, address)):
  291. add(SigFloat(self.halComponent,
  292. "%s.float.%d" % (baseName, address),
  293. address, offset))
  294. return tab, addr2sig
  295. def __tryBuildConfig(self):
  296. if not self.halComponent["config.ready"]:
  297. return False
  298. self.__activeInputs, self.__activeInputsAddr2Sig = self.__buildTable(
  299. "input",
  300. self.inputAddressBase,
  301. self.inputSize)
  302. self.__activeOutputs, self.__activeOutputsAddr2Sig = self.__buildTable(
  303. "output",
  304. self.outputAddressBase,
  305. self.outputSize)
  306. self.__configDone = True
  307. printInfo("HAL configuration done")
  308. return True
  309. def readInputs(self): #+cdef
  310. if not self.__configDone:
  311. if not self.__tryBuildConfig():
  312. return
  313. data = bytearray(self.inputSize)
  314. for sig in self.__activeInputs:
  315. sig.readInput(data, sig.offset)
  316. self.sim.cpu.storeInputRange(self.inputAddressBase, data)
  317. def writeOutputs(self): #+cdef
  318. if not self.__configDone:
  319. return
  320. data = self.sim.cpu.fetchOutputRange(self.outputAddressBase,
  321. self.outputSize)
  322. for sig in self.__activeOutputs:
  323. sig.writeOutput(data, sig.offset)
  324. def directReadInput(self, accessWidth, accessOffset): #@nocy
  325. #@cy cdef bytearray directReadInput(self, uint32_t accessWidth, uint32_t accessOffset):
  326. if not self.__configDone:
  327. if not self.__tryBuildConfig():
  328. return bytearray()
  329. try:
  330. sig = self.__activeInputsAddr2Sig[accessOffset]
  331. except KeyError as e:
  332. return bytearray()
  333. if accessWidth != sig.width:
  334. self.raiseException("Directly accessing input at I %d.0 "
  335. "with width %d bit, but only %d bit wide "
  336. "accesses are supported." % (
  337. accessOffset, accessWidth, sig.width))
  338. data = bytearray(accessWidth // 8)
  339. sig.readInput(data, 0)
  340. return data
  341. def directWriteOutput(self, accessWidth, accessOffset, data): #@nocy
  342. #@cy cdef ExBool_t directWriteOutput(self, uint32_t accessWidth, uint32_t accessOffset, bytearray data) except ExBool_val:
  343. if not self.__configDone:
  344. if not self.__tryBuildConfig():
  345. return False
  346. try:
  347. sig = self.__activeOutputsAddr2Sig[accessOffset]
  348. except KeyError as e:
  349. return False
  350. if accessWidth != sig.width:
  351. self.raiseException("Directly accessing output at Q %d.0 "
  352. "with width %d bit, but only %d bit wide "
  353. "accesses are supported." % (
  354. accessOffset, accessWidth, sig.width))
  355. sig.writeOutput(data, 0)
  356. return True
  357. # LinuxCNC HAL component singleton.
  358. linuxCNCHal = None
  359. linuxCNCHalComponent = None
  360. linuxCNCHalComponentReady = False
  361. def setLinuxCNCHalComponentSingleton(newHal, newHalComponent):
  362. global linuxCNCHal
  363. global linuxCNCHalComponent
  364. global linuxCNCHalComponentReady
  365. if linuxCNCHalComponent is not None:
  366. printWarning("linuxCNCHalComponent is already set to "
  367. "%s (new = %s)" % (
  368. str(linuxCNCHalComponent),
  369. str(newHalComponent)))
  370. linuxCNCHal = newHal
  371. linuxCNCHalComponent = newHalComponent
  372. linuxCNCHalComponentReady = False
  373. # Module entry point
  374. HardwareInterface = HardwareInterface_LinuxCNC