tracerVCD.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. # Copyright 2014 Modelling, Simulation and Design Lab (MSDL) at
  2. # McGill University and the University of Antwerp (http://msdl.cs.mcgill.ca/)
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License");
  5. # you may not use this file except in compliance with the License.
  6. # You may obtain a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS,
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. # See the License for the specific language governing permissions and
  14. # limitations under the License.
  15. from pypdevs.tracers.tracerBase import BaseTracer
  16. from pypdevs.util import runTraceAtController, toStr, DEVSException
  17. from math import floor
  18. class VCDRecord(object):
  19. """
  20. A data class to keep information about VCD variables
  21. """
  22. def __init__(self, identifier_nr, model_name, port_name):
  23. """
  24. Constructor.
  25. :param identifier_nr: the actual identifier
  26. :param model_name: name of the model
  27. :param port_name: name of the port
  28. """
  29. self.model_name = model_name
  30. self.port_name = port_name
  31. self.identifier = identifier_nr
  32. # BitSize cannot be given since there is no event created on this wire
  33. # Set to None to make sure that it will be changed
  34. self.bit_size = None
  35. class TracerVCD(BaseTracer):
  36. """
  37. A tracer for VCD output. Should only be used for binary signals!
  38. """
  39. def __init__(self, uid, server, filename):
  40. """
  41. Constructor
  42. :param uid: the UID of the tracer
  43. :param server: the server to make remote requests on
  44. :param filename: file to save the trace to
  45. """
  46. super(TracerVCD, self).__init__(uid, server)
  47. if server.getName() == 0:
  48. self.filename = filename
  49. else:
  50. self.filename = None
  51. def startTracer(self, recover):
  52. """
  53. Starts up the tracer
  54. :param recover: whether or not this is a recovery call (so whether or not the file should be appended to)
  55. """
  56. if self.filename is None:
  57. # Nothing to do here as we aren't the controller
  58. return
  59. elif recover:
  60. self.vcd_file = open(self.filename, 'ab+')
  61. else:
  62. self.vcd_file = open(self.filename, 'wb')
  63. self.vcd_var_list = []
  64. self.vcd_prevtime = 0.0
  65. self.vcdHeader()
  66. def stopTracer(self):
  67. """
  68. Stop the tracer
  69. """
  70. self.vcd_file.flush()
  71. def vcdHeader(self):
  72. """
  73. Create the VCD file header by doing calls to the coordinator
  74. """
  75. self.vcd_file.write(("$date\n").encode())
  76. from datetime import date
  77. self.vcd_file.write(("\t" + date.today().isoformat() + "\n" +
  78. "$end\n" +
  79. "$version\n" +
  80. "\tPyDEVS VCD export\n" +
  81. "$end\n" +
  82. "$comment\n" +
  83. "\tGenerated from DEVS-code\n" +
  84. "$end\n" +
  85. "$timescale 1ns $end\n").encode())
  86. variables = self.server.getProxy(0).getVCDVariables()
  87. counter = 0
  88. for i in variables:
  89. model, port = i
  90. self.vcd_var_list.append(VCDRecord(counter, model, port))
  91. counter += 1
  92. modelList = []
  93. for i in range(len(self.vcd_var_list)):
  94. if self.vcd_var_list[i].model_name not in modelList:
  95. modelList.append(self.vcd_var_list[i].model_name)
  96. for module in modelList:
  97. self.vcd_file.write(("$scope %s %s $end\n" % (module, module)).encode())
  98. for var in range(len(self.vcd_var_list)):
  99. if self.vcd_var_list[var].model_name == module:
  100. self.vcd_file.write("$var wire ".encode())
  101. if self.vcd_var_list[var].bit_size is None:
  102. self.vcd_file.write("1".encode())
  103. else:
  104. bitsize = str(self.vcd_var_list[var].bit_size)
  105. self.vcd_file.write(bitsize.encode())
  106. self.vcd_file.write((" %s %s $end\n"
  107. % (self.vcd_var_list[var].identifier,
  108. self.vcd_var_list[var].port_name)).encode())
  109. self.vcd_file.write(("$upscope $end\n").encode())
  110. self.vcd_file.write(("$enddefinitions $end\n").encode())
  111. self.vcd_file.write(("$dumpvars \n").encode())
  112. for var in range(len(self.vcd_var_list)):
  113. self.vcd_file.write(("b").encode())
  114. if self.vcd_var_list[var].bit_size is None:
  115. # The wire is a constant error signal, so the wire is never used
  116. # Assume 1 bit long
  117. self.vcd_file.write(("z").encode())
  118. else:
  119. for i in range(self.vcd_var_list[var].bit_size):
  120. self.vcd_file.write(("z").encode())
  121. self.vcd_file.write((" %s\n" % self.vcd_var_list[var].identifier).encode())
  122. self.vcd_file.write(("$end\n").encode())
  123. def trace(self, model_name, time, port_name, vcd_state):
  124. """
  125. Trace a VCD entry
  126. :param model_name: name of the model
  127. :param time: time at which transition happened
  128. :param port_name: name of the port
  129. :param vcd_state: state to trace on the specified port
  130. """
  131. # Check if the signal is a valid binary signal
  132. for i in range(len(vcd_state)):
  133. if (i == 0):
  134. if vcd_state[i] == 'b':
  135. continue
  136. else:
  137. raise DEVSException(("Port %s in model %s does not carry " +
  138. "a binary signal\n" +
  139. "VCD exports require a binary signal, " +
  140. "not: %s") % (port_name, model_name, vcd_state))
  141. char = vcd_state[i]
  142. if char not in ["0", "1", "E", "x"]:
  143. raise DEVSException(("Port %s in model %s does not carry " +
  144. "a binary signal\n" +
  145. "VCD exports require a binary signal, " +
  146. "not: %s") % (port_name, model_name, vcd_state))
  147. # Find the identifier of this wire
  148. for i in range(len(self.vcd_var_list)):
  149. if (self.vcd_var_list[i].model_name == model_name and
  150. self.vcd_var_list[i].port_name == port_name):
  151. identifier = str(self.vcd_var_list[i].identifier)
  152. break
  153. # If the bit_size is not yet defined, define it now
  154. if self.vcd_var_list[i].bit_size is None:
  155. self.vcd_var_list[i].bit_size = len(vcd_state)-1
  156. elif self.vcd_var_list[i].bit_size != len(vcd_state) - 1:
  157. raise DEVSException("Wire has changing bitsize!\n" +
  158. "You are probably not using bit encoding!")
  159. # Now we have to convert between logisim and VCD notation
  160. vcd_state = vcd_state.replace('x', 'z')
  161. vcd_state = vcd_state.replace('E', 'x')
  162. # identifier will be defined, otherwise the record was not in the list
  163. if time > self.vcd_prevtime:
  164. # Convert float to integer without losing precision
  165. # ex. 5.0 --> 50, 5.5 --> 55
  166. t = time[0]
  167. vcd_time = int(str(int(floor(t))) +
  168. str(int(t - floor(t)) * (len(str(t)) - 2)))
  169. if (self.vcd_prevtime != vcd_time):
  170. # The time has passed, so add a new VCD header
  171. self.vcd_file.write(("#" + str(vcd_time) + "\n").encode())
  172. self.vcd_prevtime = vcd_time
  173. self.vcd_file.write((vcd_state + " " + identifier + "\n").encode())
  174. def traceConfluent(self, aDEVS):
  175. """
  176. The trace functionality for VCD output at a confluent transition
  177. :param aDEVS: the model that transitioned
  178. """
  179. name = toStr(aDEVS.getModelFullName())
  180. for I in range(len(aDEVS.IPorts)):
  181. port_name = aDEVS.IPorts[I].getPortName()
  182. signal_bag = aDEVS.my_input.get(aDEVS.IPorts[I], [])
  183. if signal_bag is not None:
  184. for port_signal in signal_bag:
  185. runTraceAtController(self.server,
  186. self.uid,
  187. aDEVS,
  188. [name,
  189. aDEVS.time_last,
  190. toStr(port_name),
  191. toStr(port_signal)])
  192. for I in range(len(aDEVS.OPorts) ):
  193. if aDEVS.OPorts[I] in aDEVS.my_output:
  194. port_name = aDEVS.OPorts[I].getPortName()
  195. signal_bag = aDEVS.my_output.get(aDEVS.OPorts[I], [])
  196. if signal_bag is not None:
  197. for port_signal in signal_bag:
  198. runTraceAtController(self.server,
  199. self.uid,
  200. aDEVS,
  201. [name,
  202. aDEVS.time_last,
  203. toStr(port_name),
  204. toStr(port_signal)])
  205. def traceInternal(self, aDEVS):
  206. """
  207. The trace functionality for VCD output at an internal transition
  208. :param aDEVS: the model that transitioned
  209. """
  210. name = toStr(aDEVS.getModelFullName())
  211. for I in range(0, len(aDEVS.OPorts) ):
  212. if aDEVS.OPorts[I] in aDEVS.my_output:
  213. port_name = aDEVS.OPorts[I].getPortName()
  214. signal_bag = aDEVS.my_output.get(aDEVS.OPorts[I], [])
  215. if signal_bag is not None:
  216. for port_signal in signal_bag:
  217. runTraceAtController(self.server,
  218. self.uid,
  219. aDEVS,
  220. [name,
  221. aDEVS.time_last,
  222. toStr(port_name),
  223. toStr(port_signal)])
  224. def traceExternal(self, aDEVS):
  225. """
  226. The trace functionality for VCD output at an external transition
  227. :param aDEVS: the model that transitioned
  228. """
  229. name = toStr(aDEVS.getModelFullName())
  230. for I in range(len(aDEVS.IPorts)):
  231. port_name = aDEVS.IPorts[I].getPortName()
  232. signal_bag = aDEVS.my_input.get(aDEVS.IPorts[I], [])
  233. if signal_bag is not None:
  234. for port_signal in signal_bag:
  235. runTraceAtController(self.server,
  236. self.uid,
  237. aDEVS,
  238. [name,
  239. aDEVS.time_last,
  240. toStr(port_name),
  241. toStr(port_signal)])
  242. def traceInit(self, aDEVS, t):
  243. """
  244. The trace functionality for VCD output at initialisation
  245. :param aDEVS: the model that was initialized
  246. :param t: time at which it should be traced
  247. """
  248. pass