From 722a9bcf633f9f9820402b6a210d1c7807b492d5 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 4 Jan 2021 14:31:26 -0700 Subject: [PATCH 01/13] [HDL] Add scan-chain DFF cell with configuration enable signal --- .../openfpga_cell_library/verilog/dff.v | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/openfpga_flow/openfpga_cell_library/verilog/dff.v b/openfpga_flow/openfpga_cell_library/verilog/dff.v index e54950827..9838a6b6a 100644 --- a/openfpga_flow/openfpga_cell_library/verilog/dff.v +++ b/openfpga_flow/openfpga_cell_library/verilog/dff.v @@ -397,3 +397,50 @@ end assign Q = q_reg; endmodule //End Of Module + +//----------------------------------------------------- +// Function : D-type flip-flop with +// - asynchronous active high reset +// - scan-chain input +// - a scan-chain enable +// - a configure enable, when enabled the registered output will +// be released to the Q +//----------------------------------------------------- +module CFGSDFFR ( + input RST, // Reset input + input CK, // Clock Input + input SE, // Scan-chain Enable + input D, // Data Input + input SI, // Scan-chain input + input CFGE, // Configure enable + output Q, // Regular Q output + output QN, // Regular Qb output + output CFGQ, // Data Q output which is released when configure enable is activated + output CFGQN // Data Qb output which is released when configure enable is activated +); +//------------Internal Variables-------- +reg q_reg; + +//-------------Code Starts Here--------- +always @ ( posedge CK or posedge RST) +if (RST) begin + q_reg <= 1'b0; +end else if (SE) begin + q_reg <= SI; +end else begin + q_reg <= D; +end + +assign CFGQ = CFGE ? Q : 1'b0; +assign CFGQN = CFGE ? QN : 1'b0; + +`ifndef ENABLE_FORMAL_VERIFICATION +// Wire q_reg to Q + assign Q = q_reg; + assign QN = !Q; +`else + assign Q = 1'bZ; + assign QN = !Q; +`endif + +endmodule //End Of Module From 294ad97d38dc43cd18d051b694c235e6d1afa2fd Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 4 Jan 2021 14:56:49 -0700 Subject: [PATCH 02/13] [Arch] Add openfpga architecture example using the configure-enable scan-chain flip-flop --- .../k4_N4_40nm_cc_cfgscff_openfpga.xml | 195 ++++++++++++++++++ 1 file changed, 195 insertions(+) create mode 100644 openfpga_flow/openfpga_arch/k4_N4_40nm_cc_cfgscff_openfpga.xml diff --git a/openfpga_flow/openfpga_arch/k4_N4_40nm_cc_cfgscff_openfpga.xml b/openfpga_flow/openfpga_arch/k4_N4_40nm_cc_cfgscff_openfpga.xml new file mode 100644 index 000000000..96ddf9beb --- /dev/null +++ b/openfpga_flow/openfpga_arch/k4_N4_40nm_cc_cfgscff_openfpga.xml @@ -0,0 +1,195 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 10e-12 + + + 10e-12 + + + + + + + + + 10e-12 + + + 10e-12 + + + + + + + + + 10e-12 + + + 10e-12 + + + + + + + + + + + + + 10e-12 5e-12 5e-12 + + + 10e-12 5e-12 5e-12 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From cb34be0dc035e7fff2e653fb85a9ea706dfa0bfa Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 4 Jan 2021 15:13:54 -0700 Subject: [PATCH 03/13] [Tool] Update check functions for CCFF circuit model to be consistent with SCFF requirements --- .../src/check_circuit_library.cpp | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/libopenfpga/libarchopenfpga/src/check_circuit_library.cpp b/libopenfpga/libarchopenfpga/src/check_circuit_library.cpp index d7b19f41a..063873253 100644 --- a/libopenfpga/libarchopenfpga/src/check_circuit_library.cpp +++ b/libopenfpga/libarchopenfpga/src/check_circuit_library.cpp @@ -290,17 +290,33 @@ size_t check_ccff_circuit_model_ports(const CircuitLibrary& circuit_lib, VTR_ASSERT(CIRCUIT_MODEL_CCFF == circuit_lib.model_type(circuit_model)); /* Check if we have D, Set and Reset */ + /* We can have either 1 input which is D or 2 inputs which are D and scan input */ + size_t num_input_ports = circuit_lib.model_ports_by_type(circuit_model, CIRCUIT_MODEL_PORT_INPUT, true).size(); + if ((1 != num_input_ports) && (2 != num_input_ports)) { + VTR_LOG_ERROR("Configuration flip-flop '%s' must have either 1 or 2 %s ports!\n\tAmong which the first input is a regular input (e.g., D) and the other could be scan-chain input (e.g., SI)\n", + circuit_lib.model_name(circuit_model).c_str(), + CIRCUIT_MODEL_PORT_TYPE_STRING[size_t(CIRCUIT_MODEL_PORT_INPUT)]); + num_err++; + } + num_err += check_one_circuit_model_port_type_and_size_required(circuit_lib, circuit_model, CIRCUIT_MODEL_PORT_INPUT, - 1, 1, false); + num_input_ports, 1, false); /* Check if we have a clock */ num_err += check_one_circuit_model_port_type_and_size_required(circuit_lib, circuit_model, CIRCUIT_MODEL_PORT_CLOCK, 1, 1, true); - /* Check if we have 1 or 2 outputs */ + /* Check if we have 2 or 4 outputs */ size_t num_output_ports = circuit_lib.model_ports_by_type(circuit_model, CIRCUIT_MODEL_PORT_OUTPUT, true).size(); + if ((2 != num_output_ports) && (4 != num_output_ports)) { + VTR_LOG_ERROR("Configuration flip-flop '%s' must have either 2 or 4 %s ports!\n\tAmong which two manadatory outputs are regular data outputs (e.g., Q and QN) and the other two could be configure-enable outputs (e.g., cfg_en_Q and cgf_en_QN)\n", + circuit_lib.model_name(circuit_model).c_str(), + CIRCUIT_MODEL_PORT_TYPE_STRING[size_t(CIRCUIT_MODEL_PORT_OUTPUT)]); + num_err++; + } + num_err += check_one_circuit_model_port_type_and_size_required(circuit_lib, circuit_model, CIRCUIT_MODEL_PORT_OUTPUT, num_output_ports, 1, false); From cc91a0aebd3f38090fbbeb462966a604d4ea42c6 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 4 Jan 2021 17:14:26 -0700 Subject: [PATCH 04/13] [Tool] Patch the bug in port requirements for CCFF circuit model and now supports SCFF in module graph builder --- .../src/check_circuit_library.cpp | 10 +- .../src/openfpga_reserved_words.h | 3 + openfpga/src/base/openfpga_naming.cpp | 6 +- openfpga/src/fabric/build_memory_modules.cpp | 171 +++++++++++++----- 4 files changed, 133 insertions(+), 57 deletions(-) diff --git a/libopenfpga/libarchopenfpga/src/check_circuit_library.cpp b/libopenfpga/libarchopenfpga/src/check_circuit_library.cpp index 063873253..689d1af6f 100644 --- a/libopenfpga/libarchopenfpga/src/check_circuit_library.cpp +++ b/libopenfpga/libarchopenfpga/src/check_circuit_library.cpp @@ -293,7 +293,7 @@ size_t check_ccff_circuit_model_ports(const CircuitLibrary& circuit_lib, /* We can have either 1 input which is D or 2 inputs which are D and scan input */ size_t num_input_ports = circuit_lib.model_ports_by_type(circuit_model, CIRCUIT_MODEL_PORT_INPUT, true).size(); if ((1 != num_input_ports) && (2 != num_input_ports)) { - VTR_LOG_ERROR("Configuration flip-flop '%s' must have either 1 or 2 %s ports!\n\tAmong which the first input is a regular input (e.g., D) and the other could be scan-chain input (e.g., SI)\n", + VTR_LOG_ERROR("Configuration flip-flop '%s' must have either 1 or 2 %s ports!\n\tAmong which:\n\t\tthe first input is a regular input (e.g., D)\n\t\tand the other could be scan-chain input (e.g., SI)\n", circuit_lib.model_name(circuit_model).c_str(), CIRCUIT_MODEL_PORT_TYPE_STRING[size_t(CIRCUIT_MODEL_PORT_INPUT)]); num_err++; @@ -308,10 +308,12 @@ size_t check_ccff_circuit_model_ports(const CircuitLibrary& circuit_lib, 1, 1, true); - /* Check if we have 2 or 4 outputs */ + /* Check if we have 1 or 2 or 3 outputs */ size_t num_output_ports = circuit_lib.model_ports_by_type(circuit_model, CIRCUIT_MODEL_PORT_OUTPUT, true).size(); - if ((2 != num_output_ports) && (4 != num_output_ports)) { - VTR_LOG_ERROR("Configuration flip-flop '%s' must have either 2 or 4 %s ports!\n\tAmong which two manadatory outputs are regular data outputs (e.g., Q and QN) and the other two could be configure-enable outputs (e.g., cfg_en_Q and cgf_en_QN)\n", + if ((1 != num_output_ports) + && (2 != num_output_ports) + && (3 != num_output_ports)) { + VTR_LOG_ERROR("Configuration flip-flop '%s' must have either 1 or 2 or 3 %s ports!\n\tAmong which:\n\t\tthe first port is the manadatory regular data output (e.g., Q) and \n\t\tthe second port could be the inverted data output which can optionally be enabled by configure-enable signal (e.g., QN or cgf_en_QN) and \n\t\tthe third port could be the data output which can optionally be enabled by configure-enable signal (e.g., cgf_en_Q)\n", circuit_lib.model_name(circuit_model).c_str(), CIRCUIT_MODEL_PORT_TYPE_STRING[size_t(CIRCUIT_MODEL_PORT_OUTPUT)]); num_err++; diff --git a/libopenfpga/libopenfpgautil/src/openfpga_reserved_words.h b/libopenfpga/libopenfpgautil/src/openfpga_reserved_words.h index 3f27c6966..2f9bae817 100644 --- a/libopenfpga/libopenfpgautil/src/openfpga_reserved_words.h +++ b/libopenfpga/libopenfpgautil/src/openfpga_reserved_words.h @@ -13,7 +13,10 @@ namespace openfpga { /* Top-level module name */ constexpr char* FPGA_TOP_MODULE_NAME = "fpga_top"; +constexpr char* CONFIGURABLE_MEMORY_CHAIN_IN_NAME = "ccff_head"; +constexpr char* CONFIGURABLE_MEMORY_CHAIN_OUT_NAME = "ccff_tail"; constexpr char* CONFIGURABLE_MEMORY_DATA_OUT_NAME = "mem_out"; +constexpr char* CONFIGURABLE_MEMORY_INVERTED_DATA_OUT_NAME = "mem_outb"; /* IO PORT */ /* Prefix of global input, output and inout ports of FPGA fabric */ diff --git a/openfpga/src/base/openfpga_naming.cpp b/openfpga/src/base/openfpga_naming.cpp index d2e41f549..192015ddf 100644 --- a/openfpga/src/base/openfpga_naming.cpp +++ b/openfpga/src/base/openfpga_naming.cpp @@ -708,7 +708,7 @@ std::string generate_formal_verification_sram_port_name(const CircuitLibrary& ci * TODO: This could be replaced as a constexpr string *********************************************************************/ std::string generate_configuration_chain_head_name() { - return std::string("ccff_head"); + return std::string(CONFIGURABLE_MEMORY_CHAIN_IN_NAME); } /********************************************************************* @@ -716,7 +716,7 @@ std::string generate_configuration_chain_head_name() { * TODO: This could be replaced as a constexpr string *********************************************************************/ std::string generate_configuration_chain_tail_name() { - return std::string("ccff_tail"); + return std::string(CONFIGURABLE_MEMORY_CHAIN_OUT_NAME); } /********************************************************************* @@ -732,7 +732,7 @@ std::string generate_configurable_memory_data_out_name() { * TODO: This could be replaced as a constexpr string *********************************************************************/ std::string generate_configurable_memory_inverted_data_out_name() { - return std::string("mem_outb"); + return std::string(CONFIGURABLE_MEMORY_INVERTED_DATA_OUT_NAME); } /********************************************************************* diff --git a/openfpga/src/fabric/build_memory_modules.cpp b/openfpga/src/fabric/build_memory_modules.cpp index 32d1c93b2..2d6595653 100644 --- a/openfpga/src/fabric/build_memory_modules.cpp +++ b/openfpga/src/fabric/build_memory_modules.cpp @@ -77,8 +77,6 @@ void add_module_input_nets_to_mem_modules(ModuleManager& module_manager, * j-th pin of output port of the i-th child module is wired to the j + i*W -th * pin of output port of the memory module, where W is the size of port * 3. It assumes fixed port name for output ports - * - * We cache the module nets that have been created because they will be used later ********************************************************************/ static std::vector add_module_output_nets_to_chain_mem_modules(ModuleManager& module_manager, @@ -165,15 +163,11 @@ void add_module_output_nets_to_mem_modules(ModuleManager& module_manager, * add_module_nets_cmos_memory_chain_config_bus() !!! *********************************************************************/ static -void add_module_nets_to_cmos_memory_chain_module(ModuleManager& module_manager, - const ModuleId& parent_module, - const std::vector& output_nets, - const CircuitLibrary& circuit_lib, - const CircuitPortId& model_input_port, - const CircuitPortId& model_output_port) { - /* Counter for the nets */ - size_t net_counter = 0; - +void add_module_nets_to_cmos_memory_config_chain_module(ModuleManager& module_manager, + const ModuleId& parent_module, + const CircuitLibrary& circuit_lib, + const CircuitPortId& model_input_port, + const CircuitPortId& model_output_port) { for (size_t mem_index = 0; mem_index < module_manager.configurable_children(parent_module).size(); ++mem_index) { ModuleId net_src_module_id; size_t net_src_instance_id; @@ -219,21 +213,9 @@ void add_module_nets_to_cmos_memory_chain_module(ModuleManager& module_manager, /* Create a net for each pin */ for (size_t pin_id = 0; pin_id < net_src_port.pins().size(); ++pin_id) { /* Create a net and add source and sink to it */ - ModuleNetId net; - if (0 == mem_index) { - net = module_manager.create_module_net(parent_module); - } else { - net = output_nets[net_counter]; - } - /* Add net source */ - module_manager.add_module_net_source(parent_module, net, net_src_module_id, net_src_instance_id, net_src_port_id, net_src_port.pins()[pin_id]); + ModuleNetId net = create_module_source_pin_net(module_manager, parent_module, net_src_module_id, net_src_instance_id, net_src_port_id, net_src_port.pins()[pin_id]); /* Add net sink */ module_manager.add_module_net_sink(parent_module, net, net_sink_module_id, net_sink_instance_id, net_sink_port_id, net_sink_port.pins()[pin_id]); - - /* Update net counter */ - if (0 < mem_index) { - net_counter++; - } } } @@ -263,17 +245,91 @@ void add_module_nets_to_cmos_memory_chain_module(ModuleManager& module_manager, /* Create a net for each pin */ for (size_t pin_id = 0; pin_id < net_src_port.pins().size(); ++pin_id) { /* Create a net and add source and sink to it */ - ModuleNetId net = output_nets[net_counter]; - /* Add net source */ - module_manager.add_module_net_source(parent_module, net, net_src_module_id, net_src_instance_id, net_src_port_id, net_src_port.pins()[pin_id]); + ModuleNetId net = create_module_source_pin_net(module_manager, parent_module, net_src_module_id, net_src_instance_id, net_src_port_id, net_src_port.pins()[pin_id]); /* Add net sink */ module_manager.add_module_net_sink(parent_module, net, net_sink_module_id, net_sink_instance_id, net_sink_port_id, net_sink_port.pins()[pin_id]); - - /* Update net counter */ - net_counter++; } +} - VTR_ASSERT(net_counter == output_nets.size()); +/******************************************************************** + * Connect the scan input of all the memory modules + * under the parent module in a chain + * + * +--------+ +--------+ +--------+ + * ccff_head --->| Memory |--->| Memory |--->... --->| Memory | + * | Module | | Module | | Module | + * | [0] | | [1] | | [N-1] | + * +--------+ +--------+ +--------+ + * For the 1st memory module: + * net source is the configuration chain head of the primitive module + * net sink is the scan input of the next memory module + * + * For the rest of memory modules: + * net source is the configuration chain tail of the previous memory module + * net sink is the scan input of the next memory module + * + * Note that: + * This function is designed for memory modules ONLY! + * Do not use it to replace the + * add_module_nets_cmos_memory_chain_config_bus() !!! + *********************************************************************/ +static +void add_module_nets_to_cmos_memory_scan_chain_module(ModuleManager& module_manager, + const ModuleId& parent_module, + const CircuitLibrary& circuit_lib, + const CircuitPortId& model_input_port, + const CircuitPortId& model_output_port) { + /* Last module does not need any scan-chain connection. So it is skipped */ + for (size_t mem_index = 0; mem_index < module_manager.configurable_children(parent_module).size() - 1; ++mem_index) { + ModuleId net_src_module_id; + size_t net_src_instance_id; + ModulePortId net_src_port_id; + + ModuleId net_sink_module_id; + size_t net_sink_instance_id; + ModulePortId net_sink_port_id; + + if (0 == mem_index) { + /* Find the port name of configuration chain head */ + std::string src_port_name = generate_configuration_chain_head_name(); + net_src_module_id = parent_module; + net_src_instance_id = 0; + net_src_port_id = module_manager.find_module_port(net_src_module_id, src_port_name); + + /* Find the port name of next memory module */ + std::string sink_port_name = circuit_lib.port_prefix(model_input_port); + net_sink_module_id = module_manager.configurable_children(parent_module)[mem_index]; + net_sink_instance_id = module_manager.configurable_child_instances(parent_module)[mem_index]; + net_sink_port_id = module_manager.find_module_port(net_sink_module_id, sink_port_name); + } else { + /* Find the port name of previous memory module */ + std::string src_port_name = circuit_lib.port_prefix(model_output_port); + net_src_module_id = module_manager.configurable_children(parent_module)[mem_index - 1]; + net_src_instance_id = module_manager.configurable_child_instances(parent_module)[mem_index - 1]; + net_src_port_id = module_manager.find_module_port(net_src_module_id, src_port_name); + + /* Find the port name of next memory module */ + std::string sink_port_name = circuit_lib.port_prefix(model_input_port); + net_sink_module_id = module_manager.configurable_children(parent_module)[mem_index]; + net_sink_instance_id = module_manager.configurable_child_instances(parent_module)[mem_index]; + net_sink_port_id = module_manager.find_module_port(net_sink_module_id, sink_port_name); + } + + /* Get the pin id for source port */ + BasicPort net_src_port = module_manager.module_port(net_src_module_id, net_src_port_id); + /* Get the pin id for sink port */ + BasicPort net_sink_port = module_manager.module_port(net_sink_module_id, net_sink_port_id); + /* Port sizes of source and sink should match */ + VTR_ASSERT(net_src_port.get_width() == net_sink_port.get_width()); + + /* Create a net for each pin */ + for (size_t pin_id = 0; pin_id < net_src_port.pins().size(); ++pin_id) { + /* Create a net and add source and sink to it */ + ModuleNetId net = create_module_source_pin_net(module_manager, parent_module, net_src_module_id, net_src_instance_id, net_src_port_id, net_src_port.pins()[pin_id]); + /* Add net sink */ + module_manager.add_module_net_sink(parent_module, net, net_sink_module_id, net_sink_instance_id, net_sink_port_id, net_sink_port.pins()[pin_id]); + } + } } /********************************************************************* @@ -382,7 +438,7 @@ void build_memory_flatten_module(ModuleManager& module_manager, * scan-chain--->| CCFF |--->| CCFF |--->... --->| CCFF |---->scan-chain * input&clock | [0] | | [1] | | [N-1] | output * +-------+ +-------+ +-------+ - * | | ... | + * | | ... | config-memory output * v v v * +-----------------------------------------+ * | Multiplexer Configuration port | @@ -397,12 +453,15 @@ void build_memory_chain_module(ModuleManager& module_manager, /* Get the input ports from the SRAM */ std::vector sram_input_ports = circuit_lib.model_ports_by_type(sram_model, CIRCUIT_MODEL_PORT_INPUT, true); - /* Should have only 1 input port */ - VTR_ASSERT( 1 == sram_input_ports.size() ); + /* Should have only 1 or 2 input port */ + VTR_ASSERT( (1 == sram_input_ports.size()) + || (2 == sram_input_ports.size()) ); /* Get the output ports from the SRAM */ std::vector sram_output_ports = circuit_lib.model_ports_by_type(sram_model, CIRCUIT_MODEL_PORT_OUTPUT, true); - /* Should have only 1 or 2 output port */ - VTR_ASSERT( (1 == sram_output_ports.size()) || ( 2 == sram_output_ports.size()) ); + /* Should have only 1 or 2 or 3 output port */ + VTR_ASSERT( (1 == sram_output_ports.size()) + || (2 == sram_output_ports.size()) + || (3 == sram_output_ports.size()) ); /* Create a module and add to the module manager */ ModuleId mem_module = module_manager.add_module(module_name); @@ -428,13 +487,27 @@ void build_memory_chain_module(ModuleManager& module_manager, circuit_lib.port_size(sram_output_ports[0])); module_manager.add_port(mem_module, chain_tail_port, ModuleManager::MODULE_OUTPUT_PORT); - /* Add each output port: port width should match the number of memories */ - for (size_t iport = 0; iport < sram_output_ports.size(); ++iport) { + /* There could be 3 conditions w.r.t. the number of output ports: + * - Only one output port is defined. In this case, the 1st port is the Q + * In such case, only Q will be considered as data output ports + * - Two output port is defined. In this case, the 1st port is the Q while the 2nd port is the QN + * In such case, both Q and QN will be considered as data output ports + * - Three output port is defined. + * In this case: + * - the 1st port is the Q (the chain output) + * - the 2nd port is the QN (the inverted data output) + * - the 3nd port is the configure-enabled Q + * In such case, configure-enabled Q and QN will be considered as data output ports + */ + size_t num_data_output_ports = sram_output_ports.size(); + if (3 == sram_output_ports.size()) { + num_data_output_ports = 2; + } + for (size_t iport = 0; iport < num_data_output_ports; ++iport) { std::string port_name; if (0 == iport) { port_name = generate_configurable_memory_data_out_name(); - } else { - VTR_ASSERT( 1 == iport); + } else if (1 == iport) { port_name = generate_configurable_memory_inverted_data_out_name(); } BasicPort output_port(port_name, num_mems); @@ -444,9 +517,6 @@ void build_memory_chain_module(ModuleManager& module_manager, /* Find the sram module in the module manager */ ModuleId sram_mem_module = module_manager.find_module(circuit_lib.model_name(sram_model)); - /* Cache the output nets for non-inverted data output */ - std::vector mem_output_nets; - /* Instanciate each submodule */ for (size_t i = 0; i < num_mems; ++i) { size_t sram_mem_instance = module_manager.num_instance(mem_module, sram_mem_module); @@ -454,7 +524,7 @@ void build_memory_chain_module(ModuleManager& module_manager, module_manager.add_configurable_child(mem_module, sram_mem_module, sram_mem_instance); /* Build module nets to wire outputs of sram modules to outputs of memory module */ - for (size_t iport = 0; iport < sram_output_ports.size(); ++iport) { + for (size_t iport = 0; iport < num_data_output_ports; ++iport) { std::string port_name; if (0 == iport) { port_name = generate_configurable_memory_data_out_name(); @@ -465,17 +535,18 @@ void build_memory_chain_module(ModuleManager& module_manager, std::vector output_nets = add_module_output_nets_to_chain_mem_modules(module_manager, mem_module, port_name, circuit_lib, sram_output_ports[iport], sram_mem_module, i, sram_mem_instance); - /* Cache only for regular data outputs */ - if (0 == iport) { - mem_output_nets.insert(mem_output_nets.end(), output_nets.begin(), output_nets.end()); - } } } /* Build module nets to wire the configuration chain */ - add_module_nets_to_cmos_memory_chain_module(module_manager, mem_module, mem_output_nets, - circuit_lib, sram_input_ports[0], sram_output_ports[0]); + add_module_nets_to_cmos_memory_config_chain_module(module_manager, mem_module, + circuit_lib, sram_input_ports[0], sram_output_ports[0]); + /* If there is a second input defined, + * add nets to short wire the 2nd inputs to the first inputs + */ + add_module_nets_to_cmos_memory_scan_chain_module(module_manager, mem_module, + circuit_lib, sram_input_ports[1], sram_output_ports[0]); /* Add global ports to the pb_module: * This is a much easier job after adding sub modules (instances), From c97a92d6288c6a5bc2528b8e1d541b34b2999efa Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 4 Jan 2021 17:15:50 -0700 Subject: [PATCH 05/13] [Arch] Patch openfpga architecture for ccff circuit model port requirement --- openfpga_flow/openfpga_arch/k4_N4_40nm_cc_cfgscff_openfpga.xml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/openfpga_flow/openfpga_arch/k4_N4_40nm_cc_cfgscff_openfpga.xml b/openfpga_flow/openfpga_arch/k4_N4_40nm_cc_cfgscff_openfpga.xml index 96ddf9beb..9d8fc8a39 100644 --- a/openfpga_flow/openfpga_arch/k4_N4_40nm_cc_cfgscff_openfpga.xml +++ b/openfpga_flow/openfpga_arch/k4_N4_40nm_cc_cfgscff_openfpga.xml @@ -147,9 +147,8 @@ - - + From 709ee1b8423bb45f2131f05af469bc6c7a18649a Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 4 Jan 2021 17:17:35 -0700 Subject: [PATCH 06/13] [HDL] Update dff netlist for SCFF used in configuration chain --- openfpga_flow/openfpga_cell_library/verilog/dff.v | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openfpga_flow/openfpga_cell_library/verilog/dff.v b/openfpga_flow/openfpga_cell_library/verilog/dff.v index 9838a6b6a..a0548c54c 100644 --- a/openfpga_flow/openfpga_cell_library/verilog/dff.v +++ b/openfpga_flow/openfpga_cell_library/verilog/dff.v @@ -414,12 +414,12 @@ module CFGSDFFR ( input SI, // Scan-chain input input CFGE, // Configure enable output Q, // Regular Q output - output QN, // Regular Qb output output CFGQ, // Data Q output which is released when configure enable is activated output CFGQN // Data Qb output which is released when configure enable is activated ); //------------Internal Variables-------- reg q_reg; +wire QN; //-------------Code Starts Here--------- always @ ( posedge CK or posedge RST) @@ -432,7 +432,7 @@ end else begin end assign CFGQ = CFGE ? Q : 1'b0; -assign CFGQN = CFGE ? QN : 1'b0; +assign CFGQN = CFGE ? QN : 1'b1; `ifndef ENABLE_FORMAL_VERIFICATION // Wire q_reg to Q From bfd305b5a535d0ec232bfd8f75726d3cf5c7d718 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 4 Jan 2021 17:22:30 -0700 Subject: [PATCH 07/13] [Tool] Patch the bug in finding data output ports for CCFF --- openfpga/src/fabric/build_memory_modules.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/openfpga/src/fabric/build_memory_modules.cpp b/openfpga/src/fabric/build_memory_modules.cpp index 2d6595653..28c0d2dcf 100644 --- a/openfpga/src/fabric/build_memory_modules.cpp +++ b/openfpga/src/fabric/build_memory_modules.cpp @@ -532,8 +532,17 @@ void build_memory_chain_module(ModuleManager& module_manager, VTR_ASSERT( 1 == iport); port_name = generate_configurable_memory_inverted_data_out_name(); } + /* Find the proper data output port + * The exception is when there are 3 output ports defined + * The 3rd port is the regular data output port to be used + */ + CircuitPortId data_output_port_to_connect = sram_output_ports[iport]; + if ((3 == sram_output_ports.size()) && (0 == iport)) { + data_output_port_to_connect = sram_output_ports.back(); + } + std::vector output_nets = add_module_output_nets_to_chain_mem_modules(module_manager, mem_module, - port_name, circuit_lib, sram_output_ports[iport], + port_name, circuit_lib, data_output_port_to_connect, sram_mem_module, i, sram_mem_instance); } } From 06af30ef104a5deca82a208f34814c2eb26d1ea0 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 4 Jan 2021 17:30:19 -0700 Subject: [PATCH 08/13] [Test] Add test case for the SCFF usage in configuration chain --- .../config/task.conf | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 openfpga_flow/tasks/basic_tests/full_testbench/configuration_chain_config_enable_scff/config/task.conf diff --git a/openfpga_flow/tasks/basic_tests/full_testbench/configuration_chain_config_enable_scff/config/task.conf b/openfpga_flow/tasks/basic_tests/full_testbench/configuration_chain_config_enable_scff/config/task.conf new file mode 100644 index 000000000..8fd922fd9 --- /dev/null +++ b/openfpga_flow/tasks/basic_tests/full_testbench/configuration_chain_config_enable_scff/config/task.conf @@ -0,0 +1,42 @@ +# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = +# Configuration file for running experiments +# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = +# timeout_each_job : FPGA Task script splits fpga flow into multiple jobs +# Each job execute fpga_flow script on combination of architecture & benchmark +# timeout_each_job is timeout for each job +# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = + +[GENERAL] +run_engine=openfpga_shell +power_tech_file = ${PATH:OPENFPGA_PATH}/openfpga_flow/tech/PTM_45nm/45nm.xml +power_analysis = true +spice_output=false +verilog_output=true +timeout_each_job = 1*60 +fpga_flow=yosys_vpr + +[OpenFPGA_SHELL] +openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/configuration_chain_example_script.openfpga +openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_N4_40nm_cc_cfgscff_openfpga.xml +openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml + +[ARCHITECTURES] +arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml + +[BENCHMARKS] +bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2/and2.v +bench1=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/or2/or2.v +bench2=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2_latch/and2_latch.v + +[SYNTHESIS_PARAM] +bench0_top = and2 +bench0_chan_width = 300 + +bench1_top = or2 +bench1_chan_width = 300 + +bench2_top = and2_latch +bench2_chan_width = 300 + +[SCRIPT_PARAM_MIN_ROUTE_CHAN_WIDTH] +end_flow_with_test= From a813c9016b793987e3525542828c4423600c8092 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 4 Jan 2021 17:39:13 -0700 Subject: [PATCH 09/13] [Arch] Patch the port name in openfpga arch to avoid conflicts with OpenFPGA's reserved words --- openfpga_flow/openfpga_arch/k4_N4_40nm_cc_cfgscff_openfpga.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openfpga_flow/openfpga_arch/k4_N4_40nm_cc_cfgscff_openfpga.xml b/openfpga_flow/openfpga_arch/k4_N4_40nm_cc_cfgscff_openfpga.xml index 9d8fc8a39..e0fd1f9bb 100644 --- a/openfpga_flow/openfpga_arch/k4_N4_40nm_cc_cfgscff_openfpga.xml +++ b/openfpga_flow/openfpga_arch/k4_N4_40nm_cc_cfgscff_openfpga.xml @@ -143,7 +143,7 @@ - + From 804b721a19f34d210018eb331122725e367f4fc6 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 4 Jan 2021 17:41:29 -0700 Subject: [PATCH 10/13] [Tool] Bug fix in the configuration chain connection builder --- openfpga/src/fabric/build_memory_modules.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/openfpga/src/fabric/build_memory_modules.cpp b/openfpga/src/fabric/build_memory_modules.cpp index 28c0d2dcf..447b49107 100644 --- a/openfpga/src/fabric/build_memory_modules.cpp +++ b/openfpga/src/fabric/build_memory_modules.cpp @@ -279,8 +279,7 @@ void add_module_nets_to_cmos_memory_scan_chain_module(ModuleManager& module_mana const CircuitLibrary& circuit_lib, const CircuitPortId& model_input_port, const CircuitPortId& model_output_port) { - /* Last module does not need any scan-chain connection. So it is skipped */ - for (size_t mem_index = 0; mem_index < module_manager.configurable_children(parent_module).size() - 1; ++mem_index) { + for (size_t mem_index = 0; mem_index < module_manager.configurable_children(parent_module).size(); ++mem_index) { ModuleId net_src_module_id; size_t net_src_instance_id; ModulePortId net_src_port_id; From 62eb6e24cb1b5ad4bf706277b24e3f7ae4c7c250 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 4 Jan 2021 17:42:49 -0700 Subject: [PATCH 11/13] [Test] Add SCFF configuration chain test case to CI --- .github/workflows/basic_reg_test.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/basic_reg_test.sh b/.github/workflows/basic_reg_test.sh index 3e67023e3..1dbf7705c 100755 --- a/.github/workflows/basic_reg_test.sh +++ b/.github/workflows/basic_reg_test.sh @@ -14,6 +14,7 @@ python3 openfpga_flow/scripts/run_fpga_task.py basic_tests/full_testbench/config python3 openfpga_flow/scripts/run_fpga_task.py basic_tests/full_testbench/configuration_chain_use_set --debug --show_thread_logs python3 openfpga_flow/scripts/run_fpga_task.py basic_tests/full_testbench/configuration_chain_use_setb --debug --show_thread_logs python3 openfpga_flow/scripts/run_fpga_task.py basic_tests/full_testbench/configuration_chain_use_set_reset --debug --show_thread_logs +python3 openfpga_flow/scripts/run_fpga_task.py basic_tests/full_testbench/configuration_chain_config_enable_scff --debug --show_thread_logs python3 openfpga_flow/scripts/run_fpga_task.py basic_tests/full_testbench/multi_region_configuration_chain --debug --show_thread_logs python3 openfpga_flow/scripts/run_fpga_task.py basic_tests/full_testbench/fast_configuration_chain --debug --show_thread_logs python3 openfpga_flow/scripts/run_fpga_task.py basic_tests/full_testbench/fast_configuration_chain_use_set --debug --show_thread_logs From 226f6b8d6d686eb0fed81c3af48b32744a549610 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 4 Jan 2021 18:30:04 -0700 Subject: [PATCH 12/13] [Doc] Update documentation about FF circuit models to show capability in modeling SCFFs --- .../arch_lang/circuit_model_examples.rst | 96 +++- .../manual/arch_lang/figures/config_chain.svg | 375 ++++++++++++++ .../figures/config_chain_config_enable.svg | 411 ++++++++++++++++ .../figures/config_chain_scan_capable.svg | 462 ++++++++++++++++++ docs/source/manual/arch_lang/figures/scff.png | Bin 25712 -> 0 bytes 5 files changed, 1336 insertions(+), 8 deletions(-) create mode 100644 docs/source/manual/arch_lang/figures/config_chain.svg create mode 100644 docs/source/manual/arch_lang/figures/config_chain_config_enable.svg create mode 100644 docs/source/manual/arch_lang/figures/config_chain_scan_capable.svg delete mode 100644 docs/source/manual/arch_lang/figures/scff.png diff --git a/docs/source/manual/arch_lang/circuit_model_examples.rst b/docs/source/manual/arch_lang/circuit_model_examples.rst index 48b7fdf2f..1f4c0c1d3 100644 --- a/docs/source/manual/arch_lang/circuit_model_examples.rst +++ b/docs/source/manual/arch_lang/circuit_model_examples.rst @@ -979,18 +979,18 @@ This example shows: .. _circuit_model_ccff_example: -Configuration-chain Flip-flop -````````````````````````````` +Regular Configuration-chain Flip-flop +````````````````````````````````````` -:numref:`fig_ccff` illustrates an example of scan-chain flop-flop used to build a configuration chain. +:numref:`fig_ccff_config_chain` illustrates an example of standard flip-flops used to build a configuration chain. -.. _fig_ccff: +.. _fig_ccff_config_chain: -.. figure:: ./figures/scff.png +.. figure:: ./figures/config_chain.svg :scale: 50% :alt: SCFF symbol - An example of a Scan-Chain Flip-Flop. + An example of a Flip-Flop organized in a chain. The code describing this FF is: @@ -999,14 +999,94 @@ The code describing this FF is: - - + + This example shows: - A configuration-chain flip-flop which is defined in a Verilog netlist ``ccff.v`` and a SPICE netlist ``ccff.sp`` - The flip-flop has a global clock port, ``CK``, which will be wired a global programming clock +.. note:: + The output ports of the configuration flip-flop must follow a fixed sequence in definition: + - The first output port **MUST** be the data output port, e.g., ``Q``. + - The second output port **MUST** be the **inverted** data output port, e.g., ``QN``. + +Configuration-chain Flip-flop with Configure Enable Signals +``````````````````````````````````````````````````````````` + +Configuration chain could be built with flip-flops with outputs that are enabled by specific signals. +Consider the example in :numref:`fig_ccff_config_chain_config_enable`, the flip-flop has + +- a configure enable signal ``CFG_EN`` to release the data output ``Q`` and ``QN`` +- a pair of data outputs ``Q`` and ``QN`` which are controlled by the configure enable signal ``CFG_EN`` +- a regular data output ``SCAN_Q`` which outputs registered data + +.. _fig_ccff_config_chain_config_enable: + +.. figure:: ./figures/config_chain_config_enable.svg + :scale: 50% + :alt: SCFF symbol + + An example of a Flip-Flop with config enable feature organized in a chain. + +The code describing this FF is: + +.. code-block:: xml + + + + + + + + + + +.. note:: + The output ports of the configuration flip-flop must follow a fixed sequence in definition: + - The first output port **MUST** be the regular data output port, e.g., ``SCAN_Q``. + - The second output port **MUST** be the **inverted** data output port which is activated by the configure enable signal, e.g., ``QN``. + - The second output port **MUST** be the data output port which is activated by the configure enable signal, e.g., ``Q``. + +Configuration-chain Flip-flop with Scan Input +````````````````````````````````````````````` + +Configuration chain could be built with flip-flops with a scan chain input . +Consider the example in :numref:`fig_ccff_config_chain_scan_capable`, the flip-flop has + +- an additional input ``SI`` to enable scan-chain capabaility +- a configure enable signal ``CFG_EN`` to release the data output ``Q`` and ``QN`` +- a pair of data outputs ``Q`` and ``QN`` which are controlled by the configure enable signal ``CFG_EN`` +- a regular data output ``SCAN_Q`` which outputs registered data + +.. _fig_ccff_config_chain_scan_capable: + +.. figure:: ./figures/config_chain_scan_capable.svg + :scale: 50% + :alt: SCFF symbol + + An example of a Flip-Flop with scan input organized in a chain. + +The code describing this FF is: + +.. code-block:: xml + + + + + + + + + + + +.. note:: + The input ports of the configuration flip-flop must follow a fixed sequence in definition: + - The first input port **MUST** be the regular data input port, e.g., ``D``. + - The second input port **MUST** be the scan input port, e.g., ``SI``. + Hard Logics ~~~~~~~~~~~ diff --git a/docs/source/manual/arch_lang/figures/config_chain.svg b/docs/source/manual/arch_lang/figures/config_chain.svg new file mode 100644 index 000000000..d7e1ad8fc --- /dev/null +++ b/docs/source/manual/arch_lang/figures/config_chain.svg @@ -0,0 +1,375 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Produced by OmniGraffle 7.18.1\n2021-01-05 01:03:21 +0000 + + regular + + Layer 1 + + + + + + CCFF + + + + + D + + + + + CLK + + + + + + + + + + + + + + + + + + + + + Q + + + + + QN + + + + + + + + + CCFF + + + + + D + + + + + CLK + + + + + + + + + + + + + + + + + + + + + Q + + + + + QN + + + + + + + + + CCFF + + + + + D + + + + + CLK + + + + + + + + + + + + + + + + + + + + + Q + + + + + QN + + + + + + + + + + + + + + + + + + + + + CLK + + + + + CCFF_HEAD + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + CCFF + + + + + D + + + + + CLK + + + + + + + + + + + + + + + + + + + + + Q + + + + + QN + + + + + + + + + + + + + + + + + + + + + CCFF_TAIL + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + mem_out[0] + + + + + mem_outb[0] + + + + + mem_out[1] + + + + + mem_outb[1] + + + + + mem_out[2] + + + + + mem_outb[2] + + + + + mem_out[n] + + + + + mem_outb[n] + + + + + + + + Configurable Circuits + + + + + Configuration Chain + + + + + diff --git a/docs/source/manual/arch_lang/figures/config_chain_config_enable.svg b/docs/source/manual/arch_lang/figures/config_chain_config_enable.svg new file mode 100644 index 000000000..4df2c7854 --- /dev/null +++ b/docs/source/manual/arch_lang/figures/config_chain_config_enable.svg @@ -0,0 +1,411 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Produced by OmniGraffle 7.18.1\n2021-01-05 01:03:21 +0000 + + config_enable + + Layer 1 + + + + + + + + + CLK + + + + + CCFF_HEAD + + + + + + + + + + + + + + + + + + + + + + CCFF_TAIL + + + + + + + + + + + mem_out[0] + + + + + mem_outb[0] + + + + + mem_out[1] + + + + + mem_outb[1] + + + + + mem_out[2] + + + + + mem_outb[2] + + + + + mem_out[n] + + + + + mem_outb[n] + + + + + + + + CCFF + + + + + D + + + + + CLK + + + + + + + + + + + + + + + + + + SCAN_Q + + + + + QN + + + + + Q + + + + + + + + + + + + + + + CCFF + + + + + D + + + + + CLK + + + + + + + + + + + + + + + + + + SCAN_Q + + + + + QN + + + + + Q + + + + + + + + + + + + CCFF + + + + + D + + + + + CLK + + + + + + + + + + + + + + + + + + SCAN_Q + + + + + QN + + + + + Q + + + + + + + + + + + + CCFF + + + + + D + + + + + CLK + + + + + + + + + + + + + + + + + + SCAN_Q + + + + + QN + + + + + Q + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + CONFIG_EN + + + + + CFG_EN + + + + + CONFIG_EN + + + + + + + + + + + CONFIG_EN + + + + + + + + CONFIG_EN + + + + + + + + + + + + + + + + + + + + + + + + + + Configurable Circuits + + + + + Configuration Chain + + + + + diff --git a/docs/source/manual/arch_lang/figures/config_chain_scan_capable.svg b/docs/source/manual/arch_lang/figures/config_chain_scan_capable.svg new file mode 100644 index 000000000..68a30d37d --- /dev/null +++ b/docs/source/manual/arch_lang/figures/config_chain_scan_capable.svg @@ -0,0 +1,462 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Produced by OmniGraffle 7.18.1\n2021-01-05 01:03:21 +0000 + + scan_capable + + Layer 1 + + + + + + + + + CLK + + + + + CCFF_HEAD + + + + + + + + + + + + + + + + + + + + + + CCFF_TAIL + + + + + + + + + + + mem_out[0] + + + + + mem_outb[0] + + + + + mem_out[1] + + + + + mem_outb[1] + + + + + mem_out[2] + + + + + mem_outb[2] + + + + + mem_out[n] + + + + + mem_outb[n] + + + + + + + + CCFF + + + + + D + + + + + CLK + + + + + + + + + + + + + + + + + + SCAN_Q + + + + + QN + + + + + Q + + + + + + + + + + + + + + + CCFF + + + + + D + + + + + CLK + + + + + + + + + + + + + + + + + + SCAN_Q + + + + + QN + + + + + Q + + + + + + + + + + + + CCFF + + + + + D + + + + + CLK + + + + + + + + + + + + + + + + + + SCAN_Q + + + + + QN + + + + + Q + + + + + + + + + + + + CCFF + + + + + D + + + + + CLK + + + + + + + + + + + + + + + + + + SCAN_Q + + + + + QN + + + + + Q + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + CONFIG_EN + + + + + CFG_EN + + + + + CONFIG_EN + + + + + + + + + + + CONFIG_EN + + + + + + + + CONFIG_EN + + + + + + + + + + + + + + + + + + + + + + + + + + Configurable Circuits + + + + + Configuration Chain + + + + + SI + + + + + + + + + + + + + + + SI + + + + + + + + + + + + SI + + + + + + + + + + + + SI + + + + + + + + + + + + diff --git a/docs/source/manual/arch_lang/figures/scff.png b/docs/source/manual/arch_lang/figures/scff.png deleted file mode 100644 index db0bf66dd8519c5bead8c0ae3047c14ad4902167..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25712 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 D9ZXw! From cde26597edb00b48fc16cd0d2c5d7bc1afb1f9ee Mon Sep 17 00:00:00 2001 From: tangxifan Date: Mon, 4 Jan 2021 18:45:47 -0700 Subject: [PATCH 13/13] [Tool] Bug fix in scan chain builder calling --- openfpga/src/fabric/build_memory_modules.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/openfpga/src/fabric/build_memory_modules.cpp b/openfpga/src/fabric/build_memory_modules.cpp index 447b49107..970ef8a95 100644 --- a/openfpga/src/fabric/build_memory_modules.cpp +++ b/openfpga/src/fabric/build_memory_modules.cpp @@ -553,8 +553,10 @@ void build_memory_chain_module(ModuleManager& module_manager, /* If there is a second input defined, * add nets to short wire the 2nd inputs to the first inputs */ - add_module_nets_to_cmos_memory_scan_chain_module(module_manager, mem_module, - circuit_lib, sram_input_ports[1], sram_output_ports[0]); + if (2 == sram_input_ports.size()) { + add_module_nets_to_cmos_memory_scan_chain_module(module_manager, mem_module, + circuit_lib, sram_input_ports[1], sram_output_ports[0]); + } /* Add global ports to the pb_module: * This is a much easier job after adding sub modules (instances),