Skip to content

Commit 2a8f3b5

Browse files
ericastorcopybara-github
authored andcommitted
[codegen 1.5] Add reset requirement for valid-output control
If lowering a function with a valid-output control signal, we should always have a reset signal; otherwise we can end up with (pipeline_depth - 1) cycles of garbage at the valid-output signal, potentially signaling "valid" when the output is very much not valid. This required adding reset signal support to our unit tests' RunFunctionalTest logic for this pass. While we're at it, we also ensure that the source return value is always available in the last pipeline stage by inserting an identity node if necessary - which can be necessary when lowering a no-op function that directly returns one of its inputs. (This mostly only comes up in testing.) PiperOrigin-RevId: 853045608
1 parent 7cf36bb commit 2a8f3b5

File tree

4 files changed

+87
-42
lines changed

4 files changed

+87
-42
lines changed

xls/codegen_v_1_5/BUILD

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,12 +324,14 @@ cc_library(
324324
"//xls/ir:node_util",
325325
"//xls/ir:op",
326326
"//xls/ir:register",
327+
"//xls/ir:source_location",
327328
"//xls/ir:value_utils",
328329
"//xls/passes:pass_base",
329330
"@com_google_absl//absl/algorithm:container",
330331
"@com_google_absl//absl/status",
331332
"@com_google_absl//absl/status:statusor",
332333
"@com_google_absl//absl/strings",
334+
"@com_google_absl//absl/strings:str_format",
333335
"@com_google_absl//absl/types:span",
334336
"@cppitertools",
335337
],

xls/codegen_v_1_5/block_conversion_pass_pipeline_test.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6679,7 +6679,7 @@ TEST_F(BlockConversionTest, ReturnArrayLiteral) {
66796679
{Value(UBits(0, 1)), Value(UBits(1, 1))}))));
66806680
}
66816681

