You are using different message tags for all the sends, however in your receive you use just j, which is never altered on the root process. Also note that your implementation looks like a MPI_Gather, which I'd recommend you to use instead of implementing this yourself.
EDIT: Sorry, after your update I now, realize, that you are in fact sending multiple messages from each rank>0 (start_index+1 up to end_index), if you need that, you do need to have tags differentiating the individual messages. However, you then also need to have multiple receives on your master. Maybe it would be better to state, what you actually want to achieve.
Do you want something like this:
integer :: tag
tag = 123
if(pid.ne.0) then
print *,'pid: ',pid,'sending'
CALL MPI_SEND(datapacket(start_index+1:end_index),end_index-start_index, MPI_REAL,0, tag, MPI_COMM_WORLD)
!print *,'sending'
print *,'send complete'
else
DO slave_id = 1, npe-1
rec_start_index = slave_id*population_size+1
rec_end_index = (slave_id + 1) * population_size;
IF (slave_id == npe-1) THEN
rec_end_index = total-1;
ENDIF
print *,'received 1',rec_start_index,rec_end_index
CALL MPI_RECV(datapacket(rec_start_index:rec_end_index),rec_end_index-rec_start_index+1,MPI_REAL,slave_id,tag,MPI_COMM_WORLD, &
& status)
!print *,'received 2',rec_start_index,rec_end_index
END DO
end if