diff --git a/libs/libarchopenfpga/src/config_protocol.cpp b/libs/libarchopenfpga/src/config_protocol.cpp index 2ab7974dc..fc3a43d95 100644 --- a/libs/libarchopenfpga/src/config_protocol.cpp +++ b/libs/libarchopenfpga/src/config_protocol.cpp @@ -28,7 +28,10 @@ int ConfigProtocol::num_regions() const { return num_regions_; } size_t ConfigProtocol::num_prog_clocks() const { VTR_ASSERT(type_ == CONFIG_MEM_SCAN_CHAIN); - return prog_clk_port_.get_width(); + if (prog_clk_port_.is_valid()) { + return prog_clk_port_.get_width(); + } + return 1; } openfpga::BasicPort ConfigProtocol::prog_clock_port_info() const { diff --git a/openfpga/src/fpga_verilog/verilog_testbench_utils.cpp b/openfpga/src/fpga_verilog/verilog_testbench_utils.cpp index 6e70f8008..8c91e50d0 100644 --- a/openfpga/src/fpga_verilog/verilog_testbench_utils.cpp +++ b/openfpga/src/fpga_verilog/verilog_testbench_utils.cpp @@ -619,7 +619,7 @@ void print_verilog_testbench_check( /* If there is a config done signal specified, consider it as a trigger on * checking */ if (!config_done_name.empty()) { - fp << "if (1'b1 == " << config_done_name << ") "; + fp << "\t\t\tif (1'b1 == " << config_done_name << ") "; } fp << "begin" << std::endl; diff --git a/openfpga/src/fpga_verilog/verilog_top_testbench.cpp b/openfpga/src/fpga_verilog/verilog_top_testbench.cpp index 720f166e3..47f5c69e4 100644 --- a/openfpga/src/fpga_verilog/verilog_top_testbench.cpp +++ b/openfpga/src/fpga_verilog/verilog_top_testbench.cpp @@ -44,6 +44,20 @@ static std::string generate_top_testbench_clock_name( return prefix + port_name; } +/******************************************************************** + * In most cases we should have only one programming clock and hence a config done signals + * But there is one exception: + * When there are more than 1 programming clocks defined in CCFF chains, the port width of + * config done port should be the same as the programming clocks + *******************************************************************/ +static size_t find_config_protocol_num_prog_clocks(const ConfigProtocol& config_protocol) { + size_t num_config_done_signals = 1; + if (config_protocol.type() == CONFIG_MEM_SCAN_CHAIN) { + num_config_done_signals = config_protocol.num_prog_clocks(); + } + return num_config_done_signals; +} + /******************************************************************** * Print local wires for flatten memory (standalone) configuration protocols *******************************************************************/ @@ -270,6 +284,7 @@ static void print_verilog_top_testbench_config_protocol_port( static void print_verilog_top_testbench_global_clock_ports_stimuli( std::fstream& fp, const ModuleManager& module_manager, const ModuleId& top_module, + const ConfigProtocol& config_protocol, const FabricGlobalPortInfo& fabric_global_port_info, const SimulationSetting& simulation_parameters) { /* Validate the file stream */ @@ -304,7 +319,8 @@ static void print_verilog_top_testbench_global_clock_ports_stimuli( if (true == fabric_global_port_info.global_port_is_prog(fabric_global_port)) { stimuli_clock_port.set_name(std::string(TOP_TB_PROG_CLOCK_PORT_NAME)); - stimuli_clock_port.set_width(1); + size_t num_prog_clocks = find_config_protocol_num_prog_clocks(config_protocol); + stimuli_clock_port.set_width(num_prog_clocks); } else { VTR_ASSERT_SAFE(false == fabric_global_port_info.global_port_is_prog( fabric_global_port)); @@ -332,9 +348,17 @@ static void print_verilog_top_testbench_global_clock_ports_stimuli( simulation_parameters.clock_name(sim_clock))); } } + /* For programming clocks, they are connected pin by pin. For example, + * prog_clock[0] <= __prog_clock__[0] + * prog_clock[1] <= __prog_clock__[1] + */ + BasicPort stimuli_clock_pin(stimuli_clock_port); + if (stimuli_clock_port.get_name() == std::string(TOP_TB_PROG_CLOCK_PORT_NAME)) { + stimuli_clock_pin.set_width(stimuli_clock_pin.pins()[pin], stimuli_clock_pin.pins()[pin]); + } print_verilog_wire_connection( - fp, global_port_to_connect, stimuli_clock_port, + fp, global_port_to_connect, stimuli_clock_pin, 1 == fabric_global_port_info.global_port_default_value( fabric_global_port)); } @@ -641,6 +665,7 @@ static void print_verilog_top_testbench_regular_global_ports_stimuli( static void print_verilog_top_testbench_global_ports_stimuli( std::fstream& fp, const ModuleManager& module_manager, const ModuleId& top_module, const PinConstraints& pin_constraints, + const ConfigProtocol& config_protocol, const FabricGlobalPortInfo& fabric_global_port_info, const SimulationSetting& simulation_parameters, const bool& active_global_prog_reset, const bool& active_global_prog_set) { @@ -653,7 +678,7 @@ static void print_verilog_top_testbench_global_ports_stimuli( "----- Begin connecting global ports of FPGA fabric to stimuli -----")); print_verilog_top_testbench_global_clock_ports_stimuli( - fp, module_manager, top_module, fabric_global_port_info, + fp, module_manager, top_module, config_protocol, fabric_global_port_info, simulation_parameters); print_verilog_top_testbench_global_shift_register_clock_ports_stimuli( @@ -838,12 +863,18 @@ static void print_verilog_top_testbench_ports( * and then wire them to the ports of FPGA fabric depending on their usage */ /* Configuration done port */ - BasicPort config_done_port(std::string(TOP_TB_CONFIG_DONE_PORT_NAME), 1); + size_t num_config_done_signals = find_config_protocol_num_prog_clocks(config_protocol); + BasicPort config_done_port(std::string(TOP_TB_CONFIG_DONE_PORT_NAME), num_config_done_signals); fp << generate_verilog_port(VERILOG_PORT_REG, config_done_port) << ";" << std::endl; - /* Programming clock */ - BasicPort prog_clock_port(std::string(TOP_TB_PROG_CLOCK_PORT_NAME), 1); + /* Configuration all done port: pull up when all the config done ports are pulled up */ + BasicPort config_all_done_port(std::string(TOP_TB_CONFIG_ALL_DONE_PORT_NAME), 1); + fp << generate_verilog_port(VERILOG_PORT_REG, config_all_done_port) << ";" + << std::endl; + + /* Programming clock: same rule applied as the configuration done ports */ + BasicPort prog_clock_port(std::string(TOP_TB_PROG_CLOCK_PORT_NAME), num_config_done_signals); fp << generate_verilog_port(VERILOG_PORT_WIRE, prog_clock_port) << ";" << std::endl; BasicPort prog_clock_register_port( @@ -946,6 +977,17 @@ static size_t calculate_num_config_clock_cycles( size_t regional_bitstream_max_size = find_fabric_regional_bitstream_max_size(fabric_bitstream); + /* For configuration chain that require multiple programming clocks. Need a different calculation */ + if (config_protocol.type() == CONFIG_MEM_SCAN_CHAIN) { + if (config_protocol.num_prog_clocks() > 1) { + regional_bitstream_max_size = 0; + for (BasicPort prog_clk_pin : config_protocol.prog_clock_pins()) { + std::vector ccff_head_indices = config_protocol.prog_clock_pin_ccff_head_indices(prog_clk_pin); + regional_bitstream_max_size += find_fabric_regional_bitstream_max_size(fabric_bitstream, ccff_head_indices); + } + } + } + size_t num_config_clock_cycles = 1 + regional_bitstream_max_size; /* Branch on the type of configuration protocol */ @@ -1120,7 +1162,9 @@ static void print_verilog_top_testbench_generic_stimulus( fp, std::string("----- Number of clock cycles in configuration phase: " + std::to_string(num_config_clock_cycles) + " -----")); - BasicPort config_done_port(std::string(TOP_TB_CONFIG_DONE_PORT_NAME), 1); + size_t num_config_done_signals = find_config_protocol_num_prog_clocks(config_protocol); + BasicPort config_done_port(std::string(TOP_TB_CONFIG_DONE_PORT_NAME), num_config_done_signals); + BasicPort config_all_done_port(std::string(TOP_TB_CONFIG_ALL_DONE_PORT_NAME), 1); BasicPort op_clock_port(std::string(TOP_TB_OP_CLOCK_PORT_NAME), 1); BasicPort op_clock_register_port( @@ -1128,7 +1172,7 @@ static void print_verilog_top_testbench_generic_stimulus( std::string(TOP_TB_CLOCK_REG_POSTFIX)), 1); - BasicPort prog_clock_port(std::string(TOP_TB_PROG_CLOCK_PORT_NAME), 1); + BasicPort prog_clock_port(std::string(TOP_TB_PROG_CLOCK_PORT_NAME), num_config_done_signals); BasicPort prog_clock_register_port( std::string(std::string(TOP_TB_PROG_CLOCK_PORT_NAME) + std::string(TOP_TB_CLOCK_REG_POSTFIX)), @@ -1168,18 +1212,41 @@ static void print_verilog_top_testbench_generic_stimulus( fp, std::string("----- Actual programming clock is triggered only when " + config_done_port.get_name() + " and " + prog_reset_port.get_name() + " are disabled -----")); - fp << "\tassign " - << generate_verilog_port(VERILOG_PORT_CONKT, prog_clock_port); - fp << " = " - << generate_verilog_port(VERILOG_PORT_CONKT, prog_clock_register_port); - fp << " & (~" << generate_verilog_port(VERILOG_PORT_CONKT, config_done_port) - << ")"; - fp << " & (~" << generate_verilog_port(VERILOG_PORT_CONKT, prog_reset_port) - << ")"; - fp << ";" << std::endl; + VTR_ASSERT(prog_clock_port.get_width() == config_done_port.get_width()); + for (size_t pin : prog_clock_port.pins()) { + BasicPort curr_clk_pin(prog_clock_port.get_name(), prog_clock_port.pins()[pin], prog_clock_port.pins()[pin]); + BasicPort curr_cfg_pin(config_done_port.get_name(), config_done_port.pins()[pin], config_done_port.pins()[pin]); + fp << "\tassign " + << generate_verilog_port(VERILOG_PORT_CONKT, curr_clk_pin); + fp << " = " + << generate_verilog_port(VERILOG_PORT_CONKT, prog_clock_register_port); + if (pin > 0) { + BasicPort prev_cfg_pin(config_done_port.get_name(), config_done_port.pins()[pin - 1], config_done_port.pins()[pin - 1]); + fp << " & (" << generate_verilog_port(VERILOG_PORT_CONKT, prev_cfg_pin) + << ")"; + } + fp << " & (~" << generate_verilog_port(VERILOG_PORT_CONKT, curr_cfg_pin) + << ")"; + fp << " & (~" << generate_verilog_port(VERILOG_PORT_CONKT, prog_reset_port) + << ")"; + fp << ";" << std::endl; + } fp << std::endl; + /* Config all done signal is triggered when all the config done signals are pulled up */ + fp << "\tassign " + << generate_verilog_port(VERILOG_PORT_CONKT, config_all_done_port); + << " = "; + for (size_t pin : config_done_port.pins()) { + BasicPort curr_cfg_pin(config_done_port.get_name(), config_done_port.pins()[pin], config_done_port.pins()[pin]); + if (pin > 1) { + fp << " & "; + } + fp << generate_verilog_port(VERILOG_PORT_CONKT, curr_cfg_pin); + } + fp << ";"; + /* Generate stimuli waveform for multiple user-defined operating clock signals */ for (const SimulationClockId& sim_clock : @@ -1210,12 +1277,12 @@ static void print_verilog_top_testbench_generic_stimulus( */ print_verilog_comment( fp, std::string("----- Actual operating clock is triggered only when " + - config_done_port.get_name() + " is enabled -----")); + config_all_done_port.get_name() + " is enabled -----")); fp << "\tassign " << generate_verilog_port(VERILOG_PORT_CONKT, sim_clock_port); fp << " = " << generate_verilog_port(VERILOG_PORT_CONKT, sim_clock_register_port); - fp << " & " << generate_verilog_port(VERILOG_PORT_CONKT, config_done_port); + fp << " & " << generate_verilog_port(VERILOG_PORT_CONKT, config_all_done_port); fp << ";" << std::endl; fp << std::endl; @@ -1231,16 +1298,16 @@ static void print_verilog_top_testbench_generic_stimulus( fp, "----- End raw operating clock signal generation -----"); /* Operation clock should be enabled after programming phase finishes. - * Before configuration is done (config_done is enabled), operation clock + * Before configuration is done (config_all_done is enabled), operation clock * should be always zero. */ print_verilog_comment( fp, std::string("----- Actual operating clock is triggered only when " + - config_done_port.get_name() + " is enabled -----")); + config_all_done_port.get_name() + " is enabled -----")); fp << "\tassign " << generate_verilog_port(VERILOG_PORT_CONKT, op_clock_port); fp << " = " << generate_verilog_port(VERILOG_PORT_CONKT, op_clock_register_port); - fp << " & " << generate_verilog_port(VERILOG_PORT_CONKT, config_done_port); + fp << " & " << generate_verilog_port(VERILOG_PORT_CONKT, config_all_done_port); fp << ";" << std::endl; fp << std::endl; @@ -1283,7 +1350,7 @@ static void print_verilog_top_testbench_generic_stimulus( "----- Reset signal is enabled until the first clock " "cycle in operation phase -----"); print_verilog_pulse_stimuli(fp, reset_port, 1, reset_pulse_widths, - reset_flip_values, config_done_port.get_name()); + reset_flip_values, config_all_done_port.get_name()); print_verilog_comment(fp, "----- End operating reset signal generation -----"); @@ -2276,7 +2343,7 @@ int print_verilog_full_testbench( /* Generate stimuli for global ports or connect them to existed signals */ print_verilog_top_testbench_global_ports_stimuli( - fp, module_manager, top_module, pin_constraints, global_ports, + fp, module_manager, top_module, pin_constraints, config_protocol, global_ports, simulation_parameters, active_global_prog_reset, active_global_prog_set); /* Instanciate FPGA top-level module */ @@ -2337,13 +2404,13 @@ int print_verilog_full_testbench( std::string(TOP_TESTBENCH_REFERENCE_OUTPUT_POSTFIX), std::string(TOP_TESTBENCH_FPGA_OUTPUT_POSTFIX), std::string(TOP_TESTBENCH_CHECKFLAG_PORT_POSTFIX), - std::string(TOP_TB_CONFIG_DONE_PORT_NAME), + std::string(TOP_TB_CONFIG_ALL_DONE_PORT_NAME), std::string(TOP_TESTBENCH_ERROR_COUNTER), atom_ctx, netlist_annotation, clock_port_names, std::string(TOP_TB_OP_CLOCK_PORT_NAME)); /* Add autocheck for configuration phase */ print_verilog_top_testbench_check(fp, - std::string(TOP_TB_CONFIG_DONE_PORT_NAME), + std::string(TOP_TB_CONFIG_ALL_DONE_PORT_NAME), std::string(TOP_TESTBENCH_ERROR_COUNTER)); } diff --git a/openfpga/src/fpga_verilog/verilog_top_testbench_constants.h b/openfpga/src/fpga_verilog/verilog_top_testbench_constants.h index 1d1640d0c..8cf93135c 100644 --- a/openfpga/src/fpga_verilog/verilog_top_testbench_constants.h +++ b/openfpga/src/fpga_verilog/verilog_top_testbench_constants.h @@ -23,6 +23,7 @@ constexpr const char* TOP_TB_SET_PORT_NAME = "__gset__"; constexpr const char* TOP_TB_PROG_RESET_PORT_NAME = "__prog_reset__"; constexpr const char* TOP_TB_PROG_SET_PORT_NAME = "__prog_set_"; constexpr const char* TOP_TB_CONFIG_DONE_PORT_NAME = "__config_done__"; +constexpr const char* TOP_TB_CONFIG_ALL_DONE_PORT_NAME = "__config_all_done__"; constexpr const char* TOP_TB_OP_CLOCK_PORT_NAME = "__op_clock__"; constexpr const char* TOP_TB_OP_CLOCK_PORT_PREFIX = "__operating_clk_"; constexpr const char* TOP_TB_PROG_CLOCK_PORT_NAME = "__prog_clock__"; diff --git a/openfpga/src/utils/fabric_bitstream_utils.cpp b/openfpga/src/utils/fabric_bitstream_utils.cpp index 00afc6e55..fdefac87e 100644 --- a/openfpga/src/utils/fabric_bitstream_utils.cpp +++ b/openfpga/src/utils/fabric_bitstream_utils.cpp @@ -22,12 +22,17 @@ namespace openfpga { /******************************************************************** * Find the longest bitstream size of a fabric bitstream + * Only care the region in whitelist. If the whitelist is empty, consider all the regions *******************************************************************/ size_t find_fabric_regional_bitstream_max_size( - const FabricBitstream& fabric_bitstream) { + const FabricBitstream& fabric_bitstream, + const std::vector& region_whitelist) { size_t regional_bitstream_max_size = 0; /* Find the longest regional bitstream */ for (const auto& region : fabric_bitstream.regions()) { + if (!region_whitelist.empty() && (std::find(region_whitelist.begin(), region_whitelist.end(), size_t(region)) != region_whitelist.end())) { + continue; + } if (regional_bitstream_max_size < fabric_bitstream.region_bits(region).size()) { regional_bitstream_max_size = fabric_bitstream.region_bits(region).size(); @@ -48,12 +53,16 @@ size_t find_fabric_regional_bitstream_max_size( *******************************************************************/ size_t find_configuration_chain_fabric_bitstream_size_to_be_skipped( const FabricBitstream& fabric_bitstream, + const std::vector& region_whitelist, const BitstreamManager& bitstream_manager, const bool& bit_value_to_skip) { size_t regional_bitstream_max_size = - find_fabric_regional_bitstream_max_size(fabric_bitstream); + find_fabric_regional_bitstream_max_size(fabric_bitstream, region_whitelist); size_t num_bits_to_skip = size_t(-1); for (const auto& region : fabric_bitstream.regions()) { + if (!region_whitelist.empty() && (std::find(region_whitelist.begin(), region_whitelist.end(), size_t(region)) != region_whitelist.end())) { + continue; + } size_t curr_region_num_bits_to_skip = 0; for (const FabricBitId& bit_id : fabric_bitstream.region_bits(region)) { if (bit_value_to_skip != diff --git a/openfpga/src/utils/fabric_bitstream_utils.h b/openfpga/src/utils/fabric_bitstream_utils.h index fe8b0b9e6..1fee62fad 100644 --- a/openfpga/src/utils/fabric_bitstream_utils.h +++ b/openfpga/src/utils/fabric_bitstream_utils.h @@ -25,10 +25,12 @@ namespace openfpga { size_t find_fabric_regional_bitstream_max_size( - const FabricBitstream& fabric_bitstream); + const FabricBitstream& fabric_bitstream, + const std::vector& region_whitelist = std::vector{}); size_t find_configuration_chain_fabric_bitstream_size_to_be_skipped( const FabricBitstream& fabric_bitstream, + const std::vector& region_whitelist = std::vector{}, const BitstreamManager& bitstream_manager, const bool& bit_value_to_skip); /* Alias to a specific organization of bitstreams for frame-based configuration