6682-
TEST_F(BlockConversionTest, DISABLED_ValidSignalWithoutReset) {
6682+
TEST_F(BlockConversionTest, ValidSignalWithoutReset) {
66836683
Package package(TestName());
66846684
FunctionBuilder fb(TestName(), &package);
66856685
fb.Param("x", package.GetBitsType(8));

xls/codegen_v_1_5/function_io_lowering_pass.cc

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include "absl/status/status.h"
2626
#include "absl/status/statusor.h"
2727
#include "absl/strings/str_cat.h"
28+
#include "absl/strings/str_format.h"
2829
#include "absl/types/span.h"
2930
#include "cppitertools/reversed.hpp"
3031
#include "xls/codegen/conversion_utils.h"
@@ -40,6 +41,7 @@
4041
#include "xls/ir/op.h"
4142
#include "xls/ir/package.h"
4243
#include "xls/ir/register.h"
44+
#include "xls/ir/source_location.h"
4345
#include "xls/ir/value_utils.h"
4446
#include "xls/passes/pass_base.h"
4547

@@ -168,16 +170,37 @@ absl::StatusOr<bool> FunctionIOLoweringPass::LowerReturnValue(
168170
}
169171

170172
// The return value is always assumed to be in the last stage - or possibly
171-
// unscheduled, if (e.g.) returning a literal.
172-
if (block->IsStaged(block->source_return_value())) {
173+
// unscheduled, if (e.g.) returning a literal. If it's not in the last stage,
174+
// it must be a Param - so we need to introduce an identity node to ensure
175+
// that the value gets piped through the pipeline registers.
176+
Node* return_value = block->source_return_value();
177+
if (block->IsStaged(return_value)) {
173178
XLS_ASSIGN_OR_RETURN(int64_t stage_index,
174-
block->GetStageIndex(block->source_return_value()));
175-
XLS_RET_CHECK_EQ(stage_index, block->stages().size() - 1);
179+
block->GetStageIndex(return_value));
180+
const int64_t last_stage = block->stages().size() - 1;
181+
if (stage_index != last_stage) {
182+
XLS_ASSIGN_OR_RETURN(
183+
return_value,
184+
block->MakeNodeInStage<UnOp>(block->stages().size() - 1, SourceInfo(),
185+
return_value, Op::kIdentity));
186+
}
176187
}
177188

178189
std::optional<Node*> output_valid = std::nullopt;
179190
if (options.codegen_options.valid_control().has_value() &&
180191
!options.codegen_options.valid_control()->output_name().empty()) {
192+
// If the valid signal is passed all the way through to an output port, then
193+
// the block must have a reset port. Otherwise, garbage will be passed out
194+
// of the valid out port until the pipeline flushes. If there is not a valid
195+
// output port, it's ok for the flopped valid to have garbage values because
196+
// it is only used as a term in load enables for power savings.
197+
if (!block->GetResetBehavior().has_value()) {
198+
return absl::InvalidArgumentError(absl::StrFormat(
199+
"Block `%s` has valid signal output but no reset; this would produce "
200+
"an incorrect valid-output signal.",
201+
block->name()));
202+
}
203+
181204
output_valid = block->stages().back().outputs_valid();
182205
if (options.codegen_options.flop_outputs()) {
183206
XLS_ASSIGN_OR_RETURN(

xls/codegen_v_1_5/function_io_lowering_pass_test.cc

Lines changed: 57 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -130,22 +130,34 @@ class FunctionIOLoweringPassTest : public IrTestBase {
130130
const int64_t output_cycle =
131131
options.input_valid_delay + expected_latency - 1;
132132
in_values.resize(num_cycles);
133+
const bool has_reset = pass_options.codegen_options.reset().has_value();
134+
const bool has_input_valid = options.input_valid.has_value();
135+
const int64_t value_offset =
136+
(has_reset ? 1 : 0) + (has_input_valid ? 1 : 0);
133137
XLS_RET_CHECK_EQ(sim_block->GetInputPorts().size(),
134-
(options.input_valid.has_value() ? 1 : 0) + inputs.size());
138+
value_offset + inputs.size());
135139
for (int64_t i = 0; i < sim_block->GetInputPorts().size(); ++i) {
136140
std::string_view input_name = sim_block->GetInputPorts()[i]->name();
137-
Value input_value;
138-
if (options.input_valid.has_value()) {
139-
if (i == 0) {
140-
input_value = Value(UBits(1, 1));
141-
} else {
142-
input_value = inputs[i - 1];
141+
if (has_reset && i == 0) {
142+
Value reset_disabled = Value(UBits(
143+
pass_options.codegen_options.reset()->active_low() ? 1 : 0, 1));
144+
for (int64_t j = 0; j < num_cycles; ++j) {
145+
in_values[j].emplace(input_name, reset_disabled);
143146
}
147+
continue;
148+
}
149+
150+
Value input_value;
151+
if (i < value_offset) {
152+
XLS_RET_CHECK(has_input_valid);
153+
XLS_RET_CHECK_EQ(i, value_offset - 1);
154+
// input_valid should be 1 whenever we have a valid input.
155+
input_value = Value(UBits(1, 1));
144156
} else {
145-
input_value = inputs[i];
157+
input_value = inputs[i - value_offset];
146158
}
147159
for (int64_t j = 0; j <= options.input_valid_delay; ++j) {
148-
if (i == 0 && j < options.input_valid_delay) {
160+
if (i == value_offset - 1 && j < options.input_valid_delay) {
149161
in_values[j].emplace(input_name, Value(UBits(0, 1)));
150162
} else {
151163
in_values[j].emplace(input_name, input_value);
@@ -285,12 +297,13 @@ TEST_F(FunctionIOLoweringPassTest, SingleStageWithIOValid) {
285297
InterpreterResultToStatusOrValue(result));
286298
ASSERT_EQ(expected_output, Value(UBits(42, 32)));
287299

288-
XLS_ASSERT_OK_AND_ASSIGN(
289-
BlockConversionPassOptions options,
290-
CreateBlockConversionPassOptions(
291-
p.get(), /*pipeline_stages=*/1,
292-
::xls::verilog::CodegenOptions().clock_name("clk").valid_control(
293-
"x_valid", "out_valid")));
300+
XLS_ASSERT_OK_AND_ASSIGN(BlockConversionPassOptions options,
301+
CreateBlockConversionPassOptions(
302+
p.get(), /*pipeline_stages=*/1,
303+
::xls::verilog::CodegenOptions()
304+
.clock_name("clk")
305+
.reset("rst", false, false, false)
306+
.valid_control("x_valid", "out_valid")));
294307
XLS_ASSERT_OK_AND_ASSIGN(ScheduledBlock * sb,
295308
CreateScheduledBlock(p.get(), "id_func", options));
296309

@@ -299,7 +312,8 @@ TEST_F(FunctionIOLoweringPassTest, SingleStageWithIOValid) {
299312
IsOkAndHolds(true));
300313

301314
EXPECT_THAT(sb->GetInputPorts(),
302-
ElementsAre(Property(&PortNode::GetName, "x_valid"),
315+
ElementsAre(Property(&PortNode::GetName, "rst"),
316+
Property(&PortNode::GetName, "x_valid"),
303317
Property(&PortNode::GetName, "x")));
304318
EXPECT_THAT(sb->GetOutputPorts(),
305319
ElementsAre(Property(&PortNode::GetName, "out_valid"),
@@ -420,12 +434,13 @@ TEST_F(FunctionIOLoweringPassTest, MultiStageWithIOValidDelayed) {
420434
InterpreterResultToStatusOrValue(result));
421435
ASSERT_EQ(expected_output, Value(UBits(42, 32)));
422436

423-
XLS_ASSERT_OK_AND_ASSIGN(
424-
BlockConversionPassOptions options,
425-
CreateBlockConversionPassOptions(
426-
p.get(), /*pipeline_stages=*/3,
427-
::xls::verilog::CodegenOptions().clock_name("clk").valid_control(
428-
"x_valid", "out_valid")));
437+
XLS_ASSERT_OK_AND_ASSIGN(BlockConversionPassOptions options,
438+
CreateBlockConversionPassOptions(
439+
p.get(), /*pipeline_stages=*/3,
440+
::xls::verilog::CodegenOptions()
441+
.clock_name("clk")
442+
.reset("rst", false, false, false)
443+
.valid_control("x_valid", "out_valid")));
429444
XLS_ASSERT_OK_AND_ASSIGN(ScheduledBlock * sb,
430445
CreateScheduledBlock(p.get(), "id_func", options));
431446

@@ -434,7 +449,8 @@ TEST_F(FunctionIOLoweringPassTest, MultiStageWithIOValidDelayed) {
434449
IsOkAndHolds(true));
435450

436451
EXPECT_THAT(sb->GetInputPorts(),
437-
ElementsAre(Property(&PortNode::GetName, "x_valid"),
452+
ElementsAre(Property(&PortNode::GetName, "rst"),
453+
Property(&PortNode::GetName, "x_valid"),
438454
Property(&PortNode::GetName, "x")));
439455
EXPECT_THAT(sb->GetOutputPorts(),
440456
ElementsAre(Property(&PortNode::GetName, "out_valid"),
@@ -469,12 +485,13 @@ TEST_F(FunctionIOLoweringPassTest, MultiStageWithIOValid) {
469485
InterpreterResultToStatusOrValue(result));
470486
ASSERT_EQ(expected_output, Value(UBits(42, 32)));
471487

472-
XLS_ASSERT_OK_AND_ASSIGN(
473-
BlockConversionPassOptions options,
474-
CreateBlockConversionPassOptions(
475-
p.get(), /*pipeline_stages=*/3,
476-
::xls::verilog::CodegenOptions().clock_name("clk").valid_control(
477-
"x_valid", "out_valid")));
488+
XLS_ASSERT_OK_AND_ASSIGN(BlockConversionPassOptions options,
489+
CreateBlockConversionPassOptions(
490+
p.get(), /*pipeline_stages=*/3,
491+
::xls::verilog::CodegenOptions()
492+
.clock_name("clk")
493+
.reset("rst", false, false, false)
494+
.valid_control("x_valid", "out_valid")));
478495
XLS_ASSERT_OK_AND_ASSIGN(ScheduledBlock * sb,
479496
CreateScheduledBlock(p.get(), "id_func", options));
480497

@@ -483,7 +500,8 @@ TEST_F(FunctionIOLoweringPassTest, MultiStageWithIOValid) {
483500
IsOkAndHolds(true));
484501

485502
EXPECT_THAT(sb->GetInputPorts(),
486-
ElementsAre(Property(&PortNode::GetName, "x_valid"),
503+
ElementsAre(Property(&PortNode::GetName, "rst"),
504+
Property(&PortNode::GetName, "x_valid"),
487505
Property(&PortNode::GetName, "x")));
488506
EXPECT_THAT(sb->GetOutputPorts(),
489507
ElementsAre(Property(&PortNode::GetName, "out_valid"),
@@ -517,12 +535,13 @@ TEST_F(FunctionIOLoweringPassTest, MultiStageMultiInputWithIOValid) {
517535
InterpreterResultToStatusOrValue(result));
518536
ASSERT_EQ(expected_output, Value(UBits(63, 32)));
519537

520-
XLS_ASSERT_OK_AND_ASSIGN(
521-
BlockConversionPassOptions options,
522-
CreateBlockConversionPassOptions(
523-
p.get(), /*pipeline_stages=*/3,
524-
::xls::verilog::CodegenOptions().clock_name("clk").valid_control(
525-
"in_valid", "out_valid")));
538+
XLS_ASSERT_OK_AND_ASSIGN(BlockConversionPassOptions options,
539+
CreateBlockConversionPassOptions(
540+
p.get(), /*pipeline_stages=*/3,
541+
::xls::verilog::CodegenOptions()
542+
.clock_name("clk")
543+
.reset("rst", false, false, false)
544+
.valid_control("in_valid", "out_valid")));
526545
XLS_ASSERT_OK_AND_ASSIGN(ScheduledBlock * sb,
527546
CreateScheduledBlock(p.get(), "id_func", options));
528547

@@ -531,7 +550,8 @@ TEST_F(FunctionIOLoweringPassTest, MultiStageMultiInputWithIOValid) {
531550
IsOkAndHolds(true));
532551

533552
EXPECT_THAT(sb->GetInputPorts(),
534-
ElementsAre(Property(&PortNode::GetName, "in_valid"),
553+
ElementsAre(Property(&PortNode::GetName, "rst"),
554+
Property(&PortNode::GetName, "in_valid"),
535555
Property(&PortNode::GetName, "x"),
536556
Property(&PortNode::GetName, "y")));
537557
EXPECT_THAT(sb->GetOutputPorts(),

0 commit comments

Comments
 (0)