check.py 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. #!/usr/bin/env python3
  2. import random
  3. from bitstring import Bits
  4. import subprocess
  5. import select
  6. import os
  7. import sys
  8. import threading
  9. import time
  10. dir_path = os.path.dirname(os.path.realpath(__file__))
  11. sys.path.append(dir_path + '/../../')
  12. from rpa_shell import load_cfg, rpa_server, cfg_file_path, RPAClient, Status, RPAConnection
  13. def RandomFix16Values(value_range, count):
  14. value_range_int = [int(value_range[0]*pow(2,16)),int(value_range[1]*pow(2,16))]
  15. values = []
  16. for i in range(0,count):
  17. values.append(random.randint(value_range_int[0], value_range_int[1]))
  18. return values
  19. def Hex32ToInt(hexstring):
  20. x = int(hexstring, 16)
  21. if(x > 0x7fffffff):
  22. x = -(0xffffffff - x + 1)
  23. return x
  24. def IntToHex32(i):
  25. b = Bits(int=i, length=32)
  26. return '0x' + str(b.hex)
  27. def ToFloat(values):
  28. fvalues = []
  29. for v in values:
  30. fvalues.append(float(v)*(1/2**16))
  31. return fvalues
  32. def GenerateInputFile(x):
  33. assert len(x) % 3 == 0
  34. output = ""
  35. output += "process " +str(int(len(x)/3))+"\n"
  36. for i in x:
  37. output += IntToHex32(i) + "\n"
  38. output += "check_speed\n"
  39. output += "exit\n" #don't forget the new line
  40. return output
  41. def RunRef():
  42. output_values = []
  43. output_lines = os.popen("cat instructions | "+dir_path+"/ref").read().split()
  44. for line in output_lines[:-1]:
  45. output_values.append(Hex32ToInt(line))
  46. return (output_values, int(output_lines[-1], 0))
  47. def RunNios():
  48. output_values = []
  49. output_lines = os.popen("nios2-terminal -q < instructions").read().split()
  50. for line in output_lines[:-1]:
  51. output_values.append(Hex32ToInt(line))
  52. return (output_values, int(output_lines[-1], 0))
  53. def RunRemote():
  54. cfg = load_cfg(cfg_file_path)
  55. hw_output = ''
  56. has_place = threading.Barrier(2)
  57. check_done = threading.Lock()
  58. check_done.acquire(blocking=False)
  59. def download_sof():
  60. """ Create new rpa-master connection if necessary then run quartus_pgm
  61. and keep it open (necessary for intelFPGA-light with IP-cores)
  62. """
  63. nonlocal hw_output
  64. host_acquired = False
  65. c = RPAClient(rpa_server, cfg["username"], identity=cfg["identity"])
  66. status = c.ConnectionStatus()
  67. if status == Status.ASSIGNED:
  68. connection = c.Connection
  69. elif status == Status.NOT_ASSIGNED:
  70. c.RequestHost()
  71. # TODO: if acquiring host takes long (no error) this thread
  72. # will prevent the process to stop when the timeout is
  73. # reached in main thread
  74. c.WaitForHost()
  75. host_acquired = True
  76. connection = c.Connection
  77. else:
  78. print("Failed to lock place")
  79. exit(1)
  80. # sync with main thread that rpa-lock was successful
  81. try:
  82. has_place.wait()
  83. except threading.BrokenBarrierError:
  84. if host_acquired:
  85. c.ReleaseHost()
  86. exit(1)
  87. ssh_cmd = connection.CreateSSHCommand('quartus_pgm -mjtag \'-o"p;.rpa_shell/hw.sof"\'', ssh_args='-tt')
  88. print('-- Programming Hardware --')
  89. p = subprocess.Popen(ssh_cmd, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
  90. while True:
  91. readable, writable, error = select.select([p.stdout], [], [], 0.5)
  92. if readable:
  93. outp = p.stdout.read1(4096).decode()
  94. print(outp, end='')
  95. hw_output += outp
  96. else: # timeout, no data available
  97. pass
  98. if check_done.acquire(blocking=False):
  99. p.terminate()
  100. if host_acquired:
  101. c.ReleaseHost()
  102. break
  103. output_values = []
  104. client = RPAClient(rpa_server, cfg["username"], identity=cfg["identity"])
  105. t = threading.Thread(target=download_sof)
  106. t.start()
  107. # check if downloader was able to get seat-lock
  108. try:
  109. has_place.wait(timeout=10)
  110. except threading.BrokenBarrierError:
  111. print('Waiting for a seat took too long, exiting!')
  112. check_done.release()
  113. exit(1)
  114. time_wait = 0.5
  115. timeout = 10
  116. time_waited = 0
  117. while True:
  118. if 'Error' in hw_output:
  119. print()
  120. print('Another program is using the jtag connection already')
  121. print('Most likely there is still an instance of nios2-terminal or quartus_pgm running')
  122. print("If this is not intended, run './rpa_shell.py \"killall quartus_pgm\"'")
  123. print("or './rpa_shell.py \"killall nios2-terminal\"'")
  124. check_done.release()
  125. exit(1)
  126. elif 'Ended Programmer' in hw_output:
  127. print()
  128. print('Hardware has been programmend sucessfully!')
  129. break
  130. time.sleep(time_wait)
  131. time_waited += time_wait
  132. if time_waited > timeout:
  133. print()
  134. print('Took too long to to program hardware, exiting!')
  135. check_done.release()
  136. exit(1)
  137. # check for failure
  138. status = client.ConnectionStatus()
  139. if status != Status.ASSIGNED:
  140. print("No master session established!")
  141. print("Most likely you had another rpa-master connection open which just timed out")
  142. check_done.release()
  143. exit(1)
  144. connection = client.Connection
  145. ssh_cmd = connection.CreateSSHCommand("/opt/quartus_18.1/nios2eds/nios2_command_shell.sh nios2-download -g .rpa_shell/fw.elf")
  146. p = subprocess.run(ssh_cmd, shell=True)
  147. ssh_cmd = connection.CreateSSHCommand("/opt/quartus_18.1/nios2eds/nios2_command_shell.sh nios2-terminal -q")
  148. output_lines = ''
  149. with open(dir_path + "/../instructions") as instr_file:
  150. p = subprocess.run(ssh_cmd, shell=True, input=instr_file.read().encode(), stdout=subprocess.PIPE)
  151. output_lines = p.stdout.decode().splitlines()
  152. # checking complete, stop quartus_pgm
  153. check_done.release()
  154. for line in output_lines[:-1]:
  155. output_values.append(Hex32ToInt(line))
  156. return (output_values, int(output_lines[-1], 0))
  157. value_range = [-32,31]
  158. x = RandomFix16Values(value_range, 32*3)
  159. with open("instructions", 'w') as instructions:
  160. instructions.write(GenerateInputFile(x))
  161. (out_ref, pc_runtime) = RunRef()
  162. if(len(sys.argv)>1 and sys.argv[1]=="remote"):
  163. (out_nios, nios_runtime) = RunRemote()
  164. else:
  165. (out_nios, nios_runtime) = RunNios()
  166. print("input".rjust(10, ' ') + " " + "Nios II".rjust(10, ' ') + " " + "Ref".rjust(10, ' '))
  167. test_passed = True
  168. for i in range(0, len(out_ref)):
  169. val_nios = out_nios[i]
  170. val_ref = out_ref[i]
  171. int_error = abs(val_nios - val_ref)
  172. line = IntToHex32(x[i]) + " " + IntToHex32(val_nios) + " " + IntToHex32(val_ref)
  173. if (int_error > 1):
  174. line += " <-- ERROR (inaccuracy)!"
  175. test_passed = False
  176. print(line)
  177. if(i%3==2):
  178. print('-')
  179. if (test_passed):
  180. print(">>> value check PASSED <<<")
  181. else:
  182. print(">>> value check FAILED <<<")
  183. if (nios_runtime < 7000):
  184. print("runtime = " + str(nios_runtime) + " cycles")
  185. print(">>> speed check PASSED <<<")
  186. else:
  187. print("Your solution is too slow! runtime = " + str(nios_runtime) +" cycles")
  188. print(">>> speed check FAILED <<<")