Skip to content

Commit

Permalink
Merge pull request #3 from uwsampl/gussmith23/lakeroad-integration
Browse files Browse the repository at this point in the history
Improve Lakeroad Yosys integration
  • Loading branch information
gussmith23 committed Dec 12, 2023
2 parents 58d2e99 + e23c83a commit 2856a4d
Show file tree
Hide file tree
Showing 2 changed files with 137 additions and 66 deletions.
165 changes: 99 additions & 66 deletions passes/techmap/lakeroad.cc
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,98 @@

USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN

struct LakeroadPass : public Pass {
void
compileWithLakeroad(RTLIL::Module *module, RTLIL::Design *design)
{
log_debug("Compiling module %s with Lakeroad.\n", module->name.c_str());

auto f = [&](std::string key) {
assert(module->attributes.count(key) == 1);
return module->attributes[key];
};

auto template_ = f("\\template").decode_string();
auto architecture = f("\\architecture").decode_string();
auto initiation_interval = f("\\initiation_interval").as_int();

auto find_attr = [&](std::string attr) {
return [&](IdString port) {
auto w = module->wire(port);
assert(w);
return w->attributes.count(attr) > 0;
};
};

auto clk_port_id = std::find_if(module->ports.begin(), module->ports.end(), find_attr("\\clk"));
assert(clk_port_id != module->ports.end());

std::vector<IdString> data_ports;
std::copy_if(module->ports.begin(), module->ports.end(), std::back_inserter(data_ports), find_attr("\\data"));

auto out_port_id = std::find_if(module->ports.begin(), module->ports.end(), find_attr("\\out"));
assert(out_port_id != module->ports.end());

log_debug("Template: %s\n", template_.c_str());
log_debug("Architecture: %s\n", architecture.c_str());
log_debug("Initiation interval: %d\n", initiation_interval);
log_debug("Clock port: %s\n", clk_port_id->c_str());
for (auto port : data_ports)
log_debug("Data port: %s\n", port.c_str());
log_debug("Out port: %s\n", out_port_id->c_str());

auto top_module_name = module->name.substr(1);
// auto module_name = sprintf("%s_synthesized_by_lakeroad", top_module_name.c_str());

// Who knew getting a named temporary file was so hard in C++? This isn't a
// great solution.
auto verilog_filename = (boost::filesystem::temp_directory_path() / boost::filesystem::unique_path("%%%%-%%%%-%%%%-%%%%.v")).native();
auto out_verilog_filename = (boost::filesystem::temp_directory_path() / boost::filesystem::unique_path("%%%%-%%%%-%%%%-%%%%.v")).native();
std::vector<std::string> write_verilog_args;
write_verilog_args.push_back("write_verilog");
write_verilog_args.push_back(verilog_filename);
Pass::call(design, write_verilog_args);

auto temp_module_name = top_module_name + "_temp_output_from_lakeroad";

if (!getenv("LAKEROAD_DIR"))
log_error("LAKEROAD_DIR environment variable not set. Please set it to the location of the Lakeroad directory.\n");

std::stringstream ss;
// clang-format off
ss << getenv("LAKEROAD_DIR") << "/bin/main.rkt"
<< " --verilog-module-filepath " << verilog_filename
<< " --top-module-name " << top_module_name
<< " --out-filepath " << out_verilog_filename
<< " --out-format verilog"
<< " --verilog-module-out-signal " << out_port_id->substr(1) << ":" << module->wire(*out_port_id)->width
<< " --architecture " << architecture
<< " --template " << template_
<< " --module-name " << temp_module_name
<< " --clock-name " << clk_port_id->substr(1);
for (auto port : data_ports)
ss << " --input-signal " << port.substr(1) << ":" << module->wire(port)->width;
if (initiation_interval != 0)
ss << " --initiation-interval " << initiation_interval;
// clang-format on

log("Executing Lakeroad:\n%s\n", ss.str().c_str());
if (system(ss.str().c_str()) != 0)
log_error("Lakeroad execution failed.\n");

std::vector<std::string> read_verilog_args;
read_verilog_args.push_back("read_verilog");
read_verilog_args.push_back(out_verilog_filename);
Pass::call(design, read_verilog_args);

log("Replacing module %s with the output of Lakeroad\n", top_module_name.c_str());
design->remove(module);
auto new_module = design->module(RTLIL::escape_id(temp_module_name));
if (new_module == nullptr)
log_error("Lakeroad returned OK, but no module named %s found.\n", top_module_name.c_str());
design->rename(new_module, RTLIL::escape_id(top_module_name));
}

struct LakeroadPass : public Pass {
LakeroadPass() : Pass("lakeroad", "Invoke Lakeroad for technology mapping.") {}
void help() override
{
Expand All @@ -50,75 +141,17 @@ USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN
log("library to a target architecture.\n");
log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) override
void execute(std::vector<std::string>, RTLIL::Design *design) override
{
log_header(design, "Executing Lakeroad pass (technology mapping using Lakeroad).\n");
log_push();

if (args.size() != 9 && args.size() != 10)
log_cmd_error("Invalid number of arguments!\n");
auto top_module_name = args[1];
auto output_signal_name = args[2];
auto architecture = args[3];
auto templ8 = args[4];
auto initiation_interval = args[5];
auto clock_name = args[6];
auto input_a = args[7];
auto input_b = args[8];
auto input_c = args.size() == 10 ? args[9] : "";

auto module_name = top_module_name + "_synthesized_by_lakeroad";

// Who knew getting a named temporary file was so hard in C++? This isn't a
// great solution.
auto verilog_filename = (boost::filesystem::temp_directory_path() / boost::filesystem::unique_path("%%%%-%%%%-%%%%-%%%%.v")).native();
auto out_verilog_filename =
(boost::filesystem::temp_directory_path() / boost::filesystem::unique_path("%%%%-%%%%-%%%%-%%%%.v")).native();
std::vector<std::string> write_verilog_args;
write_verilog_args.push_back("write_verilog");
write_verilog_args.push_back(verilog_filename);
Pass::call(design, write_verilog_args);

if (!getenv("LAKEROAD_DIR"))
log_error("LAKEROAD_DIR environment variable not set. Please set it to the location of the Lakeroad directory.\n");

std::stringstream ss;
// clang-format off
ss << getenv("LAKEROAD_DIR") << "/bin/main.rkt"
<< " --verilog-module-filepath " << verilog_filename
<< " --top-module-name " << top_module_name
<< " --out-filepath " << out_verilog_filename
<< " --out-format verilog"
<< " --verilog-module-out-signal " << output_signal_name
<< " --architecture " << architecture
<< " --template " << templ8
<< " --module-name " << module_name
<< " --clock-name " << clock_name
<< " --input-signal " << input_a
<< " --input-signal " << input_b;
if (initiation_interval != "0")
ss << " --initiation-interval " << initiation_interval;
if (input_c != "")
ss << " --input-signal " << input_c;
// clang-format on

log("Executing Lakeroad:\n%s\n", ss.str().c_str());
if (system(ss.str().c_str()) != 0)
log_error("Lakeroad execution failed.\n");

std::vector<std::string> read_verilog_args;
read_verilog_args.push_back("read_verilog");
read_verilog_args.push_back(out_verilog_filename);
Pass::call(design, read_verilog_args);

auto new_module = design->module(RTLIL::escape_id(module_name));
if (new_module == nullptr)
log_error("Lakeroad returned OK, but no module named %s found.\n", module_name.c_str());

log("Replacing module %s with the output of Lakeroad", top_module_name.c_str());

design->remove(design->module(RTLIL::escape_id(top_module_name)));
design->rename(new_module, RTLIL::escape_id(top_module_name));
// Have to get around the reference counting that modules() does.
std::vector<IdString> module_names;
std::transform(design->modules().begin(), design->modules().end(), std::back_inserter(module_names),
[](Module *module) { return module->name; });
for (auto name : module_names)
compileWithLakeroad(design->module(name), design);

log_pop();
}
Expand Down
38 changes: 38 additions & 0 deletions tests/techmap/lakeroad.ys
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Test Lakeroad pass in Yosys.

# Read in Verilog design annotated with information for Lakeroad.
read_verilog << EOT
(* template = "dsp",
architecture = "xilinx-ultrascale-plus",
initiation_interval = 2 *)
module test (
(* data *)
input [17:0] a,
(* data *)
input [17:0] b,
(* data *)
input [17:0] c,
(* data *)
input [17:0] d,
(* clk *)
input clk,
(* out *)
output [17:0] o);

reg [17:0] r0;
reg [17:0] r1;
always @ (posedge clk) begin
r0 <= (d+a)*b&c;
r1 <= r0;
end
assign o = r1;

endmodule
EOT

# Call Lakeroad pass.
lakeroad

# Ensure there's exactly one cell, and that that cell is a DSP48E2.
select -assert-count 1 */t:*
select -assert-count 1 */t:DSP48E2

0 comments on commit 2856a4d

Please sign in to comment.