2 #******************************************************************************
4 # Copyright (c) 2019 Intel.
6 # Licensed under the Apache License, Version 2.0 (the "License");
7 # you may not use this file except in compliance with the License.
8 # You may obtain a copy of the License at
10 # http://www.apache.org/licenses/LICENSE-2.0
12 # Unless required by applicable law or agreed to in writing, software
13 # distributed under the License is distributed on an "AS IS" BASIS,
14 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 # See the License for the specific language governing permissions and
16 # limitations under the License.
18 #******************************************************************************/
20 """This script run test cases with O-DU and O-RU
29 from itertools import dropwhile
30 from datetime import datetime
34 # 5MHz 10MHz 15MHz 20 MHz 25 MHz 30 MHz 40 MHz 50MHz 60 MHz 70 MHz 80 MHz 90 MHz 100 MHz
35 [25, 52, 79, 106, 133, 160, 216, 270, 0, 0, 0, 0, 0], # Numerology 0 (15KHz)
36 [11, 24, 38, 51, 65, 78, 106, 133, 162, 0, 217, 245, 273], # Numerology 1 (30KHz)
37 [0, 11, 18, 24, 31, 38, 51, 65, 79, 0, 107, 121, 135] # Numerology 2 (60KHz)
41 # 50Mhz 100MHz 200MHz 400MHz
42 [66, 132, 264, 0], # Numerology 2 (60KHz)
43 [32, 66, 132, 264] # Numerology 3 (120KHz)
47 nRChBwOptions_keys = ['5','10','15','20', '25', '30', '40', '50', '60','70', '80', '90', '100', '200', '400']
48 nRChBwOptions_values = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14]
49 nRChBwOptions = dict(zip(nRChBwOptions_keys, nRChBwOptions_values))
51 nRChBwOptions_keys_mu2and3 = ['50', '100', '200', '400']
52 nRChBwOptions_values_mu2and3 = [0,1,2,3]
53 nRChBwOptions_mu2and3 = dict(zip(nRChBwOptions_keys_mu2and3, nRChBwOptions_values_mu2and3))
55 # table of all test cases
56 # (cat, mu, bw, test case)
57 all_test_cases = [(0, 0, 5, 0),
64 """ all_test_cases = [(1, 1, 100, 0),
71 #(1, 1, 100, 106), 25G not enough
74 #(1, 1, 100, 109), 25G not enough
76 #(1, 1, 100, 202), 25G not enough
82 #(1, 1, 100, 212), 25G not enough
89 dic_dir = dict({0:'DL', 1:'UL'})
90 dic_xu = dict({0:'o-du', 1:'o-ru'})
92 def init_logger(console_level, logfile_level):
93 """Initializes console and logfile logger with given logging levels"""
95 logging.basicConfig(filename="runtests.log",
97 format="%(asctime)s: %(levelname)s: %(message)s",
100 logger = logging.getLogger()
101 handler = logging.StreamHandler()
102 handler.setLevel(console_level)
103 formatter = logging.Formatter("%(levelname)s: %(message)s")
104 handler.setFormatter(formatter)
105 logger.addHandler(handler)
107 def parse_args(args):
108 """Configures parser and parses command line configuration"""
109 # Parser configuration
110 parser = argparse.ArgumentParser(description="Run test cases: category numerology bandwidth test_num")
112 parser.add_argument("--cat", type=int, default=0, help="Category: 0 (A) or 1 (B)", metavar="cat", dest="category")
113 parser.add_argument("--mu", type=int, default=0, help="numerology [0,1,3]", metavar="num", dest="numerology")
114 parser.add_argument("--bw", type=int, default=20, help="bandwidth [5,10,20,100]", metavar="bw", dest="bandwidth")
115 parser.add_argument("--testcase", type=int, default=0, help="test case number", metavar="testcase", dest="testcase")
118 options = parser.parse_args(args)
120 logging.debug("Options: category=%d num=%d bw=%d testcase=%d",
121 options.category, options.numerology, options.bandwidth, options.testcase)
125 """ function to check if a line
126 starts with some character.
129 # return true if a line starts with #
130 return s.startswith('#')
132 class GetOutOfLoops( Exception ):
135 def compare_resuts(cat, mu, bw, tcase, xran_path, test_cfg, direction):
139 nDlRB = nNumRbsPerSymF1[mu][nRChBwOptions.get(str(nDLBandwidth))]
140 nUlRB = nNumRbsPerSymF1[mu][nRChBwOptions.get(str(nULBandwidth))]
141 elif (mu >=2) & (mu <= 3):
142 nDlRB = nNumRbsPerSymF2[mu - 2][nRChBwOptions_mu2and3.get(str(nDLBandwidth))]
143 nUlRB = nNumRbsPerSymF2[mu - 2][nRChBwOptions_mu2and3.get(str(nULBandwidth))]
146 print("Incorrect arguments\n")
150 if 'compression' in globals():
155 print("compare results: {} [compression {}]\n".format(dic_dir.get(direction), comp))
158 # print("WARNING: Skip checking IQs and BF Weights for CAT B for now\n");
162 if nFrameDuplexType == 1:
164 for i in range(nTddPeriod):
166 SlotConfig.insert(i, sSlotConfig0)
168 SlotConfig.insert(i, sSlotConfig1)
170 SlotConfig.insert(i, sSlotConfig2)
172 SlotConfig.insert(i, sSlotConfig3)
174 SlotConfig.insert(i, sSlotConfig4)
176 SlotConfig.insert(i, sSlotConfig5)
178 SlotConfig.insert(i, sSlotConfig6)
180 SlotConfig.insert(i, sSlotConfig7)
182 SlotConfig.insert(i, sSlotConfig8)
184 SlotConfig.insert(i, sSlotConfig9)
186 raise Exception('i should not exceed nTddPeriod %d. The value of i was: {}'.format(nTddPeriod, i))
187 #print(SlotConfig, type(sSlotConfig0))
190 if (direction == 1) & (cat == 1): #UL
191 flowId = ccNum*antNumUL
193 flowId = ccNum*antNum
195 for i in range(0, flowId):
196 #read ref and test files
202 file_tst = xran_path+"/app/logs/"+"o-ru-rx_log_ant"+str(i)+".txt"
203 file_ref = xran_path+"/app/logs/"+"o-du-play_ant"+str(i)+".txt"
207 file_tst = xran_path+"/app/logs/"+"o-du-rx_log_ant"+str(i)+".txt"
208 file_ref = xran_path+"/app/logs/"+"o-ru-play_ant"+str(i)+".txt"
210 raise Exception('Direction is not supported %d'.format(direction))
212 print("test result :", file_tst)
213 print("test reference:", file_ref)
214 if os.path.exists(file_tst):
216 file_tst = open(file_tst, 'r')
218 print ("Could not open/read file:", file_tst)
221 print(file_tst, "doesn't exist")
224 if os.path.exists(file_ref):
226 file_ref = open(file_ref, 'r')
228 print ("Could not open/read file:", file_ref)
231 print(file_tst, "doesn't exist")
235 tst = file_tst.readlines()
236 ref = file_ref.readlines()
246 for slot_idx in range(0, numSlots):
247 for sym_idx in range(0, 14):
248 if nFrameDuplexType==1:
252 sym_dir = SlotConfig[slot_idx%nTddPeriod][sym_idx]
257 sym_dir = SlotConfig[slot_idx%nTddPeriod][sym_idx]
261 #print("Check:","[",i,"]", slot_idx, sym_idx)
262 for line_idx in range(0, nRB*12):
263 offset = (slot_idx*nRB*12*14) + sym_idx*nRB*12 + line_idx
264 line_tst = tst[offset].rstrip()
265 line_ref = ref[offset].rstrip()
267 # discard LSB bits as BFP compression is not Bit Exact
268 tst_i_value = int(line_tst.split(" ")[0]) & 0xFF80
269 tst_q_value = int(line_tst.split(" ")[1]) & 0xFF80
270 ref_i_value = int(line_ref.split(" ")[0]) & 0xFF80
271 ref_q_value = int(line_ref.split(" ")[1]) & 0xFF80
273 print("check:","ant:[",i,"]:",offset, slot_idx, sym_idx, line_idx,":","tst: ", tst_i_value, " ", tst_q_value, " " , "ref: ", ref_i_value, " ", ref_q_value, " ")
274 if (tst_i_value != ref_i_value) or (tst_q_value != ref_q_value) :
275 print("FAIL:","ant:[",i,"]:",offset, slot_idx, sym_idx, line_idx,":","tst: ", tst_i_value, " ", tst_q_value, " " , "ref: ", ref_i_value, " ", ref_q_value, " ")
280 #print("Check:", offset,"[",i,"]", slot_idx, sym_idx,":",line_tst, line_ref)
281 if line_ref != line_tst:
282 print("FAIL:","ant:[",i,"]:",offset, slot_idx, sym_idx, line_idx,":","tst:", line_tst, "ref:", line_ref)
285 except GetOutOfLoops:
290 def parse_dat_file(cat, mu, bw, tcase, xran_path, test_cfg):
292 logging.info("parse config files %s\n", test_cfg[0])
295 with open(test_cfg[0],'r') as fh:
296 for curline in dropwhile(is_comment, fh):
297 my_line = curline.rstrip().split(sep, 1)[0].strip()
299 lineList.append(my_line)
303 for line in lineList:
304 exe_line = line.replace(":", ",")
305 if exe_line.find("/") > 0 :
306 exe_line = exe_line.replace('./', "'")
307 exe_line = exe_line+"'"
309 code = compile(str(exe_line), '<string>', 'exec')
310 exec (code, global_env, local_env)
312 for k, v in local_env.items():
318 def make_copy_mlog(cat, mu, bw, tcase, xran_path):
321 src_bin = xran_path+"/app/mlog-o-du-c0.bin"
322 src_csv = xran_path+"/app/mlog-o-du-hist.csv"
323 dst_bin = xran_path+"/app/mlog-o-du-c0-cat"+str(cat)+"-mu"+str(mu)+"-bw"+str(bw)+"-tcase"+str(tcase)+".bin"
324 dst_csv = xran_path+"/app/mlog-o-du-hist-cat"+str(cat)+"-mu"+str(mu)+"-bw"+str(bw)+"-tcase"+str(tcase)+".csv"
327 d_bin = shutil.copyfile(src_bin, dst_bin)
328 d_csv = shutil.copyfile(src_csv, dst_csv)
330 logging.info("MLog is not present\n")
333 logging.info("Mlog was copied\n")
336 src_bin = xran_path+"/app/mlog-o-ru-c0.bin"
337 src_csv = xran_path+"/app/mlog-o-ru-hist.csv"
338 dst_bin = xran_path+"/app/mlog-o-ru-c0-cat"+str(cat)+"-mu"+str(mu)+"-bw"+str(bw)+"-tcase"+str(tcase)+".bin"
339 dst_csv = xran_path+"/app/mlog-o-ru-hist-cat"+str(cat)+"-mu"+str(mu)+"-bw"+str(bw)+"-tcase"+str(tcase)+".csv"
343 d_bin = shutil.copyfile(src_bin, dst_bin)
344 d_csv = shutil.copyfile(src_csv, dst_csv)
346 logging.info("MLog is not present\n")
349 logging.info("Mlog was copied\n")
355 def run_tcase(cat, mu, bw, tcase, xran_path):
357 test_config = xran_path+"/app/usecase/cat_b/mu{0:d}_{1:d}mhz".format(mu, bw)
359 test_config = xran_path+"/app/usecase/mu{0:d}_{1:d}mhz".format(mu, bw)
361 print("Incorrect arguments\n")
364 test_config = test_config+"/"+str(tcase)
366 app = xran_path+"/app/build/sample-app"
368 logging.debug("run: %s %s", app, test_config)
369 logging.debug("Started script: master.py, XRAN path %s", xran_path)
372 #TODO: add detection of ETH ports
373 eth_cp_dev = ["0000:22:02.1", "0000:22:0a.1"]
374 eth_up_dev = ["0000:22:02.0", "0000:22:0a.0"]
376 test_cfg.append(test_config+"/config_file_o_du.dat")
377 test_cfg.append(test_config+"/config_file_o_ru.dat")
380 os.chdir(xran_path+"/app/")
386 os.system('rm -rf ./logs')
389 log_file_name.append("sampleapp_log_{}_cat_{}_mu{}_{}mhz_tst_{}.log".format(dic_xu.get(i),cat, mu, bw, tcase))
390 with open(log_file_name[i], "w") as f:
391 #, stdout=f, stderr=f
392 p = subprocess.Popen([app, test_cfg[i], eth_up_dev[i], eth_cp_dev[i]], stdout=f, stderr=f)
393 logfile_xu.insert(i, f)
394 processes.append((p, logfile_xu[i]))
396 logging.info("Running O-DU and O-RU see output in: %s %s\n", logfile_xu[0].name, logfile_xu[1].name)
397 for p, f in processes:
400 if p.returncode != 0:
401 print("Application {} failed p.returncode:{}".format(dic_xu.get(i), p.returncode))
403 logging.info("FAIL\n")
405 sys.exit(p.returncode)
409 logging.info("O-DU and O-RU are done\n")
411 make_copy_mlog(cat, mu, bw, tcase, xran_path)
413 usecase_cfg = parse_dat_file(cat, mu, bw, tcase, xran_path, test_cfg)
415 res = compare_resuts(cat, mu, bw, tcase, xran_path, test_cfg, 0)
421 res = compare_resuts(cat, mu, bw, tcase, xran_path, test_cfg, 1)
433 test_executed_total = 0
439 """Processes input files to produce IACA files"""
441 xran_path = os.getenv("XRAN_DIR")
443 # Set up logging with given level (DEBUG, INFO, ERROR) for console end logfile
444 init_logger(logging.INFO, logging.DEBUG)
445 logging.info("Started script: master.py, XRAN path %s", xran_path)
447 # Parse input arguments
448 if len(sys.argv) == 1 :
449 run_total = len(all_test_cases)
451 print("Run All test cases {}\n".format(run_total))
453 options = parse_args(sys.argv[1:])
454 cat = options.category
455 mu = options.numerology
456 bw = options.bandwidth
457 tcase = options.testcase
461 for test_run_ix in range(0, run_total):
462 cat = all_test_cases[test_run_ix][0]
463 mu = all_test_cases[test_run_ix][1]
464 bw = all_test_cases[test_run_ix][2]
465 tcase = all_test_cases[test_run_ix][3]
467 res = run_tcase(cat, mu, bw, tcase, xran_path)
469 test_results.append((cat, mu, bw, tcase,'FAIL'))
472 test_results.append((cat, mu, bw, tcase,'PASS'))
474 res = run_tcase(cat, mu, bw, tcase, xran_path)
476 test_results.append((cat, mu, bw, tcase,'FAIL'))
477 test_results.append((cat, mu, bw, tcase,'PASS'))
479 with open('testresult.txt', 'w') as reshandle:
480 json.dump(test_results, reshandle)
484 if __name__ == '__main__':
485 START_TIME = datetime.now()
487 END_TIME = datetime.now()
488 logging.debug("Start time: %s, end time: %s", START_TIME, END_TIME)
489 logging.info("Execution time: %s", END_TIME - START_TIME)