diff --git a/openfpga/src/base/openfpga_naming.cpp b/openfpga/src/base/openfpga_naming.cpp index 7aaafff5d..fab27a12b 100644 --- a/openfpga/src/base/openfpga_naming.cpp +++ b/openfpga/src/base/openfpga_naming.cpp @@ -905,6 +905,22 @@ std::string generate_mux_sram_port_name(const CircuitLibrary& circuit_lib, return generate_local_sram_port_name(prefix, mux_instance_id, port_type); } +/********************************************************************* + * Generate the netlist name of a logical tile + **********************************************************************/ +std::string generate_logical_tile_netlist_name(const std::string& prefix, + const t_pb_graph_node* pb_graph_head, + const std::string& postfix) { + /* This must be the root node */ + VTR_ASSERT(true == pb_graph_head->is_root()); + /* Add the name of physical block */ + std::string module_name = prefix + std::string(pb_graph_head->pb_type->name); + + module_name += postfix; + + return module_name; +} + /********************************************************************* * Generate the prefix for naming a grid block netlist or a grid module * This function will consider the io side and add it to the prefix diff --git a/openfpga/src/base/openfpga_naming.h b/openfpga/src/base/openfpga_naming.h index 4bc22f3c9..13d4c56c0 100644 --- a/openfpga/src/base/openfpga_naming.h +++ b/openfpga/src/base/openfpga_naming.h @@ -200,6 +200,10 @@ std::string generate_mux_sram_port_name(const CircuitLibrary& circuit_lib, const size_t& mux_instance_id, const e_circuit_model_port_type& port_type); +std::string generate_logical_tile_netlist_name(const std::string& prefix, + const t_pb_graph_node* pb_graph_head, + const std::string& postfix); + std::string generate_grid_block_prefix(const std::string& prefix, const e_side& io_side); diff --git a/openfpga/src/base/openfpga_verilog.cpp b/openfpga/src/base/openfpga_verilog.cpp index 018fe1249..72b8fb100 100644 --- a/openfpga/src/base/openfpga_verilog.cpp +++ b/openfpga/src/base/openfpga_verilog.cpp @@ -50,7 +50,8 @@ void write_fabric_verilog(OpenfpgaContext& openfpga_ctx, fpga_fabric_verilog(openfpga_ctx.mutable_module_graph(), openfpga_ctx.arch().circuit_lib, openfpga_ctx.mux_lib(), - g_vpr_ctx.device().grid, + g_vpr_ctx.device(), + openfpga_ctx.vpr_device_annotation(), openfpga_ctx.device_rr_gsb(), options); } diff --git a/openfpga/src/fpga_verilog/verilog_api.cpp b/openfpga/src/fpga_verilog/verilog_api.cpp index aa68e0024..ce339e813 100644 --- a/openfpga/src/fpga_verilog/verilog_api.cpp +++ b/openfpga/src/fpga_verilog/verilog_api.cpp @@ -17,7 +17,7 @@ #include "verilog_auxiliary_netlists.h" #include "verilog_submodule.h" #include "verilog_routing.h" -//#include "verilog_grid.h" +#include "verilog_grid.h" //#include "verilog_top_module.h" /* Header file for this source file */ @@ -47,7 +47,8 @@ namespace openfpga { void fpga_fabric_verilog(ModuleManager& module_manager, const CircuitLibrary& circuit_lib, const MuxLibrary& mux_lib, - const DeviceGrid& grids, + const DeviceContext& device_ctx, + const VprDeviceAnnotation& device_annotation, const DeviceRRGSB& device_rr_gsb, const FabricVerilogOption& options) { @@ -103,9 +104,11 @@ void fpga_fabric_verilog(ModuleManager& module_manager, } /* Generate grids */ - //print_verilog_grids(module_manager, - // src_dir_path, lb_dir_path, - // dump_explicit_verilog); + print_verilog_grids(const_cast(module_manager), + device_ctx, device_annotation, + src_dir_path, lb_dir_path, + options.explicit_port_mapping(), + options.verbose_output()); /* Generate FPGA fabric */ //print_verilog_top_module(module_manager, diff --git a/openfpga/src/fpga_verilog/verilog_api.h b/openfpga/src/fpga_verilog/verilog_api.h index 569650f2a..7494a5a8f 100644 --- a/openfpga/src/fpga_verilog/verilog_api.h +++ b/openfpga/src/fpga_verilog/verilog_api.h @@ -10,7 +10,8 @@ #include "vpr_types.h" #include "mux_library.h" #include "circuit_library.h" -#include "device_grid.h" +#include "vpr_context.h" +#include "vpr_device_annotation.h" #include "device_rr_gsb.h" #include "module_manager.h" #include "verilog_options.h" @@ -25,7 +26,8 @@ namespace openfpga { void fpga_fabric_verilog(ModuleManager& module_manager, const CircuitLibrary& circuit_lib, const MuxLibrary& mux_lib, - const DeviceGrid& grids, + const DeviceContext& device_ctx, + const VprDeviceAnnotation& device_annotation, const DeviceRRGSB& device_rr_gsb, const FabricVerilogOption& options); diff --git a/openfpga/src/fpga_verilog/verilog_constants.h b/openfpga/src/fpga_verilog/verilog_constants.h index d5e1f4edb..e355b7a5f 100644 --- a/openfpga/src/fpga_verilog/verilog_constants.h +++ b/openfpga/src/fpga_verilog/verilog_constants.h @@ -45,5 +45,7 @@ constexpr char* VERILOG_MUX_BASIS_POSTFIX = "_basis"; constexpr char* VERILOG_MEM_POSTFIX = "_mem"; constexpr char* SB_VERILOG_FILE_NAME_PREFIX = "sb_"; +constexpr char* LOGICAL_MODULE_VERILOG_FILE_NAME_PREFIX = "logical_tile_"; +constexpr char* GRID_VERILOG_FILE_NAME_PREFIX = "grid_"; #endif diff --git a/openfpga/src/fpga_verilog/verilog_grid.cpp b/openfpga/src/fpga_verilog/verilog_grid.cpp new file mode 100644 index 000000000..73e69ca29 --- /dev/null +++ b/openfpga/src/fpga_verilog/verilog_grid.cpp @@ -0,0 +1,411 @@ +/******************************************************************** + * This file includes functions to print Verilog modules for a Grid + * (CLBs, I/Os, heterogeneous blocks etc.) + *******************************************************************/ +/* System header files */ +#include +#include + +/* Headers from vtrutil library */ +#include "vtr_geometry.h" +#include "vtr_assert.h" +#include "vtr_log.h" + +/* Headers from readarch library */ +#include "physical_types.h" + +/* Headers from openfpgautil library */ +#include "openfpga_digest.h" +#include "openfpga_side_manager.h" + +/* Headers from vpr library */ +#include "vpr_utils.h" + +#include "openfpga_reserved_words.h" +#include "openfpga_naming.h" +#include "pb_type_utils.h" +#include "circuit_library_utils.h" +#include "module_manager_utils.h" + +#include "verilog_constants.h" +#include "verilog_writer_utils.h" +#include "verilog_module_writer.h" +#include "verilog_grid.h" + +/* begin namespace openfpga */ +namespace openfpga { + +/******************************************************************** + * Print Verilog modules of a primitive node in the pb_graph_node graph + * This generic function can support all the different types of primitive nodes + * i.e., Look-Up Tables (LUTs), Flip-flops (FFs) and hard logic blocks such as adders. + * + * The Verilog module will consist of two parts: + * 1. Logic module of the primitive node + * This module performs the logic function of the block + * 2. Memory module of the primitive node + * This module stores the configuration bits for the logic module + * if the logic module is a programmable resource, such as LUT + * + * Verilog module structure: + * + * Primitive block + * +---------------------------------------+ + * | | + * | +---------+ +---------+ | + * in |----->| |--->| |<------|configuration lines + * | | Logic |... | Memory | | + * out|<-----| |--->| | | + * | +---------+ +---------+ | + * | | + * +---------------------------------------+ + * + *******************************************************************/ +static +void print_verilog_primitive_block(std::fstream& fp, + const ModuleManager& module_manager, + t_pb_graph_node* primitive_pb_graph_node, + const bool& use_explicit_mapping, + const bool& verbose) { + /* Ensure a valid file handler */ + VTR_ASSERT(true == valid_file_stream(fp)); + + /* Ensure a valid pb_graph_node */ + if (nullptr == primitive_pb_graph_node) { + VTR_LOGF_ERROR(__FILE__, __LINE__, + "Invalid primitive_pb_graph_node!\n"); + exit(1); + } + + /* Generate the module name for this primitive pb_graph_node*/ + std::string primitive_module_name = generate_physical_block_module_name(primitive_pb_graph_node->pb_type); + + /* Create a module of the primitive LUT and register it to module manager */ + ModuleId primitive_module = module_manager.find_module(primitive_module_name); + /* Ensure that the module has been created and thus unique! */ + VTR_ASSERT(true == module_manager.valid_module_id(primitive_module)); + + VTR_LOGV(verbose, + "Writing Verilog codes of logical tile primitive block '%s'...", + module_manager.module_name(primitive_module).c_str()); + + /* Write the verilog module */ + write_verilog_module_to_file(fp, module_manager, primitive_module, use_explicit_mapping); + + /* Add an empty line as a splitter */ + fp << std::endl; + + VTR_LOGV(verbose, "Done\n"); +} + +/******************************************************************** + * Print Verilog modules of physical blocks inside a grid (CLB, I/O. etc.) + * This function will traverse the graph of complex logic block (t_pb_graph_node) + * in a recursive way, using a Depth First Search (DFS) algorithm. + * As such, primitive physical blocks (LUTs, FFs, etc.), leaf node of the pb_graph + * will be printed out first, while the top-level will be printed out in the last + * + * Note: this function will print a unique Verilog module for each type of + * t_pb_graph_node, i.e., t_pb_type, in the graph, in order to enable highly + * hierarchical Verilog organization as well as simplify the Verilog file sizes. + * + * Note: DFS is the right way. Do NOT use BFS. + * DFS can guarantee that all the sub-modules can be registered properly + * to its parent in module manager + *******************************************************************/ +static +void rec_print_verilog_logical_tile(std::fstream& fp, + const ModuleManager& module_manager, + const VprDeviceAnnotation& device_annotation, + t_pb_graph_node* physical_pb_graph_node, + const bool& use_explicit_mapping, + const bool& verbose) { + /* Check the file handler*/ + VTR_ASSERT(true == valid_file_stream(fp)); + + /* Check cur_pb_graph_node*/ + if (nullptr == physical_pb_graph_node) { + VTR_LOGF_ERROR(__FILE__, __LINE__, + "Invalid physical_pb_graph_node\n"); + exit(1); + } + + /* Get the pb_type definition related to the node */ + t_pb_type* physical_pb_type = physical_pb_graph_node->pb_type; + + /* Find the mode that physical implementation of a pb_type */ + t_mode* physical_mode = device_annotation.physical_mode(physical_pb_type); + + /* For non-leaf node in the pb_type graph: + * Recursively Depth-First Generate all the child pb_type at the level + */ + if (false == is_primitive_pb_type(physical_pb_type)) { + for (int ipb = 0; ipb < physical_mode->num_pb_type_children; ++ipb) { + /* Go recursive to visit the children */ + rec_print_verilog_logical_tile(fp, + module_manager, device_annotation, + &(physical_pb_graph_node->child_pb_graph_nodes[physical_mode->index][ipb][0]), + use_explicit_mapping, + verbose); + } + } + + /* For leaf node, a primitive Verilog module will be generated. + * Note that the primitive may be mapped to a standard cell, we force to use + * explict port mapping. This aims to avoid any port sequence issues!!! + */ + if (true == is_primitive_pb_type(physical_pb_type)) { + print_verilog_primitive_block(fp, module_manager, + physical_pb_graph_node, + true, + verbose); + /* Finish for primitive node, return */ + return; + } + + /* Generate the name of the Verilog module for this pb_type */ + std::string pb_module_name = generate_physical_block_module_name(physical_pb_type); + + /* Register the Verilog module in module manager */ + ModuleId pb_module = module_manager.find_module(pb_module_name); + VTR_ASSERT(true == module_manager.valid_module_id(pb_module)); + + VTR_LOGV(verbose, + "Writing Verilog codes of logical tile block '%s'...", + module_manager.module_name(pb_module).c_str()); + + /* Comment lines */ + print_verilog_comment(fp, std::string("----- BEGIN Physical programmable logic block Verilog module: " + std::string(physical_pb_type->name) + " -----")); + + /* Write the verilog module */ + write_verilog_module_to_file(fp, module_manager, pb_module, use_explicit_mapping); + + print_verilog_comment(fp, std::string("----- END Physical programmable logic block Verilog module: " + std::string(physical_pb_type->name) + " -----")); + + /* Add an empty line as a splitter */ + fp << std::endl; + + VTR_LOGV(verbose, "Done\n"); +} + +/***************************************************************************** + * This function will create a Verilog file and print out a Verilog netlist + * for the logical tile (pb_graph/pb_type) + *****************************************************************************/ +static +void print_verilog_logical_tile_netlist(const ModuleManager& module_manager, + std::vector& netlist_names, + const VprDeviceAnnotation& device_annotation, + const std::string& verilog_dir, + const std::string& subckt_dir, + t_pb_graph_node* pb_graph_head, + const bool& use_explicit_mapping, + const bool& verbose) { + /* Give a name to the Verilog netlist */ + /* Create the file name for Verilog */ + std::string verilog_fname(subckt_dir + + generate_logical_tile_netlist_name(std::string(LOGICAL_MODULE_VERILOG_FILE_NAME_PREFIX), pb_graph_head, std::string(VERILOG_NETLIST_FILE_POSTFIX)) + ); + + VTR_LOG("Writing Verilog netlist '%s' for logic tile '%s' ...", + verilog_fname.c_str(), pb_graph_head->pb_type->name); + VTR_LOGV(verbose, "\n"); + + /* Create the file stream */ + std::fstream fp; + fp.open(verilog_fname, std::fstream::out | std::fstream::trunc); + + check_file_stream(verilog_fname.c_str(), fp); + + print_verilog_file_header(fp, std::string("Verilog modules for logical tile: " + std::string(pb_graph_head->pb_type->name) + "]")); + + /* Print preprocessing flags */ + print_verilog_include_defines_preproc_file(fp, verilog_dir); + + /* Print Verilog modules for all the pb_types/pb_graph_nodes + * use a Depth-First Search Algorithm to print the sub-modules + * Note: DFS is the right way. Do NOT use BFS. + * DFS can guarantee that all the sub-modules can be registered properly + * to its parent in module manager + */ + /* Print Verilog modules starting from the top-level pb_type/pb_graph_node, and traverse the graph in a recursive way */ + rec_print_verilog_logical_tile(fp, module_manager, + device_annotation, + pb_graph_head, + use_explicit_mapping, + verbose); + + /* Add an empty line as a splitter */ + fp << std::endl; + + /* Close file handler */ + fp.close(); + + /* Add fname to the netlist name list */ + netlist_names.push_back(verilog_fname); + + VTR_LOG("Done\n"); + VTR_LOG("\n"); +} + +/***************************************************************************** + * This function will create a Verilog file and print out a Verilog netlist + * for a type of physical block + * + * For IO blocks: + * The param 'border_side' is required, which is specify which side of fabric + * the I/O block locates at. + *****************************************************************************/ +static +void print_verilog_physical_tile_netlist(const ModuleManager& module_manager, + std::vector& netlist_names, + const std::string& verilog_dir, + const std::string& subckt_dir, + t_physical_tile_type_ptr phy_block_type, + const e_side& border_side, + const bool& use_explicit_mapping) { + /* Check code: if this is an IO block, the border side MUST be valid */ + if (true == is_io_type(phy_block_type)) { + VTR_ASSERT(NUM_SIDES != border_side); + } + + /* Give a name to the Verilog netlist */ + /* Create the file name for Verilog */ + std::string verilog_fname(subckt_dir + + generate_grid_block_netlist_name(std::string(phy_block_type->name), + is_io_type(phy_block_type), + border_side, + std::string(VERILOG_NETLIST_FILE_POSTFIX)) + ); + + /* Echo status */ + if (true == is_io_type(phy_block_type)) { + SideManager side_manager(border_side); + VTR_LOG("Writing Verilog Netlist '%s' for physical tile '%s' at %s side ...", + verilog_fname.c_str(), phy_block_type->name, + side_manager.c_str()); + } else { + VTR_LOG("Writing Verilog Netlist '%s' for physical_tile '%s'...", + verilog_fname.c_str(), phy_block_type->name); + } + + /* Create the file stream */ + std::fstream fp; + fp.open(verilog_fname, std::fstream::out | std::fstream::trunc); + + check_file_stream(verilog_fname.c_str(), fp); + + print_verilog_file_header(fp, std::string("Verilog modules for physical tile: " + std::string(phy_block_type->name) + "]")); + + /* Print preprocessing flags */ + print_verilog_include_defines_preproc_file(fp, verilog_dir); + + /* Create a Verilog Module for the top-level physical block, and add to module manager */ + std::string grid_module_name = generate_grid_block_module_name(std::string(GRID_VERILOG_FILE_NAME_PREFIX), std::string(phy_block_type->name), is_io_type(phy_block_type), border_side); + ModuleId grid_module = module_manager.find_module(grid_module_name); + VTR_ASSERT(true == module_manager.valid_module_id(grid_module)); + + /* Write the verilog module */ + print_verilog_comment(fp, std::string("----- BEGIN Grid Verilog module: " + module_manager.module_name(grid_module) + " -----")); + write_verilog_module_to_file(fp, module_manager, grid_module, use_explicit_mapping); + + print_verilog_comment(fp, std::string("----- END Grid Verilog module: " + module_manager.module_name(grid_module) + " -----")); + + /* Add an empty line as a splitter */ + fp << std::endl; + + /* Close file handler */ + fp.close(); + + /* Add fname to the netlist name list */ + netlist_names.push_back(verilog_fname); + + VTR_LOG("Done\n"); +} + +/***************************************************************************** + * Create logic block modules in a compact way: + * 1. Only one module for each I/O on each border side (IO_TYPE) + * 2. Only one module for each CLB (FILL_TYPE) + * 3. Only one module for each heterogeneous block + ****************************************************************************/ +void print_verilog_grids(const ModuleManager& module_manager, + const DeviceContext& device_ctx, + const VprDeviceAnnotation& device_annotation, + const std::string& verilog_dir, + const std::string& subckt_dir, + const bool& use_explicit_mapping, + const bool& verbose) { + /* Create a vector to contain all the Verilog netlist names that have been generated in this function */ + std::vector netlist_names; + + /* Enumerate the types of logical tiles, and build a module for each + * Write modules for all the pb_types/pb_graph_nodes + * use a Depth-First Search Algorithm to print the sub-modules + * Note: DFS is the right way. Do NOT use BFS. + * DFS can guarantee that all the sub-modules can be registered properly + * to its parent in module manager + */ + VTR_LOG("Writing logical tiles..."); + VTR_LOGV(verbose, "\n"); + for (const t_logical_block_type& logical_tile : device_ctx.logical_block_types) { + /* Bypass empty pb_graph */ + if (nullptr == logical_tile.pb_graph_head) { + continue; + } + print_verilog_logical_tile_netlist(module_manager, netlist_names, + device_annotation, + verilog_dir, subckt_dir, + logical_tile.pb_graph_head, + use_explicit_mapping, + verbose); + } + VTR_LOG("Writing logical tiles..."); + VTR_LOG("Done\n"); + + VTR_LOG("\n"); + + /* Enumerate the types of physical tiles + * Use the logical tile module to build the physical tiles + */ + VTR_LOG("Building physical tiles..."); + VTR_LOGV(verbose, "\n"); + for (const t_physical_tile_type& physical_tile : device_ctx.physical_tile_types) { + /* Bypass empty type or nullptr */ + if (true == is_empty_type(&physical_tile)) { + continue; + } else if (true == is_io_type(&physical_tile)) { + /* Special for I/O block, generate one module for each border side */ + for (int iside = 0; iside < NUM_SIDES; iside++) { + SideManager side_manager(iside); + print_verilog_physical_tile_netlist(module_manager, netlist_names, + verilog_dir, subckt_dir, + &physical_tile, + side_manager.get_side(), + use_explicit_mapping); + } + continue; + } else { + /* For CLB and heterogenenous blocks */ + print_verilog_physical_tile_netlist(module_manager, netlist_names, + verilog_dir, subckt_dir, + &physical_tile, + NUM_SIDES, + use_explicit_mapping); + } + } + VTR_LOG("Building physical tiles..."); + VTR_LOG("Done\n"); + VTR_LOG("\n"); + + /* Output a header file for all the logic blocks */ + std::string grid_verilog_fname(LOGIC_BLOCK_VERILOG_FILE_NAME); + VTR_LOG("Writing header file for grid Verilog modules '%s' ...", + grid_verilog_fname.c_str()); + print_verilog_netlist_include_header_file(netlist_names, + subckt_dir.c_str(), + grid_verilog_fname.c_str()); + VTR_LOG("Done\n"); +} + +} /* end namespace openfpga */ diff --git a/openfpga/src/fpga_verilog/verilog_grid.h b/openfpga/src/fpga_verilog/verilog_grid.h new file mode 100644 index 000000000..da8ea09fa --- /dev/null +++ b/openfpga/src/fpga_verilog/verilog_grid.h @@ -0,0 +1,30 @@ +#ifndef VERILOG_GRID_H +#define VERILOG_GRID_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include +#include "vpr_context.h" +#include "module_manager.h" +#include "vpr_device_annotation.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +/* begin namespace openfpga */ +namespace openfpga { + +void print_verilog_grids(const ModuleManager& module_manager, + const DeviceContext& device_ctx, + const VprDeviceAnnotation& device_annotation, + const std::string& verilog_dir, + const std::string& subckt_dir, + const bool& use_explicit_mapping, + const bool& verbose); + + +} /* end namespace openfpga */ + +#endif diff --git a/openfpga/src/fpga_verilog/verilog_routing.cpp b/openfpga/src/fpga_verilog/verilog_routing.cpp index 0d9582556..a3361fd06 100644 --- a/openfpga/src/fpga_verilog/verilog_routing.cpp +++ b/openfpga/src/fpga_verilog/verilog_routing.cpp @@ -291,6 +291,7 @@ void print_verilog_flatten_routing_modules(const ModuleManager& module_manager, subckt_dir.c_str(), ROUTING_VERILOG_FILE_NAME); VTR_LOG("Done\n"); + VTR_LOG("\n"); } @@ -350,6 +351,7 @@ void print_verilog_unique_routing_modules(const ModuleManager& module_manager, subckt_dir.c_str(), ROUTING_VERILOG_FILE_NAME); VTR_LOG("Done\n"); + VTR_LOG("\n"); } } /* end namespace openfpga */