From cff5b5cfc1f52a13c52da0808a3e9d42adfd7fe3 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 27 May 2020 18:40:45 -0600 Subject: [PATCH] break the configuration testbench. This commit is to spot which modification leads to the problem --- .../fpga_bitstream/build_fabric_bitstream.cpp | 11 +- .../src/fpga_bitstream/fabric_bitstream.cpp | 4 +- .../src/fpga_bitstream/fabric_bitstream.h | 6 +- .../fpga_verilog/verilog_top_testbench.cpp | 217 +++++++++++++++++- 4 files changed, 215 insertions(+), 23 deletions(-) diff --git a/openfpga/src/fpga_bitstream/build_fabric_bitstream.cpp b/openfpga/src/fpga_bitstream/build_fabric_bitstream.cpp index 80026cbef..4dc8ee70b 100644 --- a/openfpga/src/fpga_bitstream/build_fabric_bitstream.cpp +++ b/openfpga/src/fpga_bitstream/build_fabric_bitstream.cpp @@ -101,7 +101,7 @@ void rec_build_module_fabric_dependent_frame_bitstream(const BitstreamManager& b const std::vector& parent_blocks, const ModuleManager& module_manager, const std::vector& parent_modules, - const std::vector& addr_code, + const std::vector& addr_code, FabricBitstream& fabric_bitstream) { /* Depth-first search: if we have any children in the parent_block, @@ -154,17 +154,14 @@ void rec_build_module_fabric_dependent_frame_bitstream(const BitstreamManager& b child_modules.push_back(child_module); /* Set address, apply binary conversion from the first to the last element in the address list */ - std::vector child_addr_code = addr_code; + std::vector child_addr_code = addr_code; if (true == add_addr_code) { /* Find the address port from the decoder module */ const ModulePortId& decoder_addr_port_id = module_manager.find_module_port(decoder_module, std::string(DECODER_ADDRESS_PORT_NAME)); const BasicPort& decoder_addr_port = module_manager.module_port(decoder_module, decoder_addr_port_id); std::vector addr_bits_vec = itobin_vec(child_id, decoder_addr_port.get_width()); - for (const size_t& bit : addr_bits_vec) { - VTR_ASSERT((0 == bit) || (1 == bit)); - child_addr_code.push_back(bit); - } + child_addr_code.insert(child_addr_code.end(), addr_bits_vec.begin(), addr_bits_vec.end()); } /* Go recursively */ @@ -226,7 +223,7 @@ void build_module_fabric_dependent_bitstream(const ConfigProtocol& config_protoc std::vector(1, top_block), module_manager, std::vector(1, top_module), - std::vector(), + std::vector(), fabric_bitstream); break; } diff --git a/openfpga/src/fpga_bitstream/fabric_bitstream.cpp b/openfpga/src/fpga_bitstream/fabric_bitstream.cpp index 988839cf8..7306eba01 100644 --- a/openfpga/src/fpga_bitstream/fabric_bitstream.cpp +++ b/openfpga/src/fpga_bitstream/fabric_bitstream.cpp @@ -27,7 +27,7 @@ ConfigBitId FabricBitstream::config_bit(const FabricBitId& bit_id) const { return config_bit_ids_[bit_id]; } -std::vector FabricBitstream::bit_address(const FabricBitId& bit_id) const { +std::vector FabricBitstream::bit_address(const FabricBitId& bit_id) const { /* Ensure a valid id */ VTR_ASSERT(true == valid_bit_id(bit_id)); @@ -56,7 +56,7 @@ FabricBitId FabricBitstream::add_bit(const ConfigBitId& config_bit_id) { } void FabricBitstream::set_bit_address(const FabricBitId& bit_id, - const std::vector& address) { + const std::vector& address) { VTR_ASSERT(true == valid_bit_id(bit_id)); bit_addresses_[bit_id] = address; } diff --git a/openfpga/src/fpga_bitstream/fabric_bitstream.h b/openfpga/src/fpga_bitstream/fabric_bitstream.h index 0b4842aa8..12d1c7885 100644 --- a/openfpga/src/fpga_bitstream/fabric_bitstream.h +++ b/openfpga/src/fpga_bitstream/fabric_bitstream.h @@ -52,7 +52,7 @@ class FabricBitstream { ConfigBitId config_bit(const FabricBitId& bit_id) const; /* Find the address of bitstream */ - std::vector bit_address(const FabricBitId& bit_id) const; + std::vector bit_address(const FabricBitId& bit_id) const; /* Find the data-in of bitstream */ bool bit_din(const FabricBitId& bit_id) const; @@ -62,7 +62,7 @@ class FabricBitstream { FabricBitId add_bit(const ConfigBitId& config_bit_id); void set_bit_address(const FabricBitId& bit_id, - const std::vector& address); + const std::vector& address); void set_bit_din(const FabricBitId& bit_id, const bool& din); @@ -84,7 +84,7 @@ class FabricBitstream { * Here we store the binary format of the address, which can be loaded * to the configuration protocol directly */ - vtr::vector> bit_addresses_; + vtr::vector> bit_addresses_; /* Data input (Din) bits: this is designed for memory decoders */ vtr::vector bit_dins_; diff --git a/openfpga/src/fpga_verilog/verilog_top_testbench.cpp b/openfpga/src/fpga_verilog/verilog_top_testbench.cpp index 9edce0b52..3d2cad1ea 100644 --- a/openfpga/src/fpga_verilog/verilog_top_testbench.cpp +++ b/openfpga/src/fpga_verilog/verilog_top_testbench.cpp @@ -41,7 +41,7 @@ constexpr char* TOP_TESTBENCH_FPGA_OUTPUT_POSTFIX = "_fpga"; constexpr char* TOP_TESTBENCH_CHECKFLAG_PORT_POSTFIX = "_flag"; -constexpr char* TOP_TESTBENCH_CC_PROG_TASK_NAME = "prog_cycle_task"; +constexpr char* TOP_TESTBENCH_PROG_TASK_NAME = "prog_cycle_task"; constexpr char* TOP_TESTBENCH_SIM_START_PORT_NAME = "sim_start"; @@ -79,12 +79,49 @@ void print_verilog_top_testbench_config_chain_port(std::fstream& fp) { fp << generate_verilog_port(VERILOG_PORT_WIRE, config_chain_tail_port) << ";" << std::endl; } +/******************************************************************** + * Print local wires for frame-based decoder protocols + *******************************************************************/ +static +void print_verilog_top_testbench_frame_decoder_port(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& top_module) { + /* Validate the file stream */ + valid_file_stream(fp); + + /* Print the address port for the frame-based decoder here */ + print_verilog_comment(fp, std::string("---- Address port for frame-based decoder -----")); + ModulePortId addr_port_id = module_manager.find_module_port(top_module, + std::string(DECODER_ADDRESS_PORT_NAME)); + BasicPort addr_port = module_manager.module_port(top_module, addr_port_id); + + fp << generate_verilog_port(VERILOG_PORT_REG, addr_port) << ";" << std::endl; + + /* Print the data-input port for the frame-based decoder here */ + print_verilog_comment(fp, std::string("---- Data input port for frame-based decoder -----")); + ModulePortId din_port_id = module_manager.find_module_port(top_module, + std::string(DECODER_DATA_IN_PORT_NAME)); + BasicPort din_port = module_manager.module_port(top_module, din_port_id); + fp << generate_verilog_port(VERILOG_PORT_WIRE, din_port) << ";" << std::endl; + + /* Wire the programming clock to the enable signal */ + print_verilog_comment(fp, std::string("---- Wire enable port of frame-based decoder to programming clock -----")); + ModulePortId en_port_id = module_manager.find_module_port(top_module, + std::string(DECODER_ENABLE_PORT_NAME)); + BasicPort en_port = module_manager.module_port(top_module, en_port_id); + BasicPort prog_clock_port(std::string(TOP_TB_PROG_CLOCK_PORT_NAME), 1); + + print_verilog_wire_connection(fp, en_port, prog_clock_port, false); +} + /******************************************************************** * Print local wires for different types of configuration protocols *******************************************************************/ static void print_verilog_top_testbench_config_protocol_port(std::fstream& fp, - const e_config_protocol_type& sram_orgz_type) { + const e_config_protocol_type& sram_orgz_type, + const ModuleManager& module_manager, + const ModuleId& top_module) { switch(sram_orgz_type) { case CONFIG_MEM_STANDALONE: /* TODO */ @@ -95,6 +132,9 @@ void print_verilog_top_testbench_config_protocol_port(std::fstream& fp, case CONFIG_MEM_MEMORY_BANK: /* TODO */ break; + case CONFIG_MEM_FRAME_BASED: + print_verilog_top_testbench_frame_decoder_port(fp, module_manager, top_module); + break; default: VTR_LOGF_ERROR(__FILE__, __LINE__, "Invalid type of SRAM organization!\n"); @@ -398,7 +438,8 @@ void print_verilog_top_testbench_ports(std::fstream& fp, fp << generate_verilog_port(VERILOG_PORT_REG, set_port) << ";" << std::endl; /* Configuration ports depend on the organization of SRAMs */ - print_verilog_top_testbench_config_protocol_port(fp, sram_orgz_type); + print_verilog_top_testbench_config_protocol_port(fp, sram_orgz_type, + module_manager, top_module); /* Create a clock port if the benchmark have one but not in the default name! * We will wire the clock directly to the operating clock directly @@ -506,15 +547,72 @@ void print_verilog_top_testbench_load_bitstream_task_configuration_chain(std::fs * It aims at avoid racing the programming clock (scan-chain data changes at the rising edge). */ print_verilog_comment(fp, std::string("----- Task: input values during a programming clock cycle -----")); - fp << "task " << std::string(TOP_TESTBENCH_CC_PROG_TASK_NAME) << ";" << std::endl; + fp << "task " << std::string(TOP_TESTBENCH_PROG_TASK_NAME) << ";" << std::endl; fp << generate_verilog_port(VERILOG_PORT_INPUT, cc_head_value) << ";" << std::endl; fp << "\tbegin" << std::endl; fp << "\t\t@(negedge " << generate_verilog_port(VERILOG_PORT_CONKT, prog_clock_port) << ");" << std::endl; fp << "\t\t\t"; - fp << generate_verilog_port(VERILOG_PORT_CONKT, cc_head_port); - fp << " = "; - fp << generate_verilog_port(VERILOG_PORT_CONKT, cc_head_value); - fp << ";" << std::endl; + print_verilog_wire_connection(fp, cc_head_port, cc_head_value, false); + fp << std::endl; + + fp << "\tend" << std::endl; + fp << "endtask" << std::endl; + + /* Add an empty line as splitter */ + fp << std::endl; +} + +/******************************************************************** + * Print tasks (processes) in Verilog format, + * which is very useful in generating stimuli for each clock cycle + * This function is tuned for frame-based memory manipulation: + * During each programming cycle, we feed + * - an address to the address port of top module + * - a data input to the din port of top module + *******************************************************************/ +static +void print_verilog_top_testbench_load_bitstream_task_frame_decoder(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& top_module) { + + /* Validate the file stream */ + valid_file_stream(fp); + + BasicPort prog_clock_port(std::string(TOP_TB_PROG_CLOCK_PORT_NAME), 1); + + ModulePortId addr_port_id = module_manager.find_module_port(top_module, + std::string(DECODER_ADDRESS_PORT_NAME)); + BasicPort addr_port = module_manager.module_port(top_module, addr_port_id); + BasicPort addr_value = addr_port; + addr_value.set_name(std::string(DECODER_ADDRESS_PORT_NAME) + std::string("_val")); + + ModulePortId din_port_id = module_manager.find_module_port(top_module, + std::string(DECODER_DATA_IN_PORT_NAME)); + BasicPort din_port = module_manager.module_port(top_module, din_port_id); + BasicPort din_value = din_port; + din_value.set_name(std::string(DECODER_DATA_IN_PORT_NAME) + std::string("_val")); + + /* Add an empty line as splitter */ + fp << std::endl; + + /* Feed the address and data input at each rising edge of programming clock + * As the enable signal is wired to the programming clock, we should synchronize + * address and data with the enable signal + */ + print_verilog_comment(fp, std::string("----- Task: address and data values during a programming clock cycle -----")); + fp << "task " << std::string(TOP_TESTBENCH_PROG_TASK_NAME) << ";" << std::endl; + fp << generate_verilog_port(VERILOG_PORT_INPUT, addr_value) << ";" << std::endl; + fp << generate_verilog_port(VERILOG_PORT_INPUT, din_value) << ";" << std::endl; + fp << "\tbegin" << std::endl; + fp << "\t\t@(posedge " << generate_verilog_port(VERILOG_PORT_CONKT, prog_clock_port) << ");" << std::endl; + + fp << "\t\t\t"; + print_verilog_wire_connection(fp, addr_port, addr_value, false); + fp << std::endl; + + fp << "\t\t\t"; + print_verilog_wire_connection(fp, din_port, din_value, false); + fp << std::endl; fp << "\tend" << std::endl; fp << "endtask" << std::endl; @@ -528,7 +626,9 @@ void print_verilog_top_testbench_load_bitstream_task_configuration_chain(std::fs *******************************************************************/ static void print_verilog_top_testbench_load_bitstream_task(std::fstream& fp, - const e_config_protocol_type& sram_orgz_type) { + const e_config_protocol_type& sram_orgz_type, + const ModuleManager& module_manager, + const ModuleId& top_module) { switch (sram_orgz_type) { case CONFIG_MEM_STANDALONE: break; @@ -540,6 +640,11 @@ void print_verilog_top_testbench_load_bitstream_task(std::fstream& fp, dump_verilog_top_testbench_stimuli_serial_version_tasks_memory_bank(cur_sram_orgz_info, fp); */ break; + case CONFIG_MEM_FRAME_BASED: + print_verilog_top_testbench_load_bitstream_task_frame_decoder(fp, + module_manager, + top_module); + break; default: VTR_LOGF_ERROR(__FILE__, __LINE__, "Invalid type of SRAM organization!\n"); @@ -721,7 +826,7 @@ void print_verilog_top_testbench_configuration_chain_bitstream(std::fstream& fp, * We will visit the fabric bitstream in a reverse way */ for (const FabricBitId& bit_id : fabric_bitstream.bits()) { - fp << "\t\t" << std::string(TOP_TESTBENCH_CC_PROG_TASK_NAME); + fp << "\t\t" << std::string(TOP_TESTBENCH_PROG_TASK_NAME); fp << "(1'b" << (size_t)bitstream_manager.bit_value(fabric_bitstream.config_bit(bit_id)) << ");" << std::endl; } @@ -741,6 +846,86 @@ void print_verilog_top_testbench_configuration_chain_bitstream(std::fstream& fp, print_verilog_comment(fp, "----- End bitstream loading during configuration phase -----"); } +/******************************************************************** + * Print stimulus for a FPGA fabric with a frame-based configuration protocol + * where configuration bits are programming in serial (one by one) + * + * We will use the programming task function created before + *******************************************************************/ +static +void print_verilog_top_testbench_frame_decoder_bitstream(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& top_module, + const FabricBitstream& fabric_bitstream) { + /* Validate the file stream */ + valid_file_stream(fp); + + /* Feed addresss and data input pair one by one + * Note: the first cycle is reserved for programming reset + * We should give dummy values + */ + ModulePortId addr_port_id = module_manager.find_module_port(top_module, + std::string(DECODER_ADDRESS_PORT_NAME)); + BasicPort addr_port = module_manager.module_port(top_module, addr_port_id); + std::vector initial_addr_values(addr_port.get_width(), 0); + + ModulePortId din_port_id = module_manager.find_module_port(top_module, + std::string(DECODER_DATA_IN_PORT_NAME)); + BasicPort din_port = module_manager.module_port(top_module, din_port_id); + std::vector initial_din_values(din_port.get_width(), 0); + + print_verilog_comment(fp, "----- Begin bitstream loading during configuration phase -----"); + fp << "initial" << std::endl; + fp << "\tbegin" << std::endl; + print_verilog_comment(fp, "----- Address port default input -----"); + fp << "\t\t"; + fp << generate_verilog_port_constant_values(addr_port, initial_addr_values); + fp << ";"; + + print_verilog_comment(fp, "----- Data-input port default input -----"); + fp << "\t\t"; + fp << generate_verilog_port_constant_values(din_port, initial_din_values); + fp << ";"; + + fp << std::endl; + + /* Attention: the configuration chain protcol requires the last configuration bit is fed first + * We will visit the fabric bitstream in a reverse way + */ + for (const FabricBitId& bit_id : fabric_bitstream.bits()) { + fp << "\t\t" << std::string(TOP_TESTBENCH_PROG_TASK_NAME); + fp << "(" << addr_port.get_width() << "'b"; + VTR_ASSERT(addr_port.get_width() == fabric_bitstream.bit_address(bit_id).size()); + for (const size_t& addr_bit : fabric_bitstream.bit_address(bit_id)) { + fp << addr_bit; + } + fp << ", "; + fp <<"1'b"; + if (true == fabric_bitstream.bit_din(bit_id)) { + fp << "1"; + } else { + VTR_ASSERT(false == fabric_bitstream.bit_din(bit_id)); + fp << "0"; + } + fp << ");" << std::endl; + } + + /* Raise the flag of configuration done when bitstream loading is complete */ + BasicPort prog_clock_port(std::string(TOP_TB_PROG_CLOCK_PORT_NAME), 1); + fp << "\t\t@(negedge " << generate_verilog_port(VERILOG_PORT_CONKT, prog_clock_port) << ");" << std::endl; + + BasicPort config_done_port(std::string(TOP_TB_CONFIG_DONE_PORT_NAME), 1); + fp << "\t\t\t"; + fp << generate_verilog_port(VERILOG_PORT_CONKT, config_done_port); + fp << " <= "; + std::vector config_done_enable_values(config_done_port.get_width(), 1); + fp << generate_verilog_constant_values(config_done_enable_values); + fp << ";" << std::endl; + + fp << "\tend" << std::endl; + print_verilog_comment(fp, "----- End bitstream loading during configuration phase -----"); +} + /******************************************************************** * Generate the stimuli for the top-level testbench * The simulation consists of two phases: configuration phase and operation phase @@ -750,6 +935,8 @@ void print_verilog_top_testbench_configuration_chain_bitstream(std::fstream& fp, static void print_verilog_top_testbench_bitstream(std::fstream& fp, const e_config_protocol_type& sram_orgz_type, + const ModuleManager& module_manager, + const ModuleId& top_module, const BitstreamManager& bitstream_manager, const FabricBitstream& fabric_bitstream) { /* Branch on the type of configuration protocol */ @@ -763,6 +950,11 @@ void print_verilog_top_testbench_bitstream(std::fstream& fp, case CONFIG_MEM_MEMORY_BANK: /* TODO */ break; + case CONFIG_MEM_FRAME_BASED: + print_verilog_top_testbench_frame_decoder_bitstream(fp, + module_manager, top_module, + fabric_bitstream); + break; default: VTR_LOGF_ERROR(__FILE__, __LINE__, "Invalid SRAM organization type!\n"); @@ -875,10 +1067,13 @@ void print_verilog_top_testbench(const ModuleManager& module_manager, explicit_port_mapping); /* Print tasks used for loading bitstreams */ - print_verilog_top_testbench_load_bitstream_task(fp, sram_orgz_type); + print_verilog_top_testbench_load_bitstream_task(fp, + sram_orgz_type, + module_manager, top_module); /* load bitstream to FPGA fabric in a configuration phase */ print_verilog_top_testbench_bitstream(fp, sram_orgz_type, + module_manager, top_module, bitstream_manager, fabric_bitstream); /* Add stimuli for reset, set, clock and iopad signals */