class Thread(vtypes.VeriloggenNode): __intrinsics__ = ('run', 'join', 'done', 'reset', 'ret') def __init__(self, m, name, clk, rst, targ, datawidth=32, point=16, tid=None, fsm_as_module=False): self.m = m self.name = name self.clk = clk self.rst = rst self.targ = targ self.datawidth = datawidth self.point = point self.tid = tid self.fsm_as_module = fsm_as_module self.function_lib = OrderedDict() self.intrinsic_functions = OrderedDict() self.intrinsic_methods = OrderedDict() self.fsm = None self.is_child = False self.start_state = None self.end_state = None self.return_value = None self.start_frame = None self.args_dict = OrderedDict() self.vararg_regs = [] self.vararg_set = False self.called = None def start(self, *args, **kwargs): """ build up a new FSM based on the arguments """ if self.is_child: raise ValueError('already started as a child thread') if self.end_state is not None: raise ValueError('already started') frame = inspect.currentframe() self.start_frame = frame.f_back self.fsm = FSM(self.m, self.name, self.clk, self.rst, as_module=self.fsm_as_module) self.start_state = self.fsm.current self._synthesize_start_fsm(args, kwargs) self.end_state = self.fsm.current return self.fsm def extend(self, fsm, *args, **kwargs): """ extend a given thread FSM """ frame = inspect.currentframe() self.start_frame = frame.f_back self._synthesize_start_fsm(args, kwargs, fsm) return fsm def _embed_thread(self, fsm, *args, **kwargs): """ extend a given thread FSM (for embed_thread func) """ frame = inspect.currentframe() self.start_frame = frame.f_back.f_back self._synthesize_start_fsm(args, kwargs, fsm) return fsm def run(self, fsm, *args, **kwargs): """ start as a child thread """ if not self.is_child and self.end_state is not None: raise ValueError('already started') if self.fsm is None: self.fsm = FSM(self.m, self.name, self.clk, self.rst, as_module=self.fsm_as_module) self.is_child = True if self.start_state is not None: self.fsm._set_index(self.start_state) else: self.start_state = self.fsm.current self._synthesize_run_fsm(fsm, args, kwargs) if self.end_state is None: self.end_state = self.fsm.current start_flag = (self.fsm.state == self.start_state) return start_flag def join(self, fsm): """ wait for the completion """ if self.end_state is None: raise ValueError('not started') end_flag = (self.fsm.state == self.end_state) fsm.If(end_flag).goto_next() return 0 def done(self, fsm): """ check whethe the thread is running """ if self.end_state is None: raise ValueError('not started') end_flag = (self.fsm.state == self.end_state) return end_flag def reset(self, fsm): """ reset the FSM counter to the initial state """ if self.end_state is None: raise ValueError('not started') reset_flag = (fsm.state == fsm.current) self.fsm._set_index(self.end_state) if self.called is not None: self.fsm.If(reset_flag)(self.called(0)) self.fsm.goto_from(self.end_state, self.start_state, reset_flag) self.fsm._set_index(self.start_state) return 0 def ret(self, fsm): """ return value """ return self.return_value #-------------------------------------------------------------------------- def add_function(self, func): name = func.__name__ if name in self.function_lib: raise ValueError('Function {} is already defined'.format(name)) self.function_lib[name] = func return func def add_intrinsics(self, *funcs): for func in funcs: self.intrinsic(func) def add_intrinsic_method_prefix(self, obj, prefix): funcs = [ method for name, method in inspect.getmembers(obj, inspect.ismethod) if name.startswith(prefix) ] self.add_intrinsics(*funcs) def intrinsic(self, func): if inspect.isfunction(func): return self._add_intrinsic_function(func) if inspect.ismethod(func): return self._add_intrinsic_method(func) raise TypeError("'%s' object is not supported" % str(type(func))) def _add_intrinsic_function(self, func): name = func.__name__ if name in self.intrinsic_functions: raise ValueError( 'Intrinsic function {} is already defined'.format(name)) self.intrinsic_functions[name] = func return func def _add_intrinsic_method(self, func): name = str(func) if name in self.intrinsic_methods: raise ValueError( 'Intrinsic method {} is already defined'.format(name)) self.intrinsic_methods[name] = func return func def _synthesize_start_fsm(self, args, kwargs, fsm=None): if fsm is None: fsm = self.fsm functions = self._get_functions() cvisitor = compiler.CompileVisitor(self.m, self.name, self.clk, self.rst, fsm, functions, self.intrinsic_functions, self.intrinsic_methods, self.start_frame, datawidth=self.datawidth, point=self.point) text = textwrap.dedent(inspect.getsource(self.targ)) tree = ast.parse(text).body[0] # stack a new scope frame cvisitor.pushScope(ftype='call') # args -> scope variable (pass by reference) args_code = [] rest_args = [] for pos, arg in enumerate(args): baseobj = tree.args.args[pos] argname = ( baseobj.id if isinstance(baseobj, ast.Name) # python 2 else baseobj.arg) # python 3 cvisitor.scope.addVariable(argname, arg) args_code.append(argname) # kwargs -> scope variable (pass by reference) kwargs_code = [] for key, val in sorted(kwargs.items(), key=lambda x: x[0]): cvisitor.scope.addVariable(key, val) kwargs_code.append('{}={}'.format(key, key)) # call AST args_text = ', '.join(args_code + kwargs_code) call_code = ''.join([self.targ.__name__, '(', args_text, ')']) _call_ast = ast.parse(call_code) # start visit self.return_value = cvisitor.visit(_call_ast.body[0].value) # clean-up jump conditions cvisitor.clearBreak() cvisitor.clearContinue() cvisitor.clearReturn() cvisitor.clearReturnVariable() # return to the previous scope frame cvisitor.popScope() return self.return_value def _synthesize_run_fsm(self, parent_fsm, args, kwargs, cond=None): start_flag = (parent_fsm.state == parent_fsm.current) if self.called is None: self.called = self.m.Reg('_'.join(['', self.name, 'called']), initval=0) if cond is not None: self.fsm.If(cond) self.fsm.If(start_flag)(self.called(1)) functions = self._get_functions() cvisitor = compiler.CompileVisitor(self.m, self.name, self.clk, self.rst, self.fsm, functions, self.intrinsic_functions, self.intrinsic_methods, self.start_frame, datawidth=self.datawidth) text = textwrap.dedent(inspect.getsource(self.targ)) tree = ast.parse(text).body[0] # stack a new scope frame cvisitor.pushScope(ftype='call') used_args = [] # args args_code = [] for pos, arginfo in enumerate(tree.args.args): argname = ( arginfo.id if isinstance(arginfo, ast.Name) # python 2 else arginfo.arg) # python 3 # actual values can be assigned later in default value section args_code.append(argname) # regular args if pos < len(args): arg = args[pos] used_args.append(argname) if isinstance(arg, vtypes.numerical_types): # pass by value if argname not in self.args_dict: name = compiler._tmp_name('_'.join( ['', self.name, argname])) v = self.m.Reg(name, self.datawidth, initval=0, signed=True) cvisitor.scope.addVariable(argname, v) self.args_dict[argname] = v else: v = self.args_dict[argname] # binding if cond is not None: self.fsm.If(cond) self.fsm.If(start_flag)(v(arg)) else: # pass by reference if argname not in self.args_dict: cvisitor.scope.addVariable(argname, arg) self.args_dict[argname] = arg elif id(self.args_dict[argname]) == id(arg): pass else: raise ValueError( 'same object must be passed for non-numeric argument' ) # variable length args if tree.args.vararg is None and len(args) > len(tree.args.args): raise TypeError('takes %d positional arguments but %d were given' % (len(tree.args.args), len(args))) if tree.args.vararg is not None and not self.vararg_set: baseobj = tree.args.vararg varargname = ( baseobj.id if isinstance(baseobj, ast.Name) # python 2 else baseobj.arg) # python 3 cvisitor.scope.addVariable(varargname, self.vararg_regs) if len(args) > len(tree.args.args): used_args.append(varargname) raise ValueError( 'variable length argument is not supported in dynamic thread execution' ) #num_vararg_vars = len(args) - len(tree.args.args) # if num_vararg_vars > len(self.vararg_regs): # for i in range(num_vararg_vars - len(self.vararg_regs)): # name = compiler._tmp_name('_'.join(['', self.name, varargname])) # v = self.m.Reg(name, self.datawidth, initval=0, signed=True) # self.vararg_regs.append(v) # args_code.append(varargname) # for i, arg in enumerate(args[-num_vararg_vars:]): # if not isinstance(arg, vtypes.numerical_types): # raise TypeError('variable length argument support no non-numeric values') # v = self.vararg_regs[i] # # binding # if cond is not None: # self.fsm.If(cond) # self.fsm.If(start_flag)( # v(arg) # ) # kwargs kwargs_code = [] for argname, arg in sorted(kwargs.items(), key=lambda x: x[0]): if argname in used_args: raise TypeError("got multiple values for argument '%s'" % argname) used_args.append(argname) if isinstance(arg, vtypes.numerical_types): # pass by value if argname not in self.args_dict: name = compiler._tmp_name('_'.join( ['', self.name, argname])) v = self.m.Reg(name, self.datawidth, initval=0, signed=True) cvisitor.scope.addVariable(argname, v) self.args_dict[argname] = v else: v = self.args_dict[argname] # binding if cond is not None: self.fsm.If(cond) self.fsm.If(start_flag)(v(arg)) else: # pass by reference if argname not in self.args_dict: cvisitor.scope.addVariable(argname, arg) self.args_dict[argname] = arg elif id(self.args_dict[argname]) == id(arg): pass else: raise ValueError( 'same object must be passed for non-numeric argument') kwargs_code.append('{}={}'.format(key, key)) # defaults defaults_size = len(tree.args.defaults) if defaults_size > 0: for arg, val in zip(tree.args.args[-defaults_size:], tree.args.defaults): argname = ( arg.id if isinstance(arg, ast.Name) # python 2 else arg.arg) # python 3 if argname not in self.args_dict: name = compiler._tmp_name('_'.join( ['', self.name, argname])) v = self.m.Reg(name, self.datawidth, initval=0, signed=True) cvisitor.scope.addVariable(argname, v) self.args_dict[argname] = v else: v = self.args_dict[argname] if argname not in used_args: right = cvisitor.visit(val) # binding if cond is not None: self.fsm.If(cond) self.fsm.If(start_flag)(v(right)) c = start_flag if cond is not None: c = vtypes.Land(c, cond) self.fsm.goto_from(self.start_state, self.start_state + 1, c) self.fsm._set_index(self.start_state + 1) parent_fsm.goto_next() # if already synthesized if self.end_state is not None: return self.return_value # call AST args_text = ', '.join(args_code + kwargs_code) call_code = ''.join([self.targ.__name__, '(', args_text, ')']) _call_ast = ast.parse(call_code) # start visit self.return_value = cvisitor.visit(_call_ast.body[0].value) # clean-up jump conditions cvisitor.clearBreak() cvisitor.clearContinue() cvisitor.clearReturn() cvisitor.clearReturnVariable() # return to the previous scope frame cvisitor.popScope() return self.return_value def _get_functions(self): codes = [] for func_name, func in self.function_lib.items(): codes.append(textwrap.dedent(inspect.getsource(func))) if self.targ.__name__ not in self.function_lib: codes.append(textwrap.dedent(inspect.getsource(self.targ))) text = '\n'.join(codes) _ast = ast.parse(text) functionvisitor = compiler.FunctionVisitor() functionvisitor.visit(_ast) functions = functionvisitor.getFunctions() return functions
class Stream(BaseStream): __intrinsics__ = ('set_source', 'set_source_pattern', 'set_source_multidim', 'set_sink', 'set_sink_pattern', 'set_sink_multidim', 'set_sink_empty', 'set_constant', 'run', 'join', 'done') def __init__(self, m, name, clk, rst, datawidth=32, addrwidth=32, max_pattern_length=4, ram_sel_width=8, fsm_as_module=False): BaseStream.__init__(self, module=m, clock=clk, reset=rst, no_hook=True) self.name = name self.datawidth = datawidth self.addrwidth = addrwidth self.max_pattern_length = max_pattern_length self.ram_sel_width = ram_sel_width self.fsm_as_module = fsm_as_module self.stream_synthesized = False self.fsm_synthesized = False self.fsm = FSM(self.module, '_%s_fsm' % self.name, self.clock, self.reset, as_module=self.fsm_as_module) self.start_flag = self.module.Wire( '_'.join(['', self.name, 'start_flag'])) self.start = self.module.Reg( '_'.join(['', self.name, 'start']), initval=0) self.busy = self.module.Reg( '_'.join(['', self.name, 'busy']), initval=0) self.reduce_reset = None self.reduce_reset_var = None self.sources = OrderedDict() self.sinks = OrderedDict() self.constants = OrderedDict() self.substreams = [] self.var_name_map = OrderedDict() self.var_id_map = OrderedDict() self.var_id_name_map = OrderedDict() self.var_name_id_map = OrderedDict() self.var_id_count = 0 self.source_idle_map = OrderedDict() self.sink_when_map = OrderedDict() self.ram_id_count = 1 # '0' is reserved for idle self.ram_id_map = OrderedDict() # key: ran._id(), value: count self.fsm_id_count = 0 self.ram_delay = 4 def source(self, name=None, datawidth=None, point=0, signed=True): if self.stream_synthesized: raise ValueError( 'cannot modify the stream because already synthesized') _id = self.var_id_count if name is None: name = 'source_%d' % _id if name in self.var_name_map: raise ValueError("'%s' is already defined in stream '%s'" % (name, self.name)) prefix = self._prefix(name) self.var_id_count += 1 if datawidth is None: datawidth = self.datawidth var = self.Variable(self._dataname(name), datawidth, point, signed) self.sources[name] = var self.var_id_map[_id] = var self.var_name_map[name] = var self.var_id_name_map[_id] = name self.var_name_id_map[name] = _id var.source_fsm = None var.source_pat_fsm = None var.source_idle = self.module.Reg('_%s_idle' % prefix, initval=1) self.source_idle_map[name] = var.source_idle # 0: set_source, 1: set_source_pattern var.source_mode = self.module.Reg('_%s_source_mode' % prefix, initval=0) var.source_offset = self.module.Reg('_%s_source_offset' % prefix, self.addrwidth, initval=0) var.source_size = self.module.Reg('_%s_source_size' % prefix, self.addrwidth + 1, initval=0) var.source_stride = self.module.Reg('_%s_source_stride' % prefix, self.addrwidth, initval=0) var.source_count = self.module.Reg('_%s_source_count' % prefix, self.addrwidth + 1, initval=0) var.source_pat_offsets = None var.source_pat_sizes = None var.source_pat_strides = None var.source_pat_counts = None var.source_ram_id_map = OrderedDict() var.source_ram_sel = self.module.Reg('_%s_source_ram_sel' % prefix, self.ram_sel_width, initval=0) var.source_ram_raddr = self.module.Reg('_%s_source_ram_raddr' % prefix, self.addrwidth, initval=0) var.source_ram_renable = self.module.Reg('_%s_source_ram_renable' % prefix, initval=0) var.source_ram_rdata = self.module.Wire('_%s_source_ram_rdata' % prefix, self.datawidth) var.source_ram_rvalid = self.module.Reg('_%s_source_ram_rvalid' % prefix, initval=0) return var def sink(self, data, name=None, when=None, when_name=None): if self.stream_synthesized: raise ValueError( 'cannot modify the stream because already synthesized') _id = self.var_id_count if name is None: name = 'sink_%d' % _id if name in self.var_name_map: raise ValueError("'%s' is already defined in stream '%s'" % (name, self.name)) else: data.output(self._dataname(name)) prefix = self._prefix(name) self.var_id_count += 1 self.sinks[name] = data self.var_id_map[_id] = data self.var_name_map[name] = data self.var_id_name_map[_id] = name self.var_name_id_map[name] = _id data.sink_fsm = None data.sink_pat_fsm = None # 0: set_sink, 1: set_sink_pattern data.sink_mode = self.module.Reg('_%s_sink_mode' % prefix, initval=0) data.sink_offset = self.module.Reg('_%s_sink_offset' % prefix, self.addrwidth, initval=0) data.sink_size = self.module.Reg('_%s_sink_size' % prefix, self.addrwidth + 1, initval=0) data.sink_stride = self.module.Reg('_%s_sink_stride' % prefix, self.addrwidth, initval=0) data.sink_count = self.module.Reg('_%s_sink_count' % prefix, self.addrwidth + 1, initval=0) data.sink_pat_offsets = None data.sink_pat_sizes = None data.sink_pat_strides = None data.sink_pat_counts = None data.sink_ram_id_map = OrderedDict() data.sink_ram_sel = self.module.Reg('_%s_sink_ram_sel' % prefix, self.ram_sel_width, initval=0) data.sink_ram_waddr = self.module.Reg('_%s_sink_waddr' % prefix, self.addrwidth, initval=0) data.sink_ram_wenable = self.module.Reg('_%s_sink_wenable' % prefix, initval=0) data.sink_ram_wdata = self.module.Reg('_%s_sink_wdata' % prefix, self.datawidth, initval=0) if when is not None: self.sink(when, when_name) self.sink_when_map[name] = when def constant(self, name=None, datawidth=None, point=0, signed=True): if self.stream_synthesized: raise ValueError( 'cannot modify the stream because already synthesized') _id = self.var_id_count if name is None: name = 'constant_%d' % _id if name in self.var_name_map: raise ValueError("'%s' is already defined in stream '%s'" % (name, self.name)) prefix = self._prefix(name) self.var_id_count += 1 if datawidth is None: datawidth = self.datawidth var = self.ParameterVariable(self._dataname(name), datawidth, point, signed) self.constants[name] = var self.var_id_map[_id] = var self.var_name_map[name] = var self.var_id_name_map[_id] = name self.var_name_id_map[name] = _id return var def substream(self, substrm): sub = Substream(self.module, self.clock, self.reset, substrm, self) self.substreams.append(sub) return sub def set_source(self, fsm, name, ram, offset, size, stride=1, port=0): """ intrinsic method to assign RAM property to a source stream """ if not self.stream_synthesized: self._implement_stream() if isinstance(name, str): var = self.var_name_map[name] elif isinstance(name, vtypes.Str): name = name.value var = self.var_name_map[name] elif isinstance(name, int): var = self.var_id_map[name] elif isinstance(name, vtypes.Int): name = name.value var = self.var_id_map[name] else: raise TypeError('Unsupported index name') if name not in self.sources: raise NameError("No such stream '%s'" % name) #set_cond = fsm.here set_cond = self._set_flag(fsm) self.seq.If(set_cond)( var.source_mode(0), var.source_offset(offset), var.source_size(size), var.source_stride(stride) ) port = vtypes.to_int(port) self._setup_source_ram(ram, var, port, set_cond) self._synthesize_set_source(var, name) fsm.goto_next() def set_source_pattern(self, fsm, name, ram, offset, pattern, port=0): """ intrinsic method to assign RAM property to a source stream """ if not self.stream_synthesized: self._implement_stream() if isinstance(name, str): var = self.var_name_map[name] elif isinstance(name, vtypes.Str): name = name.value var = self.var_name_map[name] elif isinstance(name, int): var = self.var_id_map[name] elif isinstance(name, vtypes.Int): name = name.value var = self.var_id_map[name] else: raise TypeError('Unsupported index name') if name not in self.sources: raise NameError("No such stream '%s'" % name) if not isinstance(pattern, (tuple, list)): raise TypeError('pattern must be list or tuple.') if not pattern: raise ValueError( 'pattern must have one (size, stride) pair at least.') if not isinstance(pattern[0], (tuple, list)): pattern = (pattern,) pattern = tuple(pattern) if len(pattern) > self.max_pattern_length: raise ValueError( "'pattern' length exceeds maximum pattern length.") self._make_source_pattern_vars(var, name) #set_cond = fsm.here set_cond = self._set_flag(fsm) self.seq.If(set_cond)( var.source_mode(1), var.source_offset(offset) ) pad = tuple([(1, 0) for _ in range(self.max_pattern_length - len(pattern))]) for (source_pat_size, source_pat_stride, (size, stride)) in zip(var.source_pat_sizes, var.source_pat_strides, pattern + pad): self.seq.If(set_cond)( source_pat_size(size), source_pat_stride(stride) ) port = vtypes.to_int(port) self._setup_source_ram(ram, var, port, set_cond) self._synthesize_set_source_pattern(var, name) fsm.goto_next() def set_source_multidim(self, fsm, name, ram, offset, shape, order=None, port=0): """ intrinsic method to assign RAM property to a source stream """ if order is None: order = list(reversed(range(len(shape)))) pattern = self._to_pattern(shape, order) return self.set_source_pattern(fsm, name, ram, offset, pattern, port) def set_sink(self, fsm, name, ram, offset, size, stride=1, port=0): """ intrinsic method to assign RAM property to a sink stream """ if not self.stream_synthesized: self._implement_stream() if isinstance(name, str): var = self.var_name_map[name] elif isinstance(name, vtypes.Str): name = name.value var = self.var_name_map[name] elif isinstance(name, int): var = self.var_id_map[name] elif isinstance(name, vtypes.Int): name = name.value var = self.var_id_map[name] else: raise TypeError('Unsupported index name') if name not in self.sinks: raise NameError("No such stream '%s'" % name) #set_cond = fsm.here set_cond = self._set_flag(fsm) self.seq.If(set_cond)( var.sink_mode(0), var.sink_offset(offset), var.sink_size(size), var.sink_stride(stride) ) port = vtypes.to_int(port) self._setup_sink_ram(ram, var, port, set_cond) self._synthesize_set_sink(var, name) fsm.goto_next() def set_sink_pattern(self, fsm, name, ram, offset, pattern, port=0): """ intrinsic method to assign RAM property to a sink stream """ if not self.stream_synthesized: self._implement_stream() if isinstance(name, str): var = self.var_name_map[name] elif isinstance(name, vtypes.Str): name = name.value var = self.var_name_map[name] elif isinstance(name, int): var = self.var_id_map[name] elif isinstance(name, vtypes.Int): name = name.value var = self.var_id_map[name] else: raise TypeError('Unsupported index name') if name not in self.sinks: raise NameError("No such stream '%s'" % name) if not isinstance(pattern, (tuple, list)): raise TypeError('pattern must be list or tuple.') if not pattern: raise ValueError( 'pattern must have one (size, stride) pair at least.') if not isinstance(pattern[0], (tuple, list)): pattern = (pattern,) pattern = tuple(pattern) if len(pattern) > self.max_pattern_length: raise ValueError( "'pattern' length exceeds maximum pattern length.") self._make_sink_pattern_vars(var, name) #set_cond = fsm.here set_cond = self._set_flag(fsm) self.seq.If(set_cond)( var.sink_mode(1), var.sink_offset(offset) ) pad = tuple([(1, 0) for _ in range(self.max_pattern_length - len(pattern))]) for (sink_pat_size, sink_pat_stride, (size, stride)) in zip(var.sink_pat_sizes, var.sink_pat_strides, pattern + pad): self.seq.If(set_cond)( sink_pat_size(size), sink_pat_stride(stride) ) port = vtypes.to_int(port) self._setup_sink_ram(ram, var, port, set_cond) self._synthesize_set_sink_pattern(var, name) fsm.goto_next() def set_sink_multidim(self, fsm, name, ram, offset, shape, order=None, port=0): """ intrinsic method to assign RAM property to a sink stream """ if order is None: order = list(reversed(range(len(shape)))) pattern = self._to_pattern(shape, order) return self.set_sink_pattern(fsm, name, ram, offset, pattern, port) def set_sink_empty(self, fsm, name): """ intrinsic method to assign RAM property to a sink stream """ if not self.stream_synthesized: self._implement_stream() if isinstance(name, str): var = self.var_name_map[name] elif isinstance(name, vtypes.Str): name = name.value var = self.var_name_map[name] elif isinstance(name, int): var = self.var_id_map[name] elif isinstance(name, vtypes.Int): name = name.value var = self.var_id_map[name] else: raise TypeError('Unsupported index name') if name not in self.sinks: raise NameError("No such stream '%s'" % name) #set_cond = fsm.here set_cond = self._set_flag(fsm) ram_sel = var.sink_ram_sel self.seq.If(set_cond)( ram_sel(0) # '0' is reserved for empty ) fsm.goto_next() def set_constant(self, fsm, name, value): """ intrinsic method to assign constant value to a constant stream """ if not self.stream_synthesized: self._implement_stream() if isinstance(name, str): var = self.var_name_map[name] elif isinstance(name, vtypes.Str): name = name.value var = self.var_name_map[name] elif isinstance(name, int): var = self.var_id_map[name] elif isinstance(name, vtypes.Int): name = name.value var = self.var_id_map[name] else: raise TypeError('Unsupported index name') if name not in self.constants: raise NameError("No such stream '%s'" % name) #set_cond = fsm.here set_cond = self._set_flag(fsm) wdata = value wenable = set_cond var.write(wdata, wenable) fsm.goto_next() def run(self, fsm): # entry point self.fsm._set_index(0) #cond = fsm.here cond = self._set_flag(fsm) add_mux(self.start_flag, cond, 1) # after started if self.fsm_synthesized: fsm.goto_next() fsm.goto_next() return self.fsm_synthesized = True num_wdelay = self._write_delay() self.fsm.If(self.start_flag)( self.start(1), self.busy(1) ) if self.reduce_reset is not None: self.fsm.seq.If(self.seq.Prev(self.start_flag, self.ram_delay + 1))( self.reduce_reset(0) ) substreams = self._collect_substreams() for sub in substreams: reset_delay = self.ram_delay + 1 + sub.start_stage + sub.reset_delay sub_fsm = sub.substrm.fsm sub_fsm._set_index(0) if sub.substrm.reduce_reset is not None: sub_fsm.seq.If(self.seq.Prev(self.start_flag, reset_delay))( sub.substrm.reduce_reset(0) ) for cond in sub.conds.values(): sub_fsm.If(self.start_flag)( cond(1) ) self.fsm.If(self.start_flag).goto_next() self.fsm( self.start(0) ) self.fsm.goto_next() done_cond = None for key, source_idle in sorted(self.source_idle_map.items(), key=lambda x: x[0]): done_cond = make_condition(done_cond, source_idle) done = self.module.Wire('_%s_done' % self.name) done.assign(done_cond) self.fsm.If(done).goto_next() depth = self.pipeline_depth() for _ in range(depth): self.fsm.goto_next() self.fsm.goto_next() # reset accumulate pipelines if self.reduce_reset is not None: self.fsm( self.reduce_reset(1) ) end_flag = self.fsm.here for sub in substreams: sub_fsm = sub.substrm.fsm sub_fsm._set_index(0) if sub.substrm.reduce_reset is not None: sub_fsm.If(end_flag)( sub.substrm.reduce_reset(1) ) for cond in sub.conds.values(): sub_fsm.If(end_flag)( cond(0) ) self.fsm.goto_next() self.fsm( self.busy(0) ) self.fsm.goto_init() fsm.goto_next() fsm.goto_next() return 0 def join(self, fsm): fsm.If(vtypes.Not(self.busy)).goto_next() return 0 def done(self, fsm): return vtypes.Not(self.busy) def _setup_source_ram(self, ram, var, port, set_cond): if ram._id() in var.source_ram_id_map: ram_id = var.source_ram_id_map[ram._id()] self.seq.If(set_cond)( var.source_ram_sel(ram_id) ) return if ram._id() not in self.ram_id_map: ram_id = self.ram_id_count self.ram_id_count += 1 self.ram_id_map[ram._id()] = ram_id else: ram_id = self.ram_id_map[ram._id()] var.source_ram_id_map[ram._id()] = ram_id self.seq.If(set_cond)( var.source_ram_sel(ram_id) ) ram_cond = (var.source_ram_sel == ram_id) renable = vtypes.Ands(var.source_ram_renable, ram_cond) d, v = ram.read_rtl(var.source_ram_raddr, port=port, cond=renable) add_mux(var.source_ram_rdata, ram_cond, d) self.seq( var.source_ram_rvalid(self.seq.Prev(renable, 1)) ) def _synthesize_set_source(self, var, name): if var.source_fsm is not None: return wdata = var.source_ram_rdata wenable = var.source_ram_rvalid var.write(wdata, wenable) source_start = vtypes.Ands(self.start, var.source_mode == 0, var.source_size > 0) self.seq.If(source_start)( var.source_idle(0) ) fsm_id = self.fsm_id_count self.fsm_id_count += 1 prefix = self._prefix(name) fsm_name = '_%s_source_fsm_%d' % (prefix, fsm_id) var.source_fsm = FSM(self.module, fsm_name, self.clock, self.reset, as_module=self.fsm_as_module) var.source_fsm.If(source_start).goto_next() self.seq.If(var.source_fsm.here)( var.source_ram_raddr(var.source_offset), var.source_ram_renable(1), var.source_count(var.source_size) ) var.source_fsm.goto_next() self.seq.If(var.source_fsm.here)( var.source_ram_raddr.add(var.source_stride), var.source_ram_renable(1), var.source_count.dec() ) self.seq.If(var.source_fsm.here, var.source_count == 1)( var.source_ram_renable(0), var.source_idle(1) ) var.source_fsm.If(var.source_count == 1).goto_init() def _make_source_pattern_vars(self, var, name): if var.source_pat_offsets is not None: return prefix = self._prefix(name) var.source_pat_offsets = [self.module.Reg('_source_%s_pat_offset_%d' % (prefix, i), self.addrwidth, initval=0) for i in range(self.max_pattern_length)] var.source_pat_sizes = [self.module.Reg('_source_%s_pat_size_%d' % (prefix, i), self.addrwidth + 1, initval=0) for i in range(self.max_pattern_length)] var.source_pat_strides = [self.module.Reg('_source_%s_pat_stride_%d' % (prefix, i), self.addrwidth, initval=0) for i in range(self.max_pattern_length)] var.source_pat_counts = [self.module.Reg('_source_%s_pat_count_%d' % (prefix, i), self.addrwidth + 1, initval=0) for i in range(self.max_pattern_length)] def _synthesize_set_source_pattern(self, var, name): if var.source_pat_fsm is not None: return wdata = var.source_ram_rdata wenable = var.source_ram_rvalid var.write(wdata, wenable) source_start = vtypes.Ands(self.start, var.source_mode == 1) for source_pat_size in var.source_pat_sizes: source_start = vtypes.Ands(source_start, source_pat_size > 0) self.seq.If(source_start)( var.source_idle(0) ) for source_pat_offset in var.source_pat_offsets: self.seq.If(source_start)( source_pat_offset(0) ) for (source_pat_size, source_pat_count) in zip( var.source_pat_sizes, var.source_pat_counts): self.seq.If(source_start)( source_pat_count(source_pat_size - 1) ) fsm_id = self.fsm_id_count self.fsm_id_count += 1 prefix = self._prefix(name) fsm_name = '_%s_source_pat_fsm_%d' % (prefix, fsm_id) var.source_pat_fsm = FSM(self.module, fsm_name, self.clock, self.reset, as_module=self.fsm_as_module) var.source_pat_fsm.If(source_start).goto_next() source_all_offset = self.module.Wire('_%s_source_pat_all_offset' % prefix, self.addrwidth) source_all_offset_val = var.source_offset for source_pat_offset in var.source_pat_offsets: source_all_offset_val += source_pat_offset source_all_offset.assign(source_all_offset_val) self.seq.If(var.source_pat_fsm.here)( var.source_ram_raddr(source_all_offset), var.source_ram_renable(1) ) upcond = None for (source_pat_offset, source_pat_size, source_pat_stride, source_pat_count) in zip( var.source_pat_offsets, var.source_pat_sizes, var.source_pat_strides, var.source_pat_counts): self.seq.If(var.source_pat_fsm.here, upcond)( source_pat_offset.add(source_pat_stride), source_pat_count.dec() ) reset_cond = source_pat_count == 0 self.seq.If(var.source_pat_fsm.here, upcond, reset_cond)( source_pat_offset(0), source_pat_count(source_pat_size - 1) ) upcond = make_condition(upcond, reset_cond) fin_cond = upcond var.source_pat_fsm.If(fin_cond).goto_next() self.seq.If(var.source_pat_fsm.here)( var.source_ram_renable(0), var.source_idle(1) ) var.source_pat_fsm.goto_init() def _setup_sink_ram(self, ram, var, port, set_cond): if ram._id() in var.sink_ram_id_map: ram_id = var.sink_ram_id_map[ram._id()] self.seq.If(set_cond)( var.sink_ram_sel(ram_id) ) return if ram._id() not in self.ram_id_map: ram_id = self.ram_id_count self.ram_id_count += 1 self.ram_id_map[ram._id()] = ram_id else: ram_id = self.ram_id_map[ram._id()] var.sink_ram_id_map[ram._id()] = ram_id self.seq.If(set_cond)( var.sink_ram_sel(ram_id) ) ram_cond = (var.sink_ram_sel == ram_id) wenable = vtypes.Ands(var.sink_ram_wenable, ram_cond) ram.write_rtl(var.sink_ram_waddr, var.sink_ram_wdata, port=port, cond=wenable) def _synthesize_set_sink(self, var, name): if var.sink_fsm is not None: return sink_start = vtypes.Ands(self.start, var.sink_mode == 0, var.sink_size > 0) fsm_id = self.fsm_id_count self.fsm_id_count += 1 prefix = self._prefix(name) fsm_name = '_%s_sink_fsm_%d' % (prefix, fsm_id) var.sink_fsm = FSM(self.module, fsm_name, self.clock, self.reset, as_module=self.fsm_as_module) self.seq.If(var.sink_fsm.here)( var.sink_ram_wenable(0) ) var.sink_fsm.If(sink_start).goto_next() self.seq.If(var.sink_fsm.here)( var.sink_ram_waddr(var.sink_offset - var.sink_stride), var.sink_count(var.sink_size) ) num_wdelay = self._write_delay() for _ in range(num_wdelay): var.sink_fsm.goto_next() if name in self.sink_when_map: when = self.sink_when_map[name] wcond = when.read() else: wcond = None rdata = var.read() self.seq.If(var.sink_fsm.here)( var.sink_ram_wenable(0) ) self.seq.If(var.sink_fsm.here, wcond)( var.sink_ram_waddr.add(var.sink_stride), var.sink_ram_wdata(rdata), var.sink_ram_wenable(1), var.sink_count.dec() ) var.sink_fsm.If(wcond, var.sink_count == 1).goto_init() def _make_sink_pattern_vars(self, var, name): if var.sink_pat_offsets is not None: return prefix = self._prefix(name) var.sink_pat_offsets = [self.module.Reg('_sink_%s_pat_offset_%d' % (prefix, i), self.addrwidth, initval=0) for i in range(self.max_pattern_length)] var.sink_pat_sizes = [self.module.Reg('_sink_%s_pat_size_%d' % (prefix, i), self.addrwidth + 1, initval=0) for i in range(self.max_pattern_length)] var.sink_pat_strides = [self.module.Reg('_sink_%s_pat_stride_%d' % (prefix, i), self.addrwidth, initval=0) for i in range(self.max_pattern_length)] var.sink_pat_counts = [self.module.Reg('_sink_%s_pat_count_%d' % (prefix, i), self.addrwidth + 1, initval=0) for i in range(self.max_pattern_length)] def _synthesize_set_sink_pattern(self, var, name): if var.sink_pat_fsm is not None: return sink_start = vtypes.Ands(self.start, var.sink_mode == 1) for sink_pat_size in var.sink_pat_sizes: sink_start = vtypes.Ands(sink_start, sink_pat_size > 0) fsm_id = self.fsm_id_count self.fsm_id_count += 1 prefix = self._prefix(name) fsm_name = '_%s_sink_pat_fsm_%d' % (prefix, fsm_id) var.sink_pat_fsm = FSM(self.module, fsm_name, self.clock, self.reset, as_module=self.fsm_as_module) self.seq.If(var.sink_pat_fsm.here)( var.sink_ram_wenable(0) ) var.sink_pat_fsm.If(sink_start).goto_next() self.seq.If(var.sink_pat_fsm.here)( var.sink_ram_waddr(var.sink_offset - var.sink_stride) ) for sink_pat_offset in var.sink_pat_offsets: self.seq.If(var.sink_pat_fsm.here)( sink_pat_offset(0) ) for (sink_pat_size, sink_pat_count) in zip( var.sink_pat_sizes, var.sink_pat_counts): self.seq.If(var.sink_pat_fsm.here)( sink_pat_count(sink_pat_size - 1) ) num_wdelay = self._write_delay() for _ in range(num_wdelay): var.sink_pat_fsm.goto_next() if name in self.sink_when_map: when = self.sink_when_map[name] wcond = when.read() else: wcond = None sink_all_offset = self.module.Wire('_%s_sink_pat_all_offset' % prefix, self.addrwidth) sink_all_offset_val = var.sink_offset for sink_pat_offset in var.sink_pat_offsets: sink_all_offset_val += sink_pat_offset sink_all_offset.assign(sink_all_offset_val) if name in self.sink_when_map: when = self.sink_when_map[name] wcond = when.read() else: wcond = None rdata = var.read() self.seq.If(var.sink_pat_fsm.here)( var.sink_ram_wenable(0) ) self.seq.If(var.sink_pat_fsm.here, wcond)( var.sink_ram_waddr(sink_all_offset), var.sink_ram_wdata(rdata), var.sink_ram_wenable(1) ) upcond = None for (sink_pat_offset, sink_pat_size, sink_pat_stride, sink_pat_count) in zip( var.sink_pat_offsets, var.sink_pat_sizes, var.sink_pat_strides, var.sink_pat_counts): self.seq.If(var.sink_pat_fsm.here, upcond)( sink_pat_offset.add(sink_pat_stride), sink_pat_count.dec() ) reset_cond = sink_pat_count == 0 self.seq.If(var.sink_pat_fsm.here, upcond, reset_cond)( sink_pat_offset(0), sink_pat_count(sink_pat_size - 1) ) upcond = make_condition(upcond, reset_cond) fin_cond = upcond var.sink_pat_fsm.If(fin_cond).goto_init() def _set_flag(self, fsm, prefix='_set_flag'): flag = self.module.TmpReg(initval=0, prefix=prefix) cond = fsm.here self.seq( flag(0) ) self.seq.If(cond)( flag(1) ) return flag def _implement_stream(self): self.implement() self.stream_synthesized = True def _write_delay(self): depth = self.pipeline_depth() return depth + self.ram_delay def _to_pattern(self, shape, order): pattern = [] for p in order: if not isinstance(p, int): raise TypeError( "Values of 'order' must be 'int', not %s" % str(type(p))) size = shape[p] basevalue = 1 if isinstance(size, int) else vtypes.Int(1) stride = functools.reduce(lambda x, y: x * y, shape[p + 1:], basevalue) pattern.append((size, stride)) return tuple(pattern) def _prefix(self, name): return '%s_%s' % (self.name, name) def _dataname(self, name): return '%s_data' % self._prefix(name) def _collect_substreams(self): ret = [] for sub in self.substreams: ret.extend(sub._collect_substreams()) return ret def __getattr__(self, attr): f = BaseStream.__getattr__(self, attr) if callable(f) and f.__name__.startswith('Reduce'): if self.reduce_reset is None: self.reduce_reset = self.module.Reg( '_'.join(['', self.name, 'reduce_reset']), initval=1) self.reduce_reset_var = self.Variable( self.reduce_reset, width=1) return functools.partial(f, reset=self.reduce_reset_var) return f
class Thread(vtypes.VeriloggenNode): __intrinsics__ = ('run', 'join', 'done', 'reset', 'ret') def __init__(self, m, name, clk, rst, targ, datawidth=32, point=16, tid=None, fsm_as_module=False): self.m = m self.name = name self.clk = clk self.rst = rst self.targ = targ self.datawidth = datawidth self.point = point self.tid = tid self.fsm_as_module = fsm_as_module self.function_lib = OrderedDict() self.intrinsic_functions = OrderedDict() self.intrinsic_methods = OrderedDict() self.fsm = None self.is_child = False self.start_state = None self.end_state = None self.return_value = None self.start_frame = None self.args_dict = OrderedDict() self.vararg_regs = [] self.vararg_set = False self.called = None def start(self, *args, **kwargs): """ build up a new FSM based on the arguments """ if self.is_child: raise ValueError('already started as a child thread') if self.end_state is not None: raise ValueError('already started') frame = inspect.currentframe() self.start_frame = frame.f_back self.fsm = FSM(self.m, self.name, self.clk, self.rst, as_module=self.fsm_as_module) self.start_state = self.fsm.current self._synthesize_start_fsm(args, kwargs) self.end_state = self.fsm.current return self.fsm def extend(self, fsm, *args, **kwargs): """ extend a given thread FSM """ frame = inspect.currentframe() self.start_frame = frame.f_back self._synthesize_start_fsm(args, kwargs, fsm) return fsm def _embed_thread(self, fsm, *args, **kwargs): """ extend a given thread FSM (for embed_thread func) """ frame = inspect.currentframe() self.start_frame = frame.f_back.f_back self._synthesize_start_fsm(args, kwargs, fsm) return fsm def run(self, fsm, *args, **kwargs): """ start as a child thread """ if not self.is_child and self.end_state is not None: raise ValueError('already started') if self.fsm is None: self.fsm = FSM(self.m, self.name, self.clk, self.rst, as_module=self.fsm_as_module) self.is_child = True if self.start_state is not None: self.fsm._set_index(self.start_state) else: self.start_state = self.fsm.current self._synthesize_run_fsm(fsm, args, kwargs) if self.end_state is None: self.end_state = self.fsm.current start_flag = (self.fsm.state == self.start_state) return start_flag def join(self, fsm): """ wait for the completion """ if self.end_state is None: raise ValueError('not started') end_flag = (self.fsm.state == self.end_state) fsm.If(end_flag).goto_next() return 0 def done(self, fsm): """ check whethe the thread is running """ if self.end_state is None: raise ValueError('not started') end_flag = (self.fsm.state == self.end_state) return end_flag def reset(self, fsm): """ reset the FSM counter to the initial state """ if self.end_state is None: raise ValueError('not started') reset_flag = (fsm.state == fsm.current) self.fsm._set_index(self.end_state) if self.called is not None: self.fsm.If(reset_flag)( self.called(0) ) self.fsm.goto_from(self.end_state, self.start_state, reset_flag) self.fsm._set_index(self.start_state) return 0 def ret(self, fsm): """ return value """ return self.return_value #-------------------------------------------------------------------------- def add_function(self, func): name = func.__name__ if name in self.function_lib: raise ValueError( 'Function {} is already defined'.format(name)) self.function_lib[name] = func return func def add_intrinsics(self, *funcs): for func in funcs: self.intrinsic(func) def add_intrinsic_method_prefix(self, obj, prefix): funcs = [method for name, method in inspect.getmembers(obj, inspect.ismethod) if name.startswith(prefix)] self.add_intrinsics(*funcs) def intrinsic(self, func): if inspect.isfunction(func): return self._add_intrinsic_function(func) if inspect.ismethod(func): return self._add_intrinsic_method(func) raise TypeError("'%s' object is not supported" % str(type(func))) def _add_intrinsic_function(self, func): name = func.__name__ if name in self.intrinsic_functions: raise ValueError( 'Intrinsic function {} is already defined'.format(name)) self.intrinsic_functions[name] = func return func def _add_intrinsic_method(self, func): name = str(func) if name in self.intrinsic_methods: raise ValueError( 'Intrinsic method {} is already defined'.format(name)) self.intrinsic_methods[name] = func return func def _synthesize_start_fsm(self, args, kwargs, fsm=None): if fsm is None: fsm = self.fsm functions = self._get_functions() cvisitor = compiler.CompileVisitor(self.m, self.name, self.clk, self.rst, fsm, functions, self.intrinsic_functions, self.intrinsic_methods, self.start_frame, datawidth=self.datawidth, point=self.point) text = textwrap.dedent(inspect.getsource(self.targ)) tree = ast.parse(text).body[0] # stack a new scope frame cvisitor.pushScope(ftype='call') # args -> scope variable (pass by reference) args_code = [] rest_args = [] for pos, arg in enumerate(args): baseobj = tree.args.args[pos] argname = (baseobj.id if isinstance(baseobj, ast.Name) # python 2 else baseobj.arg) # python 3 cvisitor.scope.addVariable(argname, arg) args_code.append(argname) # kwargs -> scope variable (pass by reference) kwargs_code = [] for key, val in sorted(kwargs.items(), key=lambda x: x[0]): cvisitor.scope.addVariable(key, val) kwargs_code.append('{}={}'.format(key, key)) # call AST args_text = ', '.join(args_code + kwargs_code) call_code = ''.join([self.targ.__name__, '(', args_text, ')']) _call_ast = ast.parse(call_code) # start visit self.return_value = cvisitor.visit(_call_ast.body[0].value) # clean-up jump conditions cvisitor.clearBreak() cvisitor.clearContinue() cvisitor.clearReturn() cvisitor.clearReturnVariable() # return to the previous scope frame cvisitor.popScope() return self.return_value def _synthesize_run_fsm(self, parent_fsm, args, kwargs, cond=None): start_flag = (parent_fsm.state == parent_fsm.current) if self.called is None: self.called = self.m.Reg( '_'.join(['', self.name, 'called']), initval=0) if cond is not None: self.fsm.If(cond) self.fsm.If(start_flag)( self.called(1) ) functions = self._get_functions() cvisitor = compiler.CompileVisitor(self.m, self.name, self.clk, self.rst, self.fsm, functions, self.intrinsic_functions, self.intrinsic_methods, self.start_frame, datawidth=self.datawidth) text = textwrap.dedent(inspect.getsource(self.targ)) tree = ast.parse(text).body[0] # stack a new scope frame cvisitor.pushScope(ftype='call') used_args = [] # args args_code = [] for pos, arginfo in enumerate(tree.args.args): argname = (arginfo.id if isinstance(arginfo, ast.Name) # python 2 else arginfo.arg) # python 3 # actual values can be assigned later in default value section args_code.append(argname) # regular args if pos < len(args): arg = args[pos] used_args.append(argname) if isinstance(arg, vtypes.numerical_types): # pass by value if argname not in self.args_dict: name = compiler._tmp_name( '_'.join(['', self.name, argname])) v = self.m.Reg(name, self.datawidth, initval=0, signed=True) cvisitor.scope.addVariable(argname, v) self.args_dict[argname] = v else: v = self.args_dict[argname] # binding if cond is not None: self.fsm.If(cond) self.fsm.If(start_flag)( v(arg) ) else: # pass by reference if argname not in self.args_dict: cvisitor.scope.addVariable(argname, arg) self.args_dict[argname] = arg elif id(self.args_dict[argname]) == id(arg): pass else: raise ValueError( 'same object must be passed for non-numeric argument') # variable length args if tree.args.vararg is None and len(args) > len(tree.args.args): raise TypeError('takes %d positional arguments but %d were given' % (len(tree.args.args), len(args))) if tree.args.vararg is not None and not self.vararg_set: baseobj = tree.args.vararg varargname = (baseobj.id if isinstance(baseobj, ast.Name) # python 2 else baseobj.arg) # python 3 cvisitor.scope.addVariable(varargname, self.vararg_regs) if len(args) > len(tree.args.args): used_args.append(varargname) raise ValueError( 'variable length argument is not supported in dynamic thread execution') #num_vararg_vars = len(args) - len(tree.args.args) # if num_vararg_vars > len(self.vararg_regs): # for i in range(num_vararg_vars - len(self.vararg_regs)): # name = compiler._tmp_name('_'.join(['', self.name, varargname])) # v = self.m.Reg(name, self.datawidth, initval=0, signed=True) # self.vararg_regs.append(v) # args_code.append(varargname) # for i, arg in enumerate(args[-num_vararg_vars:]): # if not isinstance(arg, vtypes.numerical_types): # raise TypeError('variable length argument support no non-numeric values') # v = self.vararg_regs[i] # # binding # if cond is not None: # self.fsm.If(cond) # self.fsm.If(start_flag)( # v(arg) # ) # kwargs kwargs_code = [] for argname, arg in sorted(kwargs.items(), key=lambda x: x[0]): if argname in used_args: raise TypeError( "got multiple values for argument '%s'" % argname) used_args.append(argname) if isinstance(arg, vtypes.numerical_types): # pass by value if argname not in self.args_dict: name = compiler._tmp_name( '_'.join(['', self.name, argname])) v = self.m.Reg(name, self.datawidth, initval=0, signed=True) cvisitor.scope.addVariable(argname, v) self.args_dict[argname] = v else: v = self.args_dict[argname] # binding if cond is not None: self.fsm.If(cond) self.fsm.If(start_flag)( v(arg) ) else: # pass by reference if argname not in self.args_dict: cvisitor.scope.addVariable(argname, arg) self.args_dict[argname] = arg elif id(self.args_dict[argname]) == id(arg): pass else: raise ValueError( 'same object must be passed for non-numeric argument') kwargs_code.append('{}={}'.format(key, key)) # defaults defaults_size = len(tree.args.defaults) if defaults_size > 0: for arg, val in zip(tree.args.args[-defaults_size:], tree.args.defaults): argname = (arg.id if isinstance(arg, ast.Name) # python 2 else arg.arg) # python 3 if argname not in self.args_dict: name = compiler._tmp_name( '_'.join(['', self.name, argname])) v = self.m.Reg(name, self.datawidth, initval=0, signed=True) cvisitor.scope.addVariable(argname, v) self.args_dict[argname] = v else: v = self.args_dict[argname] if argname not in used_args: right = cvisitor.visit(val) # binding if cond is not None: self.fsm.If(cond) self.fsm.If(start_flag)( v(right) ) c = start_flag if cond is not None: c = vtypes.Land(c, cond) self.fsm.goto_from(self.start_state, self.start_state + 1, c) self.fsm._set_index(self.start_state + 1) parent_fsm.goto_next() # if already synthesized if self.end_state is not None: return self.return_value # call AST args_text = ', '.join(args_code + kwargs_code) call_code = ''.join([self.targ.__name__, '(', args_text, ')']) _call_ast = ast.parse(call_code) # start visit self.return_value = cvisitor.visit(_call_ast.body[0].value) # clean-up jump conditions cvisitor.clearBreak() cvisitor.clearContinue() cvisitor.clearReturn() cvisitor.clearReturnVariable() # return to the previous scope frame cvisitor.popScope() return self.return_value def _get_functions(self): codes = [] for func_name, func in self.function_lib.items(): codes.append(textwrap.dedent(inspect.getsource(func))) if self.targ.__name__ not in self.function_lib: codes.append(textwrap.dedent(inspect.getsource(self.targ))) text = '\n'.join(codes) _ast = ast.parse(text) functionvisitor = compiler.FunctionVisitor() functionvisitor.visit(_ast) functions = functionvisitor.getFunctions() return functions
def set_sink_pattern(self, fsm, name, ram, offset, pattern, port=0): """ intrinsic method to assign RAM property to a sink stream """ if not self.stream_synthesized: self._implement_stream() if isinstance(name, str): var = self.var_name_map[name] elif isinstance(name, vtypes.Str): name = name.value var = self.var_name_map[name] elif isinstance(name, int): var = self.var_id_map[name] elif isinstance(name, vtypes.Int): name = name.value var = self.var_id_map[name] else: raise TypeError('Unsupported index name') if name not in self.sinks: raise NameError("No such stream '%s'" % name) if not isinstance(pattern, (tuple, list)): raise TypeError('pattern must be list or tuple.') if not pattern: raise ValueError( 'pattern must have one (size, stride) pair at least.') if not isinstance(pattern[0], (tuple, list)): pattern = (pattern,) prefix = self._prefix(name) fsm_id = self.fsm_id_count fsm_name = '_%s_fsm_%d' % (prefix, fsm_id) sink_fsm = FSM(self.module, fsm_name, self.clock, self.reset) self.fsm_id_map[fsm_id] = sink_fsm self.fsm_id_count += 1 sink_offset = self.module.Reg('_%s_offset_%d' % (prefix, fsm_id), ram.addrwidth, initval=0) sink_pat_offsets = [self.module.Reg('_%s_pat_offset_%d_%d' % (prefix, fsm_id, i), ram.addrwidth, initval=0) for i, _ in enumerate(pattern)] sink_pat_sizes = [self.module.Reg('_%s_pat_size_%d_%d' % (prefix, fsm_id, i), ram.addrwidth + 1, initval=0) for i, _ in enumerate(pattern)] sink_pat_strides = [self.module.Reg('_%s_pat_stride_%d_%d' % (prefix, fsm_id, i), ram.addrwidth, initval=0) for i, _ in enumerate(pattern)] sink_pat_counts = [self.module.Reg('_%s_pat_count_%d_%d' % (prefix, fsm_id, i), ram.addrwidth + 1, initval=0) for i, _ in enumerate(pattern)] var_id = self.var_name_id_map[name] fsm_sel = self.var_id_fsm_sel_map[var_id] set_cond = (fsm.state == fsm.current) sink_fsm.If(set_cond)( sink_offset(offset) ) self.seq.If(set_cond)( fsm_sel(fsm_id) ) for sink_pat_offset in sink_pat_offsets: sink_fsm.If(set_cond)( sink_pat_offset(0) ) for (sink_pat_size, sink_pat_stride, (size, stride)) in zip( sink_pat_sizes, sink_pat_strides, pattern): sink_fsm.If(set_cond)( sink_pat_size(size), sink_pat_stride(stride) ) sink_start = vtypes.Ands(self.start, fsm_sel == fsm_id) for sink_pat_size in sink_pat_sizes: sink_start = vtypes.Ands(sink_start, sink_pat_size > 0) for (sink_pat_size, sink_pat_count) in zip( sink_pat_sizes, sink_pat_counts): sink_fsm.If(sink_start)( sink_pat_count(sink_pat_size - 1) ) sink_fsm.If(sink_start).goto_next() num_wdelay = self._write_delay() for i in range(num_wdelay): sink_fsm.goto_next() sink_all_offset = self.module.Wire('_%s_all_offset_%d' % (prefix, fsm_id), ram.addrwidth) sink_all_offset_val = sink_offset for sink_pat_offset in sink_pat_offsets: sink_all_offset_val += sink_pat_offset sink_all_offset.assign(sink_all_offset_val) waddr = self.module.Reg('_%s_waddr_%d' % (prefix, fsm_id), ram.addrwidth, initval=0) wenable = self.module.Reg('_%s_wenable_%d' % (prefix, fsm_id), initval=0) wdata = self.module.Reg('_%s_wdata_%d' % (prefix, fsm_id), ram.datawidth, initval=0, signed=True) rdata = var.read() ram.write_rtl(waddr, wdata, port=port, cond=wenable) if name in self.sink_when_map: when = self.sink_when_map[name] wcond = when.read() else: wcond = None sink_fsm.If(wcond)( waddr(sink_all_offset), wdata(rdata), wenable(1) ) sink_fsm.Delay(1)( wenable(0) ) upcond = None for (sink_pat_offset, sink_pat_size, sink_pat_stride, sink_pat_count) in zip( sink_pat_offsets, sink_pat_sizes, sink_pat_strides, sink_pat_counts): sink_fsm.If(upcond)( sink_pat_offset.add(sink_pat_stride), sink_pat_count.dec() ) reset_cond = sink_pat_count == 0 sink_fsm.If(upcond, reset_cond)( sink_pat_offset(0), sink_pat_count(sink_pat_size - 1) ) upcond = make_condition(upcond, reset_cond) fin_cond = upcond sink_fsm.If(fin_cond).goto_init() sink_fsm._set_index(0) fsm.goto_next()
def set_sink(self, fsm, name, ram, offset, size, stride=1, port=0): """ intrinsic method to assign RAM property to a sink stream """ if not self.stream_synthesized: self._implement_stream() if isinstance(name, str): var = self.var_name_map[name] elif isinstance(name, vtypes.Str): name = name.value var = self.var_name_map[name] elif isinstance(name, int): var = self.var_id_map[name] elif isinstance(name, vtypes.Int): name = name.value var = self.var_id_map[name] else: raise TypeError('Unsupported index name') if name not in self.sinks: raise NameError("No such stream '%s'" % name) prefix = self._prefix(name) fsm_id = self.fsm_id_count fsm_name = '_%s_fsm_%d' % (prefix, fsm_id) sink_fsm = FSM(self.module, fsm_name, self.clock, self.reset) self.fsm_id_map[fsm_id] = sink_fsm self.fsm_id_count += 1 sink_offset = self.module.Reg('_%s_offset_%d' % (prefix, fsm_id), ram.addrwidth, initval=0) sink_size = self.module.Reg('_%s_size_%d' % (prefix, fsm_id), ram.addrwidth + 1, initval=0) sink_stride = self.module.Reg('_%s_stride_%d' % (prefix, fsm_id), ram.addrwidth, initval=0) sink_count = self.module.Reg('_%s_count_%d' % (prefix, fsm_id), ram.addrwidth + 1, initval=0) var_id = self.var_name_id_map[name] fsm_sel = self.var_id_fsm_sel_map[var_id] set_cond = (fsm.state == fsm.current) sink_fsm.If(set_cond)( sink_offset(offset), sink_size(size), sink_stride(stride) ) self.seq.If(set_cond)( fsm_sel(fsm_id) ) sink_start = vtypes.Ands( self.start, fsm_sel == fsm_id, sink_size > 0) sink_fsm.If(sink_start)( sink_count(sink_size) ) sink_fsm.If(sink_start).goto_next() num_wdelay = self._write_delay() for i in range(num_wdelay): sink_fsm.goto_next() waddr = self.module.Reg('_%s_waddr_%d' % (prefix, fsm_id), ram.addrwidth, initval=0) wenable = self.module.Reg('_%s_wenable_%d' % (prefix, fsm_id), initval=0) wdata = self.module.Reg('_%s_wdata_%d' % (prefix, fsm_id), ram.datawidth, initval=0, signed=True) rdata = var.read() ram.write_rtl(waddr, wdata, port=port, cond=wenable) if name in self.sink_when_map: when = self.sink_when_map[name] wcond = when.read() else: wcond = None sink_fsm.If(wcond)( waddr(sink_offset), wdata(rdata), wenable(1), sink_count.dec() ) sink_fsm.Delay(1)( wenable(0) ) sink_fsm.If(wcond, sink_count == 1).goto_init() sink_fsm.If(wcond, sink_count > 1).goto_next() sink_fsm.If(wcond)( waddr.add(sink_stride), wdata(rdata), wenable(1), sink_count.dec() ) sink_fsm.Delay(1)( wenable(0) ) sink_fsm.If(wcond, sink_count == 1).goto_init() sink_fsm._set_index(0) fsm.goto_next()
class Stream(BaseStream): __intrinsics__ = ('set_source', 'set_source_pattern', 'set_source_multidim', 'set_sink', 'set_sink_pattern', 'set_sink_multidim', 'set_sink_empty', 'set_constant', 'run', 'join', 'done') def __init__(self, m, name, clk, rst, datawidth=32, fsm_sel_width=16): BaseStream.__init__(self, module=m, clock=clk, reset=rst, no_hook=True) self.name = name self.datawidth = datawidth self.fsm_sel_width = fsm_sel_width self.stream_synthesized = False self.fsm_synthesized = False self.fsm = FSM(self.module, '_%s_fsm' % self.name, self.clock, self.reset) self.start = self.module.Reg( '_'.join(['', self.name, 'start']), initval=0) self.busy = self.module.Reg( '_'.join(['', self.name, 'busy']), initval=0) self.reduce_reset = None self.reduce_reset_var = None self.sources = OrderedDict() self.sinks = OrderedDict() self.constants = OrderedDict() self.substreams = [] self.var_name_map = OrderedDict() self.var_id_map = OrderedDict() self.var_id_name_map = OrderedDict() self.var_name_id_map = OrderedDict() self.var_id_count = 0 self.source_idle_map = OrderedDict() self.sink_when_map = OrderedDict() self.fsm_id_map = OrderedDict() self.fsm_id_count = 1 # '0' is reserved for idle self.var_id_fsm_sel_map = OrderedDict() # key: var_id, value: fsm_id reg self.ram_delay = 4 def source(self, name=None, datawidth=None, point=0, signed=True): if self.stream_synthesized: raise ValueError( 'cannot modify the stream because already synthesized') _id = self.var_id_count if name is None: name = 'source_%d' % _id if name in self.var_name_map: raise ValueError("'%s' is already defined in stream '%s'" % (name, self.name)) prefix = self._prefix(name) self.var_id_count += 1 if datawidth is None: datawidth = self.datawidth var = self.Variable(self._dataname(name), datawidth, point, signed) self.sources[name] = var self.var_id_map[_id] = var self.var_name_map[name] = var self.var_id_name_map[_id] = name self.var_name_id_map[name] = _id self.var_id_fsm_sel_map[_id] = self.module.Reg('_%s_fsm_sel' % prefix, self.fsm_sel_width, initval=0) self.source_idle_map[name] = self.module.Reg('_%s_idle' % prefix, initval=1) return var def sink(self, data, name=None, when=None, when_name=None): if self.stream_synthesized: raise ValueError( 'cannot modify the stream because already synthesized') _id = self.var_id_count if name is None: name = 'sink_%d' % _id prefix = self._prefix(name) if name in self.var_name_map: raise ValueError("'%s' is already defined in stream '%s'" % (name, self.name)) else: data.output(self._dataname(name)) self.var_id_count += 1 self.sinks[name] = data self.var_id_map[_id] = data self.var_name_map[name] = data self.var_id_name_map[_id] = name self.var_name_id_map[name] = _id self.var_id_fsm_sel_map[_id] = self.module.Reg('_%s_fsm_sel' % prefix, self.fsm_sel_width, initval=0) if when is not None: self.sink(when, when_name) self.sink_when_map[name] = when def constant(self, name=None, datawidth=None, point=0, signed=True): if self.stream_synthesized: raise ValueError( 'cannot modify the stream because already synthesized') _id = self.var_id_count if name is None: name = 'constant_%d' % _id if name in self.var_name_map: raise ValueError("'%s' is already defined in stream '%s'" % (name, self.name)) prefix = self._prefix(name) self.var_id_count += 1 if datawidth is None: datawidth = self.datawidth var = self.ParameterVariable(self._dataname(name), datawidth, point, signed) self.constants[name] = var self.var_id_map[_id] = var self.var_name_map[name] = var self.var_id_name_map[_id] = name self.var_name_id_map[name] = _id self.var_id_fsm_sel_map[_id] = self.module.Reg('_%s_fsm_sel' % prefix, self.fsm_sel_width, initval=0) return var def substream(self, substrm): sub = Substream(self.module, self.clock, self.reset, substrm, self) self.substreams.append(sub) return sub def set_source(self, fsm, name, ram, offset, size, stride=1, port=0): """ intrinsic method to assign RAM property to a source stream """ if not self.stream_synthesized: self._implement_stream() if isinstance(name, str): var = self.var_name_map[name] elif isinstance(name, vtypes.Str): name = name.value var = self.var_name_map[name] elif isinstance(name, int): var = self.var_id_map[name] elif isinstance(name, vtypes.Int): name = name.value var = self.var_id_map[name] else: raise TypeError('Unsupported index name') if name not in self.sources: raise NameError("No such stream '%s'" % name) prefix = self._prefix(name) fsm_id = self.fsm_id_count fsm_name = '_%s_fsm_%d' % (prefix, fsm_id) source_fsm = FSM(self.module, fsm_name, self.clock, self.reset) self.fsm_id_map[fsm_id] = source_fsm self.fsm_id_count += 1 source_idle = self.source_idle_map[name] source_offset = self.module.Reg('_%s_offset_%d' % (prefix, fsm_id), ram.addrwidth, initval=0) source_size = self.module.Reg('_%s_size_%d' % (prefix, fsm_id), ram.addrwidth + 1, initval=0) source_stride = self.module.Reg('_%s_stride_%d' % (prefix, fsm_id), ram.addrwidth, initval=0) source_count = self.module.Reg('_%s_count_%d' % (prefix, fsm_id), ram.addrwidth + 1, initval=0) var_id = self.var_name_id_map[name] fsm_sel = self.var_id_fsm_sel_map[var_id] set_cond = (fsm.state == fsm.current) source_fsm.If(set_cond)( source_offset(offset), source_size(size), source_stride(stride) ) self.seq.If(set_cond)( fsm_sel(fsm_id) ) self.seq.If(self.start)( source_idle(0) ) source_start = vtypes.Ands( self.start, fsm_sel == fsm_id, source_size > 0) source_fsm.If(source_start)( source_count(source_size) ) source_fsm.If(source_start).goto_next() raddr = self.module.Reg('_%s_raddr_%d' % (prefix, fsm_id), ram.addrwidth, initval=0) renable = self.module.Reg('_%s_renable_%d' % (prefix, fsm_id), initval=0) rdata, rvalid = ram.read_rtl(raddr, port=port, cond=renable) wdata = rdata wenable = rvalid var.write(wdata, wenable) source_fsm( raddr(source_offset), renable(1), source_count.dec() ) source_fsm.Delay(1)( renable(0) ) self.seq.If(source_fsm.state == source_fsm.current, source_count == 1)( source_idle(1) ) source_fsm.If(source_count == 1).goto_init() source_fsm.If(source_count > 1).goto_next() source_fsm( raddr.add(source_stride), renable(1), source_count.dec() ) source_fsm.Delay(1)( renable(0) ) source_fsm.If(source_count == 1).goto_init() self.seq.If(source_fsm.state == source_fsm.current, source_count == 1)( source_idle(1) ) source_fsm._set_index(0) fsm.goto_next() def set_source_pattern(self, fsm, name, ram, offset, pattern, port=0): """ intrinsic method to assign RAM property to a source stream """ if not self.stream_synthesized: self._implement_stream() if isinstance(name, str): var = self.var_name_map[name] elif isinstance(name, vtypes.Str): name = name.value var = self.var_name_map[name] elif isinstance(name, int): var = self.var_id_map[name] elif isinstance(name, vtypes.Int): name = name.value var = self.var_id_map[name] else: raise TypeError('Unsupported index name') if name not in self.sources: raise NameError("No such stream '%s'" % name) if not isinstance(pattern, (tuple, list)): raise TypeError('pattern must be list or tuple.') if not pattern: raise ValueError( 'pattern must have one (size, stride) pair at least.') if not isinstance(pattern[0], (tuple, list)): pattern = (pattern,) prefix = self._prefix(name) fsm_id = self.fsm_id_count fsm_name = '_%s_fsm_%d' % (prefix, fsm_id) source_fsm = FSM(self.module, fsm_name, self.clock, self.reset) self.fsm_id_map[fsm_id] = source_fsm self.fsm_id_count += 1 source_idle = self.source_idle_map[name] source_offset = self.module.Reg('_%s_offset_%d' % (prefix, fsm_id), ram.addrwidth, initval=0) source_pat_offsets = [self.module.Reg('_%s_pat_offset_%d_%d' % (prefix, fsm_id, i), ram.addrwidth, initval=0) for i, _ in enumerate(pattern)] source_pat_sizes = [self.module.Reg('_%s_pat_size_%d_%d' % (prefix, fsm_id, i), ram.addrwidth + 1, initval=0) for i, _ in enumerate(pattern)] source_pat_strides = [self.module.Reg('_%s_pat_stride_%d_%d' % (prefix, fsm_id, i), ram.addrwidth, initval=0) for i, _ in enumerate(pattern)] source_pat_counts = [self.module.Reg('_%s_pat_count_%d_%d' % (prefix, fsm_id, i), ram.addrwidth + 1, initval=0) for i, _ in enumerate(pattern)] var_id = self.var_name_id_map[name] fsm_sel = self.var_id_fsm_sel_map[var_id] set_cond = (fsm.state == fsm.current) source_fsm.If(set_cond)( source_offset(offset) ) self.seq.If(set_cond)( fsm_sel(fsm_id) ) for source_pat_offset in source_pat_offsets: source_fsm.If(set_cond)( source_pat_offset(0) ) for (source_pat_size, source_pat_stride, (size, stride)) in zip( source_pat_sizes, source_pat_strides, pattern): source_fsm.If(set_cond)( source_pat_size(size), source_pat_stride(stride) ) self.seq.If(self.start)( source_idle(0) ) source_start = vtypes.Ands(self.start, fsm_sel == fsm_id) for source_pat_size in source_pat_sizes: source_start = vtypes.Ands(source_start, source_pat_size > 0) for (source_pat_size, source_pat_count) in zip( source_pat_sizes, source_pat_counts): source_fsm.If(source_start)( source_pat_count(source_pat_size - 1) ) source_fsm.If(source_start).goto_next() source_all_offset = self.module.Wire('_%s_all_offset_%d' % (prefix, fsm_id), ram.addrwidth) source_all_offset_val = source_offset for source_pat_offset in source_pat_offsets: source_all_offset_val += source_pat_offset source_all_offset.assign(source_all_offset_val) raddr = self.module.Reg('_%s_raddr_%d' % (prefix, fsm_id), ram.addrwidth, initval=0) renable = self.module.Reg('_%s_renable_%d' % (prefix, fsm_id), initval=0) rdata, rvalid = ram.read_rtl(raddr, port=port, cond=renable) wdata = rdata wenable = rvalid var.write(wdata, wenable) source_fsm( raddr(source_all_offset), renable(1) ) source_fsm.Delay(1)( renable(0) ) upcond = None for (source_pat_offset, source_pat_size, source_pat_stride, source_pat_count) in zip( source_pat_offsets, source_pat_sizes, source_pat_strides, source_pat_counts): source_fsm.If(upcond)( source_pat_offset.add(source_pat_stride), source_pat_count.dec() ) reset_cond = source_pat_count == 0 source_fsm.If(upcond, reset_cond)( source_pat_offset(0), source_pat_count(source_pat_size - 1) ) upcond = make_condition(upcond, reset_cond) fin_cond = upcond source_fsm.If(fin_cond).goto_init() self.seq.If(source_fsm.state == source_fsm.current, fin_cond)( source_idle(1) ) source_fsm._set_index(0) fsm.goto_next() def set_source_multidim(self, fsm, name, ram, offset, shape, order=None, port=0): """ intrinsic method to assign RAM property to a source stream """ if order is None: order = list(reversed(range(len(shape)))) pattern = self._to_pattern(shape, order) return self.set_source_pattern(fsm, name, ram, offset, pattern, port) def set_sink(self, fsm, name, ram, offset, size, stride=1, port=0): """ intrinsic method to assign RAM property to a sink stream """ if not self.stream_synthesized: self._implement_stream() if isinstance(name, str): var = self.var_name_map[name] elif isinstance(name, vtypes.Str): name = name.value var = self.var_name_map[name] elif isinstance(name, int): var = self.var_id_map[name] elif isinstance(name, vtypes.Int): name = name.value var = self.var_id_map[name] else: raise TypeError('Unsupported index name') if name not in self.sinks: raise NameError("No such stream '%s'" % name) prefix = self._prefix(name) fsm_id = self.fsm_id_count fsm_name = '_%s_fsm_%d' % (prefix, fsm_id) sink_fsm = FSM(self.module, fsm_name, self.clock, self.reset) self.fsm_id_map[fsm_id] = sink_fsm self.fsm_id_count += 1 sink_offset = self.module.Reg('_%s_offset_%d' % (prefix, fsm_id), ram.addrwidth, initval=0) sink_size = self.module.Reg('_%s_size_%d' % (prefix, fsm_id), ram.addrwidth + 1, initval=0) sink_stride = self.module.Reg('_%s_stride_%d' % (prefix, fsm_id), ram.addrwidth, initval=0) sink_count = self.module.Reg('_%s_count_%d' % (prefix, fsm_id), ram.addrwidth + 1, initval=0) var_id = self.var_name_id_map[name] fsm_sel = self.var_id_fsm_sel_map[var_id] set_cond = (fsm.state == fsm.current) sink_fsm.If(set_cond)( sink_offset(offset), sink_size(size), sink_stride(stride) ) self.seq.If(set_cond)( fsm_sel(fsm_id) ) sink_start = vtypes.Ands( self.start, fsm_sel == fsm_id, sink_size > 0) sink_fsm.If(sink_start)( sink_count(sink_size) ) sink_fsm.If(sink_start).goto_next() num_wdelay = self._write_delay() for i in range(num_wdelay): sink_fsm.goto_next() waddr = self.module.Reg('_%s_waddr_%d' % (prefix, fsm_id), ram.addrwidth, initval=0) wenable = self.module.Reg('_%s_wenable_%d' % (prefix, fsm_id), initval=0) wdata = self.module.Reg('_%s_wdata_%d' % (prefix, fsm_id), ram.datawidth, initval=0, signed=True) rdata = var.read() ram.write_rtl(waddr, wdata, port=port, cond=wenable) if name in self.sink_when_map: when = self.sink_when_map[name] wcond = when.read() else: wcond = None sink_fsm.If(wcond)( waddr(sink_offset), wdata(rdata), wenable(1), sink_count.dec() ) sink_fsm.Delay(1)( wenable(0) ) sink_fsm.If(wcond, sink_count == 1).goto_init() sink_fsm.If(wcond, sink_count > 1).goto_next() sink_fsm.If(wcond)( waddr.add(sink_stride), wdata(rdata), wenable(1), sink_count.dec() ) sink_fsm.Delay(1)( wenable(0) ) sink_fsm.If(wcond, sink_count == 1).goto_init() sink_fsm._set_index(0) fsm.goto_next() def set_sink_pattern(self, fsm, name, ram, offset, pattern, port=0): """ intrinsic method to assign RAM property to a sink stream """ if not self.stream_synthesized: self._implement_stream() if isinstance(name, str): var = self.var_name_map[name] elif isinstance(name, vtypes.Str): name = name.value var = self.var_name_map[name] elif isinstance(name, int): var = self.var_id_map[name] elif isinstance(name, vtypes.Int): name = name.value var = self.var_id_map[name] else: raise TypeError('Unsupported index name') if name not in self.sinks: raise NameError("No such stream '%s'" % name) if not isinstance(pattern, (tuple, list)): raise TypeError('pattern must be list or tuple.') if not pattern: raise ValueError( 'pattern must have one (size, stride) pair at least.') if not isinstance(pattern[0], (tuple, list)): pattern = (pattern,) prefix = self._prefix(name) fsm_id = self.fsm_id_count fsm_name = '_%s_fsm_%d' % (prefix, fsm_id) sink_fsm = FSM(self.module, fsm_name, self.clock, self.reset) self.fsm_id_map[fsm_id] = sink_fsm self.fsm_id_count += 1 sink_offset = self.module.Reg('_%s_offset_%d' % (prefix, fsm_id), ram.addrwidth, initval=0) sink_pat_offsets = [self.module.Reg('_%s_pat_offset_%d_%d' % (prefix, fsm_id, i), ram.addrwidth, initval=0) for i, _ in enumerate(pattern)] sink_pat_sizes = [self.module.Reg('_%s_pat_size_%d_%d' % (prefix, fsm_id, i), ram.addrwidth + 1, initval=0) for i, _ in enumerate(pattern)] sink_pat_strides = [self.module.Reg('_%s_pat_stride_%d_%d' % (prefix, fsm_id, i), ram.addrwidth, initval=0) for i, _ in enumerate(pattern)] sink_pat_counts = [self.module.Reg('_%s_pat_count_%d_%d' % (prefix, fsm_id, i), ram.addrwidth + 1, initval=0) for i, _ in enumerate(pattern)] var_id = self.var_name_id_map[name] fsm_sel = self.var_id_fsm_sel_map[var_id] set_cond = (fsm.state == fsm.current) sink_fsm.If(set_cond)( sink_offset(offset) ) self.seq.If(set_cond)( fsm_sel(fsm_id) ) for sink_pat_offset in sink_pat_offsets: sink_fsm.If(set_cond)( sink_pat_offset(0) ) for (sink_pat_size, sink_pat_stride, (size, stride)) in zip( sink_pat_sizes, sink_pat_strides, pattern): sink_fsm.If(set_cond)( sink_pat_size(size), sink_pat_stride(stride) ) sink_start = vtypes.Ands(self.start, fsm_sel == fsm_id) for sink_pat_size in sink_pat_sizes: sink_start = vtypes.Ands(sink_start, sink_pat_size > 0) for (sink_pat_size, sink_pat_count) in zip( sink_pat_sizes, sink_pat_counts): sink_fsm.If(sink_start)( sink_pat_count(sink_pat_size - 1) ) sink_fsm.If(sink_start).goto_next() num_wdelay = self._write_delay() for i in range(num_wdelay): sink_fsm.goto_next() sink_all_offset = self.module.Wire('_%s_all_offset_%d' % (prefix, fsm_id), ram.addrwidth) sink_all_offset_val = sink_offset for sink_pat_offset in sink_pat_offsets: sink_all_offset_val += sink_pat_offset sink_all_offset.assign(sink_all_offset_val) waddr = self.module.Reg('_%s_waddr_%d' % (prefix, fsm_id), ram.addrwidth, initval=0) wenable = self.module.Reg('_%s_wenable_%d' % (prefix, fsm_id), initval=0) wdata = self.module.Reg('_%s_wdata_%d' % (prefix, fsm_id), ram.datawidth, initval=0, signed=True) rdata = var.read() ram.write_rtl(waddr, wdata, port=port, cond=wenable) if name in self.sink_when_map: when = self.sink_when_map[name] wcond = when.read() else: wcond = None sink_fsm.If(wcond)( waddr(sink_all_offset), wdata(rdata), wenable(1) ) sink_fsm.Delay(1)( wenable(0) ) upcond = None for (sink_pat_offset, sink_pat_size, sink_pat_stride, sink_pat_count) in zip( sink_pat_offsets, sink_pat_sizes, sink_pat_strides, sink_pat_counts): sink_fsm.If(upcond)( sink_pat_offset.add(sink_pat_stride), sink_pat_count.dec() ) reset_cond = sink_pat_count == 0 sink_fsm.If(upcond, reset_cond)( sink_pat_offset(0), sink_pat_count(sink_pat_size - 1) ) upcond = make_condition(upcond, reset_cond) fin_cond = upcond sink_fsm.If(fin_cond).goto_init() sink_fsm._set_index(0) fsm.goto_next() def set_sink_multidim(self, fsm, name, ram, offset, shape, order=None, port=0): """ intrinsic method to assign RAM property to a sink stream """ if order is None: order = list(reversed(range(len(shape)))) pattern = self._to_pattern(shape, order) return self.set_sink_pattern(fsm, name, ram, offset, pattern, port) def set_sink_empty(self, fsm, name): """ intrinsic method to assign RAM property to a sink stream """ if not self.stream_synthesized: self._implement_stream() if isinstance(name, str): var = self.var_name_map[name] elif isinstance(name, vtypes.Str): name = name.value var = self.var_name_map[name] elif isinstance(name, int): var = self.var_id_map[name] elif isinstance(name, vtypes.Int): name = name.value var = self.var_id_map[name] else: raise TypeError('Unsupported index name') if name not in self.sinks: raise NameError("No such stream '%s'" % name) prefix = self._prefix(name) fsm_id = self.fsm_id_count fsm_name = '_%s_fsm_%d' % (prefix, fsm_id) sink_fsm = None self.fsm_id_map[fsm_id] = sink_fsm self.fsm_id_count += 1 var_id = self.var_name_id_map[name] fsm_sel = self.var_id_fsm_sel_map[var_id] set_cond = (fsm.state == fsm.current) self.seq.If(set_cond)( fsm_sel(fsm_id) ) fsm.goto_next() def set_constant(self, fsm, name, value): """ intrinsic method to assign constant value to a constant stream """ if not self.stream_synthesized: self._implement_stream() if isinstance(name, str): var = self.var_name_map[name] elif isinstance(name, vtypes.Str): name = name.value var = self.var_name_map[name] elif isinstance(name, int): var = self.var_id_map[name] elif isinstance(name, vtypes.Int): name = name.value var = self.var_id_map[name] else: raise TypeError('Unsupported index name') if name not in self.constants: raise NameError("No such stream '%s'" % name) set_cond = (fsm.state == fsm.current) wdata = value wenable = set_cond var.write(wdata, wenable) fsm.goto_next() def run(self, fsm): # entry point self.fsm._set_index(0) start_flag = (fsm.state == fsm.current) self.fsm.If(start_flag)( self.start(1), self.busy(1) ) self.fsm.If(start_flag).Delay(1)( self.start(0) ) if self.reduce_reset is not None: self.fsm.If(start_flag).Delay(self.ram_delay + 1)( self.reduce_reset(0) ) substreams = self._collect_substreams() for sub in substreams: reset_delay = self.ram_delay + 1 + sub.start_stage if sub.substrm.reduce_reset is not None: self.fsm.If(start_flag).Delay(reset_delay)( sub.substrm.reduce_reset(0) ) for cond in sub.conds.values(): self.fsm.If(start_flag)( cond(1) ) self.fsm.If(start_flag).goto_next() # after started if self.fsm_synthesized: fsm.goto_next() return self.fsm_synthesized = True self.fsm.goto_next() done_cond = None for key, source_idle in sorted(self.source_idle_map.items(), key=lambda x: x[0]): done_cond = make_condition(done_cond, source_idle) done = self.module.Wire('_%s_done' % self.name) done.assign(done_cond) self.fsm.If(done).goto_next() depth = self.pipeline_depth() num_wdelay = self._write_delay() # pipeline delay for i in range(depth): self.fsm.goto_next() self.fsm.goto_next() # reset accumulate pipelines if self.reduce_reset is not None: self.fsm( self.reduce_reset(1) ) for sub in substreams: reset_delay = sub.start_stage if sub.substrm.reduce_reset is not None: self.fsm( sub.substrm.reduce_reset(1) ) for cond in sub.conds.values(): self.fsm( cond(0) ) for i in range(num_wdelay - depth - 1): self.fsm.goto_next() # finish self.fsm( self.busy(0) ) self.fsm.goto_init() fsm.goto_next() return 0 def join(self, fsm): fsm.If(vtypes.Not(self.busy)).goto_next() return 0 def done(self, fsm): return vtypes.Not(self.busy) def _implement_stream(self): self.implement() self.stream_synthesized = True def _write_delay(self): depth = self.pipeline_depth() return depth + self.ram_delay def _to_pattern(self, shape, order): pattern = [] for p in order: if not isinstance(p, int): raise TypeError( "Values of 'order' must be 'int', not %s" % str(type(p))) size = shape[p] basevalue = 1 if isinstance(size, int) else vtypes.Int(1) stride = functools.reduce(lambda x, y: x * y, shape[p + 1:], basevalue) pattern.append((size, stride)) return pattern def _prefix(self, name): return '%s_%s' % (self.name, name) def _dataname(self, name): return '%s_data' % self._prefix(name) def _collect_substreams(self): ret = [] for sub in self.substreams: ret.extend(sub._collect_substreams()) return ret def __getattr__(self, attr): f = BaseStream.__getattr__(self, attr) if callable(f) and f.__name__.startswith('Reduce'): if self.reduce_reset is None: self.reduce_reset = self.module.Reg( '_'.join(['', self.name, 'reduce_reset']), initval=1) self.reduce_reset_var = self.Variable( self.reduce_reset, width=1) return functools.partial(f, reset=self.reduce_reset_var) return f
def set_source(self, fsm, name, ram, offset, size, stride=1, port=0): """ intrinsic method to assign RAM property to a source stream """ if not self.stream_synthesized: self._implement_stream() if isinstance(name, str): var = self.var_name_map[name] elif isinstance(name, vtypes.Str): name = name.value var = self.var_name_map[name] elif isinstance(name, int): var = self.var_id_map[name] elif isinstance(name, vtypes.Int): name = name.value var = self.var_id_map[name] else: raise TypeError('Unsupported index name') if name not in self.sources: raise NameError("No such stream '%s'" % name) prefix = self._prefix(name) fsm_id = self.fsm_id_count fsm_name = '_%s_fsm_%d' % (prefix, fsm_id) source_fsm = FSM(self.module, fsm_name, self.clock, self.reset) self.fsm_id_map[fsm_id] = source_fsm self.fsm_id_count += 1 source_idle = self.source_idle_map[name] source_offset = self.module.Reg('_%s_offset_%d' % (prefix, fsm_id), ram.addrwidth, initval=0) source_size = self.module.Reg('_%s_size_%d' % (prefix, fsm_id), ram.addrwidth + 1, initval=0) source_stride = self.module.Reg('_%s_stride_%d' % (prefix, fsm_id), ram.addrwidth, initval=0) source_count = self.module.Reg('_%s_count_%d' % (prefix, fsm_id), ram.addrwidth + 1, initval=0) var_id = self.var_name_id_map[name] fsm_sel = self.var_id_fsm_sel_map[var_id] set_cond = (fsm.state == fsm.current) source_fsm.If(set_cond)( source_offset(offset), source_size(size), source_stride(stride) ) self.seq.If(set_cond)( fsm_sel(fsm_id) ) self.seq.If(self.start)( source_idle(0) ) source_start = vtypes.Ands( self.start, fsm_sel == fsm_id, source_size > 0) source_fsm.If(source_start)( source_count(source_size) ) source_fsm.If(source_start).goto_next() raddr = self.module.Reg('_%s_raddr_%d' % (prefix, fsm_id), ram.addrwidth, initval=0) renable = self.module.Reg('_%s_renable_%d' % (prefix, fsm_id), initval=0) rdata, rvalid = ram.read_rtl(raddr, port=port, cond=renable) wdata = rdata wenable = rvalid var.write(wdata, wenable) source_fsm( raddr(source_offset), renable(1), source_count.dec() ) source_fsm.Delay(1)( renable(0) ) self.seq.If(source_fsm.state == source_fsm.current, source_count == 1)( source_idle(1) ) source_fsm.If(source_count == 1).goto_init() source_fsm.If(source_count > 1).goto_next() source_fsm( raddr.add(source_stride), renable(1), source_count.dec() ) source_fsm.Delay(1)( renable(0) ) source_fsm.If(source_count == 1).goto_init() self.seq.If(source_fsm.state == source_fsm.current, source_count == 1)( source_idle(1) ) source_fsm._set_index(0) fsm.goto_next()
class Thread(vtypes.VeriloggenNode): __intrinsics__ = ('run', 'join', 'busy') def __init__(self, m, clk, rst, name, targ, datawidth=32): self.m = m self.clk = clk self.rst = rst self.name = name self.targ = targ self.datawidth = datawidth self.function_lib = OrderedDict() self.intrinsic_functions = OrderedDict() self.intrinsic_methods = OrderedDict() self.fsm = None self.is_child = False self.start_state = None self.end_state = None self.local_objects = {} def start(self, *args, **kwargs): """ build up a new FSM based on the arguments """ if self.is_child: raise ValueError('already started as a child thread.') if self.end_state is not None: raise ValueError('already started.') frame = inspect.currentframe() _locals = frame.f_back.f_locals self.local_objects = OrderedDict() for key, value in _locals.items(): self.local_objects[key] = value self.fsm = FSM(self.m, self.name, self.clk, self.rst) self.start_state = self.fsm.current self._synthesize_fsm(self.fsm, args, kwargs) self.end_state = self.fsm.current return self.fsm def extend(self, fsm, *args, **kwargs): """ extend a given thread FSM """ frame = inspect.currentframe() _locals = frame.f_back.f_locals self.local_objects = OrderedDict() for key, value in _locals.items(): self.local_objects[key] = value self._synthesize_fsm(fsm, args, kwargs) return fsm def run(self, fsm, *args, **kwargs): """ start as a child thread """ if not self.is_child and self.end_state is not None: raise ValueError('already started.') self.fsm = FSM(self.m, self.name, self.clk, self.rst) self.is_child = True if self.start_state is None: self.start_state = self.fsm.current start_flag = (fsm.state == fsm.current) self.fsm.goto_from(self.start_state, self.start_state + 1, start_flag) self.fsm._set_index(self.start_state + 1) if self.end_state is not None: return 0 self._synthesize_fsm(self.fsm, args, kwargs) if self.end_state is None: self.end_state = self.fsm.current return 0 def join(self, fsm): if self.end_state is None: raise ValueError('not started') end_flag = (self.fsm.state == self.end_state) fsm.If(end_flag).goto_next() return 0 def busy(self, fsm): if self.end_state is None: raise ValueError('not started') end_flag = (self.fsm.state == self.end_state) return end_flag def add_function(self, func): name = func.__name__ if name in self.function_lib: raise ValueError('Function {} is already defined'.format(name)) self.function_lib[name] = func return func def add_intrinsics(self, *funcs): for func in funcs: self.intrinsic(func) def add_intrinsic_method_prefix(self, obj, prefix): funcs = [ method for name, method in inspect.getmembers(obj, inspect.ismethod) if name.startswith(prefix) ] self.add_intrinsics(*funcs) def intrinsic(self, func): if inspect.isfunction(func): return self._add_intrinsic_function(func) if inspect.ismethod(func): return self._add_intrinsic_method(func) raise TypeError("'%s' object is not supported" % str(type(func))) def _add_intrinsic_function(self, func): name = func.__name__ if name in self.intrinsic_functions: raise ValueError( 'Intrinsic function {} is already defined'.format(name)) self.intrinsic_functions[name] = func return func def _add_intrinsic_method(self, func): name = str(func) if name in self.intrinsic_methods: raise ValueError( 'Intrinsic method {} is already defined'.format(name)) self.intrinsic_methods[name] = func return func def _synthesize_fsm(self, fsm, args, kwargs): codes = [] for func_name, func in self.function_lib.items(): codes.append(textwrap.dedent(inspect.getsource(func))) if self.targ.__name__ not in self.function_lib: codes.append(textwrap.dedent(inspect.getsource(self.targ))) text = '\n'.join(codes) _ast = ast.parse(text) functionvisitor = compiler.FunctionVisitor() functionvisitor.visit(_ast) functions = functionvisitor.getFunctions() local_objects = {} for key, value in self.local_objects.items(): local_objects[key] = value # function argument args_code = [] for i, arg in enumerate(args): argkey = '__arg_%d' % i local_objects[argkey] = arg args_code.append(argkey) kwargs_code = [] i = 0 for k, v in sorted(kwargs.items(), key=lambda x: x[0]): argkey = '__kwarg_%d' % i local_objects[argkey] = v kwargs_code.append('{}={}'.format(k, argkey)) i += 1 args_text = ', '.join(args_code + kwargs_code) call_code = ''.join([self.targ.__name__, '(', args_text, ')']) _call_ast = ast.parse(call_code) compilevisitor = compiler.CompileVisitor(self.m, self.name, self.clk, self.rst, fsm, functions, self.intrinsic_functions, self.intrinsic_methods, local_objects, datawidth=self.datawidth) compilevisitor.visit(_call_ast)