From 5c5a044c68a9af946fe68e79e23335718d34e671 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 27 May 2020 14:25:06 -0600 Subject: [PATCH] add architecture decoder (for frame-based config memory) to Verilog writer --- openfpga/src/base/openfpga_verilog.cpp | 1 + openfpga/src/fpga_verilog/verilog_api.cpp | 3 +- openfpga/src/fpga_verilog/verilog_api.h | 2 + openfpga/src/fpga_verilog/verilog_constants.h | 1 + .../src/fpga_verilog/verilog_decoders.cpp | 178 +++++++++++++++++- openfpga/src/fpga_verilog/verilog_decoders.h | 7 + .../src/fpga_verilog/verilog_submodule.cpp | 7 + openfpga/src/fpga_verilog/verilog_submodule.h | 2 + 8 files changed, 198 insertions(+), 3 deletions(-) diff --git a/openfpga/src/base/openfpga_verilog.cpp b/openfpga/src/base/openfpga_verilog.cpp index 481bd2866..c67c270eb 100644 --- a/openfpga/src/base/openfpga_verilog.cpp +++ b/openfpga/src/base/openfpga_verilog.cpp @@ -48,6 +48,7 @@ int write_fabric_verilog(OpenfpgaContext& openfpga_ctx, openfpga_ctx.mutable_verilog_netlists(), openfpga_ctx.arch().circuit_lib, openfpga_ctx.mux_lib(), + openfpga_ctx.decoder_lib(), g_vpr_ctx.device(), openfpga_ctx.vpr_device_annotation(), openfpga_ctx.device_rr_gsb(), diff --git a/openfpga/src/fpga_verilog/verilog_api.cpp b/openfpga/src/fpga_verilog/verilog_api.cpp index 06ef0e58e..2f92156fb 100644 --- a/openfpga/src/fpga_verilog/verilog_api.cpp +++ b/openfpga/src/fpga_verilog/verilog_api.cpp @@ -54,6 +54,7 @@ void fpga_fabric_verilog(ModuleManager& module_manager, NetlistManager& netlist_manager, const CircuitLibrary& circuit_lib, const MuxLibrary& mux_lib, + const DecoderLibrary& decoder_lib, const DeviceContext& device_ctx, const VprDeviceAnnotation& device_annotation, const DeviceRRGSB& device_rr_gsb, @@ -90,7 +91,7 @@ void fpga_fabric_verilog(ModuleManager& module_manager, * Without the modules in the module manager, core logic generation is not possible!!! */ print_verilog_submodule(module_manager, netlist_manager, - mux_lib, circuit_lib, + mux_lib, decoder_lib, circuit_lib, submodule_dir_path, options); diff --git a/openfpga/src/fpga_verilog/verilog_api.h b/openfpga/src/fpga_verilog/verilog_api.h index 301e68b3e..3076beb47 100644 --- a/openfpga/src/fpga_verilog/verilog_api.h +++ b/openfpga/src/fpga_verilog/verilog_api.h @@ -8,6 +8,7 @@ #include #include #include "mux_library.h" +#include "decoder_library.h" #include "circuit_library.h" #include "vpr_context.h" #include "vpr_device_annotation.h" @@ -32,6 +33,7 @@ void fpga_fabric_verilog(ModuleManager& module_manager, NetlistManager& netlist_manager, const CircuitLibrary& circuit_lib, const MuxLibrary& mux_lib, + const DecoderLibrary& decoder_lib, const DeviceContext& device_ctx, const VprDeviceAnnotation& device_annotation, const DeviceRRGSB& device_rr_gsb, diff --git a/openfpga/src/fpga_verilog/verilog_constants.h b/openfpga/src/fpga_verilog/verilog_constants.h index 4456a1048..3266fe2f6 100644 --- a/openfpga/src/fpga_verilog/verilog_constants.h +++ b/openfpga/src/fpga_verilog/verilog_constants.h @@ -37,6 +37,7 @@ constexpr char* LUTS_VERILOG_FILE_NAME = "luts.v"; constexpr char* ROUTING_VERILOG_FILE_NAME = "routing.v"; constexpr char* MUXES_VERILOG_FILE_NAME = "muxes.v"; constexpr char* LOCAL_ENCODER_VERILOG_FILE_NAME = "local_encoder.v"; +constexpr char* ARCH_ENCODER_VERILOG_FILE_NAME = "arch_encoder.v"; constexpr char* MEMORIES_VERILOG_FILE_NAME = "memories.v"; constexpr char* WIRES_VERILOG_FILE_NAME = "wires.v"; constexpr char* ESSENTIALS_VERILOG_FILE_NAME = "inv_buf_passgate.v"; diff --git a/openfpga/src/fpga_verilog/verilog_decoders.cpp b/openfpga/src/fpga_verilog/verilog_decoders.cpp index 30789e36f..66a1a8ed8 100644 --- a/openfpga/src/fpga_verilog/verilog_decoders.cpp +++ b/openfpga/src/fpga_verilog/verilog_decoders.cpp @@ -1,7 +1,6 @@ /*************************************************************************************** * This file includes functions to generate Verilog modules of decoders ***************************************************************************************/ -/* TODO: merge verilog_decoder.c to this source file and rename to verilog_decoder.cpp */ #include /* Headers from vtrutil library */ @@ -15,6 +14,7 @@ #include "decoder_library_utils.h" #include "module_manager.h" +#include "openfpga_reserved_words.h" #include "openfpga_naming.h" #include "verilog_constants.h" @@ -133,7 +133,6 @@ void print_verilog_mux_local_decoder_module(std::fstream& fp, print_verilog_module_end(fp, module_name); } - /*************************************************************************************** * This function will generate all the unique Verilog modules of local decoders for * the multiplexers used in a FPGA fabric @@ -227,4 +226,179 @@ void print_verilog_submodule_mux_local_decoders(const ModuleManager& module_mana VTR_LOG("Done\n"); } +/*************************************************************************************** + * Create a Verilog module for a decoder used as a configuration protocol + * in FPGA architecture + * + * Address + * | | ... | + * v v v + * +-----------+ + * Enable->/ \ + * / Decoder \ + * +-----------------+ + * | | | ... | | | + * v v v v v v + * Data output + * + * The outputs are assumes to be one-hot codes (at most only one '1' exist) + * Considering this fact, there are only num_of_outputs conditions to be encoded. + * Therefore, the number of inputs is ceil(log(num_of_outputs)/log(2)) + * + * The decoder has an enable signal which is active at logic '1'. + * When activated, the decoder will output decoding results to the data output port + * Otherwise, the data output port will be always all-zero + ***************************************************************************************/ +static +void print_verilog_arch_decoder_module(std::fstream& fp, + const ModuleManager& module_manager, + const DecoderLibrary& decoder_lib, + const DecoderId& decoder) { + /* Get the number of inputs */ + size_t addr_size = decoder_lib.addr_size(decoder); + size_t data_size = decoder_lib.data_size(decoder); + + /* Validate the FILE handler */ + VTR_ASSERT(true == valid_file_stream(fp)); + + /* Create a name for the decoder */ + std::string module_name = generate_frame_memory_decoder_subckt_name(addr_size, data_size); + + /* Create a Verilog Module based on the circuit model, and add to module manager */ + ModuleId module_id = module_manager.find_module(module_name); + VTR_ASSERT(true == module_manager.valid_module_id(module_id)); + /* Find module ports */ + /* Enable port */ + ModulePortId enable_port_id = module_manager.find_module_port(module_id, std::string(DECODER_ENABLE_PORT_NAME)); + BasicPort enable_port = module_manager.module_port(module_id, enable_port_id); + /* Address port */ + ModulePortId addr_port_id = module_manager.find_module_port(module_id, std::string(DECODER_ADDRESS_PORT_NAME)); + BasicPort addr_port = module_manager.module_port(module_id, addr_port_id); + /* Find each output port */ + ModulePortId data_port_id = module_manager.find_module_port(module_id, std::string(DECODER_DATA_OUT_PORT_NAME)); + BasicPort data_port = module_manager.module_port(module_id, data_port_id); + /* Data port is registered. It should be outputted as + * output reg [lsb:msb] data + */ + BasicPort data_inv_port(std::string(DECODER_DATA_OUT_INV_PORT_NAME), data_size); + if (true == decoder_lib.use_data_inv_port(decoder)) { + ModulePortId data_inv_port_id = module_manager.find_module_port(module_id, std::string(DECODER_DATA_OUT_INV_PORT_NAME)); + data_inv_port = module_manager.module_port(module_id, data_inv_port_id); + } + + /* dump module definition + ports */ + print_verilog_module_declaration(fp, module_manager, module_id); + /* Finish dumping ports */ + + print_verilog_comment(fp, std::string("----- BEGIN Verilog codes for Decoder convert " + std::to_string(addr_size) + "-bit addr to " + std::to_string(data_size) + "-bit data -----")); + + /* Print the truth table of this decoder */ + /* Internal logics */ + /* Early exit: Corner case for data size = 1 the logic is very simple: + * data = addr; + * data_inv = ~data_inv + */ + if (1 == data_size) { + print_verilog_wire_connection(fp, data_port, addr_port, false); + + /* Depend on if the inverted data output port is needed or not */ + if (true == decoder_lib.use_data_inv_port(decoder)) { + print_verilog_wire_connection(fp, data_inv_port, addr_port, true); + } + + print_verilog_comment(fp, std::string("----- END Verilog codes for Decoder convert " + std::to_string(addr_size) + "-bit addr to " + std::to_string(data_size) + "-bit data -----")); + + /* Put an end to the Verilog module */ + print_verilog_module_end(fp, module_name); + return; + } + + /* We use a magic number -1 as the addr=1 should be mapped to ...1 + * Otherwise addr will map addr=1 to ..10 + * Note that there should be a range for the shift operators + * We should narrow the encoding to be applied to a given set of data + * This will lead to that any addr which falls out of the op code of data + * will give a all-zero code + * For example: + * data is 5-bit while addr is 3-bit + * data=8'b0_0000 will be encoded to addr=3'b001; + * data=8'b0_0001 will be encoded to addr=3'b010; + * data=8'b0_0010 will be encoded to addr=3'b011; + * data=8'b0_0100 will be encoded to addr=3'b100; + * data=8'b0_1000 will be encoded to addr=3'b101; + * data=8'b1_0000 will be encoded to addr=3'b110; + * The rest of addr codes 3'b110, 3'b111 will be decoded to data=8'b0_0000; + */ + + fp << "\t" << "always@(" << generate_verilog_port(VERILOG_PORT_CONKT, addr_port) << ")" << std::endl; + fp << "\tif (" << generate_verilog_port(VERILOG_PORT_CONKT, enable_port) << "1'b1) begin" << std::endl; + fp << "\t\t" << "case (" << generate_verilog_port(VERILOG_PORT_CONKT, addr_port) << ")" << std::endl; + /* Create a string for addr and data */ + for (size_t i = 0; i < data_size; ++i) { + fp << "\t\t\t" << generate_verilog_constant_values(itobin_vec(i, addr_size)); + fp << " : "; + fp << generate_verilog_port_constant_values(data_port, ito1hot_vec(i, data_size)); + fp << ";" << std::endl; + } + fp << "\t\t\t" << "default : "; + fp << generate_verilog_port_constant_values(data_port, ito1hot_vec(data_size - 1, data_size)); + fp << ";" << std::endl; + fp << "\t\t" << "endcase" << std::endl; + fp << "\t" << "end" << std::endl; + + if (true == decoder_lib.use_data_inv_port(decoder)) { + print_verilog_wire_connection(fp, data_inv_port, data_port, true); + } + + print_verilog_comment(fp, std::string("----- END Verilog codes for Decoder convert " + std::to_string(addr_size) + "-bit addr to " + std::to_string(data_size) + "-bit data -----")); + + /* Put an end to the Verilog module */ + print_verilog_module_end(fp, module_name); +} + +/*************************************************************************************** + * This function will generate all the unique Verilog modules of decoders for + * configuration protocols in a FPGA fabric + * It will generate these decoder Verilog modules using behavioral description. + * Note that the implementation of local decoders can be dependent on the technology + * and standard cell libraries. + * Therefore, behavioral Verilog is used and the local decoders should be synthesized + * before running the back-end flow for FPGA fabric + * See more details in the function print_verilog_arch_decoder() for more details + ***************************************************************************************/ +void print_verilog_submodule_arch_decoders(const ModuleManager& module_manager, + NetlistManager& netlist_manager, + const DecoderLibrary& decoder_lib, + const std::string& submodule_dir) { + std::string verilog_fname(submodule_dir + std::string(ARCH_ENCODER_VERILOG_FILE_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 out debugging information for if the file is not opened/created properly */ + VTR_LOG("Writing Verilog netlist for configuration decoders '%s'...", + verilog_fname.c_str()); + + print_verilog_file_header(fp, "Decoders for fabric configuration protocol "); + + /* Generate Verilog modules for the found unique local encoders */ + for (const auto& decoder : decoder_lib.decoders()) { + print_verilog_arch_decoder_module(fp, module_manager, decoder_lib, decoder); + } + + /* Close the file stream */ + fp.close(); + + /* Add fname to the netlist name list */ + NetlistId nlist_id = netlist_manager.add_netlist(verilog_fname); + VTR_ASSERT(NetlistId::INVALID() != nlist_id); + netlist_manager.set_netlist_type(nlist_id, NetlistManager::SUBMODULE_NETLIST); + + VTR_LOG("Done\n"); +} + + } /* end namespace openfpga */ diff --git a/openfpga/src/fpga_verilog/verilog_decoders.h b/openfpga/src/fpga_verilog/verilog_decoders.h index 9b2bbb6b1..e15d5299a 100644 --- a/openfpga/src/fpga_verilog/verilog_decoders.h +++ b/openfpga/src/fpga_verilog/verilog_decoders.h @@ -11,6 +11,7 @@ #include "circuit_library.h" #include "mux_graph.h" #include "mux_library.h" +#include "decoder_library.h" #include "module_manager.h" #include "netlist_manager.h" @@ -27,6 +28,12 @@ void print_verilog_submodule_mux_local_decoders(const ModuleManager& module_mana const CircuitLibrary& circuit_lib, const std::string& submodule_dir); +void print_verilog_submodule_arch_decoders(const ModuleManager& module_manager, + NetlistManager& netlist_manager, + const DecoderLibrary& decoder_lib, + const std::string& submodule_dir); + + } /* end namespace openfpga */ #endif diff --git a/openfpga/src/fpga_verilog/verilog_submodule.cpp b/openfpga/src/fpga_verilog/verilog_submodule.cpp index 37df77c60..6756824ab 100644 --- a/openfpga/src/fpga_verilog/verilog_submodule.cpp +++ b/openfpga/src/fpga_verilog/verilog_submodule.cpp @@ -34,6 +34,7 @@ namespace openfpga { void print_verilog_submodule(ModuleManager& module_manager, NetlistManager& netlist_manager, const MuxLibrary& mux_lib, + const DecoderLibrary& decoder_lib, const CircuitLibrary& circuit_lib, const std::string& submodule_dir, const FabricVerilogOption& fpga_verilog_opts) { @@ -49,6 +50,12 @@ void print_verilog_submodule(ModuleManager& module_manager, submodule_dir, circuit_lib); + /* Decoders for architecture */ + print_verilog_submodule_arch_decoders(const_cast(module_manager), + netlist_manager, + decoder_lib, + submodule_dir); + /* Routing multiplexers */ /* NOTE: local decoders generation must go before the MUX generation!!! * because local decoders modules will be instanciated in the MUX modules diff --git a/openfpga/src/fpga_verilog/verilog_submodule.h b/openfpga/src/fpga_verilog/verilog_submodule.h index 06bb7aeef..27bf7fdba 100644 --- a/openfpga/src/fpga_verilog/verilog_submodule.h +++ b/openfpga/src/fpga_verilog/verilog_submodule.h @@ -7,6 +7,7 @@ #include "module_manager.h" #include "netlist_manager.h" #include "mux_library.h" +#include "decoder_library.h" #include "fabric_verilog_options.h" /******************************************************************** @@ -19,6 +20,7 @@ namespace openfpga { void print_verilog_submodule(ModuleManager& module_manager, NetlistManager& netlist_manager, const MuxLibrary& mux_lib, + const DecoderLibrary& decoder_lib, const CircuitLibrary& circuit_lib, const std::string& submodule_dir, const FabricVerilogOption& fpga_verilog_opts);