def _sparse_adder(wire_array_2, adder): result = [] for single_w_index in range(len(wire_array_2)): if len(wire_array_2[single_w_index]) == 2: # Check if the two wire vectors overlap yet break result.append(wire_array_2[single_w_index][0]) import six wires_to_zip = wire_array_2[single_w_index:] add_wires = tuple(six.moves.zip_longest(*wires_to_zip, fillvalue=pyrtl.Const(0))) adder_result = adder(pyrtl.concat_list(add_wires[0]), pyrtl.concat_list(add_wires[1])) return pyrtl.concat(adder_result, *reversed(result))
def _sparse_adder(wire_array_2, adder): result = [] for single_w_index in range(len(wire_array_2)): if len(wire_array_2[single_w_index] ) == 2: # Check if the two wire vectors overlap yet break result.append(wire_array_2[single_w_index][0]) import six wires_to_zip = wire_array_2[single_w_index:] add_wires = tuple( six.moves.zip_longest(*wires_to_zip, fillvalue=pyrtl.Const(0))) adder_result = adder(pyrtl.concat_list(add_wires[0]), pyrtl.concat_list(add_wires[1])) return pyrtl.concat(adder_result, *reversed(result))
def kogge_stone(a, b, cin=0): """ Creates a Kogge-Stone adder given two inputs :param a, b: The two Wirevectors to add up (bitwidths don't need to match) :param cin: An optimal carry in Wirevector or value :return: a Wirevector representing the output of the adder The Kogge-Stone adder is a fast tree-based adder with O(log(n)) propagation delay, useful for performance critical designs. However, it has O(n log(n)) area usage, and large fan out. """ a, b = libutils.match_bitwidth(a, b) prop_orig = a ^ b prop_bits = [i for i in prop_orig] gen_bits = [i for i in a & b] prop_dist = 1 # creation of the carry calculation while prop_dist < len(a): for i in reversed(range(prop_dist, len(a))): prop_old = prop_bits[i] gen_bits[i] = gen_bits[i] | (prop_old & gen_bits[i - prop_dist]) if i >= prop_dist * 2: # to prevent creating unnecessary nets and wires prop_bits[i] = prop_old & prop_bits[i - prop_dist] prop_dist *= 2 # assembling the result of the addition # preparing the cin (and conveniently shifting the gen bits) gen_bits.insert(0, pyrtl.as_wires(cin)) return pyrtl.concat_list(gen_bits) ^ prop_orig
def _sub_bytes(self, in_vector, inverse=False): self._build_memories_if_not_exists() subbed = [ self.inv_sbox[byte] if inverse else self.sbox[byte] for byte in libutils.partition_wire(in_vector, 8) ] return pyrtl.concat_list(subbed)
def _mix_col_subgroup(self, in_vector, gm_multipliers): def _mix_single(index): mult_items = [self._galois_mult(a[(index + loc) % 4], mult_table) for loc, mult_table in enumerate(gm_multipliers)] return mult_items[0] ^ mult_items[1] ^ mult_items[2] ^ mult_items[3] a = libutils.partition_wire(in_vector, 8) return pyrtl.concat_list([_mix_single(index) for index in range(len(a))])
def _key_expansion(self, old_key, key_expand_round): self._build_memories_if_not_exists() w = libutils.partition_wire(old_key, 32) x = [w[3] ^ self._g(w[0], key_expand_round)] x.insert(0, x[0] ^ w[2]) x.insert(0, x[0] ^ w[1]) x.insert(0, x[0] ^ w[0]) return pyrtl.concat_list(x)
def test_key_expansion(self): # This is not at all correct. Needs to be completely rewritten self.out_vector <<= pyrtl.concat_list(self.aes_encrypt._key_gen(self.in_vector)) in_vals = [0x4c9c1e66f771f0762c3f868e534df256, 0xc57e1c159a9bd286f05f4be098c63439] true_result = [0x3bd92268fc74fb735767cbe0c0590e2d, 0xb458124c68b68a014b99f82e5f15554c] calculated_result = testingutils.sim_and_ret_out(self.out_vector, (self.in_vector,), (in_vals,)) self.assertEqual(calculated_result, true_result)
def _sparse_adder(wire_array_2, adder): bitwidth = len(wire_array_2) add_wires = [], [] result = [] for single_w_index in range(bitwidth): if len(wire_array_2[single_w_index]) == 2: # Check if the two wire vectors overlap yet break result.append(wire_array_2[single_w_index][0]) for w_loc in range(single_w_index, bitwidth): for i in range(2): if len(wire_array_2[w_loc]) >= i + 1: add_wires[i].append(wire_array_2[w_loc][i]) else: add_wires[i].append(pyrtl.Const(0)) adder_result = adder(pyrtl.concat_list(add_wires[0]), pyrtl.concat_list(add_wires[1])) return pyrtl.concat(adder_result, *reversed(result))
def _sparse_adder(wire_array_2, adder): bitwidth = len(wire_array_2) add_wires = [], [] result = [] for single_w_index in range(bitwidth): if len(wire_array_2[single_w_index] ) == 2: # Check if the two wire vectors overlap yet break result.append(wire_array_2[single_w_index][0]) for w_loc in range(single_w_index, bitwidth): for i in range(2): if len(wire_array_2[w_loc]) >= i + 1: add_wires[i].append(wire_array_2[w_loc][i]) else: add_wires[i].append(pyrtl.Const(0)) adder_result = adder(pyrtl.concat_list(add_wires[0]), pyrtl.concat_list(add_wires[1])) return pyrtl.concat(adder_result, *reversed(result))
def _mix_col_subgroup(self, in_vector, gm_multipliers): def _mix_single(index): mult_items = [ self._galois_mult(a[(index + loc) % 4], mult_table) for loc, mult_table in enumerate(gm_multipliers) ] return mult_items[0] ^ mult_items[1] ^ mult_items[2] ^ mult_items[3] a = libutils.partition_wire(in_vector, 8) return pyrtl.concat_list( [_mix_single(index) for index in range(len(a))])
def _g(self, word, key_expand_round): """ One-byte left circular rotation, substitution of each byte """ import numbers self._build_memories_if_not_exists() a = libutils.partition_wire(word, 8) sub = [self.sbox[a[index]] for index in (3, 0, 1, 2)] if isinstance(key_expand_round, numbers.Number): rcon_data = self._rcon_data[key_expand_round + 1] # int value else: rcon_data = self.rcon[key_expand_round + 1] sub[3] = sub[3] ^ rcon_data return pyrtl.concat_list(sub)
def _trivial_mult(A, B): """ turns a multiplication into an And gate if one of the wires is a bitwidth of 1 :param A: :param B: :return: """ if len(B) == 1: A, B = B, A # so that we can reuse the code below :) if len(A) == 1: a_vals = A.sign_extended(len(B)) # keep the wirevector len consistent return pyrtl.concat_list([a_vals & B, pyrtl.Const(0)])
def test_key_expansion(self): # This is not at all correct. Needs to be completely rewritten self.out_vector <<= pyrtl.concat_list( self.aes_encrypt._key_gen(self.in_vector)) in_vals = [ 0x4c9c1e66f771f0762c3f868e534df256, 0xc57e1c159a9bd286f05f4be098c63439 ] true_result = [ 0x3bd92268fc74fb735767cbe0c0590e2d, 0xb458124c68b68a014b99f82e5f15554c ] calculated_result = testingutils.sim_and_ret_out( self.out_vector, (self.in_vector, ), (in_vals, )) self.assertEqual(calculated_result, true_result)
def _shift_rows(in_vector): a = libutils.partition_wire(in_vector, 8) return pyrtl.concat_list( (a[4], a[9], a[14], a[3], a[8], a[13], a[2], a[7], a[12], a[1], a[6], a[11], a[0], a[5], a[10], a[15]))
def _mix_columns(self, in_vector, inverse=False): self._build_memories_if_not_exists() igm_mults = [14, 9, 13, 11] if inverse else [2, 1, 1, 3] subgroups = libutils.partition_wire(in_vector, 32) return pyrtl.concat_list( [self._mix_col_subgroup(sg, igm_mults) for sg in subgroups])
def _mix_columns(self, in_vector, inverse=False): self._build_memories_if_not_exists() igm_mults = [14, 9, 13, 11] if inverse else [2, 1, 1, 3] subgroups = libutils.partition_wire(in_vector, 32) return pyrtl.concat_list([self._mix_col_subgroup(sg, igm_mults) for sg in subgroups])
def _shift_rows(in_vector): a = libutils.partition_wire(in_vector, 8) return pyrtl.concat_list((a[4], a[9], a[14], a[3], a[8], a[13], a[2], a[7], a[12], a[1], a[6], a[11], a[0], a[5], a[10], a[15]))
def _sub_bytes(self, in_vector, inverse=False): self._build_memories_if_not_exists() subbed = [self.inv_sbox[byte] if inverse else self.sbox[byte] for byte in libutils.partition_wire(in_vector, 8)] return pyrtl.concat_list(subbed)
sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'racetrees')) from flat_racetree import FlatRaceTree from racelogic_sim_input_stimuli import race_testval ### Testing ### inp_res = 4 # input resolution -- in this case we are using 4-bit inputs tree_depth = 2 # depth of the tree # Build x = pyrtl.Input(bitwidth=1, name='x') y = pyrtl.Input(bitwidth=1, name='y') out_bin = pyrtl.Output(bitwidth=tree_depth, name='out_bin') valid_out = pyrtl.Output(bitwidth=1, name='valid_out') attributes = pyrtl.concat_list([x, y]) # list of the tree's attributes tree_nodes = [[2, 0], [1, 0], [1, 1]] # [threshold, attribute_index] RT = FlatRaceTree(inp_res, tree_depth, attributes, tree_nodes) tree_out = RT.tree() out_bin <<= tree_out[0] valid_out <<= tree_out[1] # Simulate xin, yin = race_testval(2), race_testval(3) sim_trace = pyrtl.SimulationTrace() sim = pyrtl.Simulation(tracer=sim_trace) for cycle in range(2**inp_res + 1): sim.step({'x': xin.next(), 'y': yin.next()}) sim_trace.render_trace(symbol_len=5, segment_size=1)