def __init__(s, interface): UseInterface(s, interface) nreqs = s.interface.nreqs s.mask = Register(RegisterInterface(Bits(nreqs)), reset_value=0) s.masker = ThermometerMask(ThermometerMaskInterface(nreqs)) s.raw_arb = PriorityArbiter(ArbiterInterface(nreqs)) s.masked_arb = PriorityArbiter(ArbiterInterface(nreqs)) s.final_grant = Wire(nreqs) s.connect(s.raw_arb.grant_reqs, s.grant_reqs) s.connect(s.masker.mask_in_, s.mask.read_data) @s.combinational def compute(): s.masked_arb.grant_reqs.v = s.grant_reqs & s.masker.mask_out if s.masked_arb.grant_grant == 0: s.final_grant.v = s.raw_arb.grant_grant else: s.final_grant.v = s.masked_arb.grant_grant @s.combinational def shift_write(): s.mask.write_data.v = s.final_grant << 1 s.connect(s.grant_grant, s.final_grant)
def __init__(s): UseInterface(s, StageInterface(None, Bits(8))) s.counter = Register(RegisterInterface(Bits(8), enable=True), reset_value=0) s.connect(s.process_accepted, 1) s.connect(s.process_out, s.counter.read_data) s.connect(s.counter.write_call, s.process_call) @s.combinational def count(): s.counter.write_data.v = s.counter.read_data + 1
def __init__(s, interface): UseInterface(s, interface) s.require( MethodSpec( 'input', args=None, rets={ 'data': s.interface.Data, }, call=True, rdy=True, )) s.drop_pending = Register( RegisterInterface(Bits(1), False, False), reset_value=0) s.drop_pending_curr = Wire(1) s.connect(s.output_data, s.input_data) @s.combinational def handle_drop(): s.drop_pending_curr.v = s.drop_pending.read_data or s.drop_call s.drop_status_occurred.v = s.drop_pending_curr and s.input_rdy s.drop_rdy.v = not s.drop_pending.read_data if s.drop_status_occurred: s.input_call.v = 1 s.output_rdy.v = 0 s.drop_pending.write_data.v = 0 elif s.drop_pending_curr: s.input_call.v = 0 s.output_rdy.v = 0 s.drop_pending.write_data.v = 1 else: s.input_call.v = s.output_call s.output_rdy.v = s.input_rdy s.drop_pending.write_data.v = 0
def __init__(s, mul_interface, nstages): UseInterface(s, mul_interface) assert nstages > 0 m = s.interface.DataLen n = 2 * m if s.interface.KeepUpper else m s.valids_ = [ Register(RegisterInterface(1), reset_value=0) for _ in range(nstages) ] s.vals_ = [ Register(RegisterInterface(n, enable=True)) for _ in range(nstages) ] s.exec_ = [Wire(Bits(1)) for _ in range(nstages)] s.rdy_ = [Wire(Bits(1)) for _ in range(nstages)] s.value_ = Wire(2 * m) # All the inputs get converted to unsigned s.src1_usign_ = Wire(Bits(m)) s.src2_usign_ = Wire(Bits(m)) s.sign_in_ = Wire(1) # Execute call s.connect(s.mult_rdy, s.rdy_[0]) # Result call s.connect(s.peek_rdy, s.valids_[nstages - 1].read_data) s.connect(s.take_rdy, s.valids_[nstages - 1].read_data) s.connect(s.peek_res, s.vals_[nstages - 1].read_data) for i in range(nstages): s.connect(s.vals_[i].write_call, s.exec_[i]) # HERE is the actual multiply that will be retimed @s.combinational def comb_mult(): s.value_.v = s.src1_usign_ * s.src2_usign_ @s.combinational def unsign_srcs_in(): s.src1_usign_.v = 0 s.src2_usign_.v = 0 s.sign_in_.v = 0 s.sign_in_.v = (s.mult_src1_signed and s.mult_src1[m - 1]) ^ ( s.mult_src2_signed and s.mult_src2[m - 1]) s.src1_usign_.v = (~s.mult_src1 + 1) if (s.mult_src1[m - 1] and s.mult_src1_signed) else s.mult_src1 s.src2_usign_.v = (~s.mult_src2 + 1) if (s.mult_src2[m - 1] and s.mult_src2_signed) else s.mult_src2 @s.combinational def set_rdy_last(): # Incoming call: s.rdy_[nstages - 1].v = s.take_call or not s.valids_[nstages - 1].read_data for i in range(nstages - 1): @s.combinational def set_rdy(i=i): # A stage is ready to accept if it is invalid or next stage is ready s.rdy_[i].v = not s.valids_[i].read_data or s.rdy_[i + 1] @s.combinational def set_exec_first(): s.exec_[0].v = s.rdy_[0] and s.mult_call for i in range(1, nstages): @s.combinational def set_exec(i=i): # Will execute if stage ready and current work is valid s.exec_[i].v = s.rdy_[i] and s.valids_[i - 1].read_data @s.combinational def set_valids_last(): s.valids_[nstages - 1].write_data.v = ( not s.take_call and s.valids_[nstages - 1].read_data) or s.exec_[nstages - 1] for i in range(nstages - 1): @s.combinational def set_valids(i=i): # Valid if blocked on next stage, or multuted this cycle s.valids_[i].write_data.v = ( not s.rdy_[i + 1] and s.valids_[i].read_data) or s.exec_[i] @s.combinational def mult(): s.vals_[ 0].write_data.v = ~s.value_[: n] + 1 if s.sign_in_ else s.value_[: n] for i in range(1, nstages): s.vals_[i].write_data.v = s.vals_[i - 1].read_data
def __init__(s, mul_interface, nstages, use_mul=True): UseInterface(s, mul_interface) # For now must be evenly divisible assert nstages > 0 assert s.interface.DataLen % nstages == 0 m = s.interface.DataLen n = 2 * m if s.interface.KeepUpper else m k = s.interface.DataLen // nstages last = nstages - 1 # All the inputs get converted to unsigned s.src1_usign_ = Wire(Bits(m)) s.src2_usign_ = Wire(Bits(m)) # At step i, i = [0, nstages), product needs at most m + k(i+1) bits s.valids_ = [ Register(RegisterInterface(1), reset_value=0) for _ in range(nstages) ] if s.interface.KeepUpper: s.vals_ = [ # nbits = m + k * (i + 1) Register(RegisterInterface(2 * m, enable=True)) for i in range(nstages) ] s.units_ = [ MulCombinational( # input nbits = m,k, output = k+m MulCombinationalInterface(m, k, 2 * m), use_mul) for i in range(nstages) ] s.src2_ = [ # nbits = m - k * i Register(RegisterInterface(m, enable=True)) for i in range(nstages - 1) ] else: s.vals_ = [ Register(RegisterInterface(m, enable=True)) for _ in range(nstages) ] s.units_ = [ MulCombinational(MulCombinationalInterface(m, k, m), use_mul) for _ in range(nstages) ] s.src2_ = [ Register(RegisterInterface(m, enable=True)) for _ in range(nstages - 1) ] s.src1_ = [ Register(RegisterInterface(m, enable=True)) for _ in range(nstages - 1) ] s.signs_ = [ Register(RegisterInterface(1, enable=True)) for _ in range(nstages - 1) ] s.exec_ = [Wire(Bits(1)) for _ in range(nstages)] s.rdy_ = [Wire(Bits(1)) for _ in range(nstages)] s.sign_out_ = Wire(Bits(1)) s.sign_in_ = Wire(Bits(1)) # Connect the sign bit in the last stage if nstages == 1: s.connect_wire(s.sign_out_, s.sign_in_) else: s.connect(s.sign_out_, s.signs_[last - 1].read_data) # Execute call rdy s.connect(s.mult_rdy, s.rdy_[0]) # Result call rdy s.connect(s.peek_rdy, s.valids_[last].read_data) s.connect(s.take_rdy, s.valids_[last].read_data) s.connect(s.peek_res, s.vals_[last].read_data) for i in range(nstages): s.connect(s.vals_[i].write_call, s.exec_[i]) s.connect(s.units_[i].mult_call, s.exec_[i]) # Last stage does not have these if i < nstages - 1: s.connect(s.src1_[i].write_call, s.exec_[i]) s.connect(s.src2_[i].write_call, s.exec_[i]) s.connect(s.signs_[i].write_call, s.exec_[i]) # Take twos compliment @s.combinational def unsign_srcs_in(): s.src1_usign_.v = 0 s.src2_usign_.v = 0 s.sign_in_.v = 0 s.sign_in_.v = (s.mult_src1_signed and s.mult_src1[m - 1]) ^ ( s.mult_src2_signed and s.mult_src2[m - 1]) s.src1_usign_.v = (~s.mult_src1 + 1) if (s.mult_src1[m - 1] and s.mult_src1_signed) else s.mult_src1 s.src2_usign_.v = (~s.mult_src2 + 1) if (s.mult_src2[m - 1] and s.mult_src2_signed) else s.mult_src2 @s.combinational def connect_unit0(): s.units_[0].mult_src1.v = s.src1_usign_ s.units_[0].mult_src2.v = s.src2_usign_[:k] for i in range(1, nstages): @s.combinational def connect_unitk(i=i): s.units_[i].mult_src1.v = s.src1_[i - 1].read_data s.units_[i].mult_src2.v = s.src2_[i - 1].read_data[:k] @s.combinational def set_rdy_last(): s.rdy_[last].v = s.take_call or not s.valids_[last].read_data for i in range(nstages - 1): @s.combinational def set_rdy(i=i): # A stage is ready to accept if it is invalid or next stage is ready s.rdy_[i].v = not s.valids_[i].read_data or s.rdy_[i + 1] @s.combinational def set_exec_first(): s.exec_[0].v = s.rdy_[0] and s.mult_call for i in range(1, nstages): @s.combinational def set_exec(i=i): # Will execute if stage ready and current work is valid s.exec_[i].v = s.rdy_[i] and s.valids_[i - 1].read_data @s.combinational def set_valids_last(): s.valids_[last].write_data.v = ( not s.take_call and s.valids_[last].read_data) or s.exec_[last] for i in range(nstages - 1): @s.combinational def set_valids(i=i): # Valid if blocked on next stage, or multuted this cycle s.valids_[i].write_data.v = ( not s.rdy_[i + 1] and s.valids_[i].read_data) or s.exec_[i] # Hook up the pipeline stages if nstages == 1: @s.combinational def connect_stage(): s.vals_[0].write_data.v = ~s.units_[ 0].mult_res + 1 if s.sign_out_ else s.units_[0].mult_res else: @s.combinational def connect_first_stage(): s.vals_[0].write_data.v = s.units_[0].mult_res s.src1_[0].write_data.v = s.src1_usign_ s.src2_[0].write_data.v = s.src2_usign_ >> k s.signs_[0].write_data.v = s.sign_in_ for i in range(1, nstages - 1): @s.combinational def connect_stage(i=i): s.vals_[i].write_data.v = s.vals_[i - 1].read_data + ( s.units_[i].mult_res << (k * i)) s.src1_[i].write_data.v = s.src1_[i - 1].read_data s.src2_[i].write_data.v = s.src2_[i - 1].read_data >> k s.signs_[i].write_data.v = s.signs_[i - 1].read_data @s.combinational def connect_last_stage(): if s.sign_out_: s.vals_[last].write_data.v = ~( s.vals_[last - 1].read_data + (s.units_[last].mult_res << (k * last))) + 1 else: s.vals_[last].write_data.v = s.vals_[ last - 1].read_data + (s.units_[last].mult_res << (k * last))
def __init__(s, interface, MemMsg): UseInterface(s, interface) s.require( MethodSpec( 'mb_send', args={'msg': MemMsg.req}, rets=None, call=True, rdy=True, ), MethodSpec( 'mb_recv', args=None, rets={'msg': MemMsg.resp}, call=True, rdy=True, ), ) s.store_in_flight = Register(RegisterInterface(Bits(1))) s.store_in_flight_after_recv = Wire(1) @s.combinational def handle_recv(): s.recv_load_data.v = s.mb_recv_msg.data if s.mb_recv_rdy: if s.store_in_flight.read_data: s.mb_recv_call.v = 1 s.recv_load_rdy.v = 0 s.store_in_flight_after_recv.v = 0 else: s.mb_recv_call.v = s.recv_load_call s.recv_load_rdy.v = 1 s.store_in_flight_after_recv.v = 0 else: s.mb_recv_call.v = 0 s.recv_load_rdy.v = 0 s.store_in_flight_after_recv.v = s.store_in_flight.read_data @s.combinational def handle_send_rdy(): if s.mb_send_rdy: s.send_store_rdy.v = 1 s.send_load_rdy.v = not s.send_store_call else: s.send_store_rdy.v = 0 s.send_load_rdy.v = 0 @s.combinational def handle_send(size=s.interface.Size.nbits - 1): s.mb_send_msg.v = 0 if s.send_store_call: s.mb_send_call.v = 1 s.mb_send_msg.type_.v = MemMsgType.WRITE s.mb_send_msg.opaque.v = 0 s.mb_send_msg.addr.v = s.send_store_addr # This size will have to be truncated by 1 bit because full for a mem msg # is 0. The length field must always be a power of 2 so this works s.mb_send_msg.len_.v = s.send_store_size[0:size] s.mb_send_msg.data.v = s.send_store_data s.store_in_flight.write_data.v = 1 elif s.send_load_call: s.mb_send_call.v = 1 s.mb_send_msg.type_.v = MemMsgType.READ s.mb_send_msg.opaque.v = 0 s.mb_send_msg.addr.v = s.send_load_addr s.mb_send_msg.len_.v = s.send_load_size[0:size] s.mb_send_msg.data.v = 0 s.store_in_flight.write_data.v = 0 else: s.mb_send_call.v = 0 s.store_in_flight.write_data.v = s.store_in_flight_after_recv s.connect(s.store_acks_outstanding_ret, s.store_in_flight_after_recv)
def __init__(s, interface, nregs): UseInterface(s, interface) Addr = Bits(clog2nz(nregs)) Key = s.interface.Key Value = s.interface.Value s.Entry = Entry(Key, Value) s.entries = [ Register(RegisterInterface(s.Entry(), enable=True), reset_value=0) for _ in range(nregs) ] s.overwrite_counter = Register( RegisterInterface(Addr, enable=True), reset_value=0) s.read_addr_chain = [Wire(Addr) for _ in range(nregs)] s.read_addr_valid = [Wire(1) for _ in range(nregs)] # PYMTL_BROKEN s.entries_read_data_key = [Wire(Key) for _ in range(nregs)] s.entries_read_data_value = [Wire(Value) for _ in range(nregs)] s.entries_read_data_valid = [Wire(1) for _ in range(nregs)] s.entries_write_data_key = [Wire(Key) for _ in range(nregs)] s.entries_write_data_value = [Wire(Value) for _ in range(nregs)] s.entries_write_data_valid = [Wire(1) for _ in range(nregs)] for i in range(nregs): s.connect(s.entries_read_data_key[i], s.entries[i].read_data.key) s.connect(s.entries_read_data_value[i], s.entries[i].read_data.value) s.connect(s.entries_read_data_valid[i], s.entries[i].read_data.valid) s.connect(s.entries[i].write_data.key, s.entries_write_data_key[i]) s.connect(s.entries[i].write_data.value, s.entries_write_data_value[i]) s.connect(s.entries[i].write_data.valid, s.entries_write_data_valid[i]) for i in range(nregs): if i == 0: @s.combinational def handle_read_addr_0(i=i): s.read_addr_chain[i].v = i s.read_addr_valid[i].v = s.entries_read_data_key[ i] == s.read_key and s.entries_read_data_valid[i] else: @s.combinational def handle_read_addr(i=i, j=i - 1): if s.entries_read_data_key[ i] == s.read_key and s.entries_read_data_valid[i]: s.read_addr_chain[i].v = i s.read_addr_valid[i].v = 1 else: s.read_addr_chain[i].v = s.read_addr_chain[j] s.read_addr_valid[i].v = s.read_addr_valid[j] @s.combinational def handle_read(): s.read_value.v = s.entries_read_data_value[s.read_addr_chain[nregs - 1]] s.read_valid.v = s.read_addr_valid[nregs - 1] s.write_addr_chain = [Wire(Addr) for _ in range(nregs)] s.write_addr_valid = [Wire(1) for _ in range(nregs)] s.invalid_addr_chain = [Wire(Addr) for _ in range(nregs)] s.invalid_addr_valid = [Wire(1) for _ in range(nregs)] for i in range(nregs): if i == 0: @s.combinational def handle_write_addr_0(i=i): s.write_addr_chain[i].v = i s.write_addr_valid[i].v = s.entries_read_data_key[ i] == s.write_key and s.entries_read_data_valid[i] s.invalid_addr_chain[i].v = i s.invalid_addr_valid[i].v = not s.entries_read_data_valid[i] else: @s.combinational def handle_write_addr(i=i, j=i - 1): if s.entries_read_data_key[ i] == s.write_key and s.entries_read_data_valid[i]: s.write_addr_chain[i].v = i s.write_addr_valid[i].v = 1 else: s.write_addr_chain[i].v = s.write_addr_chain[j] s.write_addr_valid[i].v = s.write_addr_valid[j] if not s.entries_read_data_valid[i]: s.invalid_addr_chain[i].v = i s.invalid_addr_valid[i].v = 1 else: s.invalid_addr_chain[i].v = s.invalid_addr_chain[j] s.invalid_addr_valid[i].v = s.invalid_addr_valid[j] s.overwrite = Wire(1) @s.combinational def compute_overwrite(): s.overwrite.v = not s.write_remove and not s.write_addr_valid[ nregs - 1] and not s.invalid_addr_valid[nregs - 1] for i in range(nregs): @s.combinational def handle_write(i=i): if not s.clear_call: s.entries[i].write_call.v = s.write_call and ( (s.overwrite and s.overwrite_counter.read_data == i) or (s.write_addr_chain[nregs - 1] == i and s.write_addr_valid[nregs - 1]) or (not s.write_addr_valid[nregs - 1] and s.invalid_addr_chain[nregs - 1] == i and s.invalid_addr_valid[nregs - 1] and not s.write_remove)) s.entries_write_data_key[i].v = s.write_key s.entries_write_data_value[i].v = s.write_value s.entries_write_data_valid[i].v = not s.write_remove else: s.entries[i].write_call.v = 1 s.entries_write_data_key[i].v = 0 s.entries_write_data_value[i].v = 0 s.entries_write_data_valid[i].v = 0 @s.combinational def update_overwrite_counter(nregsm1=nregs - 1): if s.write_call and s.overwrite: s.overwrite_counter.write_call.v = 1 if s.overwrite_counter.read_data == nregsm1: s.overwrite_counter.write_data.v = 0 else: s.overwrite_counter.write_data.v = s.overwrite_counter.read_data + 1 else: s.overwrite_counter.write_call.v = 0 s.overwrite_counter.write_data.v = 0
def __init__(s, interface): UseInterface(s, interface) seqidx_nbits = s.interface.SeqIdxNbits max_entries = 1 << seqidx_nbits # ROB stuff: Dealloc from head, alloc at tail s.tail = Register(RegisterInterface(Bits(seqidx_nbits), enable=True), reset_value=0) s.head = Register(RegisterInterface(Bits(seqidx_nbits), enable=True), reset_value=0) s.num = Register(RegisterInterface(Bits(seqidx_nbits + 1), enable=True), reset_value=0) s.head_next = Wire(seqidx_nbits) s.tail_next = Wire(seqidx_nbits) s.empty_ = Wire(1) s.full_ = Wire(1) # Connect methods s.connect(s.allocate_idx, s.tail.read_data) s.connect(s.get_head_idx, s.head.read_data) # All the following comb blocks are for ROB stuff: @s.combinational def set_method_rdy(): s.allocate_rdy.v = not s.full_ s.get_head_rdy.v = not s.empty_ s.free_rdy.v = not s.empty_ @s.combinational def set_flags(): s.full_.v = s.num.read_data == max_entries s.empty_.v = s.num.read_data == 0 @s.combinational def update_tail(): s.tail.write_call.v = s.allocate_call or s.rollback_call s.tail_next.v = s.tail.read_data + 1 if s.rollback_call: s.tail_next.v = s.rollback_idx + 1 s.tail.write_data.v = s.tail_next.v @s.combinational def update_head(): s.head_next.v = s.head.read_data + 1 if s.free_call else s.head.read_data s.head.write_call.v = s.free_call s.head.write_data.v = s.head_next s.head_tail_delta = Wire(seqidx_nbits) @s.combinational def update_num(seqp1=seqidx_nbits + 1): s.head_tail_delta.v = s.tail_next - s.head_next s.num.write_call.v = s.tail.write_call or s.head.write_call s.num.write_data.v = s.num.read_data if s.rollback_call: # If it is going to be full (head=tail and not rolling back to head) if s.head_tail_delta == 0 and (s.rollback_idx != s.head.read_data): s.num.write_data.v = max_entries else: s.num.write_data.v = zext( s.head_tail_delta, seqp1) # An exception clears everything elif s.allocate_call ^ s.free_call: if s.allocate_call: s.num.write_data.v = s.num.read_data + 1 elif s.free_call: s.num.write_data.v = s.num.read_data - 1
def __init__(s, interface, make_kill, bypass_ready=True): """ This model implements a generic issue slot, an issue queue has an instance of this for each slot in the queue SlotType: Should subclass AbstractSlotType and add any additional fields """ UseInterface(s, interface) # The storage for everything #s.valid_ = Register(RegisterInterface(Bits(1)), reset_value=0) # Make the valid manager from the DropControllerInterface passed in s.val_manager_ = gen_valid_value_manager(make_kill)() s.opaque_ = Register(RegisterInterface(s.interface.Opaque, enable=True)) s.src0_ = Register(RegisterInterface(s.interface.SrcTag, enable=True)) s.src0_val_ = Register(RegisterInterface(Bits(1), enable=True)) s.src0_rdy_ = Register(RegisterInterface(Bits(1), enable=True)) s.src1_ = Register(RegisterInterface(s.interface.SrcTag, enable=True)) s.src1_val_ = Register(RegisterInterface(Bits(1), enable=True)) s.src1_rdy_ = Register(RegisterInterface(Bits(1), enable=True)) if s.interface.WithOrder: s.ordered_ = Register(RegisterInterface(Bits(1), enable=True)) s.connect(s.peek_value.ordered, s.ordered_.read_data) s.connect(s.status_ordered, s.ordered_.read_data) s.connect(s.ordered_.write_data, s.input_value.ordered) s.connect(s.ordered_.write_call, s.input_call) s.srcs_ready_ = Wire(1) s.kill_ = Wire(1) # Does it match this cycle? s.src0_match_ = Wire(1) s.src1_match_ = Wire(1) # Connect the output method s.connect(s.peek_value.opaque, s.opaque_.read_data) s.connect(s.peek_value.src0, s.src0_.read_data) s.connect(s.peek_value.src0_val, s.src0_val_.read_data) s.connect(s.peek_value.src1, s.src1_.read_data) s.connect(s.peek_value.src1_val, s.src1_val_.read_data) # Connect inputs into registers s.connect(s.opaque_.write_data, s.input_value.opaque) s.connect(s.src0_.write_data, s.input_value.src0) s.connect(s.src0_val_.write_data, s.input_value.src0_val) s.connect(s.src1_.write_data, s.input_value.src1) s.connect(s.src1_val_.write_data, s.input_value.src1_val) # Connect all the enables s.connect(s.opaque_.write_call, s.input_call) s.connect(s.src0_.write_call, s.input_call) s.connect(s.src0_val_.write_call, s.input_call) s.connect(s.src1_.write_call, s.input_call) s.connect(s.src1_val_.write_call, s.input_call) # Connect up val manager s.connect(s.val_manager_.add_msg, s.input_value.kill_opaque) s.connect(s.peek_value.kill_opaque, s.val_manager_.peek_msg) s.connect(s.val_manager_.add_call, s.input_call) s.connect(s.status_valid, s.val_manager_.peek_rdy) s.connect(s.val_manager_.take_call, s.take_call) # Lift the global kill notify signal s.connect_m(s.val_manager_.kill_notify, s.kill_notify) s.src0_notify_match = Wire(s.interface.NumNotify) s.src1_notify_match = Wire(s.interface.NumNotify) @s.combinational def match_src(): for i in range(s.interface.NumNotify): s.src0_notify_match[i].v = s.src0_val_.read_data and s.notify_call[ i] and (s.src0_.read_data == s.notify_tag[i]) s.src1_notify_match[i].v = s.src1_val_.read_data and s.notify_call[ i] and (s.src1_.read_data == s.notify_tag[i]) s.src0_match_.v = reduce_or(s.src0_notify_match) s.src1_match_.v = reduce_or(s.src1_notify_match) @s.combinational def handle_ready(): s.peek_value.src0_rdy.v = s.src0_rdy_.read_data or s.src0_match_ s.peek_value.src1_rdy.v = s.src1_rdy_.read_data or s.src1_match_ s.status_ready.v = s.status_valid and s.srcs_ready_ if bypass_ready: @s.combinational def handle_srcs_ready(): s.srcs_ready_.v = s.peek_value.src0_rdy and s.peek_value.src1_rdy else: @s.combinational def handle_srcs_ready(): s.srcs_ready_.v = s.src0_rdy_.read_data and s.src1_rdy_.read_data @s.combinational def set_reg_rdy(): s.src0_rdy_.write_call.v = s.input_call or (s.src0_match_ and s.status_valid) s.src1_rdy_.write_call.v = s.input_call or (s.src1_match_ and s.status_valid) if s.input_call: s.src0_rdy_.write_data.v = s.input_value.src0_rdy or not s.input_value.src0_val s.src1_rdy_.write_data.v = s.input_value.src1_rdy or not s.input_value.src1_val else: s.src0_rdy_.write_data.v = s.src0_match_ s.src1_rdy_.write_data.v = s.src1_match_
def __init__(s, interface): UseInterface(s, interface) tracking = s.interface.tracking s.require( DropControllerInterface(s.interface.DataIn, s.interface.DataOut, s.interface.KillArgType)['check']) s.val_reg = Register(RegisterInterface(Bits(1)), reset_value=0) s.out_reg = Register(RegisterInterface(s.interface.DataIn)) if tracking: s.dead_reg = Register(RegisterInterface(Bits(1))) s.output_rdy = Wire(1) s.output_clear = Wire(1) if s.interface.KillArgType is not None: s.connect(s.check_msg, s.kill_notify_msg) s.connect(s.check_in_, s.out_reg.read_data) s.connect(s.peek_msg, s.check_out) s.should_keep = Wire(1) # If tracking we always keep it, but just mark it dead if tracking: s.connect(s.should_keep, 1) else: s.connect(s.should_keep, s.check_keep) @s.combinational def handle_rdy(): if s.val_reg.read_data: s.output_rdy.v = s.should_keep s.dropping_out.v = not s.should_keep else: s.output_rdy.v = 0 s.dropping_out.v = 0 s.connect(s.peek_rdy, s.output_rdy) @s.combinational def handle_clear(): s.output_clear.v = not s.output_rdy or s.take_call s.connect(s.add_rdy, s.output_clear) @s.combinational def handle_val_reg_in(): if s.add_call: s.val_reg.write_data.v = 1 else: s.val_reg.write_data.v = not s.output_clear @s.combinational def handle_out_reg(): if s.add_call: s.out_reg.write_data.v = s.add_msg else: s.out_reg.write_data.v = s.check_out if tracking: @s.combinational def handle_dead_reg(): # It is dead if it is already dead or we are not keeping it s.peek_dead.v = s.dead_reg.read_data or not s.check_keep if s.add_call: s.dead_reg.write_data.v = s.add_dead else: s.dead_reg.write_data.v = s.peek_dead
def __init__(s, interface, ncycles): UseInterface(s, interface) assert s.interface.DataLen % ncycles == 0 nsteps = s.interface.DataLen // ncycles END = s.interface.DataLen - 1 AEND = s.interface.DataLen iface = NonRestoringDividerStepInterface(s.interface.DataLen) s.unit = NonRestoringDividerStep(iface, nsteps) s.acc = Register( RegisterInterface(s.interface.DataLen + 1, enable=True)) s.divisor = Register( RegisterInterface(s.interface.DataLen, enable=True)) s.dividend = Register( RegisterInterface(s.interface.DataLen, enable=True)) # Set if we need to take twos compliment at end s.negate = Register(RegisterInterface(1, enable=True)) s.negate_rem = Register(RegisterInterface(1, enable=True)) s.connect(s.negate.write_call, s.div_call) s.connect(s.negate_rem.write_call, s.div_call) s.connect(s.divisor.write_call, s.div_call) # Connect up the unit s.connect(s.unit.div_acc, s.acc.read_data) s.connect(s.unit.div_divisor, s.divisor.read_data) s.connect(s.unit.div_dividend, s.dividend.read_data) s.counter = Register(RegisterInterface(clog2(ncycles + 1), enable=True)) s.busy = Register(RegisterInterface(1, enable=True), reset_value=0) @s.combinational def handle_calls(): # Arguments s.div_rdy.v = not s.busy.read_data or s.result_call # Results s.result_rdy.v = s.busy.read_data and s.counter.read_data == 0 s.result_quotient.v = s.dividend.read_data s.result_rem.v = s.acc.read_data[:s.interface.DataLen] # Figure out if we need to negative s.negate.write_data.v = s.div_signed and (s.div_divisor[END] ^ s.div_dividend[END]) s.negate_rem.write_data.v = s.div_signed and s.div_dividend[END] @s.combinational def handle_counter(): s.counter.write_call.v = s.counter.read_data != 0 or s.div_call s.counter.write_data.v = 0 if s.div_call: s.counter.write_data.v = ncycles else: s.counter.write_data.v = s.counter.read_data - 1 @s.combinational def set_div_regs(): s.acc.write_call.v = s.div_call or s.counter.read_data > 0 s.dividend.write_call.v = s.div_call or s.counter.read_data > 0 # Load the values s.acc.write_data.v = 0 s.divisor.write_data.v = 0 s.divisor.write_data.v = ~s.div_divisor + 1 if ( s.div_signed and s.div_divisor[END]) else s.div_divisor s.dividend.write_data.v = ~s.div_dividend + 1 if ( s.div_signed and s.div_dividend[END]) else s.div_dividend if not s.div_call: s.dividend.write_data.v = s.unit.div_dividend_next s.acc.write_data.v = s.unit.div_acc_next # Special case last cycle if s.counter.read_data == 1: if s.unit.div_acc_next[AEND]: s.acc.write_data.v += s.divisor.read_data if s.negate_rem.read_data: # Last cycle, compliment s.acc.write_data.v = ~s.acc.write_data + 1 # Only if not divided by zero if s.negate.read_data and s.divisor.read_data != 0: s.dividend.write_data.v = ~s.dividend.write_data + 1 @s.combinational def handle_busy(): s.busy.write_call.v = s.div_call or s.result_call or s.preempt_call s.busy.write_data.v = s.div_call
def __init__(s, fetch_interface, MemMsg, enable_btb): UseInterface(s, fetch_interface) s.MemMsg = MemMsg xlen = XLEN ilen = ILEN ilen_bytes = ilen / 8 s.require( MethodSpec( 'mem_recv', args=None, rets={'msg': s.MemMsg.resp}, call=True, rdy=True, ), MethodSpec( 'mem_send', args={'msg': s.MemMsg.req}, rets=None, call=True, rdy=True, ), MethodSpec( 'check_redirect', args={}, rets={ 'redirect': Bits(1), 'target': Bits(xlen), }, call=False, rdy=False, ), MethodSpec( 'btb_read', args={ 'key': XLEN, }, rets={ 'value': XLEN, 'valid': Bits(1) }, call=False, rdy=False, ), ) s.drop_unit = DropUnit(DropUnitInterface(s.MemMsg.resp)) s.connect_m(s.drop_unit.input, s.mem_recv, { 'msg': 'data', }) # PYMTL_BROKEN s.drop_unit_output_data_data = Wire(s.drop_unit.output_data.data.nbits) s.connect(s.drop_unit_output_data_data, s.drop_unit.output_data.data) s.inst_from_mem = Wire(ILEN) # PYMTL_BROKEN @s.combinational def pymtl_is_broken_connect_does_not_work(): s.inst_from_mem.v = s.drop_unit_output_data_data[0:ilen] s.fetch_val = Register(RegisterInterface(Bits(1), True, False), reset_value=0) s.fetch_msg = Register(RegisterInterface(FetchMsg(), True, False)) s.in_flight = Register(RegisterInterface(Bits(1), True, False), reset_value=0) s.pc = Register(RegisterInterface(Bits(xlen), True, False), reset_value=0) s.advance_f1 = Wire(1) s.advance_f0 = Wire(1) @s.combinational def handle_advance(): s.advance_f1.v = s.drop_unit.output_rdy and ( not s.fetch_val.read_data or s.take_call) s.advance_f0.v = not s.in_flight.read_data or s.drop_unit.drop_status_occurred or s.advance_f1 @s.combinational def handle_redirect(): # Insert BTB here! s.btb_read_key.v = s.pc.read_data if s.check_redirect_redirect: # drop if in flight s.drop_unit.drop_call.v = s.in_flight.read_data # the new PC is the target s.pc.write_data.v = s.check_redirect_target s.pc.write_call.v = 1 else: s.drop_unit.drop_call.v = 0 # if we are issuing now, the new PC is just ilen_bytes more than the last one if s.btb_read_valid and enable_btb: s.pc.write_data.v = s.btb_read_value else: s.pc.write_data.v = s.pc.read_data + ilen_bytes s.pc.write_call.v = s.advance_f0 s.connect(s.in_flight.write_data, 1) s.connect(s.in_flight.write_call, s.advance_f0) s.connect(s.peek_msg, s.fetch_msg.read_data) @s.combinational def handle_f1(): s.fetch_val.write_call.v = 0 s.fetch_val.write_data.v = 0 s.fetch_msg.write_call.v = 0 s.fetch_msg.write_data.v = 0 s.drop_unit.output_call.v = 0 if s.check_redirect_redirect: # invalidate the output s.peek_rdy.v = 0 # write a 0 into the valid register s.fetch_val.write_call.v = 1 else: s.peek_rdy.v = s.fetch_val.read_data if s.drop_unit.output_rdy and (not s.fetch_val.read_data or s.take_call): s.fetch_val.write_call.v = 1 s.fetch_val.write_data.v = 1 s.fetch_msg.write_call.v = 1 s.drop_unit.output_call.v = 1 s.fetch_msg.write_data.hdr_pc.v = s.pc.read_data if s.drop_unit.output_data.stat != MemMsgStatus.OK: s.fetch_msg.write_data.hdr_status.v = PipelineMsgStatus.PIPELINE_MSG_STATUS_EXCEPTION_RAISED if s.drop_unit.output_data.stat == MemMsgStatus.ADDRESS_MISALIGNED: s.fetch_msg.write_data.exception_info_mcause.v = ExceptionCode.INSTRUCTION_ADDRESS_MISALIGNED elif s.drop_unit.output_data.stat == MemMsgStatus.ACCESS_FAULT: s.fetch_msg.write_data.exception_info_mcause.v = ExceptionCode.INSTRUCTION_ACCESS_FAULT # save the faulting PC as mtval s.fetch_msg.write_data.exception_info_mtval.v = s.pc.read_data else: s.fetch_msg.write_data.hdr_status.v = PipelineMsgStatus.PIPELINE_MSG_STATUS_VALID s.fetch_msg.write_data.inst.v = s.inst_from_mem s.fetch_msg.write_data.pc_succ.v = s.pc.write_data elif s.take_call: # someone is calling, but we are stalled, so give them output but # unset valid s.fetch_val.write_call.v = 1 s.fetch_val.write_data.v = 0 # handle_f0 s.connect(s.mem_send_msg.type_, int(MemMsgType.READ)) @s.combinational def write_addr(): s.mem_send_msg.addr.v = s.pc.write_data s.connect(s.mem_send_msg.len_, ilen_bytes) # can only send it if advancing s.connect(s.mem_send_call, s.advance_f0)
def __init__(s, cflow_interface, reset_vector): UseInterface(s, cflow_interface) xlen = s.interface.DataLen seqidx_nbits = s.interface.SeqIdxNbits specidx_nbits = s.interface.SpecIdxNbits specmask_nbits = s.interface.SpecMaskNbits store_id_nbits = s.interface.StoreIdNbits max_entries = 1 << seqidx_nbits s.require( MethodSpec( 'dflow_get_store_id', args=None, rets={ 'store_id': store_id_nbits, }, call=True, rdy=True, ), # Snapshot call on dataflow MethodSpec( 'dflow_snapshot', args=None, rets={ 'id_': specidx_nbits, }, call=True, rdy=True, ), MethodSpec( 'dflow_restore', args={ 'source_id': specidx_nbits, }, rets=None, call=True, rdy=False, ), MethodSpec( 'dflow_free_snapshot', args={ 'id_': specidx_nbits, }, rets=None, call=True, rdy=False, ), MethodSpec( 'dflow_rollback', args=None, rets=None, call=True, rdy=False, ), ) # The speculative predicted PC table s.pc_pred = AsynchronousRAM( AsynchronousRAMInterface(xlen, specmask_nbits, 1, 1, False)) s.seq = SequenceAllocator(SequenceAllocatorInterface(seqidx_nbits)) # The redirect registers (needed for sync reset) s.reset_redirect_valid_ = Wire(1) # The OR of all the redirect signals s.is_redirect_ = Wire(1) # Redirects caused by branches s.branch_redirect_ = Wire(1) s.redirect_target_ = Wire(xlen) # Redirects caused by exceptions, traps, etc... s.commit_redirect_ = Wire(1) s.commit_redirect_target_ = Wire(xlen) # Note that these signals are guaranteed to be zero if register_call = 0 s.register_success_ = Wire(1) s.spec_register_success_ = Wire(1) s.store_register_success_ = Wire(1) # Branch mask stuff: s.kill_mask_ = Wire(specmask_nbits) s.clear_mask_ = Wire(specmask_nbits) s.bmask_curr_ = Wire(specmask_nbits) s.bmask_next_ = Wire(specmask_nbits) # The kill and clear signals are registered s.update_kills_ = Wire(1) s.kill_pend = Register(RegisterInterface(Bits(1), enable=True), reset_value=0) s.reg_force = Register(RegisterInterface(Bits(1), enable=True), reset_value=0) s.reg_kill = Register(RegisterInterface(Bits(specmask_nbits), enable=True), reset_value=0) s.reg_clear = Register(RegisterInterface(Bits(specmask_nbits), enable=True), reset_value=0) s.connect(s.kill_pend.write_call, s.update_kills_) s.connect(s.reg_force.write_call, s.update_kills_) s.connect(s.reg_kill.write_call, s.update_kills_) s.connect(s.reg_clear.write_call, s.update_kills_) # Every instruction is registered under a branch mask s.bmask = Register(RegisterInterface(Bits(specmask_nbits), enable=True), reset_value=0) s.bmask_alloc = OneHotEncoder(specmask_nbits, enable=True) s.redirect_mask = OneHotEncoder(specmask_nbits) # Are we currently in a serialized instruction s.serial = Register(RegisterInterface(Bits(1), enable=True), reset_value=0) # connect bmask related signals s.connect(s.bmask.write_data, s.bmask_next_) # This will create the alloc mask s.connect(s.bmask_alloc.encode_number, s.dflow_snapshot_id_) s.connect(s.bmask_alloc.encode_call, s.spec_register_success_) # This will create the reidrection mask s.connect(s.redirect_mask.encode_number, s.redirect_spec_idx) # Things that need to be called on a successful speculative register s.connect(s.dflow_snapshot_call, s.spec_register_success_) # Connect up the dflow restore signals s.connect(s.dflow_restore_source_id, s.redirect_spec_idx) s.connect(s.dflow_restore_call, s.branch_redirect_) # Connect up free snapshot val s.connect(s.dflow_free_snapshot_id_, s.redirect_spec_idx) # Alloc the store ID if needed s.connect(s.dflow_get_store_id_call, s.store_register_success_) s.connect(s.register_store_id, s.dflow_get_store_id_store_id) # Save the speculative PC s.connect(s.pc_pred.write_call[0], s.spec_register_success_) s.connect(s.pc_pred.write_addr[0], s.dflow_snapshot_id_) s.connect(s.pc_pred.write_data[0], s.register_pc_succ) s.connect(s.pc_pred.read_addr[0], s.redirect_spec_idx) # Connect up check_kill method s.connect(s.check_kill_kill.force, s.reg_force.read_data) s.connect(s.check_kill_kill.kill_mask, s.reg_kill.read_data) s.connect(s.check_kill_kill.clear_mask, s.reg_clear.read_data) # Connect up register method rets s.connect(s.register_seq, s.seq.allocate_idx) s.connect(s.register_spec_idx, s.dflow_snapshot_id_) s.connect(s.register_branch_mask, s.bmask_curr_) s.connect(s.seq.allocate_call, s.register_success_) # Connect get head method s.connect(s.get_head_seq, s.seq.get_head_idx) s.connect(s.get_head_rdy, s.seq.get_head_rdy) # Connect commit s.connect(s.seq.free_call, s.commit_call) # All the backend kill signals are registered to avoid comb. loops @s.combinational def set_kill_pend(): # We need to update this if there is a redirect even if branch resolved correctly s.update_kills_.v = s.kill_pend.read_data or s.is_redirect_ or s.redirect_call s.kill_pend.write_data.v = s.is_redirect_ or s.redirect_call s.reg_force.write_data.v = (s.branch_redirect_ and s.redirect_force ) or (s.commit_redirect_) s.reg_kill.write_data.v = s.kill_mask_ s.reg_clear.write_data.v = s.clear_mask_ # This prioritizes reset redirection, then exceptions, then a branch reidrect call @s.combinational def handle_check_redirect(): s.is_redirect_.v = s.reset_redirect_valid_ or s.commit_redirect_ or s.branch_redirect_ s.check_redirect_redirect.v = s.is_redirect_ s.check_redirect_target.v = 0 if s.reset_redirect_valid_: s.check_redirect_target.v = reset_vector elif s.commit_redirect_: s.check_redirect_target.v = s.commit_redirect_target_ else: # s.branch_redirect_ s.check_redirect_target.v = s.redirect_target_ @s.combinational def set_serial(): s.serial.write_call.v = ((s.register_success_ and s.register_serialize) or (s.serial.read_data and s.commit_call)) s.serial.write_data.v = not s.serial.read_data # we are always inverting it # This is only for a redirect call @s.combinational def handle_branch_redirection(): # These are set after a redirect call s.kill_mask_.v = 0 s.clear_mask_.v = 0 s.redirect_target_.v = s.redirect_target # Free the snapshot s.dflow_free_snapshot_call.v = s.redirect_call # Look up if the predicted PC saved during register is correct s.branch_redirect_.v = s.redirect_call and ( s.redirect_target != s.pc_pred.read_data[0] or s.redirect_force) if s.branch_redirect_: # Kill everything except preceeding branches s.kill_mask_.v = s.redirect_mask.encode_onehot | ~s.redirect_branch_mask elif s.redirect_call: s.clear_mask_.v = s.redirect_mask.encode_onehot @s.combinational def handle_bmask(): s.bmask.write_call.v = s.spec_register_success_ or s.redirect_call or s.commit_redirect_ # Update the current branch mask s.bmask_curr_.v = s.bmask.read_data.v & ( ~(s.kill_mask_ | s.clear_mask_)) s.bmask_next_.v = s.bmask_curr_ if s.commit_redirect_: s.bmask_next_.v = 0 elif s.register_speculative: s.bmask_next_.v = s.bmask_curr_ | s.bmask_alloc.encode_onehot @s.combinational def handle_register(): s.register_success.v = ( s.seq.allocate_rdy and # ROB slot availible (not s.register_speculative or s.dflow_snapshot_rdy) and (not s.register_store or s.dflow_get_store_id_rdy) and # RT snapshot (not s.register_serialize or not s.seq.free_rdy) and # Serialized inst not s.serial.read_data) s.register_success_.v = s.register_call and s.register_success s.spec_register_success_.v = s.register_success_.v and s.register_speculative s.store_register_success_.v = s.register_success_.v and s.register_store @s.combinational def handle_commit(): s.commit_redirect_.v = 0 s.commit_redirect_target_.v = s.commit_redirect_target # If we are committing there are a couple cases s.commit_redirect_.v = s.commit_call and s.commit_redirect s.dflow_rollback_call.v = s.commit_call and s.commit_redirect @s.combinational def update_seq(): s.seq.rollback_call.v = s.commit_redirect_ or s.branch_redirect_ s.seq.rollback_idx.v = s.redirect_seq if s.commit_redirect_: # On an exception, the tail = head + 1, since head will be incremented s.seq.rollback_idx.v = s.seq.get_head_idx @s.tick_rtl def handle_reset(): s.reset_redirect_valid_.n = s.reset