Skip to content

Commit aaf5383

Browse files
angelomatni1copybara-github
authored andcommitted
Reset the scheduler so that slack variables used to explain infeasibility do not corrupt future attempts to schedule. Also, always check if the user wants to try finding the minimum feasible clock period that would satisfy the scheduler while still propagating the scheduler's error
New example, more helpful error message: ``` Error: INVALID_ARGUMENT: cannot achieve the specified clock period. Try `--clock_period_ps=721`. The reason the scheduler gave is: INVALID_ARGUMENT: __alu__AluProc_0_next: cannot satisfy the given I/O constraints. Would succeed with: {alu__p3_data_ins→alu__p5_outs with maximum latency ≥ 3}, {alu__p3_ins→alu__p5_outs with maximum latency ≥ 3} ``` PiperOrigin-RevId: 847823548
1 parent f5beb2e commit aaf5383

File tree

3 files changed

+143
-18
lines changed

3 files changed

+143
-18
lines changed

xls/scheduling/BUILD

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -685,6 +685,7 @@ cc_test(
685685
"@org_theopenroadproject//:opensta",
686686
],
687687
deps = [
688+
":pipeline_schedule",
688689
":pipeline_scheduling_pass",
689690
":scheduling_options",
690691
":scheduling_pass",
@@ -706,6 +707,7 @@ cc_test(
706707
"//xls/passes:pass_base",
707708
"@com_google_absl//absl/status:status_matchers",
708709
"@com_google_absl//absl/status:statusor",
710+
"@com_google_absl//absl/strings:str_format",
709711
"@googletest//:gtest",
710712
],
711713
)

xls/scheduling/pipeline_scheduling_pass_test.cc

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "gtest/gtest.h"
2323
#include "absl/status/status_matchers.h"
2424
#include "absl/status/statusor.h"
25+
#include "absl/strings/str_format.h"
2526
#include "xls/common/file/get_runfile_path.h"
2627
#include "xls/common/status/matchers.h"
2728
#include "xls/common/status/status_macros.h"
@@ -37,6 +38,7 @@
3738
#include "xls/ir/source_location.h"
3839
#include "xls/ir/value.h"
3940
#include "xls/passes/pass_base.h"
41+
#include "xls/scheduling/pipeline_schedule.h"
4042
#include "xls/scheduling/scheduling_options.h"
4143
#include "xls/scheduling/scheduling_pass.h"
4244

@@ -431,5 +433,114 @@ TEST_F(PipelineSchedulingPassTest, MultiProcScopedChannels) {
431433
SchedulingOptions().pipeline_stages(2).schedule_all_procs(true)));
432434
}
433435

