class RegTB(Module): def instantiate(self): self.ra = Reg(0) self.rb = Reg(10) def tick(self): print("ra val: %d" % self.ra.rd()) print("rb val: %d" % self.rb.rd()) self.ra.wr(self.ra.rd() + 1) self.rb.wr(self.ra.rd() + self.rb.rd())
class Channel(Module): def instantiate(self, depth=2, name=None): self.data = [None] * depth self.depth = depth self.name = name self.rd_ptr = Reg(0) self.wr_ptr = Reg(0) def peek(self, idx=0): if not self.valid(idx): raise ChannelError("Reading from empty channel") return self.data[(self.rd_ptr.rd() + idx) % self.depth] def push(self, x): if not self.vacancy(): raise ChannelError("Enqueueing into full channel") self.data[self.wr_ptr.rd() % self.depth] = x self.wr_ptr.wr((self.wr_ptr.rd() + 1) % (2 * self.depth)) if self.name != None: self.output_file.write("chn {} push\n".format(self.name)) def free(self, count=1): if not self.valid(count - 1): raise ChannelError("Dequeueing from empty channel") self.rd_ptr.wr((self.rd_ptr.rd() + count) % (2 * self.depth)) def pop(self): self.free(1) ret = self.peek(0) if self.name != None: self.output_file.write("chn {} pop\n".format(self.name)) return ret def valid(self, idx=0): return ((self.wr_ptr.rd() - self.rd_ptr.rd()) % (2 * self.depth)) > idx def vacancy(self, idx=0): return ((self.rd_ptr.rd() + self.depth - self.wr_ptr.rd()) % (2 * self.depth)) > idx def clear(self): # Use with care since it conflicts with enq and deq self.rd_ptr.wr(self.wr_ptr.rd())
class Channel(Module): def instantiate(self, depth=2): self.data = [None] * depth self.depth = depth self.rd_ptr = Reg(0) self.wr_ptr = Reg(0) def peek(self, idx=0): if not self.valid(idx): raise ChannelError("Reading from empty channel") return self.data[(self.rd_ptr.rd() + idx) % self.depth] def push(self, x): if not self.vacancy(): raise ChannelError("Enqueueing into full channel") self.data[self.wr_ptr.rd() % self.depth] = x self.wr_ptr.wr((self.wr_ptr.rd() + 1) % (2 * self.depth)) def free(self, count=1): if not self.valid(count - 1): raise ChannelError("Dequeueing from empty channel") self.rd_ptr.wr((self.rd_ptr.rd() + count) % (2 * self.depth)) def pop(self): self.free(1) return self.peek(0) def valid(self, idx=0): # check not empty return ((self.wr_ptr.rd() - self.rd_ptr.rd()) % (2 * self.depth)) > idx def vacancy(self, idx=0): # check not full return ((self.rd_ptr.rd() + self.depth - self.wr_ptr.rd()) % (2 * self.depth)) > idx def clear(self): # Use with care since it conflicts with enq and deq self.rd_ptr.wr(self.wr_ptr.rd())
class FIFOTB(Module): def instantiate(self, check_fifo=True): self.check_fifo = check_fifo self.counter = Reg(0) self.fifo = FIFO(4) def tick(self): count = self.counter.rd() self.counter.wr((count + 1) % 256) if count % 4 < 2 and (self.fifo.not_full() or not self.check_fifo): self.fifo.enq(count) print("enq: %d" % count) if count % 4 == 3 and (self.fifo.not_empty() or not self.check_fifo): peek = self.fifo.peek() self.fifo.deq() print("deq: %d" % peek)
class InputSerializer(Module): def instantiate(self, arch_input_chn, arr_x, arr_y, chn_per_word): # PE static configuration (immutable) self.arr_x = arr_x self.arr_y = arr_y self.chn_per_word = chn_per_word self.arch_input_chn = arch_input_chn self.ifmap = None self.weights = None self.image_size = (0, 0) self.filter_size = (0, 0) self.ifmap_psum_done = True self.pass_done = Reg(False) # State Counters self.curr_set = 0 self.curr_filter = 0 self.iteration = 0 self.fmap_idx = 0 self.bias_idx = 0 def configure(self, ifmap, weights, biases, image_size, filter_size): #self.ifmap = ifmap #self.weights = weights self.biases = biases self.image_size = image_size self.filter_size = image_size self.num_tiles = 4 self.send_ifmap = True self.fmap_idx = 0 self.fmap_tile = 0 self.weight_idx = 0 self.bias_sets = 2 self.fmap_wr_done = False self.weight_wr_done = False self.bias_wr_done = False self.pass_done.wr(False) # pad the ifmaps ifmap_padded = np.pad(ifmap, 1, 'constant') ifmap_padded = ifmap_padded[:, :, 1:5] # Winograd transforms B_T = np.array([[1, 0, -1, 0], [0, 1, 1, 0], [0, -1, 1, 0], [0, 1, 0, -1]]) B = B_T.transpose() G = np.array([[1, 0, 0], [0.5, 0.5, 0.5], [0.5, -0.5, 0.5], [0, 0, 1]]) G_T = G.transpose() A_T = np.array([[1, 1, 1, 0], [0, 1, -1, -1]]) A = A_T.transpose() C = 4 # num channels K = 8 # num filters T = 4 # num tiles U = np.zeros([4, 4, C, K]) # 4,4,4,8 V = np.zeros([4, 4, C, T]) # 4,4,4 # FOR LOOPS USED B/C NOT COUNTING OFF CHIP PROCESSING IN PERFORMANCE STATISTICS (will unroll loops in on chip processing) for t in range(T): for k in range(K): # filter for c in range(C): # channel g = weights[:, :, c, k] # 3x3 filter U[:, :, c, k] = np.dot(G, np.dot(g, G_T)) # 4x4 for c in range(C): # channel x_idx = (t // 2) * 2 y_idx = (t % 2) * 2 d = ifmap_padded[x_idx:x_idx + 4, y_idx:y_idx + 4, c] # 4x4 ifmap tile V[:, :, c, t] = np.dot(B_T, np.dot(d, B)) # Convert to integers for on chip processing, LOSE ACCURACY -> bit shift U = 128 * U # left shift by 7 bits to avoid precision loss when convert float to int #V = 128*V; self.weights = U.astype(np.int64) # transformed weights self.ifmap = V.astype(np.int64) # transformed ifmap #print ("U ser: ", self.weights) #print ("V ser: ", self.ifmap) def tick(self): if self.pass_done.rd(): return in_sets = self.arr_y // self.chn_per_word # 1 out_sets = self.arr_x // self.chn_per_word # 2 fmap_per_iteration = self.image_size[0] * self.image_size[1] weights_per_filter = self.filter_size[0] * self.filter_size[1] if self.arch_input_chn.vacancy() and not self.pass_done.rd(): if not self.bias_wr_done: kmin = self.bias_idx * self.chn_per_word kmax = kmin + self.chn_per_word data = np.array([self.biases[k] for k in range(kmin, kmax)]) self.bias_idx += 1 #print ("input ser kmin,kmax,biases: ",kmin,kmax,data) elif not self.fmap_wr_done: # send ifmap # send 4 elements of ifmap x = self.fmap_idx % self.image_size[0] y = self.fmap_idx // self.image_size[0] cmin = self.curr_set * self.chn_per_word # 0 cmax = cmin + self.chn_per_word # 4 data = np.array([ self.ifmap[x, y, c, self.fmap_tile] for c in range(cmin, cmax) ]) self.fmap_tile += 1 #print ("input ser x,y,cmin,cmax,ifmaps: ",x,y,cmin,cmax,data) else: # send weight # send 4 elements of weights (twice in succession) x = self.weight_idx % self.filter_size[0] y = self.weight_idx // self.filter_size[1] cmin = 0 cmax = cmin + self.chn_per_word data = np.array([ self.weights[x, y, c, self.curr_filter] for c in range(cmin, cmax) ]) self.curr_filter += 1 #print ("input ser x,y,cmin,cmax,curr_filter,weights: ",x,y,cmin,cmax,self.curr_filter,data) self.arch_input_chn.push(data) if self.fmap_tile == self.num_tiles: self.fmap_tile = 0 self.fmap_idx += 1 if self.fmap_idx == fmap_per_iteration: self.fmap_wr_done = True self.fmap_idx = 0 if self.curr_filter == self.arr_x: self.weight_idx += 1 self.curr_filter = 0 if self.weight_idx == weights_per_filter: self.weight_wr_done = True self.pass_done.wr(True) if self.bias_idx == self.bias_sets: #2 self.bias_wr_done = True
class PostTransform(Module): def instantiate(self, locx, locy, bias_chn, ofmap_in_chn, ofmap_out_chn): #ofmap_in self.locx = locx self.locy = locy self.bias_chn = bias_chn self.ofmap_in_chn = ofmap_in_chn self.ofmap_out_chn = ofmap_out_chn self.transform_done = Reg(False) self.stat_type = 'aggregate' self.raw_stats = {'post_tr_alu_comp' : 0, 'post_tr_rf_rd' : 0, 'post_tr_ifmap_rf_wr' : 0} def configure(self): self.bias = 0 self.iteration = 0 self.y00 = None self.y01 = None self.y10 = None self.y11 = None self.transform_done.wr(False) self.bias_read = False # Explanation of algorithm: transform ofmap M into y, performing inverse Winograd transform y = A_T*M*A # M = [M00 M01 M02 M03 # M10 M11 M12 M13 # M20 M21 M22 M23 # M30 M31 M32 M33] # # A_T = [1 1 1 0 # 0 1 -1 -1] # # A = [1 0 # 1 1 # 1 -1 # 0 -1] # # Performing this transform yields a 2x2 output for a given 4x4 input: # # y = [y00 y01 # y10 y11] # ... such that: # y00 = M00+M01+M02+M10+M11+M12+M20+M21+M22 # y01 = M01-M02-M03+M11-M12-M13+M21-M22-M23 # y10 = M10+M11+M12-M20-M21-M22-M30-M31-M32 # y11 = M11-M12-M13-M21+M22+M23-M31+M32+M33 def tick(self): if self.transform_done.rd(): return if self.bias_chn.valid(): # should only ever be valid once self.bias = self.bias_chn.pop() self.bias_read = True self.y00 = self.bias self.y01 = self.bias self.y10 = self.bias self.y11 = self.bias self.raw_stats['post_tr_alu_comp'] += 4 self.raw_stats['post_tr_ifmap_rf_wr'] += 4 elif self.ofmap_in_chn.valid() and self.ofmap_out_chn.vacancy(): m = (self.ofmap_in_chn.pop())//(128) # right shift by 7 bits self.raw_stats['post_tr_alu_comp'] += 1 #print("post tr -- iteration ", self.iteration) if (self.iteration == 0): # get M_00 self.y00 += m self.raw_stats['post_tr_alu_comp'] += 1 self.raw_stats['post_tr_rf_rd'] += 1 self.raw_stats['post_tr_ifmap_rf_wr'] += 1 self.iteration += 1 elif (self.iteration == 1): # get M_01 self.y00 += m self.y01 += m self.raw_stats['post_tr_alu_comp'] += 2 self.raw_stats['post_tr_rf_rd'] += 2 self.raw_stats['post_tr_ifmap_rf_wr'] += 2 self.iteration += 1 elif (self.iteration == 2): # get M_02 self.y00 += m self.y01 -= m self.raw_stats['post_tr_alu_comp'] += 2 self.raw_stats['post_tr_rf_rd'] += 2 self.raw_stats['post_tr_ifmap_rf_wr'] += 2 self.iteration += 1 elif (self.iteration == 3): # get M_03 self.y01 -= m self.raw_stats['post_tr_alu_comp'] += 1 self.raw_stats['post_tr_rf_rd'] += 1 self.raw_stats['post_tr_ifmap_rf_wr'] += 1 self.iteration += 1 elif (self.iteration == 4): # get M_10 self.y00 += m self.y10 += m self.raw_stats['post_tr_alu_comp'] += 2 self.raw_stats['post_tr_rf_rd'] += 2 self.raw_stats['post_tr_ifmap_rf_wr'] += 2 self.iteration += 1 elif (self.iteration == 5): # get M_11 self.y00 += m self.y01 += m self.y10 += m self.y11 += m self.raw_stats['post_tr_alu_comp'] += 4 self.raw_stats['post_tr_rf_rd'] += 4 self.raw_stats['post_tr_ifmap_rf_wr'] += 4 self.iteration += 1 elif (self.iteration == 6): # get M_12 self.y00 += m self.y01 -= m self.y10 += m self.y11 -= m self.raw_stats['post_tr_alu_comp'] += 4 self.raw_stats['post_tr_rf_rd'] += 4 self.raw_stats['post_tr_ifmap_rf_wr'] += 4 self.iteration += 1 elif (self.iteration == 7): # get M_13 self.y01 -= m self.y11 -= m self.raw_stats['post_tr_alu_comp'] += 2 self.raw_stats['post_tr_rf_rd'] += 2 self.raw_stats['post_tr_ifmap_rf_wr'] += 2 self.iteration += 1 elif (self.iteration == 8): # get M_20 self.y00 += m self.y10 -= m self.raw_stats['post_tr_alu_comp'] += 2 self.raw_stats['post_tr_rf_rd'] += 2 self.raw_stats['post_tr_ifmap_rf_wr'] += 2 self.iteration += 1 elif (self.iteration == 9): # get M_21 self.y00 += m self.y01 += m self.y10 -= m self.y11 -= m self.raw_stats['post_tr_alu_comp'] += 4 self.raw_stats['post_tr_rf_rd'] += 4 self.raw_stats['post_tr_ifmap_rf_wr'] += 4 self.iteration += 1 elif (self.iteration == 10 and self.bias_read == True): # get M_22 self.y00 += m self.y01 -= m self.y10 -= m self.y11 += m self.raw_stats['post_tr_alu_comp'] += 4 self.raw_stats['post_tr_rf_rd'] += 4 self.raw_stats['post_tr_ifmap_rf_wr'] += 4 self.iteration += 1 #print("post tr pushing y00: ", self.y00, self.bias) self.ofmap_out_chn.push(self.y00) # y00 done self.raw_stats['post_tr_ifmap_rf_wr'] -= 1 # send y00 immediately w/o writing to rf elif (self.iteration == 11): # get M_23 self.y01 -= m self.y11 += m self.raw_stats['post_tr_alu_comp'] += 2 self.raw_stats['post_tr_rf_rd'] += 2 self.raw_stats['post_tr_ifmap_rf_wr'] += 2 self.iteration += 1 #print("post tr pushing y01: ", self.y01, self.bias) self.ofmap_out_chn.push(self.y01) # y01 done self.raw_stats['post_tr_ifmap_rf_wr'] -= 1 # send y01 immediately w/o writing to rf elif (self.iteration == 12): # get M_30 self.y10 -= m self.raw_stats['post_tr_alu_comp'] += 1 self.raw_stats['post_tr_rf_rd'] += 1 self.raw_stats['post_tr_ifmap_rf_wr'] += 1 self.iteration += 1 elif (self.iteration == 13): # get M_31 self.y10 -= m self.y11 -= m self.raw_stats['post_tr_alu_comp'] += 2 self.raw_stats['post_tr_rf_rd'] += 2 self.raw_stats['post_tr_ifmap_rf_wr'] += 2 self.iteration += 1 elif (self.iteration == 14): # get M_32 self.y10 -= m self.y11 += m self.raw_stats['post_tr_alu_comp'] += 2 self.raw_stats['post_tr_rf_rd'] += 2 self.raw_stats['post_tr_ifmap_rf_wr'] += 2 self.iteration += 1 #print("post tr pushing y10: ", self.y10, self.bias) self.ofmap_out_chn.push(self.y10) # y10 done self.raw_stats['post_tr_ifmap_rf_wr'] -= 1 # send y10 immediately w/o writing to rf elif (self.iteration == 15): # get M_33 self.y11 += m self.raw_stats['post_tr_alu_comp'] += 1 self.raw_stats['post_tr_rf_rd'] += 1 self.raw_stats['post_tr_ifmap_rf_wr'] += 1 self.iteration += 1 #print("post tr pushing y11: ", self.y11, self.bias) self.ofmap_out_chn.push(self.y11) # y11 done self.raw_stats['post_tr_ifmap_rf_wr'] -= 1 # send y11 immediately w/o writing to rf #self.iteration += 1 if self.iteration == 16: self.transform_done.wr(True)
class OutputDeserializer(Module): def instantiate(self, arch_output_chn, arr_x, arr_y, chn_per_word, finish_signal_chn): # PE static configuration (immutable) self.arr_x = arr_x self.arr_y = arr_y self.chn_per_word = chn_per_word self.arch_output_chn = arch_output_chn self.finish_signal_chn = finish_signal_chn self.ofmap = None self.ofmap_transformed = None self.reference = None self.image_size = (0, 0) self.curr_set = 0 self.fmap_idx = 0 self.pass_done = Reg(False) def configure(self, ofmap, reference, image_size, bias): # self.ofmap = np.zeros((2, 2, self.arr_x, 4)).astype(np.int64) # 2x2x8x4 self.ofmap = np.zeros((image_size[0], image_size[1], self.arr_x)).astype(np.int64) # 4x4x8 self.reference = reference self.num_tiles = 4 self.curr_tile = 0 self.image_size = image_size self.bias = bias # TODO self.curr_set = 0 self.fmap_idx = 0 self.curr_chn = 0 self.A_T = np.array([[1, 1, 1, 0], [0, 1, -1, -1]]) self.A = self.A_T.transpose() self.pass_done.wr(False) def tick(self): if self.pass_done.rd(): # partly parallelized to be on chip: # x_idx = (self.curr_tile // 2)*2 # y_idx = (self.curr_tile % 2)*2 # self.ofmap_transformed[x_idx:x_idx+2, y_idx:y_idx+2, self.curr_chn] += np.dot(self.A_T, np.dot(self.ofmap[:,:,self.curr_chn, self.curr_tile],self.A)) # self.curr_tile += 1 # if self.curr_tile == 4: # self.curr_tile = 0 # self.ofmap_transformed[:,:,self.curr_chn] += self.bias[self.curr_chn] # add bias # self.curr_chn += 1 # if self.curr_chn == 8: # print ("reference shape: ", self.reference.shape) # print ("ofmap shape: ", self.ofmap.shape) # FOR LOOPS USED B/C NOT COUNTING OFF CHIP PROCESSING IN PERFORMANCE STATISTICS (will unroll loops in on chip processing) # for k in range(8): # self.ofmap_transformed[:,:,k] += self.bias[k] # add bias # for t in range(self.num_tiles): # x_idx = (t // 2)*2 # y_idx = (t % 2)*2 # self.ofmap_transformed[x_idx:x_idx+2,y_idx:y_idx+2,k] += np.dot(self.A_T,np.dot(self.ofmap[:,:,k,t],self.A)) # self.finish_signal_chn.push(True) if np.all(self.ofmap == self.reference): raise Finish("Success") else: print("ofmap: ") print(self.ofmap) print("reference: ") print(self.reference) print("difference: ") print(self.ofmap - self.reference) raise Finish("Validation Failed") else: #print ("output deser curr_tile, fmap_idx: ", self.curr_tile, self.fmap_idx) out_sets = self.arr_x // self.chn_per_word # 2 fmap_per_iteration = 4 # ofmap size, parametrize .. TODO if self.arch_output_chn.valid(): data = [e for e in self.arch_output_chn.pop()] x_idx = (self.curr_tile // 2) * 2 y_idx = (self.curr_tile % 2) * 2 x = (self.fmap_idx % 2) + x_idx y = self.fmap_idx // 2 + y_idx # self.ofmap_transformed[x_idx:x_idx+2,y_idx:y_idx+2,k] += np.dot(self.A_T,np.dot(self.ofmap[:,:,k,t],self.A)) if self.curr_set < out_sets: cmin = self.curr_set * self.chn_per_word cmax = cmin + self.chn_per_word for c in range(cmin, cmax): self.ofmap[x, y, c] = data[c - cmin] self.curr_set += 1 if self.curr_set == out_sets: self.curr_set = 0 #self.fmap_idx += 1 self.curr_tile += 1 if self.curr_tile == 4: self.fmap_idx += 1 self.curr_tile = 0 if self.fmap_idx == fmap_per_iteration: self.fmap_idx = 0 self.curr_tile = 0 # self.ofmap = self.ofmap//(128*128) self.pass_done.wr(True)
class OutputDeserializer(Module): def instantiate(self, arch_output_chn, arr_x, arr_y, chn_per_word): # PE static configuration (immutable) self.arr_x = arr_x self.arr_y = arr_y self.chn_per_word = chn_per_word self.arch_output_chn = arch_output_chn self.ofmap = None self.reference = None self.image_size = (0, 0) self.curr_set = 0 self.fmap_idx = 0 self.pass_done = Reg(False) def configure(self, ofmap, reference, image_size, filter_size): self.ofmap = ofmap self.reference = reference self.image_size = image_size self.filter_size = filter_size self.curr_set = 0 self.fmap_idx = 0 self.pass_done.wr(False) def tick(self): if self.pass_done.rd(): return out_sets = self.arr_x // self.chn_per_word #fmap_per_iteration = self.image_size[0]*self.image_size[1] fmap_per_iteration = (self.image_size[0] - self.filter_size[0] + 1) * ( self.image_size[1] - self.filter_size[1] + 1) if self.arch_output_chn.valid(): data = [e for e in self.arch_output_chn.pop()] x = self.fmap_idx % self.image_size[0] y = self.fmap_idx // self.image_size[0] x = self.fmap_idx % (self.image_size[0] - self.filter_size[0] + 1) y = self.fmap_idx // (self.image_size[0] - self.filter_size[0] + 1) if self.curr_set < out_sets: cmin = self.curr_set * self.chn_per_word cmax = cmin + self.chn_per_word for c in range(cmin, cmax): self.ofmap[x, y, c] = data[c - cmin] self.curr_set += 1 if self.curr_set == out_sets: self.curr_set = 0 self.fmap_idx += 1 if self.fmap_idx == fmap_per_iteration: self.fmap_idx = 0 self.pass_done.wr(True) if np.all(self.ofmap == self.reference): raise Finish("Success") else: print(self.ofmap) print(self.reference) print(self.ofmap - self.reference) raise Finish("Validation Failed")
class InputSerializer(Module): def instantiate(self, arch_input_chn, arr_x, arr_y, chn_per_word): # PE static configuration (immutable) self.arr_x = arr_x self.arr_y = arr_y self.chn_per_word = chn_per_word self.arch_input_chn = arch_input_chn self.ifmap = None self.weights = None self.bias = None self.image_size = (0, 0) self.filter_size = (0, 0) self.ifmap_psum_done = True self.pass_done = Reg(False) # State Counters self.curr_set = 0 self.curr_filter = 0 self.iteration = 0 self.fmap_idx = 0 def configure(self, ifmap, weights, bias, image_size, filter_size): self.ifmap = ifmap self.weights = weights self.bias = bias self.image_size = image_size self.filter_size = filter_size self.ifmap_psum_done = False self.pass_done.wr(False) def tick(self): if self.pass_done.rd(): return in_sets = self.arr_y // self.chn_per_word out_sets = self.arr_x // self.chn_per_word fmap_per_iteration = self.image_size[0] * self.image_size[1] num_iteration = self.filter_size[0] * self.filter_size[1] if not self.ifmap_psum_done: if self.arch_input_chn.vacancy(): # print "input append" x = self.fmap_idx % self.image_size[0] y = self.fmap_idx // self.image_size[0] if self.curr_set < in_sets: cmin = self.curr_set * self.chn_per_word cmax = cmin + self.chn_per_word # Write ifmap to glb data = np.array( [self.ifmap[x, y, c] for c in range(cmin, cmax)]) else: cmin = (self.curr_set - in_sets) * self.chn_per_word cmax = cmin + self.chn_per_word # Write bias to glb data = np.array([self.bias[c] for c in range(cmin, cmax)]) self.arch_input_chn.push(data) self.curr_set += 1 if self.curr_set == (in_sets + out_sets): self.curr_set = 0 self.fmap_idx += 1 if self.fmap_idx == fmap_per_iteration: self.fmap_idx = 0 self.ifmap_psum_done = True # print "---- Wrote inputs and biases ----" else: f_x = self.iteration % self.filter_size[0] f_y = self.iteration // self.filter_size[0] # Push filters to PE columns. (PE is responsible for pop) if self.arch_input_chn.vacancy( ) and self.iteration < num_iteration: cmin = self.curr_set * self.chn_per_word cmax = cmin + self.chn_per_word data = np.array([self.weights[f_x, f_y, c, self.curr_filter] \ for c in range(cmin, cmax) ]) self.arch_input_chn.push(data) self.curr_set += 1 if self.curr_set == in_sets: self.curr_set = 0 self.curr_filter += 1 if self.curr_filter == self.arr_x: self.curr_filter = 0 # print "---- Wrote weights iteration: %d ----" % self.iteration self.iteration += 1 if self.iteration == num_iteration: # print "---- Wrote all weights ----" self.pass_done.wr(True)
class OutputDeserializer(Module): def instantiate(self, arch_output_chn, psum_chn, arr_x, arr_y, chn_per_word): # PE static configuration (immutable) self.arr_x = arr_x self.arr_y = arr_y self.chn_per_word = chn_per_word self.arch_output_chn = arch_output_chn self.psum_chn = psum_chn self.ofmap = None self.reference = None self.image_size = (0, 0) self.curr_set = 0 self.fmap_idx = 0 self.pass_done = Reg(False) def configure(self, ofmap, reference, image_size, curr_pass): if (curr_pass == 0): # so that ofmap doesnt get rewritten with zeros self.ofmap = ofmap self.reference = reference self.image_size = image_size self.curr_set = 0 self.fmap_idx = 0 self.curr_pass = curr_pass self.num_passes = 4 self.pass_done.wr(False) def tick(self): if self.pass_done.rd(): return out_sets = self.arr_x // self.chn_per_word fmap_per_iteration = self.image_size[0] * self.image_size[1] if self.arch_output_chn.valid() and (self.psum_chn.vacancy() or self.curr_pass % 2 == 1): data = [e for e in self.arch_output_chn.pop()] if ((self.curr_pass % 2) == 0): # push ofmap psum to serializer on pass 0 and 2 self.psum_chn.push(data) x = self.fmap_idx % self.image_size[0] y = self.fmap_idx // self.image_size[0] if self.curr_set < out_sets: channel_offset = 0 if (self.curr_pass > 1): channel_offset = 8 cmin = self.curr_set * self.chn_per_word + channel_offset cmax = cmin + self.chn_per_word for c in range(cmin, cmax): self.ofmap[x, y, c] = data[c - cmin] self.curr_set += 1 if self.curr_set == out_sets: self.curr_set = 0 self.fmap_idx += 1 if self.fmap_idx == fmap_per_iteration: self.fmap_idx = 0 if (self.curr_pass == self.num_passes - 1): self.pass_done.wr(True) if np.all(self.ofmap == self.reference): raise Finish("Success") else: print(self.ofmap) print(self.reference) print(self.ofmap - self.reference) raise Finish("Validation Failed")
class PreTransformIFMap(Module): def instantiate(self, locx, locy, ifmap_in_chn, ifmap_out_chn): self.locx = locx self.locy = locy self.ifmap_in_chn = ifmap_in_chn self.ifmap_out_chn = ifmap_out_chn self.transform_done = Reg(False) self.stat_type = 'aggregate' self.raw_stats = { 'pre_tr_ifmap_alu_comp': 0, 'pre_tr_ifmap_rf_rd': 0, 'pre_tr_ifmap_rf_wr': 0 } def configure(self): self.iteration = 0 self.push_ctr = 0 self.V = np.zeros([4, 4]).astype(np.int64) self.raw_stats['pre_tr_ifmap_rf_wr'] += 16 # write zeros into rf self.transform_done.wr(False) # Explanation of algorithm: transform ifmap D into V, performing Winograd transform v = B_T*D*M # D = [D00 D01 D02 D03 # D10 D11 D12 D13 # D20 D21 D22 D23 # D30 D31 D32 D33] # # B_T = [1 0 -1 0 # 0 1 1 0 # 0 -1 1 0 # 0 1 0 -1] # # B = [ 1 0 0 0 # 0 1 -1 1 # -1 1 1 0 # 0 0 0 -1] # # Performing this transform yields a 4x4 output for a given 4x4 input: # # V = [v00 v01 v02 v03 # v10 v11 v12 v13 # v20 v21 v22 v23 # v30 v31 v32 v33] # # ... such that: # v00 = (D00 - D02 - D20 + D22); # v01 = (D01 + D02 - D21 - D22); # v02 = (D02 - D01 + D21 - D22); # v03 = (D01 - D03 - D21 + D23); # v10 = (D10 - D12 + D20 - D22); # v11 = (D11 + D12 + D21 + D22); # v12 = (D12 - D11 - D21 + D22); # v13 = (D11 - D13 + D21 - D23); # v20 = (D12 - D10 + D20 - D22); # v21 = (D21 - D12 - D11 + D22); # v22 = (D11 - D12 - D21 + D22); # v23 = (D13 - D11 + D21 - D23); # v30 = (D10 - D12 - D30 + D32); # v31 = (D11 + D12 - D31 - D32); # v32 = (D12 - D11 + D31 - D32); # v33 = (D11 - D13 - D31 + D33); def tick(self): if self.transform_done.rd(): return if self.ifmap_in_chn.valid() and self.ifmap_out_chn.vacancy(): d = (self.ifmap_in_chn.pop()) #print ("pre transform ifmap pop - locx, locy, data: ",self.locx,self.locy,d) if (self.iteration == 0): # get D_00 self.V[0][0] += d self.raw_stats['pre_tr_ifmap_alu_comp'] += 1 self.raw_stats['pre_tr_ifmap_rf_rd'] += 1 self.raw_stats['pre_tr_ifmap_rf_wr'] += 1 self.iteration += 1 elif (self.iteration == 1): # get D_01 self.V[0][1] += d self.V[0][2] -= d self.V[0][3] += d self.raw_stats['pre_tr_ifmap_alu_comp'] += 3 self.raw_stats['pre_tr_ifmap_rf_rd'] += 3 self.raw_stats['pre_tr_ifmap_rf_wr'] += 3 self.iteration += 1 elif (self.iteration == 2): # get D_02 self.V[0][0] -= d self.V[0][1] += d self.V[0][2] += d self.raw_stats['pre_tr_ifmap_alu_comp'] += 3 self.raw_stats['pre_tr_ifmap_rf_rd'] += 3 self.raw_stats['pre_tr_ifmap_rf_wr'] += 3 self.iteration += 1 elif (self.iteration == 3): # get D_03 self.V[0][3] -= d self.raw_stats['pre_tr_ifmap_alu_comp'] += 1 self.raw_stats['pre_tr_ifmap_rf_rd'] += 1 self.raw_stats['pre_tr_ifmap_rf_wr'] += 1 self.iteration += 1 elif (self.iteration == 4): # get D_10 self.V[1][0] += d self.V[2][0] -= d self.V[3][0] += d self.raw_stats['pre_tr_ifmap_alu_comp'] += 3 self.raw_stats['pre_tr_ifmap_rf_rd'] += 3 self.raw_stats['pre_tr_ifmap_rf_wr'] += 3 self.iteration += 1 elif (self.iteration == 5): # get D_11 self.V[1][1] += d self.V[1][2] -= d self.V[1][3] += d self.V[2][1] -= d self.V[2][2] += d self.V[2][3] -= d self.V[3][1] += d self.V[3][2] -= d self.V[3][3] += d self.raw_stats['pre_tr_ifmap_alu_comp'] += 9 self.raw_stats['pre_tr_ifmap_rf_rd'] += 9 self.raw_stats['pre_tr_ifmap_rf_wr'] += 9 self.iteration += 1 elif (self.iteration == 6): # get D_12 self.V[1][0] -= d self.V[1][1] += d self.V[1][2] += d self.V[2][0] += d self.V[2][1] -= d self.V[2][2] -= d self.V[3][0] -= d self.V[3][1] += d self.V[3][2] += d self.raw_stats['pre_tr_ifmap_alu_comp'] += 9 self.raw_stats['pre_tr_ifmap_rf_rd'] += 9 self.raw_stats['pre_tr_ifmap_rf_wr'] += 9 self.iteration += 1 elif (self.iteration == 7): # get D_13 self.V[1][3] -= d self.V[2][3] += d self.V[3][3] -= d self.raw_stats['pre_tr_ifmap_alu_comp'] += 3 self.raw_stats['pre_tr_ifmap_rf_rd'] += 3 self.raw_stats['pre_tr_ifmap_rf_wr'] += 3 self.iteration += 1 elif (self.iteration == 8): # get D_20 self.V[0][0] -= d self.V[1][0] += d self.V[2][0] += d self.raw_stats['pre_tr_ifmap_alu_comp'] += 3 self.raw_stats['pre_tr_ifmap_rf_rd'] += 3 self.raw_stats['pre_tr_ifmap_rf_wr'] += 3 self.iteration += 1 elif (self.iteration == 9): # get D_21 self.V[0][1] -= d self.V[0][2] += d self.V[0][3] -= d self.V[1][1] += d self.V[1][2] -= d self.V[1][3] += d self.V[2][1] += d self.V[2][2] -= d self.V[2][3] += d self.raw_stats['pre_tr_ifmap_alu_comp'] += 9 self.raw_stats['pre_tr_ifmap_rf_rd'] += 9 self.raw_stats['pre_tr_ifmap_rf_wr'] += 9 self.iteration += 1 elif (self.iteration == 10 ): # get D_22 & start pushing transformed data out self.V[0][0] += d self.V[0][1] -= d self.V[0][2] -= d self.V[1][0] -= d self.V[1][1] += d self.V[1][2] += d self.V[2][0] -= d self.V[2][1] += d self.V[2][2] += d self.raw_stats['pre_tr_ifmap_alu_comp'] += 9 self.raw_stats['pre_tr_ifmap_rf_rd'] += 9 self.raw_stats['pre_tr_ifmap_rf_wr'] += 9 self.ifmap_out_chn.push( self.V[self.push_ctr // 4][self.push_ctr % 4]) # push v00 self.raw_stats[ 'pre_tr_ifmap_rf_rd'] -= 1 # push v00 immediately w/o writing to rf #print ("pre transform ifmap - locx, locy, iteration, transformed ifmap: ", \ # self.locx, self.locy, self.iteration, self.V[self.push_ctr // 4][self.push_ctr % 4]) self.push_ctr += 1 self.iteration += 1 elif (self.iteration == 11): # get D_23 self.V[0][3] += d self.V[1][3] -= d self.V[2][3] -= d self.raw_stats['pre_tr_ifmap_alu_comp'] += 3 self.raw_stats['pre_tr_ifmap_rf_rd'] += 3 self.raw_stats['pre_tr_ifmap_rf_wr'] += 3 self.ifmap_out_chn.push( self.V[self.push_ctr // 4][self.push_ctr % 4]) # push v01 self.raw_stats['pre_tr_ifmap_rf_rd'] += 1 # read v01 from rf #print ("pre transform ifmap - locx, locy, iteration, transformed ifmap: ", \ # self.locx, self.locy, self.iteration, self.V[self.push_ctr // 4][self.push_ctr % 4]) self.push_ctr += 1 self.iteration += 1 elif (self.iteration == 12): # get D_30 self.V[3][0] -= d self.raw_stats['pre_tr_ifmap_alu_comp'] += 1 self.raw_stats['pre_tr_ifmap_rf_rd'] += 1 self.raw_stats['pre_tr_ifmap_rf_wr'] += 1 self.ifmap_out_chn.push( self.V[self.push_ctr // 4][self.push_ctr % 4]) # push v02 self.raw_stats['pre_tr_ifmap_rf_rd'] += 1 # read v02 from rf #print ("pre transform ifmap - locx, locy, iteration, transformed ifmap: ", \ # self.locx, self.locy, self.iteration, self.V[self.push_ctr // 4][self.push_ctr % 4]) self.push_ctr += 1 self.iteration += 1 elif (self.iteration == 13): # get D_31 self.V[3][1] -= d self.V[3][2] += d self.V[3][3] -= d self.raw_stats['pre_tr_ifmap_alu_comp'] += 3 self.raw_stats['pre_tr_ifmap_rf_rd'] += 3 self.raw_stats['pre_tr_ifmap_rf_wr'] += 3 self.ifmap_out_chn.push( self.V[self.push_ctr // 4][self.push_ctr % 4]) # push v03 self.raw_stats['pre_tr_ifmap_rf_rd'] += 1 # read v03 from rf #print ("pre transform ifmap - locx, locy, iteration, transformed ifmap: ", \ # self.locx, self.locy, self.iteration, self.V[self.push_ctr // 4][self.push_ctr % 4]) self.push_ctr += 1 self.iteration += 1 elif (self.iteration == 14): # get D_32 self.V[3][0] += d self.V[3][1] -= d self.V[3][2] -= d self.raw_stats['pre_tr_ifmap_alu_comp'] += 3 self.raw_stats['pre_tr_ifmap_rf_rd'] += 3 self.raw_stats['pre_tr_ifmap_rf_wr'] += 3 self.ifmap_out_chn.push( self.V[self.push_ctr // 4][self.push_ctr % 4]) # push v10 self.raw_stats['pre_tr_ifmap_rf_wr'] += 1 # read v10 from rf #print ("pre transform ifmap - locx, locy, iteration, transformed ifmap: ", \ # self.locx, self.locy, self.iteration, self.V[self.push_ctr // 4][self.push_ctr % 4]) self.push_ctr += 1 self.iteration += 1 elif (self.iteration == 15): # get D_33 self.V[3][3] += d self.raw_stats['pre_tr_ifmap_alu_comp'] += 1 self.raw_stats['pre_tr_ifmap_rf_rd'] += 1 self.raw_stats['pre_tr_ifmap_rf_wr'] += 1 self.ifmap_out_chn.push( self.V[self.push_ctr // 4][self.push_ctr % 4]) # push v11 self.raw_stats['pre_tr_ifmap_rf_wr'] += 1 # read v11 from rf #print ("pre transform ifmap - locx, locy, iteration, transformed ifmap: ", \ # self.locx, self.locy, self.iteration, self.V[self.push_ctr // 4][self.push_ctr % 4]) self.push_ctr += 1 self.iteration += 1 elif self.iteration == 16 and self.ifmap_out_chn.vacancy( ): # done computing transform, push remaining V's sequentially self.ifmap_out_chn.push(self.V[self.push_ctr // 4][self.push_ctr % 4]) self.raw_stats['pre_tr_ifmap_rf_rd'] += 1 # read vXX from rf #print ("pre transform ifmap - locx, locy, iteration, transformed ifmap: ", \ # self.locx, self.locy, self.iteration, self.V[self.push_ctr // 4][self.push_ctr % 4]) self.push_ctr += 1 if self.push_ctr == 16: # all 16 transformed ifmap values have been pushed self.transform_done.wr(True)
class InputSerializer(Module): def instantiate(self, arch_input_chn, arr_x, arr_y, chn_per_word): # PE static configuration (immutable) self.arr_x = arr_x self.arr_y = arr_y self.chn_per_word = chn_per_word self.arch_input_chn = arch_input_chn self.ifmap = None self.weights = None self.bias = None self.image_size = (0, 0) self.filter_size = (0, 0) self.ifmap_psum_done = True self.pass_done = Reg(False) # State Counters self.curr_set = 0 self.curr_filter = 0 self.iteration = 0 self.fmap_idx = 0 self.bias_idx = 0 self.weight_idx = 0 def configure(self, ifmap, weights, bias, image_size, filter_size): self.ifmap = ifmap self.weights = weights self.bias = bias self.image_size = image_size self.filter_size = filter_size self.bias_wr_done = False self.fmap_wr_done = False self.weight_wr_done = False self.pass_done.wr(False) self.send_ifmap = True # used to interleave sending weights and ifmaps to chip self.bias_sets = 2 def tick(self): if self.pass_done.rd(): return # in_sets = self.arr_y//self.chn_per_word # out_sets = self.arr_x//self.chn_per_word fmap_per_iteration = self.image_size[0] * self.image_size[1] num_iteration = self.filter_size[0] * self.filter_size[1] weights_per_filter = self.filter_size[0] * self.filter_size[1] if self.arch_input_chn.vacancy() and not self.pass_done.rd(): if not self.bias_wr_done: kmin = self.bias_idx * self.chn_per_word kmax = kmin + self.chn_per_word data = np.array([self.bias[k] for k in range(kmin, kmax)]) self.bias_idx += 1 #print ("input ser kmin,kmax,bias: ",kmin,kmax,data) elif (not self.fmap_wr_done) and self.send_ifmap: # send ifmap # send 4 elements of ifmap x = self.fmap_idx % self.image_size[0] y = self.fmap_idx // self.image_size[0] cmin = self.curr_set * self.chn_per_word # 0 cmax = cmin + self.chn_per_word # 4 data = np.array( [self.ifmap[x, y, c] for c in range(cmin, cmax)]) self.fmap_idx += 1 #print ("input ser x,y,cmin,cmax,ifmaps: ",x,y,cmin,cmax,data) self.send_ifmap = False else: # send weight # send 4 elements of weights (twice in succession) x = self.weight_idx % self.filter_size[0] y = self.weight_idx // self.filter_size[1] cmin = 0 cmax = cmin + self.chn_per_word data = np.array([ self.weights[x, y, c, self.curr_filter] for c in range(cmin, cmax) ]) self.curr_filter += 1 if (not self.fmap_wr_done): self.send_ifmap = True #print ("input ser x,y,cmin,cmax,curr_filter,weights: ",x,y,cmin,cmax,self.curr_filter,data) self.arch_input_chn.push(data) if self.fmap_idx == fmap_per_iteration: self.fmap_wr_done = True self.fmap_idx = 0 if self.curr_filter == self.arr_x: self.weight_idx += 1 self.curr_filter = 0 if self.weight_idx == weights_per_filter: self.weight_wr_done = True self.pass_done.wr(True) if self.bias_idx == self.bias_sets: #2 self.bias_wr_done = True
class WindowFIFO(Module): def instantiate(self, depth, peek_window, enq_window, deq_window): self.data = [0] * depth self.depth = depth self.peek_window = peek_window self.enq_window = enq_window self.deq_window = deq_window self.rd_ptr = Reg(0) self.wr_ptr = Reg(0) def peek(self): if (self.wr_ptr.rd() - self.rd_ptr.rd()) % (2*self.depth) \ < self.peek_window: raise FIFOError("Reading from empty FIFO") peek_output = [0] * self.peek_window for i in xrange(self.peek_window): peek_output[i] = self.data[(self.rd_ptr.rd() + i) % self.depth] return peek_output def enq(self, x): if (self.wr_ptr.rd() - self.rd_ptr.rd() + self.enq_window - 1) % \ (2*self.depth) >= self.depth: raise FIFOError("Enqueueing into full FIFO") for i in xrange(self.enq_window): self.data[(self.wr_ptr.rd() + i) % self.depth] = x[i] self.wr_ptr.wr((self.wr_ptr.rd() + self.enq_window) % (2 * self.depth)) def deq(self): if (self.wr_ptr.rd() - self.rd_ptr.rd()) % (2*self.depth) \ < self.deq_window: raise FIFOError("Dequeueing from empty FIFO") self.rd_ptr.wr((self.rd_ptr.rd() + self.deq_window) % (2 * self.depth)) def not_full(self): return not ( (self.wr_ptr.rd() - self.rd_ptr.rd() + self.enq_window - 1) % (2 * self.depth) >= self.depth) def valid(self): return not ((self.wr_ptr.rd() - self.rd_ptr.rd()) % (2 * self.depth) < self.peek_window) def not_empty(self): return not ((self.wr_ptr.rd() - self.rd_ptr.rd()) % (2 * self.depth) < self.deq_window) def clear(self): self.rd_ptr.wr(self.wr_ptr.rd())
class PreTransformWeights(Module): def instantiate(self, locx, locy, weight_in_chn, weight_out_chn): self.locx = locx self.locy = locy self.weight_in_chn = weight_in_chn self.weight_out_chn = weight_out_chn self.transform_done = Reg(False) self.stat_type = 'aggregate' self.raw_stats = { 'pre_tr_weights_alu_comp': 0, 'pre_tr_weights_rf_rd': 0, 'pre_tr_weights_rf_wr': 0 } def configure(self): self.iteration = 0 self.push_ctr = 0 self.U = np.zeros([4, 4]).astype(np.int64) self.transform_done.wr(False) # Explanation of algorithm: transform filter weights G into U, performing Winograd transform U = H*G*H_T # G = [G00 G01 G02 # G10 G11 G12 # G20 G21 G22] # # H = [1 0 0 # 0.5 0.5 0.5 # 0.5 -0.5 0.5 # 0 0 1 ] # # H_T = [1 0.5 0.5 0 # 0 0.5 -0.5 0 # 0 0.5 0.5 1] # # Performing this transform yields a 4x4 output for a given 4x4 input: # # U = [u00 u01 u02 u03 # u10 u11 u12 u13 # u20 u21 u22 u23 # u30 u31 u32 u33] # ... such that: # u00 = (G00)<<7; # u01 = (G00 + G01 + G02)<<6; # u02 = (G00 - G01 + G02)<<6; # u03 = (G02)<<7; # u10 = (G00 + G10 + G20)<<6; # u11 = (G00 + G01 + G02 + G10 + G11 + G12 + G20 + G21 + G22)<<5; # u12 = (G00 - G01 + G02 + G10 - G11 + G12 + G20 - G21 + G22)<<5; # u13 = (G02 + G12 + G22)<<6; # u20 = (G00 - G10 + G20)<<6; # u21 = (G00 + G01 + G02 - G10 - G11 - G12 + G20 + G21 + G22)<<5; # u22 = (G00 - G01 + G02 - G10 + G11 - G12 + G20 - G21 + G22)<<5; # u23 = (G02 - G12 + G22)<<6; # u30 = (G20)<<7; # u31 = (G20 + G21 + G22)<<6; # u32 = (G20 - G21 + G22)<<6; # u33 = (G22)<<7; def tick(self): if self.transform_done.rd(): return if self.weight_in_chn.valid() and self.weight_out_chn.vacancy(): g = (self.weight_in_chn.pop()) #print("pre tr weight: locx, locy, receive weight: ", self.locx, self.locy, g) if (self.iteration == 0): # get G_00 self.U[0][0] += g self.U[0][1] += g self.U[0][2] += g self.U[1][0] += g self.U[1][1] += g self.U[1][2] += g self.U[2][0] += g self.U[2][1] += g self.U[2][2] += g self.U[0][0] = self.U[0][0] * 128 # left shift by 7 self.raw_stats[ 'pre_tr_weights_alu_comp'] += 10 #9 adds, 1 shift self.raw_stats['pre_tr_weights_rf_rd'] += 9 self.raw_stats['pre_tr_weights_rf_wr'] += 9 self.weight_out_chn.push( self.U[self.push_ctr // 4][self.push_ctr % 4]) # send U00 self.raw_stats[ 'pre_tr_weights_rf_wr'] -= 1 # u00 sent immediately, not written back to rf #print ("pre transform weights - locx, locy, iteration, transformed weight: ", \ # self.locx, self.locy, self.iteration, self.U[self.push_ctr // 4][self.push_ctr % 4]) self.push_ctr += 1 self.iteration += 1 elif (self.iteration == 1): # get G_01 self.U[0][1] += g self.U[0][2] -= g self.U[1][1] += g self.U[1][2] -= g self.U[2][1] += g self.U[2][2] -= g self.raw_stats['pre_tr_weights_alu_comp'] += 6 self.raw_stats['pre_tr_weights_rf_rd'] += 6 self.raw_stats['pre_tr_weights_rf_wr'] += 6 self.iteration += 1 elif (self.iteration == 2): # get G_02 self.U[0][1] += g self.U[0][2] += g self.U[0][3] += g self.U[1][1] += g self.U[1][2] += g self.U[1][3] += g self.U[2][1] += g self.U[2][2] += g self.U[2][3] += g self.U[0][1] = self.U[0][1] * 64 # left shift by 6 self.U[0][2] = self.U[0][2] * 64 # left shift by 6 self.U[0][3] = self.U[0][3] * 128 # left shift by 7 self.raw_stats[ 'pre_tr_weights_alu_comp'] += 12 #9 adds/subt, 3 shift self.raw_stats['pre_tr_weights_rf_rd'] += 9 self.raw_stats['pre_tr_weights_rf_wr'] += 9 self.weight_out_chn.push( self.U[self.push_ctr // 4][self.push_ctr % 4]) # send U01 self.raw_stats[ 'pre_tr_weights_rf_wr'] -= 1 # u01 sent immediately, not written back to rf #print ("pre transform weights - locx, locy, iteration, transformed weight: ", \ # self.locx, self.locy, self.iteration, self.U[self.push_ctr // 4][self.push_ctr % 4]) self.push_ctr += 1 self.iteration += 1 elif (self.iteration == 3): # get G_10 self.U[1][0] += g self.U[1][1] += g self.U[1][2] += g self.U[2][0] -= g self.U[2][1] -= g self.U[2][2] -= g self.raw_stats['pre_tr_weights_alu_comp'] += 6 self.raw_stats['pre_tr_weights_rf_rd'] += 6 self.raw_stats['pre_tr_weights_rf_wr'] += 6 self.weight_out_chn.push( self.U[self.push_ctr // 4][self.push_ctr % 4]) # send U02 self.raw_stats['pre_tr_weights_rf_rd'] += 1 # read u02 from rf #print ("pre transform weights - locx, locy, iteration, transformed weight: ", \ # self.locx, self.locy, self.iteration, self.U[self.push_ctr // 4][self.push_ctr % 4]) self.push_ctr += 1 self.iteration += 1 elif (self.iteration == 4): # get G_11 self.U[1][1] += g self.U[1][2] -= g self.U[2][1] -= g self.U[2][2] += g self.raw_stats['pre_tr_weights_alu_comp'] += 4 self.raw_stats['pre_tr_weights_rf_rd'] += 4 self.raw_stats['pre_tr_weights_rf_wr'] += 4 self.weight_out_chn.push( self.U[self.push_ctr // 4][self.push_ctr % 4]) # send U03 self.raw_stats['pre_tr_weights_rf_rd'] += 1 # read u03 from rf #print ("pre transform weights - locx, locy, iteration, transformed weight: ", \ # self.locx, self.locy, self.iteration, self.U[self.push_ctr // 4][self.push_ctr % 4]) self.push_ctr += 1 self.iteration += 1 elif (self.iteration == 5): # get G_12 self.U[1][1] += g self.U[1][2] += g self.U[1][3] += g self.U[2][1] -= g self.U[2][2] -= g self.U[2][3] -= g self.raw_stats['pre_tr_weights_alu_comp'] += 6 self.raw_stats['pre_tr_weights_rf_rd'] += 6 self.raw_stats['pre_tr_weights_rf_wr'] += 6 # no new completed weights this round self.iteration += 1 elif (self.iteration == 6): # get G_20 self.U[1][0] += g self.U[1][1] += g self.U[1][2] += g self.U[2][0] += g self.U[2][1] += g self.U[2][2] += g self.U[3][0] += g self.U[3][1] += g self.U[3][2] += g self.U[1][0] = self.U[1][0] * 64 # left shift 6 self.U[2][0] = self.U[2][0] * 64 # left shift 6 self.U[3][0] = self.U[3][0] * 128 # left shift 7 self.raw_stats[ 'pre_tr_weights_alu_comp'] += 12 # 9 add, 3 shift ops self.raw_stats['pre_tr_weights_rf_rd'] += 9 self.raw_stats['pre_tr_weights_rf_wr'] += 9 self.weight_out_chn.push( self.U[self.push_ctr // 4][self.push_ctr % 4]) # send U10 self.raw_stats[ 'pre_tr_weights_rf_wr'] -= 1 # send u10 immediately, w/o writing to rf #print ("pre transform weights - locx, locy, iteration, transformed weight: ", \ # self.locx, self.locy, self.iteration, self.U[self.push_ctr // 4][self.push_ctr % 4]) self.push_ctr += 1 self.iteration += 1 elif (self.iteration == 7): # get G_21 self.U[1][1] += g self.U[1][2] -= g self.U[2][1] += g self.U[2][2] -= g self.U[3][1] += g self.U[3][2] -= g self.raw_stats[ 'pre_tr_weights_alu_comp'] += 6 # 9 add, 3 shift ops self.raw_stats['pre_tr_weights_rf_rd'] += 6 self.raw_stats['pre_tr_weights_rf_wr'] += 6 # no new completed weights this round self.iteration += 1 elif (self.iteration == 8): # get G_22 #print ("iteration 8") self.U[1][1] += g self.U[1][2] += g self.U[1][3] += g self.U[2][1] += g self.U[2][2] += g self.U[2][3] += g self.U[3][1] += g self.U[3][2] += g self.U[3][3] += g self.raw_stats['pre_tr_weights_alu_comp'] += 9 # 9 add self.raw_stats['pre_tr_weights_rf_rd'] += 9 self.raw_stats['pre_tr_weights_rf_wr'] += 9 self.iteration += 1 elif (self.iteration == 9 and self.weight_out_chn.vacancy()): #print ("iteration 9") self.U[1][1] = self.U[1][1] * 32 # left shift by 5 self.U[1][2] = self.U[1][2] * 32 # left shift by 5 self.U[1][3] = self.U[1][3] * 64 self.U[2][1] = self.U[2][1] * 32 self.U[2][2] = self.U[2][2] * 32 self.U[2][3] = self.U[2][3] * 64 self.U[3][1] = self.U[3][1] * 64 self.U[3][2] = self.U[3][2] * 64 self.U[3][3] = self.U[3][3] * 128 self.raw_stats['pre_tr_weights_alu_comp'] += 9 # 9 shift ops self.raw_stats['pre_tr_weights_rf_rd'] += 9 self.raw_stats['pre_tr_weights_rf_wr'] += 9 self.weight_out_chn.push(self.U[self.push_ctr // 4][self.push_ctr % 4]) # send U11 self.raw_stats[ 'pre_tr_weights_rf_wr'] -= 1 # send u11 immediately w/o writing to rf # print ("pre transform weights - locx, locy, iteration, transformed weight: ", \ # self.locx, self.locy, self.iteration, self.U[self.push_ctr // 4][self.push_ctr % 4]) self.push_ctr += 1 self.iteration += 1 elif self.iteration == 10 and self.weight_out_chn.vacancy( ): # finish pushing transformed weights self.weight_out_chn.push(self.U[self.push_ctr // 4][self.push_ctr % 4]) self.raw_stats['pre_tr_weights_rf_rd'] += 1 # read uXX from rf #print ("pre transform weights - locx, locy, iteration, transformed weight: ", \ # self.locx, self.locy, self.iteration, self.U[self.push_ctr // 4][self.push_ctr % 4]) self.push_ctr += 1 if self.push_ctr == 16: # all 16 transformed weight values have been pushed self.transform_done.wr(True)
class FIFO(Module): def instantiate(self, depth=2): self.data = [0] * depth self.depth = depth self.rd_ptr = Reg(0) self.wr_ptr = Reg(0) def peek(self): if self.wr_ptr.rd() == self.rd_ptr.rd(): raise FIFOError("Reading from empty FIFO") return self.data[self.rd_ptr.rd() % self.depth] def enq(self, x): if (self.wr_ptr.rd() - self.rd_ptr.rd()) % (2 * self.depth) == self.depth: raise FIFOError("Enqueueing into full FIFO") self.data[self.wr_ptr.rd() % self.depth] = x self.wr_ptr.wr((self.wr_ptr.rd() + 1) % (2 * self.depth)) def deq(self): if self.wr_ptr.rd() == self.rd_ptr.rd(): raise FIFOError("Dequeueing from empty FIFO") self.rd_ptr.wr((self.rd_ptr.rd() + 1) % (2 * self.depth)) def not_full(self): return not ((self.wr_ptr.rd() - self.rd_ptr.rd()) % (2 * self.depth) == self.depth) def not_empty(self): return not (self.wr_ptr.rd() == self.rd_ptr.rd()) def reset(self): self.rd_ptr.wr(self.wr_ptr.rd())
class OutputDeserializer(Module): def instantiate(self, arch_output_chn, arr_y, block_size, num_nonzero): # PE static configuration (immutable) self.arr_y = arr_y self.block_size = block_size self.num_nonzero = num_nonzero self.arch_output_chn = arch_output_chn self.ofmap = None self.reference = None self.image_size = (0, 0) self.curr_set = 0 self.fmap_idx = 0 self.pass_done = Reg(False) def configure(self, ofmap, reference, image_size, out_chn): self.ofmap = ofmap self.reference = reference self.out_chn = out_chn self.image_size = image_size self.curr_set = 0 self.fmap_idx = 0 self.pass_done.wr(False) def tick(self): if self.pass_done.rd(): return out_sets = self.out_chn // self.block_size fmap_per_iteration = self.image_size[0] * self.image_size[1] if self.arch_output_chn.valid(): rcvd = self.arch_output_chn.pop() loc_tag = [e[0] for e in rcvd] data = [e[1] for e in rcvd] #print(loc_tag) x = loc_tag[0] // self.image_size[1] y = loc_tag[0] % self.image_size[1] #x = self.fmap_idx % self.image_size[0] #y = self.fmap_idx // self.image_size[0] self.fmap_idx = x + y * self.image_size[0] #print("{},{} received (output deserializer)".format(x,y)) #print(data) if self.curr_set < out_sets: cmin = self.curr_set * self.block_size cmax = cmin + self.block_size for c in range(cmin, cmax): assert (self.ofmap[x, y, c] == 0 ) # should never replace an existing value self.ofmap[x, y, c] = data[c - cmin] self.curr_set += 1 if self.curr_set == out_sets: self.curr_set = 0 self.fmap_idx += 1 if self.fmap_idx == fmap_per_iteration: self.fmap_idx = 0 self.pass_done.wr(True) raise Finish("Done processing")
class InputSerializer(Module): def instantiate(self, arch_input_chn, arr_y, block_size, num_nonzero, pruner_name): # PE static configuration (immutable) #self.arr_x = arr_x self.arr_y = arr_y #self.chn_per_word = chn_per_word self.block_size = block_size self.num_nonzero = num_nonzero self.convert_chn = Channel() self.prune_chn = Channel() self.arch_input_chn = arch_input_chn # Although both InputSerializer and pruner will be pushing to arch_input_chn # There is no conflict issue because all weights will be pushed by IS first # then all inputs by pruner self.converter = Converter(self.convert_chn, self.prune_chn, \ self.block_size, self.block_size) # self.pruner = NaivePruner(self.prune_chn,self.arch_input_chn, \ # self.num_nonzero,True) #user defined pruner for this layer, default to naive pruner self.pruner = getattr(pruner, pruner_name)(self.prune_chn,self.arch_input_chn, \ self.num_nonzero, self.block_size, True) self.ifmap = None self.weights = None self.bias = None self.image_size = (0, 0) self.filter_size = (0, 0) self.ifmap_psum_done = True self.pass_done = Reg(False) # State Counters self.curr_set = 0 self.curr_filter = 0 self.iteration = 0 self.fmap_idx = 0 self.curr_chn = 0 self.curr_x = 0 # run through first two dimensions of input self.curr_y = 0 self.bias_set = 0 #self.send_bias = False def configure(self, ifmap, weights, bias, in_chn, out_chn, image_size, filter_size): self.ifmap = ifmap self.weights = weights self.bias = bias self.in_chn = in_chn self.out_chn = out_chn self.image_size = image_size self.filter_size = filter_size self.ifmap_psum_done = False self.weights_done = False self.pass_done.wr(False) # State Counters self.curr_set = 0 self.curr_filter = 0 self.iteration = 0 self.curr_chn = 0 self.curr_x = 0 # run through first two dimensions of input self.curr_y = 0 self.bias_set = 0 #self.send_bias = False def tick(self): if self.pass_done.rd(): return if self.ifmap_psum_done: if self.convert_chn.vacancy(): data = np.zeros(self.block_size) self.convert_chn.push(data) return in_sets = self.in_chn // self.block_size out_sets = self.out_chn // self.block_size num_iteration = self.filter_size[0] * self.filter_size[1] # read and hold all weights at the beginning for ease of implementation if not self.weights_done: f_x = self.iteration // self.filter_size[0] f_y = self.iteration % self.filter_size[0] # Push filters to PE columns. (PE is responsible for pop) if self.arch_input_chn.vacancy( ) and self.iteration < num_iteration: cmin = self.curr_filter * self.block_size cmax = cmin + self.block_size data = np.array([self.weights[f_x, f_y, self.curr_chn, c] \ for c in range(cmin, cmax) ]) #print("{},{},{},{}-{}".format(f_x,f_y,self.curr_chn,cmin,cmax)) #print(data) self.arch_input_chn.push( data) # Gives groups of four along num_filters axis self.curr_filter += 1 if (self.curr_filter == out_sets ): # Loop through blocks of filters self.curr_filter = 0 self.curr_chn += 1 if (self.curr_chn == self.in_chn): # Loop through channels self.curr_chn = 0 self.iteration += 1 if (self.iteration == num_iteration ): # Loop through 2D filter support self.iteration = 0 #print("Weights done") self.weights_done = True elif self.arch_input_chn.vacancy() and self.bias_set < out_sets: cmin = self.bias_set * self.block_size cmax = cmin + self.block_size data = np.array([self.bias[c] for c in range(cmin, cmax)]) #print("bias (input serializer):") #print(data) self.arch_input_chn.push(data) self.bias_set += 1 elif not self.ifmap_psum_done: if self.convert_chn.vacancy(): cmin = self.curr_set * self.block_size cmax = cmin + self.block_size #xmin = x #xmax = x+self.arr_x # Write ifmap to glb #data = np.array([ self.ifmap[x, self.curr_y, self.curr_chn] for x in range(xmin, xmax) ]) data = np.array([ self.ifmap[self.curr_x, self.curr_y, c] for c in range(cmin, cmax) ]) #print("{},{},{}-{}".format(self.curr_x, self.curr_y, cmin, cmax)) #print(data) self.curr_set += 1 if (self.curr_set == in_sets): self.curr_set = 0 self.curr_y += 1 if (self.curr_y == self.image_size[1]): self.curr_y = 0 self.curr_x += 1 self.convert_chn.push(data) if (self.curr_x == self.image_size[0]): self.curr_x = 0 self.ifmap_psum_done = True
class OutputDeserializer(Module): def instantiate(self, arch_output_chn, arr_x, arr_y, chn_per_word): # PE static configuration (immutable) self.arr_x = arr_x self.arr_y = arr_y self.chn_per_word = chn_per_word self.arch_output_chn = arch_output_chn self.ofmap = None self.reference = None self.image_size = (0, 0) self.curr_set = 0 self.fmap_idx = 0 self.pass_done = Reg(False) def configure(self, ofmap, reference, image_size): self.ofmap = ofmap self.reference = reference self.image_size = image_size self.curr_set = 0 self.fmap_idx = 0 self.pass_done.wr(False) def tick(self): if self.pass_done.rd(): return # How many psums packets we expect to receive out_sets = self.arr_x // self.chn_per_word fmap_per_iteration = self.image_size[0] * self.image_size[1] if self.arch_output_chn.valid(): data = [e for e in self.arch_output_chn.pop()] # Calculate the end coords of where these ending psums must go... x = self.fmap_idx % self.image_size[0] y = self.fmap_idx // self.image_size[0] if self.curr_set < out_sets: cmin = self.curr_set * self.chn_per_word cmax = cmin + self.chn_per_word for c in range(cmin, cmax): self.ofmap[x, y, c] = data[c - cmin] self.curr_set += 1 # After recieving all the elements for pixel 0, do pixel 1, etc... if self.curr_set == out_sets: self.curr_set = 0 self.fmap_idx += 1 if self.fmap_idx == fmap_per_iteration: self.fmap_idx = 0 self.pass_done.wr(True) if np.all(self.ofmap == self.reference): raise Finish("Success") else: print(self.ofmap) print(self.reference) print(self.ofmap - self.reference) raise Finish("Validation Failed")