Pyalpha tools for performance audio demo in PRSDK.
[processor-sdk/performance-audio-tools.git] / pyalpha / pyalpha / pyalpha.py
1 """
2 Copyright (c) 2004 - 2017, Texas Instruments Incorporated - http://www.ti.com/
3 All rights reserved.
5 * Redistribution and use in source and binary forms, with or without 
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the
15 * distribution.
16 *
17 * Neither the name of Texas Instruments Incorporated nor the names of
18 * its contributors may be used to endorse or promote products derived
19 * from this software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 *
33 """
34 __version__='1.2.2'
36 #### Library imports ####
37 import os
38 import re
39 import sys
40 import time
41 import errno
42 import subprocess
43 from serial import Serial
44 from random import randint
46 import logging
47 log = logging.getLogger('PASTA')
49 #### Package imports ####
50 from srecord import sRecord
52 ##########################
53 #### Module functions ####
54 def silentremove(filename):
55     """Delete a file if exists."""
56     try:
57         os.remove(filename)
58     except OSError as e:
59         if e.errno != errno.ENOENT:
60             raise # re-raise exception if a different error occured
61 #### Module Functions ####
62 ##########################
64 ###########################
65 #### Module Exceptions ####
66 class ConversionError(Exception):
67     def __init__(self, value):
68         self.value = value
69     def __str__(self):
70         return repr(self.value)        
71 #### Module Exceptions ####
72 ###########################
74 #################
75 #### PyAlpha ####
76 class PyAlpha:
77     """Interface for sending alpha commands"""
80     @staticmethod
81     def asdf():
82         print "AYYYYY"
83     
84     def __init__(self, header, port='COM1', baud=19200, retain=False,
85                  inc='./alpha', bins='./bin', alfas='.', defs=[]):
86         self.port  = port            
87         self.baud = baud
88         self.header = header
89         self.inc = inc
90         self.bins = bins
91         self.alfas = alfas
92         self.defs = defs
93         
94         self.inverse = {}
95         self.retain = retain
96         self.build_inverse_mapping() # populates self.inverse
98     def send(self, alpha_list, fast=False, separate=False):
100         if separate:
101             resp = []
102             for alpha in alpha_list:
103                 resp.append(self._send([alpha])) # making a list is a workaround, for now
105             log.debug(resp)
107             resp = [r.lstrip('alpha ') for r in resp if r != "alpha /* none */"]
108             if not resp:
109                 resp = ['/*none*/']
111             return 'alpha ' + ','.join(resp)
113         else:
114             return self._send(alpha_list, fast)
115             
116     def _send(self, alpha_list, fast=False):
117         """Sends alpha_list as S-Record over UART to the DUT"""
118         
119         if not alpha_list: return # die early
120         
121         # Test number a la calfa
122         testnum = randint(1000,9999)
123         fbase = self.alfas +  '/alfa' + str(testnum)
124         
125         # Generate C file
126         self.gen_c(alpha_list, fbase)
128         # Preprocess C file
129         self.preprocess(fbase)
131         # Convert to S-Record
132         ret = self.itox(fbase)
133         if ret:
134             self.__clean(fbase)
135             raise ConversionError('itox failed with code: ' + str(ret))
137         # Send S-Record to target device and read response
138         ret = self.write_read(fbase)
139         if not ret:
140             self.__clean(fbase)
141             return 'TIMEOUT'
143         # Don't do any postprocessing, just return after receiving S-record
144         if fast:
145             self.__clean(fbase)
146             return ret
147             
148         # Parse S-Record into alpha code
149         self.xtoi(fbase)
151         # Parse alpha code to alpha command, remove CRLF from the end
152         ret = self.inverse_compile(fbase).strip()
153         
154         # Clean up temporary files
155         self.__clean(fbase)
157         return ret
159                 
160     def write_read(self, fbase, timeout=5):
161         """Read S-Record file (.s) and write. Read response and save to S-Record response file (.x)"""
162         s_fname = fbase + '.s'
163         x_fname = fbase + '.x'
164         
165         with Serial(self.port, self.baud, timeout=1) as ser:
166             # WRITE
167             with open(s_fname, 'rb') as sfile:
168                 records = sfile.read()
169                 log.debug('Sending: \n%s', str(records))
170                 ser.write('\r\n') # A single CRLF must be sent first for proper comm
171                 ser.write(records)
173             # READ
174             response = []
175             line = None
176             t = 0
177             while line != "S9030000FC\r\n" and t < timeout:
178                 line = ser.readline()
179                 if line:
180                     response.append(line)
181                 else:
182                     t += 1
184             # Strip away the random leading chars and emove empty lines
185             outarr = [line.lstrip('\x13\x11') for line in response if line]
186             log.debug('Received: %s', ''.join(outarr))
188         with open(x_fname, 'w') as xfile:
189             xfile.write(''.join(outarr))
191         return outarr
192     
193     def gen_c(self, alpha_list, fbase):
194         """Generate a C file (.c) to be pre-processed"""
195         c_fname = fbase + '.c'
196         
197         with open(c_fname, 'w') as cfile:
198             cfile.write('#include "' + self.header + '.h"\n')
199             cfile.write('alpha ' + ','.join(alpha_list) + '\n')
200     
201     def preprocess(self, fbase):
202         """Pre-process the C file (.c) to get hex representations of alpha commands in (.pp) file"""
203         pp_fname = fbase + '.pp'
205         # Begin building arguments list
206         args = [ self.bins + '/acp6x.exe']
208         # Add pre-processor macro definitions
209         for d in self.defs:
210             if d:
211                 args.append("-D%s" % d)
213         # Include the specified file, export to the current alfa*.c file
214         args += [
215             '-I', self.inc,
216             '-E',
217             fbase + '.c',
218         ]
220         # Run the pre-processor
221         with open(pp_fname, 'w') as ppfile:
222             print args
223             pid = subprocess.Popen(args, stdout=ppfile, stderr=subprocess.PIPE)
224             pid.wait()
226     def itox(self, fbase):
227         """Calls itox.exe to convert .pp file to S-Record format (.s)"""
228         pp_fname = fbase + '.pp'
229         s_fname = fbase + '.s'
231         args = [
232             self.bins + '/itox.exe',
233             '-S', # S-record
234         ]
236         retcode = None
237         with open(s_fname, 'wb') as sfile, open(pp_fname, 'rb') as ppfile:
238             pid = subprocess.Popen(args, stdout=sfile, stdin=ppfile)
239             retcode = pid.wait()
241         return retcode
242     
243     def xtoi(self, fbase):
244         """Calls xtoi.exe to convert .x file to hex representation (.y)"""
245         x_fname = fbase + '.x'
246         y_fname = fbase + '.y'
247         
248         args = [
249             self.bins + '/xtoi.exe',
250             '-S'
251         ]
252         
253         with open(x_fname, 'rb') as xfile, open(y_fname, 'wb') as yfile:
254             pid = subprocess.Popen(args, stdout=yfile, stdin=xfile)
255             pid.wait()
256             
258     def inverse_compile(self, fbase):
259         """Converts hex representation (.y) to textual representation of alpha command (.z)"""
260         y_fname = fbase + '.y'
261         z_fname = fbase + '.z'
263         s = ''
264         with open(y_fname, 'rb') as yfile:
265             s = yfile.read()
267             for code,name in self.inverse.iteritems():
268                 s = re.sub(code, name, s)
270         with open(z_fname, 'wb') as zfile:
271             zfile.write(s)
273         return s
275     def __clean(self, fbase):
276         """Removes temporary files, unless otherwise specified (retain)"""
278         if not self.retain:
279             self.clean_up(fbase)
281     
282     def clean_up(self, fbase):
283         """Removes temporary files"""
284         
285         file_types = ['.c', '.pp', '.s', '.x', '.y', '.z']
287         # Apply file extensions to the fbase
288         fnames = [fbase + ft for ft in file_types]
289         map(silentremove, fnames)
291     def build_inverse_mapping(self):
292         """Parses through inverse-compilation file (.hdM) and creates a mapping
293         for creation of .z files"""
295         # .hdM file is in the include directory with specified name
296         fname = self.inc + '/' + self.header + '.hdM'
297         
298         # Each line in hdM is of the form:
299         #       # define alphaCommandName 0x####,0x####,...
300         define_str = r"^#define"
301         alpha_str  = r"(?P<alpha>\S+)"
302         hex_str    = r"(?P<hex>(0x[a-f0-9]{4},?)+)[\r\n]+$"
304         # Compile the regular expression
305         alpha_re = re.compile(define_str + " " +
306                               alpha_str + " " +
307                               hex_str)
309         # Parse file
310         with open(fname, 'rb') as f:
311             for line in f:
312                 mat = re.match(alpha_re, line)
313                 if not mat: continue
315                 name = mat.group('alpha')
316                 codes = mat.group('hex')
318                 self.inverse[codes] = name
320 #### PyAlpha ####
321 #################