436+
TEST_F(PipelineSchedulingPassTest, MinimizeClockOnFailure) {
437+
auto p = CreatePackage();
438+
FunctionBuilder fb("main", p.get());
439+
BValue x = fb.Param("x", p->GetBitsType(32));
440+
BValue y = fb.Param("y", p->GetBitsType(32));
441+
BValue add = fb.Add(x, y);
442+
BValue div = fb.UDiv(x, y);
443+
BValue div_two = fb.UDiv(x, div);
444+
BValue ored = fb.Or({add, div_two});
445+
XLS_ASSERT_OK_AND_ASSIGN(Function * f, fb.BuildWithReturnValue(ored));
446+
447+
// Fail if trying to schedule with a clock period that is too small.
448+
auto failed_scheduled_result = RunPipelineSchedulingPass(
449+
f, SchedulingOptions().clock_period_ps(1).pipeline_stages(2));
450+
EXPECT_THAT(failed_scheduled_result.status().message(),
451+
AllOf(HasSubstr("cannot achieve the specified clock period"),
452+
HasSubstr("clock_period_ps=3")));
453+
454+
// Succeed if trying to schedule with a large enough clock period.
455+
XLS_ASSERT_OK_AND_ASSIGN(
456+
auto success_scheduled_result,
457+
RunPipelineSchedulingPass(
458+
f, SchedulingOptions().clock_period_ps(3).pipeline_stages(2)));
459+
EXPECT_THAT(success_scheduled_result,
460+
Pair(true, SchedulingContextWithElements(UnorderedElementsAre(
461+
Pair(f, VerifiedPipelineSchedule())))));
462+
PipelineSchedule f_schedule =
463+
success_scheduled_result.second.package_schedule().GetSchedule(f);
464+
EXPECT_THAT(
465+
f_schedule.GetCycleMap(),
466+
UnorderedElementsAre(Pair(x.node(), 0), Pair(y.node(), 0),
467+
Pair(add.node(), 0), Pair(div.node(), 0),
468+
Pair(div_two.node(), 1), Pair(ored.node(), 1)));
469+
}
470+
471+
TEST_F(PipelineSchedulingPassTest, MinimizeClockOnFailureWithIOConstraints) {
472+
auto p = CreatePackage();
473+
auto make_proc = [p = p.get()](
474+
std::string_view name, Channel* channel_in,
475+
Channel* channel_out) -> absl::StatusOr<Proc*> {
476+
ProcBuilder pb(name, p);
477+
BValue tok = pb.Literal(Value::Token());
478+
BValue st = pb.StateElement("st", Value(UBits(0, 32)));
479+
BValue recv = pb.Receive(channel_in, tok, SourceInfo(), "recv");
480+
BValue recv_tok = pb.TupleIndex(recv, 0);
481+
BValue recv_data = pb.TupleIndex(recv, 1);
482+
for (int i = 0; i < 3; ++i) {
483+
recv_data = pb.Add(recv_data, pb.Literal(UBits(1, 32)));
484+
recv_data = pb.Subtract(recv_data, pb.Literal(UBits(1, 32)));
485+
}
486+
BValue add_st = pb.Add(st, recv_data);
487+
pb.Send(channel_out, recv_tok, add_st, SourceInfo(), "send");
488+
return pb.Build({add_st});
489+
};
490+
491+
XLS_ASSERT_OK_AND_ASSIGN(
492+
Channel * ch0, p->CreateStreamingChannel("ch0", ChannelOps::kReceiveOnly,
493+
p->GetBitsType(32)));
494+
XLS_ASSERT_OK_AND_ASSIGN(
495+
Channel * ch1, p->CreateStreamingChannel("ch1", ChannelOps::kSendReceive,
496+
p->GetBitsType(32)));
497+
XLS_ASSERT_OK_AND_ASSIGN(
498+
Channel * ch2, p->CreateStreamingChannel("ch2", ChannelOps::kSendOnly,
499+
p->GetBitsType(32)));
500+
501+
XLS_ASSERT_OK(make_proc("proc0", ch0, ch1));
502+
XLS_ASSERT_OK(make_proc("proc1", ch1, ch2));
503+
504+
auto failed_result = RunPipelineSchedulingPass(
505+
p.get(), SchedulingOptions()
506+
.clock_period_ps(2)
507+
.pipeline_stages(4)
508+
.add_constraint(IOConstraint(
509+
/*source_channel=*/"ch0",
510+
/*source_direction=*/IODirection::kReceive,
511+
/*target_channel=*/"ch1",
512+
/*target_direction=*/IODirection::kSend,
513+
/*minimum_latency=*/1, /*maximum_latency=*/1))
514+
.add_constraint(IOConstraint(
515+
/*source_channel=*/"ch1",
516+
/*source_direction=*/IODirection::kReceive,
517+
/*target_channel=*/"ch2",
518+
/*target_direction=*/IODirection::kSend,
519+
/*minimum_latency=*/1, /*maximum_latency=*/1)));
520+
EXPECT_THAT(
521+
failed_result.status().message(),
522+
AllOf(HasSubstr("cannot achieve the specified clock period"),
523+
HasSubstr("clock_period_ps=4"),
524+
HasSubstr("cannot satisfy the given I/O constraints. Would succeed "
525+
"with: {ch0→ch1 with maximum latency ≥ 3}")));
526+
527+
XLS_ASSERT_OK(RunPipelineSchedulingPass(
528+
p.get(), SchedulingOptions()
529+
.clock_period_ps(4)
530+
.pipeline_stages(4)
531+
.add_constraint(IOConstraint(
532+
/*source_channel=*/"ch0",
533+
/*source_direction=*/IODirection::kReceive,
534+
/*target_channel=*/"ch1",
535+
/*target_direction=*/IODirection::kSend,
536+
/*minimum_latency=*/1, /*maximum_latency=*/1))
537+
.add_constraint(IOConstraint(
538+
/*source_channel=*/"ch1",
539+
/*source_direction=*/IODirection::kReceive,
540+
/*target_channel=*/"ch2",
541+
/*target_direction=*/IODirection::kSend,
542+
/*minimum_latency=*/1, /*maximum_latency=*/1))));
543+
}
544+
434545
} // namespace
435546
} // namespace xls

xls/scheduling/run_pipeline_schedule.cc

Lines changed: 30 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -468,12 +468,20 @@ absl::StatusOr<PipelineSchedule> RunPipelineScheduleInternal(
468468
}
469469

