Skip to content

Commit

Permalink
Multi-block eval_proc_main support
Browse files Browse the repository at this point in the history
PiperOrigin-RevId: 682496973
  • Loading branch information
allight authored and copybara-github committed Oct 4, 2024
1 parent 90d31cd commit d8a8ca0
Show file tree
Hide file tree
Showing 7 changed files with 291 additions and 10 deletions.
42 changes: 42 additions & 0 deletions xls/examples/dslx_module/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ load(
"xls_dslx_library",
"xls_dslx_opt_ir",
"xls_dslx_test",
"xls_ir_verilog",
)

package(
Expand Down Expand Up @@ -63,6 +64,12 @@ xls_dslx_library(
xls_dslx_ir(
name = "some_caps_streaming_configured_ir",
dslx_top = "some_caps_specialized",
ir_conv_args = {
# Set fifo config for multi-proc codegen.
# It needs to know what configuration to use for the fifo.
"default_fifo_config": "depth: 4, bypass: true, " +
"register_push_outputs: false, register_pop_outputs: false",
},
ir_file = "some_caps_streaming_configured.ir",
library = ":some_caps_streaming_configured",
)
Expand All @@ -83,11 +90,46 @@ xls_dslx_opt_ir(
# Note: The optimized ir has different top since the channels are used to determine proc
# liveness instead of spawn tree. This is done to avoid having to deal with mangled names.
dslx_top = "manual_chan_caps_specialized",
ir_conv_args = {
# Set fifo config for multi-proc codegen.
# It needs to know what configuration to use for the fifo.
"default_fifo_config": "depth: 4, bypass: true, " +
"register_push_outputs: false, register_pop_outputs: false",
},
ir_file = "manual_chan_caps_streaming_configured.ir",
library = ":some_caps_streaming_configured",
visibility = ["//xls:xls_internal"],
)

xls_ir_verilog(
name = "manual_chan_caps_streaming_configured_multiproc_verilog",
src = ":manaul_chan_caps_streaming_configured_opt_ir",
block_ir_file = "manual_chan_caps_streaming_configured_multiproc.block.ir",
codegen_args = {
"module_name": "manual_chan_caps_streaming",
"generator": "pipeline",
"pipeline_stages": "4",
"delay_model": "unit",
"reset": "rst",
"reset_data_path": "false",
"reset_active_low": "false",
"reset_asynchronous": "false",
"flop_inputs": "false",
"flop_single_value_channels": "false",
"flop_outputs": "false",
"add_idle_output": "false",
"streaming_channel_data_suffix": "_data",
"streaming_channel_ready_suffix": "_ready",
"streaming_channel_valid_suffix": "_valid",
"use_system_verilog": "true",
"assert_format": "\\;",
"multi_proc": "true",
},
module_sig_file = "manual_chan_caps_streaming_configured_multiproc.sig.textproto",
verilog_file = "manual_chan_caps_streaming_configured_multiproc.sv",
visibility = ["//xls:xls_internal"],
)

cc_xls_ir_jit_wrapper(
name = "some_caps_opt_jit_wrapper",
src = ":manaul_chan_caps_streaming_configured_opt_ir",
Expand Down
9 changes: 9 additions & 0 deletions xls/ir/block.cc
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,15 @@ absl::Status Block::SetPortNameExactly(std::string_view name, Node* node) {
return absl::OkStatus();
}

bool Block::HasInputPort(std::string_view name) const {
return ports_by_name_.contains(name) &&
std::holds_alternative<InputPort*>(ports_by_name_.at(name));
}
bool Block::HasOutputPort(std::string_view name) const {
return ports_by_name_.contains(name) &&
std::holds_alternative<OutputPort*>(ports_by_name_.at(name));
}

absl::StatusOr<InputPort*> Block::GetInputPort(std::string_view name) const {
auto port_iter = ports_by_name_.find(name);
if (port_iter == ports_by_name_.end()) {
Expand Down
2 changes: 2 additions & 0 deletions xls/ir/block.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,9 @@ class Block : public FunctionBase {

// Returns a given input/output port by name.
absl::StatusOr<InputPort*> GetInputPort(std::string_view name) const;
bool HasInputPort(std::string_view name) const;
absl::StatusOr<OutputPort*> GetOutputPort(std::string_view name) const;
bool HasOutputPort(std::string_view name) const;

// Adds an input/output port to the block. These methods should be used to add
// ports rather than FunctionBase::AddNode and FunctionBase::MakeNode (checked
Expand Down
11 changes: 11 additions & 0 deletions xls/ir/function_builder_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1109,6 +1109,17 @@ TEST(FunctionBuilderTest, NaryBitwiseAnd) {
m::And(m::Param("a"), m::Param("b"), m::Param("c")));
}

TEST(FunctionBuilderTest, Ports) {
Package p("p");
BlockBuilder b("b", &p);
b.OutputPort("bar", b.InputPort("foo", p.GetBitsType(32)));
XLS_ASSERT_OK_AND_ASSIGN(Block * blk, b.Build());
EXPECT_TRUE(blk->HasInputPort("foo"));
EXPECT_FALSE(blk->HasOutputPort("foo"));
EXPECT_TRUE(blk->HasOutputPort("bar"));
EXPECT_FALSE(blk->HasInputPort("bar"));
}

TEST(FunctionBuilderTest, Registers) {
Package p("p");
BlockBuilder b("b", &p);
Expand Down
6 changes: 6 additions & 0 deletions xls/tools/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,7 @@ cc_binary(
"//xls/interpreter:serial_proc_runtime",
"//xls/ir",
"//xls/ir:bits",
"//xls/ir:block_elaboration",
"//xls/ir:channel",
"//xls/ir:channel_cc_proto",
"//xls/ir:events",
Expand Down Expand Up @@ -792,11 +793,16 @@ py_test(
"//xls/tools/testdata:eval_proc_main_zero_size_test.block.ir",
"//xls/tools/testdata:eval_proc_main_zero_size_test.sig.textproto",
":eval_proc_main",
"//xls/examples:delay.block.ir",
"//xls/examples:delay.sig.textproto",
"//xls/examples/dslx_module:manual_chan_caps_streaming_configured_multiproc.block.ir",
"//xls/examples/dslx_module:manual_chan_caps_streaming_configured_multiproc.sig.textproto",
],
python_version = "PY3",
srcs_version = "PY3",
deps = [
":node_coverage_stats_py_pb2",
":proc_channel_values_py_pb2",
"//xls/common:runfiles",
"//xls/ir:xls_value_py_pb2",
"@com_google_absl_py//absl/logging",
Expand Down
67 changes: 57 additions & 10 deletions xls/tools/eval_proc_main.cc
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
#include "absl/status/statusor.h"
#include "absl/strings/escaping.h"
#include "absl/strings/numbers.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_format.h"
#include "absl/strings/str_join.h"
#include "absl/strings/str_split.h"
Expand All @@ -62,13 +63,16 @@
#include "xls/interpreter/interpreter_proc_runtime.h"
#include "xls/interpreter/serial_proc_runtime.h"
#include "xls/ir/bits.h"
#include "xls/ir/block.h"
#include "xls/ir/block_elaboration.h"
#include "xls/ir/channel.h"
#include "xls/ir/channel.pb.h"
#include "xls/ir/events.h"
#include "xls/ir/function_builder.h"
#include "xls/ir/ir_parser.h"
#include "xls/ir/nodes.h"
#include "xls/ir/package.h"
#include "xls/ir/proc.h"
#include "xls/ir/register.h"
#include "xls/ir/value.h"
#include "xls/ir/value_utils.h"
Expand All @@ -87,6 +91,12 @@ value of each proc will be printed to the terminal upon completion.
Initial states are set according to their declarations inside the IR itself.
)";

ABSL_FLAG(
std::optional<std::string>, top, std::nullopt,
"If present the top construct to simulate. Must be an exact match to "
"the name of an appropriate proc/block. Until new-style-procs are "
"available this is mostly just to support module-name for block "
"simulations as the specified top must be the actual top of the design.");
ABSL_FLAG(std::vector<std::string>, ticks, {},
"Can be a comma-separated list of runs. "
"Number of clock ticks to execute for each, with proc state "
Expand Down Expand Up @@ -207,6 +217,7 @@ struct EvaluateProcsOptions {
bool use_jit = false;
bool fail_on_assert = false;
std::vector<int64_t> ticks = {-1};
std::optional<std::string> top = std::nullopt;
};

static absl::Status EvaluateProcs(
Expand All @@ -222,6 +233,13 @@ static absl::Status EvaluateProcs(
bool uses_observers =
absl::GetFlag(FLAGS_output_node_coverage_stats_proto).has_value() ||
absl::GetFlag(FLAGS_output_node_coverage_stats_textproto).has_value();
if (options.top) {
XLS_ASSIGN_OR_RETURN(Proc * proc, package->GetProc(*options.top));
if (proc != package->GetTop()) {
return absl::UnimplementedError(
"Simulating subsets of the proc network is not implemented yet.");
}
}
evaluator_options.set_support_observers(uses_observers);
if (options.use_jit) {
XLS_ASSIGN_OR_RETURN(
Expand Down Expand Up @@ -674,6 +692,7 @@ struct RunBlockOptions {
bool use_jit = false;
std::vector<int64_t> ticks = {-1};
int64_t max_cycles_no_output = 100;
std::optional<std::string> top;
int random_seed;
double prob_input_valid_assert;
bool show_trace;
Expand Down Expand Up @@ -734,15 +753,31 @@ static absl::Status RunBlock(
const absl::flat_hash_map<std::string, std::pair<int64_t, Value>>&
model_memories_param,
std::string_view output_stats_path, const RunBlockOptions& options = {}) {
if (package->blocks().size() != 1) {
Block* block;
if (options.top) {
XLS_ASSIGN_OR_RETURN(block, package->GetBlock(*options.top));
} else if (package->HasTop()) {
if (package->GetTop().value()->IsBlock()) {
XLS_ASSIGN_OR_RETURN(block, package->GetTopAsBlock());
} else if (package->blocks().size() == 1) {
block = package->blocks().front().get();
} else {
// This is result of codegen-ing a proc so use the block for the top proc
// as top.
XLS_ASSIGN_OR_RETURN(Proc * top_proc, package->GetTopAsProc());
XLS_ASSIGN_OR_RETURN(
block, package->GetBlock(top_proc->name()),
_ << "Unable to determine top. Pass --top to select one manually.");
}
} else if (package->blocks().size() == 1) {
block = package->blocks().front().get();
} else {
return absl::InvalidArgumentError(
"Input IR should contain exactly one block");
"Input IR should contain exactly one block or a top");
}

std::mt19937_64 bit_gen(options.random_seed);

Block* block = package->blocks()[0].get();

// TODO: Support multiple resets
CHECK_EQ(options.ticks.size(), 1);

Expand Down Expand Up @@ -777,13 +812,23 @@ static absl::Status RunBlock(
/*read_disabled_value=*/XsOfType(port->GetType()), options.show_trace);
}

// Initial register state is one for all registers.
// Ideally this would be randomized, but at least 1s are more likely to
// expose bad behavior than 0s.
absl::flat_hash_map<std::string, Value> reg_state;
for (Register* reg : block->GetRegisters()) {
Value def = ZeroOfType(reg->type());
reg_state[reg->name()] = XsOfType(reg->type());
{
XLS_ASSIGN_OR_RETURN(BlockElaboration elab,
BlockElaboration::Elaborate(block));
for (BlockInstance* inst : elab.instances()) {
if (!inst->block()) {
// Actually a fifo or something without real registers.
continue;
}
for (Register* reg : (*inst->block())->GetRegisters()) {
// Initial register state is one for all registers.
// Ideally this would be randomized, but at least 1s are more likely to
// expose bad behavior than 0s.
reg_state[absl::StrCat(inst->RegisterPrefix(), reg->name())] =
XsOfType(reg->type());
}
}
}

bool needs_observer =
Expand Down Expand Up @@ -1187,6 +1232,7 @@ static absl::Status RealMain(
RunBlockOptions block_options = {
.ticks = ticks,
.max_cycles_no_output = max_cycles_no_output,
.top = absl::GetFlag(FLAGS_top),
.random_seed = random_seed,
.prob_input_valid_assert = prob_input_valid_assert,
.show_trace = show_trace,
Expand All @@ -1209,6 +1255,7 @@ static absl::Status RealMain(
EvaluateProcsOptions evaluate_procs_options = {
.fail_on_assert = fail_on_assert,
.ticks = ticks,
.top = absl::GetFlag(FLAGS_top),
};

if (backend == "serial_jit") {
Expand Down
Loading

0 comments on commit d8a8ca0

Please sign in to comment.