The previous method for task assignment (assigning task
to different PEs) is not effective. I revised the heuristics
to the following:

First, the reason that we can simply use V_opt of each task
for our Vdd design is that those V_opt is the optimal Vdd when
only that task is running. With other tasks' interfering and
scheduling involved, the V_opt of such task will increase, say
to V_opt', and such optimal Vdd depends on the task assignment 
and scheduling. Second, for those tasks with V_opt < V_critical, 
there is no need to worry the incresae of V_opt because we'd 
like to choose V_critical for them. Therefore, we only focus 
on those tasks with V_opt > V_critical. For those tasks, the 
following algorithm can be applied:
1. grouping of tasks: put task with similar V_opt into one
group and generate G groups in total, where G is a input
parameter to this algorithm. G may depends on the number of
Vdd levels to design.
2. for each group, evenly distributed tasks to all PEs.
3. Check the schedulability of each PE. If on any PE, the
taskset is not schedulable, select one group of task and
increase their V_opt to V_opt' = (1+step%)*V_opt, where
step is another parameter.

Finally, we put in those ignored tasks with V_opt < V_critical
ad schedule them with least priority, i.e., their Vdd will
very likely be larger than their V_opt but their do not
have impact on existing schedule.

I am also working on implementation of low power EDF method 
and gradient method to get discrete vdd from a set of continuely
vdd solution.