def create_tree_conn(tab, scope, chan, phase, group_size, base_indices_value, loc_base, loc_diff, connid_min, connid_offset, connid_diff, i_elem): location = ast.ExprBinop( '-', i_elem, ast.ExprSingle(ast.ElemNumber(base_indices_value))) location = ast.ExprBinop( '/', ast.ElemGroup(location), ast.ExprSingle(ast.ElemNumber(group_size))) location = ast.ExprBinop('*', ast.ElemNumber(loc_diff), ast.ExprSingle(ast.ElemGroup(location))) location = ast.ExprBinop('+', ast.ElemNumber(loc_base), ast.ExprSingle(ast.ElemGroup(location))) chanend = ast.ElemId(chan.chanend) chanend.symbol = Symbol(chan.chanend, self.chanend_type(chan), None, scope=T_SCOPE_PROC) elem0 = chan.elems[phase] #connid = ast.ExprBinop('+', i_elem, # ast.ExprSingle(ast.ElemNumber(connid_offset))) connid = ast.ExprBinop('-', i_elem, ast.ExprSingle(ast.ElemNumber(phase))) connid = ast.ExprBinop('rem', ast.ElemGroup(connid), ast.ExprSingle(ast.ElemNumber(group_size))) connid = ast.ExprBinop('*', ast.ElemGroup(connid), ast.ExprSingle(ast.ElemNumber(connid_diff))) connid = ast.ExprBinop('+', ast.ElemGroup(connid), ast.ExprSingle(ast.ElemNumber(connid_min))) master = tab.lookup_is_master(chan, elem0, scope) return ast.StmtConnect(chanend, connid, location, self.connect_type(chan, master))
def create_range_conn(s, chan, i_elem, group): diff2 = group[0] elem0 = group[1][0][0] offset = target_loc(chan, elem0, scope) # Form the location expression if elem0.indices_value > 0: location = ast.ElemGroup( ast.ExprBinop( '-', i_elem, ast.ExprSingle(ast.ElemNumber(elem0.indices_value)))) else: location = i_elem location = ast.ExprBinop('*', ast.ElemNumber(diff2 + 1), ast.ExprSingle(location)) location = ast.ExprBinop('+', ast.ElemGroup(location), ast.ExprSingle(ast.ElemNumber(offset))) chanend = ast.ElemId(chan.chanend) chanend.symbol = Symbol(chan.chanend, self.chanend_type(chan), None, scope=T_SCOPE_PROC) connid = tab.lookup_connid(chan.name, elem0.index, scope) chanid = ast.ExprSingle(ast.ElemNumber(connid)) begin = elem0.indices_value end = group[1][-1][0].indices_value cond = ast.ExprBinop( '>=', i_elem, ast.ExprSingle(ast.ElemNumber(min(begin, end)))) master = tab.lookup_is_master(chan, elem0, scope) conn = ast.StmtConnect(chanend, chanid, location, self.connect_type(chan, master)) return ast.StmtIf(cond, conn, s) if s else conn
def create_single_conn(s, chan, scope, i_elem, elem): debug(self.debug, 'New connection for index {}'.format(elem.index)) master = tab.lookup_is_master(chan, elem, scope) if master: location = ast.ExprSingle( ast.ElemNumber( tab.lookup_slave_location(chan.name, elem.index, scope))) else: location = ast.ExprSingle( ast.ElemNumber( tab.lookup_master_location(chan.name, elem.index, scope))) chanend = ast.ElemId(chan.chanend) chanend.symbol = Symbol(chan.chanend, self.chanend_type(chan), None, scope=T_SCOPE_PROC) connid = tab.lookup_connid(chan.name, elem.index, scope) chanid = ast.ExprSingle(ast.ElemNumber(connid)) cond = ast.ExprBinop( '=', i_elem, ast.ExprSingle(ast.ElemNumber(elem.indices_value))) conn = ast.StmtConnect(chanend, chanid, location, self.connect_type(chan, master)) return ast.StmtIf(cond, conn, s) if s != None else conn
def create_decls(self, tab, scope, chansets): """ Given 'chansets' (a list of 'ChanElemSet's) convert them into chanend declarations. """ decls = [] for x in chansets: #if tab.lookup(x.name, None, base=scope, scoped=True) != None: if x.symbol.scope == T_SCOPE_PROC: d = ast.VarDecl(x.chanend, T_CHANEND_SINGLE, None) d.symbol = Symbol(x.chanend, T_CHANEND_SINGLE, None, scope=T_SCOPE_PROC) decls.append(d) elif x.symbol.scope == T_SCOPE_BLOCK: d = ast.VarDecl(x.chanend, T_CHANEND_SINGLE, None) d.symbol = Symbol(x.chanend, T_CHANEND_SINGLE, None, scope=T_SCOPE_BLOCK) decls.append(d) elif x.symbol.scope == T_SCOPE_SERVER: d = ast.VarDecl(x.chanend, T_CHANEND_SERVER_SINGLE, None) d.symbol = Symbol(x.chanend, T_CHANEND_SERVER_SINGLE, None, scope=T_SCOPE_SERVER) decls.append(d) elif x.symbol.scope == T_SCOPE_CLIENT: d = ast.VarDecl(x.chanend, T_CHANEND_CLIENT_SINGLE, None) d.symbol = Symbol(x.chanend, T_CHANEND_CLIENT_SINGLE, None, scope=T_SCOPE_CLIENT) decls.append(d) else: assert 0 return decls
def insert(self, stmt): # Create the assignment e = ast.ElemId(PROC_ID_VAR) e.symbol = Symbol(PROC_ID_VAR, T_VAR_SINGLE, None, T_SCOPE_PROC) s = ast.StmtAss(e, ast.ExprSingle(ast.ElemFcall('procid', []))) # Add the assignent in sequence with the existing body process #if isinstance(node.stmt, ast.StmtSeq): # node.stmt.stmt.insert(0, s) #else: return ast.StmtSeq([s, stmt])
def gen_single_conn(self, tab, scope, chan): """ Generate a connection for a single channel declaration. 'chan' is a ChanElemSet with one element. """ elem = chan.elems[0] chanend = ast.ElemId(chan.chanend) chanend.symbol = Symbol(chan.chanend, self.chanend_type(chan), None, scope=T_SCOPE_PROC) connid = tab.lookup_connid(chan.name, elem.index, scope) chanid = ast.ExprSingle(ast.ElemNumber(connid)) # Server-end connections don't need targets if chan.symbol.scope == T_SCOPE_SERVER: return ast.StmtConnect(chanend, chanid, ast.ExprSingle(ast.ElemNumber(0)), self.connect_type(chan, False)) # All other connections do else: master = tab.lookup_is_master(chan, elem, scope) if master: location = ast.ExprSingle( ast.ElemNumber( tab.lookup_slave_location(chan.name, elem.index, scope))) else: location = ast.ExprSingle( ast.ElemNumber( tab.lookup_master_location(chan.name, elem.index, scope))) chanend = ast.ElemId(chan.chanend) chanend.symbol = Symbol(chan.chanend, self.chanend_type(chan), None, scope=T_SCOPE_PROC) return ast.StmtConnect(chanend, chanid, location, self.connect_type(chan, master))
def indices_expr(indices): """ Given a set of indices, return an expression computing their combined value. """ dims = [x.count_value for x in indices] r = None for (i, x) in enumerate(indices): c = reduce(lambda x, y: x * y, dims[i + 1:], 1) c_expr = ast.ExprSingle(ast.ElemNumber(c)) eid = ast.ElemId(x.name) eid.symbol = Symbol(x.name, T_VAL_SINGLE, None, scope=T_SCOPE_PROC) e = ast.ExprBinop('*', eid, c_expr) if c > 1 else ast.ExprSingle(eid) r = e if r == None else ast.ExprBinop('+', ast.ElemGroup(r), e) return r
def rename_chan(self, elem, chans): """ Rename a channel (elem) using the set of channel elements (ChanElemSet) which will contain the name of the channel end allocated to this instance. """ if isinstance(elem, ast.ElemId) and elem.symbol.type == T_CHAN_SINGLE: for x in chans: if elem.name == x.name: t = T_CHANEND_SINGLE if x.symbol.scope == T_SCOPE_SERVER: t = T_CHANEND_SERVER_SINGLE elif x.symbol.scope == T_SCOPE_CLIENT: t = T_CHANEND_CLIENT_SINGLE s = Symbol(x.chanend, t, T_SCOPE_PROC) e = ast.ElemId(x.chanend) e.symbol = s #print('renamed {} to {} as type {}'.format(x.name, x.chanend, t)) return ast.ExprSingle(e) elif isinstance(elem, ast.ElemSub) and elem.symbol.type == T_CHAN_ARRAY: for x in chans: if elem.name == x.name and CmpExpr().expr(elem.expr, x.expr): t = T_CHANEND_SINGLE if x.symbol.scope == T_SCOPE_SERVER: t = T_CHANEND_SERVER_SINGLE elif x.symbol.scope == T_SCOPE_CLIENT: t = T_CHANEND_CLIENT_SINGLE s = Symbol(x.chanend, t, T_SCOPE_PROC) e = ast.ElemId(x.chanend) e.symbol = s #print('renamed {} to {} as type {}'.format(x.name, x.chanend, t)) return ast.ExprSingle(e) else: # Don't worry about chanends return ast.ExprSingle(elem)
def gen_array_conn(self, tab, scope, chan): """ Generate a conncection for an array channel declaration. We must analyse the subscript by generating nested conditional statements. 'chan' is a ChanElemSet with multiple elements. """ def target_loc(chan, elem, scope): master = tab.lookup_is_master(chan, elem, scope) return (tab.lookup_slave_location(chan.name, elem.index, scope) if master else tab.lookup_master_location( chan.name, elem.index, scope)) def create_single_conn(s, chan, scope, i_elem, elem): debug(self.debug, 'New connection for index {}'.format(elem.index)) master = tab.lookup_is_master(chan, elem, scope) if master: location = ast.ExprSingle( ast.ElemNumber( tab.lookup_slave_location(chan.name, elem.index, scope))) else: location = ast.ExprSingle( ast.ElemNumber( tab.lookup_master_location(chan.name, elem.index, scope))) chanend = ast.ElemId(chan.chanend) chanend.symbol = Symbol(chan.chanend, self.chanend_type(chan), None, scope=T_SCOPE_PROC) connid = tab.lookup_connid(chan.name, elem.index, scope) chanid = ast.ExprSingle(ast.ElemNumber(connid)) cond = ast.ExprBinop( '=', i_elem, ast.ExprSingle(ast.ElemNumber(elem.indices_value))) conn = ast.StmtConnect(chanend, chanid, location, self.connect_type(chan, master)) return ast.StmtIf(cond, conn, s) if s != None else conn def create_range_conn(s, chan, i_elem, group): diff2 = group[0] elem0 = group[1][0][0] offset = target_loc(chan, elem0, scope) # Form the location expression if elem0.indices_value > 0: location = ast.ElemGroup( ast.ExprBinop( '-', i_elem, ast.ExprSingle(ast.ElemNumber(elem0.indices_value)))) else: location = i_elem location = ast.ExprBinop('*', ast.ElemNumber(diff2 + 1), ast.ExprSingle(location)) location = ast.ExprBinop('+', ast.ElemGroup(location), ast.ExprSingle(ast.ElemNumber(offset))) chanend = ast.ElemId(chan.chanend) chanend.symbol = Symbol(chan.chanend, self.chanend_type(chan), None, scope=T_SCOPE_PROC) connid = tab.lookup_connid(chan.name, elem0.index, scope) chanid = ast.ExprSingle(ast.ElemNumber(connid)) begin = elem0.indices_value end = group[1][-1][0].indices_value cond = ast.ExprBinop( '>=', i_elem, ast.ExprSingle(ast.ElemNumber(min(begin, end)))) master = tab.lookup_is_master(chan, elem0, scope) conn = ast.StmtConnect(chanend, chanid, location, self.connect_type(chan, master)) return ast.StmtIf(cond, conn, s) if s else conn def create_tree_conn(tab, scope, chan, phase, group_size, base_indices_value, loc_base, loc_diff, connid_min, connid_offset, connid_diff, i_elem): location = ast.ExprBinop( '-', i_elem, ast.ExprSingle(ast.ElemNumber(base_indices_value))) location = ast.ExprBinop( '/', ast.ElemGroup(location), ast.ExprSingle(ast.ElemNumber(group_size))) location = ast.ExprBinop('*', ast.ElemNumber(loc_diff), ast.ExprSingle(ast.ElemGroup(location))) location = ast.ExprBinop('+', ast.ElemNumber(loc_base), ast.ExprSingle(ast.ElemGroup(location))) chanend = ast.ElemId(chan.chanend) chanend.symbol = Symbol(chan.chanend, self.chanend_type(chan), None, scope=T_SCOPE_PROC) elem0 = chan.elems[phase] #connid = ast.ExprBinop('+', i_elem, # ast.ExprSingle(ast.ElemNumber(connid_offset))) connid = ast.ExprBinop('-', i_elem, ast.ExprSingle(ast.ElemNumber(phase))) connid = ast.ExprBinop('rem', ast.ElemGroup(connid), ast.ExprSingle(ast.ElemNumber(group_size))) connid = ast.ExprBinop('*', ast.ElemGroup(connid), ast.ExprSingle(ast.ElemNumber(connid_diff))) connid = ast.ExprBinop('+', ast.ElemGroup(connid), ast.ExprSingle(ast.ElemNumber(connid_min))) master = tab.lookup_is_master(chan, elem0, scope) return ast.StmtConnect(chanend, connid, location, self.connect_type(chan, master)) def conn_diff_groups(chan, s, i_elem, d=DEBUG_COMPRESSION): """ Compress connecitons based on the difference between differences of indices value and destination. """ # Build a list of channel elements and index-dest differences diffs = [] for x in chan.elems: diff = target_loc(chan, x, scope) - x.indices_value diffs.append((x, diff)) debug(d, 'Differences for chan {}:'.format(chan.name)) for (elem, diff) in diffs: connid = tab.lookup_connid(chan.name, elem.index, scope) debug( d, ' {:>3}: [{}]:{} - {}'.format(elem.indices_value, elem.index, connid, diff)) # Group consecutive elements with a constant second difference groups = [] newgroup = True for ((elemA, diffA), (elemB, diffB)) in zip(diffs[:-1], diffs[1:]): diff2 = diffB - diffA connid = tab.lookup_connid(chan.name, elemB.index, scope) master = tab.lookup_is_master(chan, elemB, scope) if newgroup: groups.append((diff2, [(elemA, diffA)])) groupdiff2 = diff2 groupconnid = tab.lookup_connid(chan.name, elemA.index, scope) groupmaster = tab.lookup_is_master(chan, elemA, scope) newgroup = False if (groupdiff2 == diff2 and groupconnid == connid and groupmaster == master): groups[-1][1].append((elemB, diffB)) else: newgroup = True if newgroup: groups.append((None, [diffs[-1]])) debug(d, 'Groups:') for x in groups: diff2 = x[0] elem0 = x[1][0][0] offset = target_loc(chan, elem0, scope) debug(d, ' diff2: {}'.format(diff2)) debug(d, ' offset: {}'.format(offset)) debug(d, ' base: {}'.format(elem0.indices_value)) if len(x[1]) > 1: for (i, (elem, diff)) in enumerate(x[1]): loc = target_loc(chan, elem, scope) computed = ((diff2 + 1) * (elem.indices_value - elem0.indices_value)) + offset assert computed == loc debug( d, ' {:>3}: [{:>3}]->{:>3}, diff: {:>3}, computed: {}' .format(elem.indices_value, elem.index, loc, diff, computed)) else: debug( d, ' {:>3}: [{:>3}]->{:>3}'.format( elem0.indices_value, elem0.index, offset)) # If compression was inneffective then abort if len(groups) == len(chan.elems): debug(d, 'Aborting group diff compression.') return None debug(d, 'Diff compression successful.') # Construct connection syntax s = None for x in groups: elem0 = x[1][0][0] if len(x[1]) == 1: s = create_single_conn(s, chan, scope, i_elem, elem0) else: s = create_range_conn(s, chan, i_elem, x) return s def conn_tree_groups(chan, s, i_elem, d=DEBUG_COMPRESSION): """ Compress connections based on monotonically increasing or decreasing sets with the same target destination. Within a set, connection IDs can be monotonically increasing or decreasing. """ locs = [] debug(d, 'Locations:') for x in chan.elems: loc = target_loc(chan, x, scope) locs.append((x, loc)) debug( d, ' {:>4} : {}[{}] -> {}'.format(x.indices_value, chan.name, x.index, loc)) # Separate the first odd element if there is one phase = 0 if locs[0][1] != locs[1][1]: odd_elem = locs[0][0] locs = locs[1:] phase = 1 # Count the group size group_size = 1 while group_size < len(locs) and locs[group_size - 1][1] == locs[group_size][1]: group_size += 1 # Only consider connections with more than one group if len(locs) <= group_size + 1: debug(d, 'Aborting tree compression.') return None # Set parameters loc_diff = locs[group_size][1] - locs[group_size - 1][1] loc_base = locs[0][1] base_indices_value = locs[0][0].indices_value connidA = tab.lookup_connid(chan.name, locs[0][0].index, scope) connidB = tab.lookup_connid(chan.name, locs[1][0].index, scope) connid_min = min(connidA, connidB) connid_diff = max(connidA, connidB) - connid_min connid_offset = (phase + (1 if connidA > connidB else 0)) % 2 # Print some debug info debug(d, 'Attempting tree compression.') debug(d, ' Group size: {}'.format(group_size)) debug(d, ' Location base: {}'.format(loc_base)) debug(d, ' Location diff: {}'.format(loc_diff)) debug(d, ' Base ival: {}'.format(base_indices_value)) debug(d, ' ConnID base: {}'.format(connidA)) debug(d, ' ConnID diff: {}'.format(connid_diff)) # Check each group contains the same location debug(d, 'Checking groups...') i = 0 while i * group_size < len(locs): debug(d, ' Group {}'.format(i)) for j in range(1, group_size): if locs[i * group_size][1] != locs[(i * group_size) + j][1]: debug(d, 'Aborting tree compression.') return None i += 1 # Check each step between groups has same location and connection diffs debug(d, 'Checking diffs...') for (i, (x, y)) in enumerate( zip(locs[1::group_size], locs[group_size::group_size])): debug( d, ' Group {} and {}: {} and {}'.format( i, i + 1, y[1], x[1])) connidX = tab.lookup_connid(chan.name, x[0].index, scope) connidY = tab.lookup_connid(chan.name, y[0].index, scope) connid_diff_ = connidX - connidY if y[1] - x[1] != loc_diff or connid_diff != connid_diff_: debug(d, 'Aborting tree compression.') return None # Check matching computed location debug(d, 'Checking computed...') debug(d, 'connid_min = {}'.format(connid_min)) debug(d, 'connid_off = {}'.format(connid_offset)) if phase == 1: connid = tab.lookup_connid(chan.name, odd_elem.index, scope) debug(d, ' {}: connid={}'.format(odd_elem.indices_value, connid)) for (elem, loc) in locs: computed_loc = loc_base + (loc_diff * (math.floor( ((elem.indices_value - base_indices_value)) / group_size))) connid = tab.lookup_connid(chan.name, elem.index, scope) #computed_connid = (connid_min + # ((elem.indices_value + connid_offset) % group_size) * connid_diff) computed_connid = (connid_min + ( (elem.indices_value - phase) % group_size) * connid_diff) debug( d, ' {}: connid={}, loc={} computed({}, {})'.format( elem.indices_value, connid, loc, computed_connid, computed_loc)) assert computed_loc == loc assert computed_connid == connid debug(d, 'Tree compression successful.') # Construct connection syntax if phase == 0: return create_tree_conn(tab, scope, chan, phase, group_size, base_indices_value, loc_base, loc_diff, connid_min, connid_offset, connid_diff, i_elem) else: s = create_tree_conn(tab, scope, chan, phase, group_size, base_indices_value, loc_base, loc_diff, connid_min, connid_offset, connid_diff, i_elem) return create_single_conn(s, chan, scope, i_elem, odd_elem) def conn_singles(chan, s, i_elem, d=DEBUG_COMPRESSION): """ Create (uncompressed) connections for each case. """ debug(d, 'Creating uncompressed connection range.') for x in chan.elems: s = create_single_conn(s, chan, scope, i_elem, x) debug( d, ' {}: {}[{}]'.format(x.indices_value, chan.name, x.index)) return s # Sort the channel elements into increasing order of indices chan.elems = sorted(chan.elems, key=lambda x: x.indices_value) # Compress conditional connections and return the AST construction i_expr = indices_expr(chan.indices) i_elem = ast.ElemId('_i') i_elem.symbol = Symbol(i_elem.name, T_VAR_SINGLE, scope=T_SCOPE_BLOCK) s = None if COMPRESS: s = conn_tree_groups(chan, s, i_elem) s = conn_diff_groups(chan, s, i_elem) if s == None else s s = conn_singles(chan, s, i_elem) if s == None else s s = [ast.StmtAss(i_elem, i_expr), s] s = ast.StmtSeq([ast.VarDecl(i_elem.name, T_VAR_SINGLE, None)], s) return s