#!/usr/bin/env python #BEGIN_LEGAL # #Copyright (c) 2017 Intel Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # #END_LEGAL # scatter bits from b in to a, one bit at a time. Emit C code to do it. # optimizations: # - avoid 0 shifts (done) # - work with groups of bits (harder) must find bit runs. Compiler's optimizer does a good job # - don't or-in initial value, just assign it (done) from __future__ import print_function import re import genutil underscore_pattern = re.compile(r'_') def scatter_generate_chunks(length, trimmed_bits, fields, code, verbose=False): """ @type length: integer @param length: length of trimmed_bits @type trimmed_bits: string @param trimmed_bits: bit pattern without underscores @type fields: list @param fields: [(field_name, bits)] @type code: dictionary @param code: dictionary of code snippets @type verbose: boolean @param verbose: verbosity @rtype: string @return: None if cannot map using this functoin, or a string if we can map it """ runs = genutil.find_runs(list(trimmed_bits)) s = [] first = True total_bit_count = 0 for (letter,count) in runs: found = False for (field_name, bits) in fields: field_len = len(bits) t=bits[0]*field_len if t == bits: # all bits in the field are the same... if bits[0] == letter: # and we match the current run-component total_bit_count += count shift = length - total_bit_count if first: first=False else: s.append('|') s.append("(%s" % code[bits]) if shift != 0: s.append("<< %s" % shift) s.append(")") found = True break if not found: return None return ''.join(s) def scatter_generate_bit_by_bit(length, trimmed_bits, code, verbose=False): """Generate c-code expressions one bit at a time. @type length: integer @param length: length of trimmed_bits @type trimmed_bits: string @param trimmed_bits: bit pattern without underscores @type code: dictionary @param code: dictionary of code snippets @type verbose: boolean @param verbose: verbosity @rtype: string @return: C code expression """ bit_count = {} # count number of each kind of bit s = [] # the return string first = True bar = '' # go bit by bit through the pattern for (i,b) in enumerate(trimmed_bits): shift = length-i-1 if not first: bar = '|' first = False if b == '0' or b == '1': # binary bits, just jam them in there... if shift == 0: s.append("%s %s " % (bar, b)) else: s.append("%s (%s << %s)" % (bar, b, length-i-1)) else: # letter -- need to extract from field if b in bit_count: bit_count[b] += 1 else: bit_count[b] = 0 nth_b = bit_count[b] key = b + str(nth_b) if verbose: print("#", i,b, nth_b,key) if shift == 0: s.append("%s %s " % (bar, code[key] )) else: s.append("%s (%s << %s)" % (bar, code[key], length-i-1)) return ''.join(s) def scatter_gen(pattern, fields): """Generate packer code to fill in patter from fields. Fields is a list of tuples of (fieldname, bits). pattern is a string of bits in the order we need them. @type pattern: string @param pattern: @type fields: list of tuples @param fields: [(fieldname, bits) ] @rtype: string @return: C-code for packing the bits from the input fields """ # generate code patterns for the bits in fields verbose = False #verbose = True if verbose: print("pattern: %s fields: %s" %(pattern, str(fields))) code = {} for (n,p) in fields: length = len(p) # we need length bits from n mask = (1 << length) - 1 # we use this for bit runs we recognize #code[p] = '(%s & 0x%x)' % (n, mask) # the whole field code[p] = n if verbose: print(p, code[p]) # we must handle hetergenous bit sequences like jii. In this # case, j is 0 (j0), the first i is zero (i0) and the 2nd i is # 1 (i1). If we used the enumerate index, then the 1st i would # be one and that would not handle the 'ii' subseqeunce that # we need to emit. bit_sequence = 0 last = None for (i,s) in enumerate(p): shift = length-i-1 if shift == 0: c = "(%s & %s)" % ( n, 1) else: c = "((%s >> %s) & %s)" % ( n, shift, 1) # OLD key = s + str(i) # the i'th letter s if last and s != last: bit_sequence = 0 key = s + str(bit_sequence) bit_sequence += 1 last = s code[key] = c if verbose: print('key: %s c-expression: %s' % (key, c)) trimmed = underscore_pattern.sub('', pattern) length = len(trimmed) # Scatter those code patterns and cobble together a output code that # assigns to some variable. c_code = scatter_generate_chunks(length, trimmed, fields, code, verbose) if c_code == None: c_code = scatter_generate_bit_by_bit(length, trimmed, code, verbose) return (length, c_code) def test_scatter(): # this is a pattern for encoding a = 'ss_iii_bbb' # these are the bound fields b = [ ('SCALE','ss'), ('INDEX', 'iii'), ('BASE', 'bbb') ] (length,s) = scatter_gen(a,b) #s += ';' print(length) print(s) # this is a pattern for encoding a = '11_i1i_b01' # these are the bound fields b = [ ('SCALE','ss'), ('INDEX', 'iii'), ('BASE', 'bbb') ] (length,s) = scatter_gen(a,b) #s += ';' print(length) print(s) # this is a pattern for encoding a = 'xxx_yyyyy' # these are the bound fields b = [ ('IMM','yyyy'), ('IGNORED', 'xxx') ] (length,s) = scatter_gen(a,b) #s += ';' print(length) print(s) if __name__ == '__main__': test_scatter()