470470
std::unique_ptr<SDCScheduler> sdc_scheduler;
471+
// TODO: replace with handling reverting model constraint changes made while
472+
// trying to explain infeasibility, thereby never needing to invalidate the
473+
// SDCScheduler object regardless of settings.
474+
bool invalidated = false;
471475
auto initialize_sdc_scheduler = [&]() -> absl::Status {
472-
if (sdc_scheduler == nullptr) {
473-
XLS_ASSIGN_OR_RETURN(sdc_scheduler,
474-
SDCScheduler::Create(f, io_delay_added));
475-
XLS_RETURN_IF_ERROR(sdc_scheduler->AddConstraints(options.constraints()));
476+
// We need to recreate the SDC scheduler because slack variables may be
477+
// introduced if trying to explain infeasibility upon failing to schedule.
478+
if (sdc_scheduler != nullptr && invalidated) {
479+
sdc_scheduler.reset(nullptr);
480+
invalidated = false;
476481
}
482+
XLS_ASSIGN_OR_RETURN(sdc_scheduler,
483+
SDCScheduler::Create(f, io_delay_added));
484+
XLS_RETURN_IF_ERROR(sdc_scheduler->AddConstraints(options.constraints()));
477485
return absl::OkStatus();
478486
};
479487

@@ -620,12 +628,9 @@ absl::StatusOr<PipelineSchedule> RunPipelineScheduleInternal(
620628
/*check_feasibility=*/false,
621629
worst_case_throughput,
622630
options.dynamic_throughput_objective_weight());
631+
invalidated = !schedule_cycle_map.ok() &&
632+
options.failure_behavior().explain_infeasibility;
623633
if (!schedule_cycle_map.ok()) {
624-
if (absl::IsInvalidArgument(schedule_cycle_map.status())) {
625-
// The scheduler was able to explain the failure; report it up without
626-
// further analysis.
627-
return std::move(schedule_cycle_map).status();
628-
}
629634
if (options.clock_period_ps().has_value()) {
630635
// The user specified a specific clock period; see if we can confirm
631636
// that that's the issue.
@@ -649,8 +654,9 @@ absl::StatusOr<PipelineSchedule> RunPipelineScheduleInternal(
649654
// Just increasing the clock period suffices.
650655
return absl::InvalidArgumentError(absl::StrFormat(
651656
"cannot achieve the specified clock period. Try "
652-
"`--clock_period_ps=%d`.",
653-
*min_clock_period_ps));
657+
"`--clock_period_ps=%d`. The reason the scheduler gave is: "
658+
"%v",
659+
*min_clock_period_ps, schedule_cycle_map.status()));
654660
}
655661
if (absl::IsInvalidArgument(min_clock_period_ps.status())) {
656662
// We failed with an explained error at the longest possible clock
@@ -659,8 +665,10 @@ absl::StatusOr<PipelineSchedule> RunPipelineScheduleInternal(
659665
return xabsl::StatusBuilder(std::move(min_clock_period_ps).status())
660666
.SetPrepend()
661667
<< absl::StrFormat(
662-
"cannot achieve the specified clock period; try "
663-
"increasing `--clock_period_ps`. Also, ");
668+
"cannot achieve the specified clock period; the "
669+
"reason the scheduler gave is: %v. Try "
670+
"increasing `--clock_period_ps`. Also, ",
671+
schedule_cycle_map.status());
664672
}
665673
// We fail with an unexplained error even at the longest possible
666674
// clock period. Report the original error.
@@ -686,9 +694,11 @@ absl::StatusOr<PipelineSchedule> RunPipelineScheduleInternal(
686694
.status();
687695
if (pessimistic_status.ok()) {
688696
// Just increasing the clock period suffices.
689-
return absl::InvalidArgumentError(
690-
"cannot achieve the specified clock period. Try increasing "
691-
"`--clock_period_ps`.");
697+
return absl::InvalidArgumentError(absl::StrFormat(
698+
"cannot achieve the specified clock period. The reason the "
699+
"scheduler gave is: %v. Try increasing "
700+
"`--clock_period_ps`.",
701+
schedule_cycle_map.status()));
692702
}
693703
if (absl::IsInvalidArgument(pessimistic_status)) {
694704
// We failed with an explained error at the pessimistic clock period.
@@ -697,8 +707,10 @@ absl::StatusOr<PipelineSchedule> RunPipelineScheduleInternal(
697707
return xabsl::StatusBuilder(std::move(pessimistic_status))
698708
.SetPrepend()
699709
<< absl::StrFormat(
700-
"cannot achieve the specified clock period; try "
701-
"increasing `--clock_period_ps`. Also, ");
710+
"cannot achieve the specified clock period; the reason "
711+
"the scheduler gave is: %v. Try "
712+
"increasing `--clock_period_ps`. Also, ",
713+
schedule_cycle_map.status());
702714
}
703715
return pessimistic_status;
704716
}

0 commit comments

Comments
 (0)