diff --git a/docs/source/manual/arch_lang/circuit_library.rst b/docs/source/manual/arch_lang/circuit_library.rst index c11a8c611..726d29c56 100644 --- a/docs/source/manual/arch_lang/circuit_library.rst +++ b/docs/source/manual/arch_lang/circuit_library.rst @@ -201,7 +201,7 @@ A circuit model may consist of a number of ports. The port list is mandatory in .. note:: Different types of ``circuit_model`` have different XML syntax, with which users can highly customize their circuit topologies. See refer to examples of :ref:``circuit_model_example`` for more details. -.. note:: Note that we have a list of reserved port names, which indicate the usage of these ports when building FPGA fabrics. Please do not use ``mem_out``, ``mem_inv``, ``bl``, ``wl``, ``blb``, ``wlb``, ``ccff_head`` and ``ccff_tail``. +.. note:: Note that we have a list of reserved port names, which indicate the usage of these ports when building FPGA fabrics. Please do not use ``mem_out``, ``mem_inv``, ``bl``, ``wl``, ``blb``, ``wlb``, ``wlr``, ``ccff_head`` and ``ccff_tail``. FPGA I/O Port ^^^^^^^^^^^^^ diff --git a/docs/source/manual/arch_lang/circuit_model_examples.rst b/docs/source/manual/arch_lang/circuit_model_examples.rst index 7113be1c0..437cea837 100644 --- a/docs/source/manual/arch_lang/circuit_model_examples.rst +++ b/docs/source/manual/arch_lang/circuit_model_examples.rst @@ -333,6 +333,36 @@ The following XML codes describes the SRAM cell shown in :numref:`fig_sram_blwl` .. note:: When the ``memory_bank`` type of configuration procotol is specified, SRAM modules should have a BL and a WL. +.. _circuit_model_sram_blwlr_example: + +SRAM with BL/WL/WLR +``````````````````` +.. _fig_sram_blwlr: + +.. figure:: ./figures/sram_blwlr.svg + :scale: 100% + + An example of a SRAM with Bit-Line (BL), Word-Line (WL) and WL read control signals + +The following XML codes describes the SRAM cell shown in :numref:`fig_sram_blwlr`. + +.. code-block:: xml + + + + + + + + + + + + +.. note:: OpenFPGA always assume that a ``WL`` port should be the write enable signal, a ``WLR`` port should be the read enable signal, while a ``BL`` port is the data input. + +.. note:: When the ``memory_bank`` type of configuration procotol is specified, SRAM modules should have a BL and a WL. WLR is optional + .. _circuit_model_config_latch_example: Configurable Latch diff --git a/docs/source/manual/arch_lang/figures/sram_blwlr.svg b/docs/source/manual/arch_lang/figures/sram_blwlr.svg new file mode 100644 index 000000000..838dd54ed --- /dev/null +++ b/docs/source/manual/arch_lang/figures/sram_blwlr.svg @@ -0,0 +1,213 @@ + + + + + + + + + + + + + + + + + + + + + Produced by OmniGraffle 7.18.5\n2021-09-21 03:12:41 +0000 + + Canvas 1 + + Layer 1 + + + + + + WL + + + + + BL + + + + + + + + + + + + + + + + + SRAM + + + + + out + + + + + outb + + + + + BL + + + + + WL + + + + + + + + + + + + + + + + + + + + + WLR + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + outb + + + + + + + + + + + out + + + + + + + + + + + + + + + + + + + WLR + + + + + GND + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libopenfpga/libarchopenfpga/src/circuit_types.h b/libopenfpga/libarchopenfpga/src/circuit_types.h index 3a2604a42..12b881db0 100644 --- a/libopenfpga/libarchopenfpga/src/circuit_types.h +++ b/libopenfpga/libarchopenfpga/src/circuit_types.h @@ -101,10 +101,11 @@ enum e_circuit_model_port_type { CIRCUIT_MODEL_PORT_BLB, CIRCUIT_MODEL_PORT_WL, CIRCUIT_MODEL_PORT_WLB, + CIRCUIT_MODEL_PORT_WLR, NUM_CIRCUIT_MODEL_PORT_TYPES }; /* Strings correspond to each port type */ -constexpr std::array CIRCUIT_MODEL_PORT_TYPE_STRING = {{"input", "output", "inout", "clock", "sram", "bl", "blb", "wl", "wlb"}}; +constexpr std::array CIRCUIT_MODEL_PORT_TYPE_STRING = {{"input", "output", "inout", "clock", "sram", "bl", "blb", "wl", "wlb", "wlr"}}; enum e_circuit_model_delay_type { CIRCUIT_MODEL_DELAY_RISE, diff --git a/libopenfpga/libopenfpgautil/src/openfpga_reserved_words.h b/libopenfpga/libopenfpgautil/src/openfpga_reserved_words.h index 732247178..af151a7a5 100644 --- a/libopenfpga/libopenfpgautil/src/openfpga_reserved_words.h +++ b/libopenfpga/libopenfpgautil/src/openfpga_reserved_words.h @@ -33,6 +33,7 @@ constexpr char* CONNECTION_BLOCK_MEM_INSTANCE_PREFIX = "mem_"; constexpr char* MEMORY_MODULE_POSTFIX = "_mem"; constexpr char* MEMORY_BL_PORT_NAME = "bl"; constexpr char* MEMORY_WL_PORT_NAME = "wl"; +constexpr char* MEMORY_WLR_PORT_NAME = "wlr"; /* Multiplexer naming constant strings */ constexpr char* MUX_BASIS_MODULE_POSTFIX = "_basis"; @@ -48,6 +49,8 @@ constexpr char* DECODER_DATA_OUT_PORT_NAME = "data_out"; constexpr char* DECODER_DATA_OUT_INV_PORT_NAME = "data_out_inv"; constexpr char* DECODER_BL_ADDRESS_PORT_NAME = "bl_address"; constexpr char* DECODER_WL_ADDRESS_PORT_NAME = "wl_address"; +constexpr char* DECODER_READBACK_PORT_NAME = "readback"; +constexpr char* DECODER_DATA_READ_ENABLE_PORT_NAME = "data_out_ren"; /* Inverted port naming */ constexpr char* INV_PORT_POSTFIX = "_inv"; diff --git a/openfpga/src/base/openfpga_naming.cpp b/openfpga/src/base/openfpga_naming.cpp index c898066b3..3dab1ca7f 100644 --- a/openfpga/src/base/openfpga_naming.cpp +++ b/openfpga/src/base/openfpga_naming.cpp @@ -719,9 +719,11 @@ std::string generate_sram_port_name(const e_config_protocol_type& sram_orgz_type */ if (CIRCUIT_MODEL_PORT_BL == port_type) { port_name = std::string(MEMORY_BL_PORT_NAME); - } else { - VTR_ASSERT( CIRCUIT_MODEL_PORT_WL == port_type ); + } else if (CIRCUIT_MODEL_PORT_WL == port_type) { port_name = std::string(MEMORY_WL_PORT_NAME); + } else { + VTR_ASSERT( CIRCUIT_MODEL_PORT_WLR == port_type ); + port_name = std::string(MEMORY_WLR_PORT_NAME); } break; case CONFIG_MEM_FRAME_BASED: diff --git a/openfpga/src/fabric/build_decoder_modules.cpp b/openfpga/src/fabric/build_decoder_modules.cpp index 949058ba8..85ed342aa 100644 --- a/openfpga/src/fabric/build_decoder_modules.cpp +++ b/openfpga/src/fabric/build_decoder_modules.cpp @@ -184,6 +184,19 @@ ModuleId build_wl_memory_decoder_module(ModuleManager& module_manager, module_manager.add_port(module_id, data_inv_port, ModuleManager::MODULE_OUTPUT_PORT); } + /* Add readback port */ + if (true == decoder_lib.use_readback(decoder)) { + BasicPort readback_port(std::string(DECODER_READBACK_PORT_NAME), 1); + module_manager.add_port(module_id, readback_port, ModuleManager::MODULE_INPUT_PORT); + } + + /* Add data read-enable port */ + if (true == decoder_lib.use_readback(decoder)) { + BasicPort data_ren_port(std::string(DECODER_DATA_READ_ENABLE_PORT_NAME), data_size); + module_manager.add_port(module_id, data_ren_port, ModuleManager::MODULE_OUTPUT_PORT); + module_manager.set_port_is_register(module_id, data_ren_port.get_name(), true); + } + return module_id; } diff --git a/openfpga/src/fabric/build_memory_modules.cpp b/openfpga/src/fabric/build_memory_modules.cpp index 8a354da75..a8f7fceb4 100644 --- a/openfpga/src/fabric/build_memory_modules.cpp +++ b/openfpga/src/fabric/build_memory_modules.cpp @@ -366,12 +366,15 @@ void build_memory_flatten_module(ModuleManager& module_manager, /* Get the BL/WL ports from the SRAM */ std::vector sram_bl_ports = circuit_lib.model_ports_by_type(sram_model, CIRCUIT_MODEL_PORT_BL, true); std::vector sram_wl_ports = circuit_lib.model_ports_by_type(sram_model, CIRCUIT_MODEL_PORT_WL, true); + /* Optional: Get the WLR ports from the SRAM */ + std::vector sram_wlr_ports = circuit_lib.model_ports_by_type(sram_model, CIRCUIT_MODEL_PORT_WLR, true); /* 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); - /* Ensure that we have only 1 BL, 1 WL and 2 output ports*/ + /* Ensure that we have only 1 BL, 1 WL and 2 output ports, as well as an optional WLR*/ VTR_ASSERT(1 == sram_bl_ports.size()); VTR_ASSERT(1 == sram_wl_ports.size()); + VTR_ASSERT(2 > sram_wlr_ports.size()); VTR_ASSERT(2 == sram_output_ports.size()); /* Create a module and add to the module manager */ @@ -389,6 +392,12 @@ void build_memory_flatten_module(ModuleManager& module_manager, BasicPort wl_port(std::string(MEMORY_WL_PORT_NAME), num_mems); ModulePortId mem_wl_port = module_manager.add_port(mem_module, wl_port, ModuleManager::MODULE_INPUT_PORT); + BasicPort wlr_port(std::string(MEMORY_WLR_PORT_NAME), num_mems); + ModulePortId mem_wlr_port = ModulePortId::INVALID(); + if (!sram_wlr_ports.empty()) { + mem_wlr_port = module_manager.add_port(mem_module, wlr_port, ModuleManager::MODULE_INPUT_PORT); + } + /* Add each output port: port width should match the number of memories */ for (size_t iport = 0; iport < sram_output_ports.size(); ++iport) { std::string port_name; @@ -419,6 +428,9 @@ void build_memory_flatten_module(ModuleManager& module_manager, for (const CircuitPortId& port : sram_wl_ports) { add_module_input_nets_to_mem_modules(module_manager, mem_module, mem_wl_port, circuit_lib, port, sram_mem_module, i, sram_mem_instance); } + for (const CircuitPortId& port : sram_wlr_ports) { + add_module_input_nets_to_mem_modules(module_manager, mem_module, mem_wlr_port, circuit_lib, port, sram_mem_module, i, sram_mem_instance); + } /* Wire outputs of child module to outputs of parent module */ add_module_output_nets_to_mem_modules(module_manager, mem_module, circuit_lib, sram_output_ports, sram_mem_module, i, sram_mem_instance); } @@ -644,9 +656,9 @@ void build_frame_memory_module(ModuleManager& module_manager, * If we find one, we use the module. * Otherwise, we create one and add it to the decoder library */ - DecoderId decoder_id = frame_decoder_lib.find_decoder(addr_size, data_size, true, false, use_data_inv); + DecoderId decoder_id = frame_decoder_lib.find_decoder(addr_size, data_size, true, false, use_data_inv, false); if (DecoderId::INVALID() == decoder_id) { - decoder_id = frame_decoder_lib.add_decoder(addr_size, data_size, true, false, use_data_inv); + decoder_id = frame_decoder_lib.add_decoder(addr_size, data_size, true, false, use_data_inv, false); } VTR_ASSERT(DecoderId::INVALID() != decoder_id); diff --git a/openfpga/src/fabric/build_top_module_memory.cpp b/openfpga/src/fabric/build_top_module_memory.cpp index bcc093062..c9ee2b722 100644 --- a/openfpga/src/fabric/build_top_module_memory.cpp +++ b/openfpga/src/fabric/build_top_module_memory.cpp @@ -870,6 +870,12 @@ void add_top_module_sram_ports(ModuleManager& module_manager, BasicPort wl_addr_port(std::string(DECODER_WL_ADDRESS_PORT_NAME), wl_addr_size); module_manager.add_port(module_id, wl_addr_port, ModuleManager::MODULE_INPUT_PORT); + /* Optional: If we have WLR port, we should add a read-back port */ + if (!circuit_lib.model_ports_by_type(sram_model, CIRCUIT_MODEL_PORT_WLR).empty()) { + BasicPort readback_port(std::string(DECODER_READBACK_PORT_NAME), config_protocol.num_regions()); + module_manager.add_port(module_id, readback_port, ModuleManager::MODULE_INPUT_PORT); + } + /* Data input should be dependent on the number of configuration regions*/ BasicPort din_port(std::string(DECODER_DATA_IN_PORT_NAME), config_protocol.num_regions()); module_manager.add_port(module_id, din_port, ModuleManager::MODULE_INPUT_PORT); @@ -1056,9 +1062,9 @@ void add_top_module_nets_cmos_memory_bank_config_bus(ModuleManager& module_manag * Otherwise, we create one and add it to the decoder library */ DecoderId bl_decoder_id = decoder_lib.find_decoder(bl_addr_size, num_bls, - true, true, false); + true, true, false, false); if (DecoderId::INVALID() == bl_decoder_id) { - bl_decoder_id = decoder_lib.add_decoder(bl_addr_size, num_bls, true, true, false); + bl_decoder_id = decoder_lib.add_decoder(bl_addr_size, num_bls, true, true, false, false); } VTR_ASSERT(DecoderId::INVALID() != bl_decoder_id); @@ -1084,9 +1090,9 @@ void add_top_module_nets_cmos_memory_bank_config_bus(ModuleManager& module_manag * Otherwise, we create one and add it to the decoder library */ DecoderId wl_decoder_id = decoder_lib.find_decoder(wl_addr_size, num_wls, - true, false, false); + true, false, false, false); if (DecoderId::INVALID() == wl_decoder_id) { - wl_decoder_id = decoder_lib.add_decoder(wl_addr_size, num_wls, true, false, false); + wl_decoder_id = decoder_lib.add_decoder(wl_addr_size, num_wls, true, false, false, false); } VTR_ASSERT(DecoderId::INVALID() != wl_decoder_id); @@ -1533,9 +1539,9 @@ void add_top_module_nets_cmos_memory_frame_decoder_config_bus(ModuleManager& mod /* Search the decoder library and try to find one * If not found, create a new module and add it to the module manager */ - DecoderId decoder_id = decoder_lib.find_decoder(addr_size, data_size, true, false, false); + DecoderId decoder_id = decoder_lib.find_decoder(addr_size, data_size, true, false, false, false); if (DecoderId::INVALID() == decoder_id) { - decoder_id = decoder_lib.add_decoder(addr_size, data_size, true, false, false); + decoder_id = decoder_lib.add_decoder(addr_size, data_size, true, false, false, false); } VTR_ASSERT(DecoderId::INVALID() != decoder_id); diff --git a/openfpga/src/fabric/build_top_module_memory_bank.cpp b/openfpga/src/fabric/build_top_module_memory_bank.cpp index c1bfac4b2..7c60a03d8 100644 --- a/openfpga/src/fabric/build_top_module_memory_bank.cpp +++ b/openfpga/src/fabric/build_top_module_memory_bank.cpp @@ -137,6 +137,16 @@ void add_top_module_nets_cmos_ql_memory_bank_config_bus(ModuleManager& module_ma /* Data in port should match the number of configuration regions */ VTR_ASSERT(din_port_info.get_width() == module_manager.regions(top_module).size()); + /* Find readback port from the top-level module */ + ModulePortId readback_port = module_manager.find_module_port(top_module, std::string(DECODER_READBACK_PORT_NAME)); + BasicPort readback_port_info; + + /* Readback port if available, should be a 1-bit port */ + if (readback_port) { + readback_port_info = module_manager.module_port(top_module, readback_port); + VTR_ASSERT(readback_port_info.get_width() == 1); + } + /* Find BL and WL address port from the top-level module */ ModulePortId bl_addr_port = module_manager.find_module_port(top_module, std::string(DECODER_BL_ADDRESS_PORT_NAME)); BasicPort bl_addr_port_info = module_manager.module_port(top_module, bl_addr_port); @@ -168,9 +178,9 @@ void add_top_module_nets_cmos_ql_memory_bank_config_bus(ModuleManager& module_ma * Otherwise, we create one and add it to the decoder library */ DecoderId bl_decoder_id = decoder_lib.find_decoder(bl_addr_size, num_bls, - true, true, false); + true, true, false, false); if (DecoderId::INVALID() == bl_decoder_id) { - bl_decoder_id = decoder_lib.add_decoder(bl_addr_size, num_bls, true, true, false); + bl_decoder_id = decoder_lib.add_decoder(bl_addr_size, num_bls, true, true, false, false); } VTR_ASSERT(DecoderId::INVALID() != bl_decoder_id); @@ -196,9 +206,9 @@ void add_top_module_nets_cmos_ql_memory_bank_config_bus(ModuleManager& module_ma * Otherwise, we create one and add it to the decoder library */ DecoderId wl_decoder_id = decoder_lib.find_decoder(wl_addr_size, num_wls, - true, false, false); + true, false, false, readback_port != ModulePortId::INVALID()); if (DecoderId::INVALID() == wl_decoder_id) { - wl_decoder_id = decoder_lib.add_decoder(wl_addr_size, num_wls, true, false, false); + wl_decoder_id = decoder_lib.add_decoder(wl_addr_size, num_wls, true, false, false, readback_port != ModulePortId::INVALID()); } VTR_ASSERT(DecoderId::INVALID() != wl_decoder_id); @@ -264,7 +274,13 @@ void add_top_module_nets_cmos_ql_memory_bank_config_bus(ModuleManager& module_ma BasicPort wl_decoder_en_port_info = module_manager.module_port(wl_decoder_module, wl_decoder_en_port); ModulePortId wl_decoder_addr_port = module_manager.find_module_port(wl_decoder_module, std::string(DECODER_ADDRESS_PORT_NAME)); - BasicPort wl_decoder_addr_port_info = module_manager.module_port(wl_decoder_module, bl_decoder_addr_port); + BasicPort wl_decoder_addr_port_info = module_manager.module_port(wl_decoder_module, wl_decoder_addr_port); + + ModulePortId wl_decoder_readback_port = module_manager.find_module_port(wl_decoder_module, std::string(DECODER_READBACK_PORT_NAME)); + BasicPort wl_decoder_readback_port_info; + if (wl_decoder_readback_port) { + wl_decoder_readback_port_info = module_manager.module_port(wl_decoder_module, wl_decoder_readback_port); + } /* Top module Enable port -> WL Decoder Enable port */ add_module_bus_nets(module_manager, @@ -278,6 +294,14 @@ void add_top_module_nets_cmos_ql_memory_bank_config_bus(ModuleManager& module_ma top_module, 0, wl_addr_port, wl_decoder_module, curr_wl_decoder_instance_id, wl_decoder_addr_port); + /* Top module readback port -> WL Decoder readback port */ + if (wl_decoder_readback_port) { + add_module_bus_nets(module_manager, + top_module, + top_module, 0, readback_port, + wl_decoder_module, curr_wl_decoder_instance_id, wl_decoder_readback_port); + } + /************************************************************** * Precompute the BLs and WLs distribution across the FPGA fabric * The distribution is a matrix which contains the starting index of BL/WL for each column or row @@ -391,6 +415,45 @@ void add_top_module_nets_cmos_ql_memory_bank_config_bus(ModuleManager& module_ma } } + /************************************************************** + * Optional: Add nets from WLR data out to each configurable child + */ + ModulePortId wl_decoder_data_ren_port = module_manager.find_module_port(wl_decoder_module, std::string(DECODER_DATA_READ_ENABLE_PORT_NAME)); + BasicPort wl_decoder_data_ren_port_info; + if (wl_decoder_data_ren_port) { + wl_decoder_data_ren_port_info = module_manager.module_port(wl_decoder_module, wl_decoder_data_ren_port); + for (size_t child_id = 0; child_id < module_manager.region_configurable_children(top_module, config_region).size(); ++child_id) { + ModuleId child_module = module_manager.region_configurable_children(top_module, config_region)[child_id]; + vtr::Point coord = module_manager.region_configurable_child_coordinates(top_module, config_region)[child_id]; + + size_t child_instance = module_manager.region_configurable_child_instances(top_module, config_region)[child_id]; + + /* Find the WL port */ + ModulePortId child_wlr_port = module_manager.find_module_port(child_module, std::string(MEMORY_WLR_PORT_NAME)); + BasicPort child_wlr_port_info = module_manager.module_port(child_module, child_wlr_port); + + size_t cur_wlr_index = 0; + + for (const size_t& sink_wlr_pin : child_wlr_port_info.pins()) { + size_t wlr_pin_id = wl_start_index_per_tile[coord.y()] + cur_wlr_index; + VTR_ASSERT(wlr_pin_id < wl_decoder_data_ren_port_info.pins().size()); + + /* Create net */ + ModuleNetId net = create_module_source_pin_net(module_manager, top_module, + wl_decoder_module, curr_wl_decoder_instance_id, + wl_decoder_data_ren_port, + wl_decoder_data_ren_port_info.pins()[wlr_pin_id]); + VTR_ASSERT(ModuleNetId::INVALID() != net); + + /* Add net sink */ + module_manager.add_module_net_sink(top_module, net, + child_module, child_instance, child_wlr_port, sink_wlr_pin); + + cur_wlr_index++; + } + } + } + /************************************************************** * Add the BL and WL decoders to the end of configurable children list * Note: this MUST be done after adding all the module nets to other regular configurable children diff --git a/openfpga/src/fpga_verilog/verilog_decoders.cpp b/openfpga/src/fpga_verilog/verilog_decoders.cpp index 6960ba19e..9b6f4da45 100644 --- a/openfpga/src/fpga_verilog/verilog_decoders.cpp +++ b/openfpga/src/fpga_verilog/verilog_decoders.cpp @@ -289,6 +289,20 @@ void print_verilog_arch_decoder_module(std::fstream& fp, data_inv_port = module_manager.module_port(module_id, data_inv_port_id); } + /* Find readback port */ + ModulePortId readback_port_id = module_manager.find_module_port(module_id, std::string(DECODER_READBACK_PORT_NAME)); + BasicPort readback_port; + if (readback_port_id) { + readback_port = module_manager.module_port(module_id, readback_port_id); + } + + /* Find data read-enable port */ + ModulePortId data_ren_port_id = module_manager.find_module_port(module_id, std::string(DECODER_DATA_READ_ENABLE_PORT_NAME)); + BasicPort data_ren_port; + if (data_ren_port_id) { + data_ren_port = module_manager.module_port(module_id, data_ren_port_id); + } + /* dump module definition + ports */ print_verilog_module_declaration(fp, module_manager, module_id, default_net_type); /* Finish dumping ports */ @@ -303,10 +317,18 @@ void print_verilog_arch_decoder_module(std::fstream& fp, * else data_out is driven by '0' */ if (1 == data_size) { + /* Output logics for data output */ fp << "always@(" << generate_verilog_port(VERILOG_PORT_CONKT, addr_port); fp << " or " << generate_verilog_port(VERILOG_PORT_CONKT, enable_port); + /* If there is a readback port, the data output is only enabled when readback is disabled */ + if (readback_port_id) { + fp << " or " << "~" << generate_verilog_port(VERILOG_PORT_CONKT, readback_port); + } fp << ") begin" << std::endl; fp << "\tif ((" << generate_verilog_port(VERILOG_PORT_CONKT, enable_port) << " == 1'b1) && ("; + if (readback_port_id) { + fp << generate_verilog_port(VERILOG_PORT_CONKT, readback_port) << " == 1'b0) && ("; + } fp << generate_verilog_port(VERILOG_PORT_CONKT, addr_port) << " == 1'b0))"; fp << " begin" << std::endl; fp << "\t\t" << generate_verilog_port_constant_values(data_port, std::vector(1, 1)) << ";" << std::endl; @@ -315,6 +337,26 @@ void print_verilog_arch_decoder_module(std::fstream& fp, fp << "\t" << "end" << std::endl; fp << "end" << std::endl; + /* Output logics for data readback output */ + if (data_ren_port_id) { + fp << "always@(" << generate_verilog_port(VERILOG_PORT_CONKT, addr_port); + fp << " or " << generate_verilog_port(VERILOG_PORT_CONKT, enable_port); + /* If there is a readback port, the data output is only enabled when readback is disabled */ + if (readback_port_id) { + fp << " or " << generate_verilog_port(VERILOG_PORT_CONKT, readback_port); + } + fp << ") begin" << std::endl; + fp << "\tif ((" << generate_verilog_port(VERILOG_PORT_CONKT, enable_port) << " == 1'b1) && ("; + fp << generate_verilog_port(VERILOG_PORT_CONKT, readback_port) << " == 1'b1) && ("; + fp << generate_verilog_port(VERILOG_PORT_CONKT, addr_port) << " == 1'b0))"; + fp << " begin" << std::endl; + fp << "\t\t" << generate_verilog_port_constant_values(data_ren_port, std::vector(1, 1)) << ";" << std::endl; + fp << "\t" << "end else begin" << std::endl; + fp << "\t\t" << generate_verilog_port_constant_values(data_ren_port, std::vector(1, 0)) << ";" << std::endl; + fp << "\t" << "end" << std::endl; + fp << "end" << std::endl; + } + /* 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); @@ -344,10 +386,23 @@ void print_verilog_arch_decoder_module(std::fstream& fp, * The rest of addr codes 3'b110, 3'b111 will be decoded to data=8'b0_0000; */ + /* Output logics for data output */ fp << "always@(" << generate_verilog_port(VERILOG_PORT_CONKT, addr_port); + /* If there is a readback port, the data output is only enabled when readback is disabled */ + if (readback_port_id) { + fp << " or " << "~" << generate_verilog_port(VERILOG_PORT_CONKT, readback_port); + } fp << " or " << generate_verilog_port(VERILOG_PORT_CONKT, enable_port); fp << ") begin" << std::endl; - fp << "\tif (" << generate_verilog_port(VERILOG_PORT_CONKT, enable_port) << " == 1'b1) begin" << std::endl; + if (readback_port_id) { + fp << "\tif ("; + fp << "(" << generate_verilog_port(VERILOG_PORT_CONKT, enable_port) << " == 1'b1) "; + fp << "&&"; + fp << "(" << generate_verilog_port(VERILOG_PORT_CONKT, readback_port) << " == 1'b0) "; + fp << ") begin" << std::endl; + } else { + 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) { @@ -373,6 +428,46 @@ void print_verilog_arch_decoder_module(std::fstream& fp, fp << "end" << std::endl; + /* Output logics for data readback output */ + if (data_ren_port_id) { + fp << "always@(" << generate_verilog_port(VERILOG_PORT_CONKT, addr_port); + /* If there is a readback port, the data output is only enabled when readback is disabled */ + if (readback_port_id) { + fp << " or " << generate_verilog_port(VERILOG_PORT_CONKT, readback_port); + } + fp << " or " << generate_verilog_port(VERILOG_PORT_CONKT, enable_port); + fp << ") begin" << std::endl; + fp << "\tif ("; + fp << "(" << generate_verilog_port(VERILOG_PORT_CONKT, enable_port) << " == 1'b1) "; + fp << "&&"; + fp << "(" << generate_verilog_port(VERILOG_PORT_CONKT, readback_port) << " == 1'b1) "; + fp << ") 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_ren_port, ito1hot_vec(i, data_size)); + fp << ";" << std::endl; + } + /* Different from MUX decoder, we assign default values which is all zero */ + fp << "\t\t\t" << "default"; + fp << " : "; + fp << generate_verilog_port_constant_values(data_ren_port, ito1hot_vec(data_size, data_size)); + fp << ";" << std::endl; + + fp << "\t\t" << "endcase" << std::endl; + fp << "\t" << "end" << std::endl; + + /* If enable is not active, we should give all zero */ + fp << "\t" << "else begin" << std::endl; + fp << "\t\t" << generate_verilog_port_constant_values(data_ren_port, ito1hot_vec(data_size, data_size)); + fp << ";" << std::endl; + fp << "\t" << "end" << std::endl; + + fp << "end" << std::endl; + } + if (true == decoder_lib.use_data_inv_port(decoder)) { print_verilog_wire_connection(fp, data_inv_port, data_port, true); } diff --git a/openfpga/src/fpga_verilog/verilog_top_testbench.cpp b/openfpga/src/fpga_verilog/verilog_top_testbench.cpp index 289e4d667..eb070690d 100644 --- a/openfpga/src/fpga_verilog/verilog_top_testbench.cpp +++ b/openfpga/src/fpga_verilog/verilog_top_testbench.cpp @@ -128,6 +128,17 @@ void print_verilog_top_testbench_memory_bank_port(std::fstream& fp, BasicPort din_port = module_manager.module_port(top_module, din_port_id); fp << generate_verilog_port(VERILOG_PORT_REG, din_port) << ";" << std::endl; + /* Print the optional readback port for the decoder here */ + print_verilog_comment(fp, std::string("---- Readback port for memory decoders -----")); + ModulePortId readback_port_id = module_manager.find_module_port(top_module, + std::string(DECODER_READBACK_PORT_NAME)); + if (readback_port_id) { + BasicPort readback_port = module_manager.module_port(top_module, readback_port_id); + fp << generate_verilog_port(VERILOG_PORT_WIRE, readback_port) << ";" << std::endl; + /* Disable readback in full testbenches */ + print_verilog_wire_constant_values(fp, readback_port, std::vector(readback_port.get_width(), 0)); + } + /* Generate enable signal waveform here: * which is a 90 degree phase shift than the programming clock */ diff --git a/openfpga/src/mux_lib/decoder_library.cpp b/openfpga/src/mux_lib/decoder_library.cpp index a7c4a8840..8d6017f40 100644 --- a/openfpga/src/mux_lib/decoder_library.cpp +++ b/openfpga/src/mux_lib/decoder_library.cpp @@ -47,6 +47,11 @@ bool DecoderLibrary::use_data_inv_port(const DecoderId& decoder) const { return use_data_inv_port_[decoder]; } +bool DecoderLibrary::use_readback(const DecoderId& decoder) const { + VTR_ASSERT_SAFE(valid_decoder_id(decoder)); + return use_readback_[decoder]; +} + /* Find a decoder to the library, with the specification. * If found, return the id of decoder. * If not found, return an invalid id of decoder @@ -61,13 +66,15 @@ DecoderId DecoderLibrary::find_decoder(const size_t& addr_size, const size_t& data_size, const bool& use_enable, const bool& use_data_in, - const bool& use_data_inv_port) const { + const bool& use_data_inv_port, + const bool& use_readback) const { for (auto decoder : decoders()) { if ( (addr_size == addr_sizes_[decoder]) && (data_size == data_sizes_[decoder]) && (use_enable == use_enable_[decoder]) && (use_data_in == use_data_in_[decoder]) - && (use_data_inv_port == use_data_inv_port_[decoder]) ) { + && (use_data_inv_port == use_data_inv_port_[decoder]) + && (use_readback == use_readback_[decoder]) ) { return decoder; } } @@ -92,7 +99,8 @@ DecoderId DecoderLibrary::add_decoder(const size_t& addr_size, const size_t& data_size, const bool& use_enable, const bool& use_data_in, - const bool& use_data_inv_port) { + const bool& use_data_inv_port, + const bool& use_readback) { DecoderId decoder = DecoderId(decoder_ids_.size()); /* Push to the decoder list */ decoder_ids_.push_back(decoder); @@ -102,6 +110,7 @@ DecoderId DecoderLibrary::add_decoder(const size_t& addr_size, use_enable_.push_back(use_enable); use_data_in_.push_back(use_data_in); use_data_inv_port_.push_back(use_data_inv_port); + use_readback_.push_back(use_readback); return decoder; } diff --git a/openfpga/src/mux_lib/decoder_library.h b/openfpga/src/mux_lib/decoder_library.h index 397308db5..7340f4375 100644 --- a/openfpga/src/mux_lib/decoder_library.h +++ b/openfpga/src/mux_lib/decoder_library.h @@ -50,6 +50,8 @@ class DecoderLibrary { bool use_data_in(const DecoderId& decoder) const; /* Get the flag if a decoder includes a data_inv port which is an inversion of the regular data output port */ bool use_data_inv_port(const DecoderId& decoder) const; + /* Get the flag if a decoder includes a readback port which enables readback from configurable memories */ + bool use_readback(const DecoderId& decoder) const; /* Find a decoder to the library, with the specification. * If found, return the id of decoder. * If not found, return an invalid id of decoder @@ -64,7 +66,8 @@ class DecoderLibrary { const size_t& data_size, const bool& use_enable, const bool& use_data_in, - const bool& use_data_inv_port) const; + const bool& use_data_inv_port, + const bool& use_readback) const; public: /* Public validators */ /* valid ids */ @@ -76,7 +79,8 @@ class DecoderLibrary { const size_t& data_size, const bool& use_enable, const bool& use_data_in, - const bool& use_data_inv_port); + const bool& use_data_inv_port, + const bool& use_readback); private: /* Internal Data */ vtr::vector decoder_ids_; @@ -85,6 +89,7 @@ class DecoderLibrary { vtr::vector use_enable_; vtr::vector use_data_in_; vtr::vector use_data_inv_port_; + vtr::vector use_readback_; }; } /* End namespace openfpga*/ diff --git a/openfpga/src/utils/decoder_library_utils.cpp b/openfpga/src/utils/decoder_library_utils.cpp index 502620661..b44bb106c 100644 --- a/openfpga/src/utils/decoder_library_utils.cpp +++ b/openfpga/src/utils/decoder_library_utils.cpp @@ -112,11 +112,11 @@ DecoderId add_mux_local_decoder_to_library(DecoderLibrary& decoder_lib, const size_t data_size) { size_t addr_size = find_mux_local_decoder_addr_size(data_size); - DecoderId decoder_id = decoder_lib.find_decoder(addr_size, data_size, false, false, true); + DecoderId decoder_id = decoder_lib.find_decoder(addr_size, data_size, false, false, true, false); if (DecoderId::INVALID() == decoder_id) { /* Add the decoder */ - return decoder_lib.add_decoder(addr_size, data_size, false, false, true); + return decoder_lib.add_decoder(addr_size, data_size, false, false, true, false); } /* There is already a decoder in the library, return the decoder id */ diff --git a/openfpga/src/utils/memory_utils.cpp b/openfpga/src/utils/memory_utils.cpp index bcca022ab..8e2038b06 100644 --- a/openfpga/src/utils/memory_utils.cpp +++ b/openfpga/src/utils/memory_utils.cpp @@ -330,7 +330,8 @@ std::vector generate_sram_port_names(const CircuitLibrary& circuit_ std::vector ports_to_search; ports_to_search.push_back(CIRCUIT_MODEL_PORT_BL); ports_to_search.push_back(CIRCUIT_MODEL_PORT_WL); - /* Try to find a BL/WL/BLB/WLB port and update the port types/module port types to be added */ + ports_to_search.push_back(CIRCUIT_MODEL_PORT_WLR); + /* Try to find a BL/WL/WLR port and update the port types/module port types to be added */ for (const auto& port_to_search : ports_to_search) { std::vector found_port = circuit_lib.model_ports_by_type(sram_model, port_to_search); if (0 == found_port.size()) { diff --git a/openfpga/src/utils/module_manager_utils.cpp b/openfpga/src/utils/module_manager_utils.cpp index 421e7b95f..4c25c3a4a 100644 --- a/openfpga/src/utils/module_manager_utils.cpp +++ b/openfpga/src/utils/module_manager_utils.cpp @@ -195,6 +195,7 @@ ModuleId add_circuit_model_to_module_manager(ModuleManager& module_manager, port_type2type_map[CIRCUIT_MODEL_PORT_BLB] = ModuleManager::MODULE_INPUT_PORT; port_type2type_map[CIRCUIT_MODEL_PORT_WL] = ModuleManager::MODULE_INPUT_PORT; port_type2type_map[CIRCUIT_MODEL_PORT_WLB] = ModuleManager::MODULE_INPUT_PORT; + port_type2type_map[CIRCUIT_MODEL_PORT_WLR] = ModuleManager::MODULE_INPUT_PORT; port_type2type_map[CIRCUIT_MODEL_PORT_OUTPUT] = ModuleManager::MODULE_OUTPUT_PORT; /* Input ports (ignore all the global ports when searching the circuit_lib */ @@ -390,8 +391,9 @@ void add_pb_sram_ports_to_module_manager(ModuleManager& module_manager, for (const std::string& sram_port_name : sram_port_names) { /* Add generated ports to the ModuleManager */ BasicPort sram_port(sram_port_name, sram_port_size); - /* For WL ports, we need to fine-tune it */ - if (CIRCUIT_MODEL_PORT_WL == circuit_lib.port_type(circuit_lib.model_port(sram_model, sram_port_name))) { + /* For WL and WLR ports, we need to fine-tune it */ + if ( (CIRCUIT_MODEL_PORT_WL == circuit_lib.port_type(circuit_lib.model_port(sram_model, sram_port_name))) + || (CIRCUIT_MODEL_PORT_WLR == circuit_lib.port_type(circuit_lib.model_port(sram_model, sram_port_name))) ) { sram_port.set_width(find_memory_wl_decoder_data_size(num_config_bits, sram_port_size)); } module_manager.add_port(module_id, sram_port, ModuleManager::MODULE_INPUT_PORT); @@ -885,7 +887,7 @@ void add_module_nets_between_logic_and_memory_sram_bus(ModuleManager& module_man * | | | * +------------+----------------------+ * | - * WL + * WL/WLR * * Note: * - This function will do the connection for only one type of the port, @@ -900,15 +902,16 @@ void add_module_nets_cmos_flatten_memory_config_bus(ModuleManager& module_manage /* A counter for the current pin id for the source port of parent module */ size_t cur_src_pin_id = 0; - ModuleId net_src_module_id; - size_t net_src_instance_id; - ModulePortId net_src_port_id; - /* Find the port name of parent module */ std::string src_port_name = generate_sram_port_name(sram_orgz_type, config_port_type); - 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); + ModuleId net_src_module_id = parent_module; + size_t net_src_instance_id = 0; + ModulePortId net_src_port_id = module_manager.find_module_port(net_src_module_id, src_port_name); + + /* We may not be able to find WLR port, return now */ + if (!net_src_port_id) { + return; + } /* Get the pin id for source port */ BasicPort net_src_port = module_manager.module_port(net_src_module_id, net_src_port_id); @@ -1025,7 +1028,7 @@ void add_module_nets_cmos_memory_bank_bl_config_bus(ModuleManager& module_manage * | | | * +------------+----------------------+ * | - * WL<0> + * WL<0>/WLR<0> * * +--------+ +--------+ +--------+ * | Memory | | Memory | ... | Memory | @@ -1036,7 +1039,7 @@ void add_module_nets_cmos_memory_bank_bl_config_bus(ModuleManager& module_manage * | | | * +------------+----------------------+ * | - * WL<1> + * WL<1>/WLR<1> * *********************************************************************/ void add_module_nets_cmos_memory_bank_wl_config_bus(ModuleManager& module_manager, @@ -1055,6 +1058,11 @@ void add_module_nets_cmos_memory_bank_wl_config_bus(ModuleManager& module_manage ModulePortId net_src_port_id = module_manager.find_module_port(net_src_module_id, src_port_name); ModulePortId net_bl_port_id = module_manager.find_module_port(net_src_module_id, bl_port_name); + /* We may not be able to find WLR port, return now */ + if (!net_src_port_id) { + return; + } + /* Get the pin id for source port */ BasicPort net_src_port = module_manager.module_port(net_src_module_id, net_src_port_id); BasicPort net_bl_port = module_manager.module_port(net_src_module_id, net_bl_port_id); @@ -1300,9 +1308,9 @@ void add_module_nets_cmos_memory_frame_decoder_config_bus(ModuleManager& module_ /* Search the decoder library and try to find one * If not found, create a new module and add it to the module manager */ - DecoderId decoder_id = decoder_lib.find_decoder(addr_size, data_size, true, false, false); + DecoderId decoder_id = decoder_lib.find_decoder(addr_size, data_size, true, false, false, false); if (DecoderId::INVALID() == decoder_id) { - decoder_id = decoder_lib.add_decoder(addr_size, data_size, true, false, false); + decoder_id = decoder_lib.add_decoder(addr_size, data_size, true, false, false, false); } VTR_ASSERT(DecoderId::INVALID() != decoder_id); @@ -1519,6 +1527,8 @@ void add_module_nets_cmos_memory_config_bus(ModuleManager& module_manager, sram_orgz_type, CIRCUIT_MODEL_PORT_BL); add_module_nets_cmos_flatten_memory_config_bus(module_manager, parent_module, sram_orgz_type, CIRCUIT_MODEL_PORT_WL); + add_module_nets_cmos_flatten_memory_config_bus(module_manager, parent_module, + sram_orgz_type, CIRCUIT_MODEL_PORT_WLR); break; case CONFIG_MEM_FRAME_BASED: add_module_nets_cmos_memory_frame_config_bus(module_manager, decoder_lib, parent_module); @@ -1580,6 +1590,8 @@ void add_pb_module_nets_cmos_memory_config_bus(ModuleManager& module_manager, sram_orgz_type, CIRCUIT_MODEL_PORT_BL); add_module_nets_cmos_memory_bank_wl_config_bus(module_manager, parent_module, sram_orgz_type, CIRCUIT_MODEL_PORT_WL); + add_module_nets_cmos_memory_bank_wl_config_bus(module_manager, parent_module, + sram_orgz_type, CIRCUIT_MODEL_PORT_WLR); break; case CONFIG_MEM_MEMORY_BANK: add_module_nets_cmos_flatten_memory_config_bus(module_manager, parent_module, diff --git a/openfpga_flow/openfpga_arch/k4_N4_40nm_qlbank_wlr_openfpga.xml b/openfpga_flow/openfpga_arch/k4_N4_40nm_qlbank_wlr_openfpga.xml new file mode 100644 index 000000000..62f449b25 --- /dev/null +++ b/openfpga_flow/openfpga_arch/k4_N4_40nm_qlbank_wlr_openfpga.xml @@ -0,0 +1,199 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 10e-12 + + + 10e-12 + + + + + + + + + 10e-12 + + + 10e-12 + + + + + + + + + 10e-12 + + + 10e-12 + + + + + + + + + + + + + 10e-12 5e-12 5e-12 + + + 10e-12 5e-12 5e-12 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/openfpga_flow/openfpga_cell_library/verilog/sram.v b/openfpga_flow/openfpga_cell_library/verilog/sram.v index 86f3ddf99..5f3c62852 100644 --- a/openfpga_flow/openfpga_cell_library/verilog/sram.v +++ b/openfpga_flow/openfpga_cell_library/verilog/sram.v @@ -256,6 +256,41 @@ module SRAMSR( endmodule +//----------------------------------------------------- +// Function : A SRAM cell with WL read signal +//----------------------------------------------------- +module SRAM_RE( + input WE, // Word line control signal as write enable + input RE, // Word line read signal as read enable + inout D, // Bit line control signal + output Q, // Data output + output QN // Data output +); + + //----- local variable need to be registered + reg data; + reg data_readback; + + //----- when wl is enabled, we can read in data from bl + always @(WE or RE or D) + begin + if ((1'b1 == D)&&(1'b1 == WE)) begin + //----- Cases to program internal memory bit + //----- case 1: bl = 1, wl = 1, a -> 0 + data <= 1'b1; + end else if ((1'b0 == D)&&(1'b1 == WE)) begin + //----- case 2: bl = 0, wl = 1, a -> 0 + data <= 1'b0; + end + end + + // Wire q_reg to Q + assign Q = data; + assign QN = ~data; + +endmodule + + //----------------------------------------------------- // Function : A SRAM cell with // - an active-low reset diff --git a/openfpga_flow/regression_test_scripts/basic_reg_test.sh b/openfpga_flow/regression_test_scripts/basic_reg_test.sh index 5bef6a05f..531ba5f42 100755 --- a/openfpga_flow/regression_test_scripts/basic_reg_test.sh +++ b/openfpga_flow/regression_test_scripts/basic_reg_test.sh @@ -55,6 +55,7 @@ run-task basic_tests/preconfig_testbench/memory_bank --debug --show_thread_logs echo -e "Testing physical design friendly memory bank configuration protocol of a K4N4 FPGA"; run-task basic_tests/full_testbench/ql_memory_bank --debug --show_thread_logs +run-task basic_tests/full_testbench/ql_memory_bank_use_wlr --debug --show_thread_logs echo -e "Testing testbenches without self checking features"; run-task basic_tests/full_testbench/full_testbench_without_self_checking --debug --show_thread_logs diff --git a/openfpga_flow/tasks/basic_tests/full_testbench/ql_memory_bank_use_wlr/config/task.conf b/openfpga_flow/tasks/basic_tests/full_testbench/ql_memory_bank_use_wlr/config/task.conf new file mode 100644 index 000000000..9ed4b8323 --- /dev/null +++ b/openfpga_flow/tasks/basic_tests/full_testbench/ql_memory_bank_use_wlr/config/task.conf @@ -0,0 +1,44 @@ +# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = +# 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 = 20*60 +fpga_flow=yosys_vpr + +[OpenFPGA_SHELL] +openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/write_full_testbench_example_script.openfpga +openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_N4_40nm_qlbank_wlr_openfpga.xml +openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml +openfpga_vpr_device_layout= +openfpga_fast_configuration= + +[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=