From 32fc0a1692c89273c7c9907b8b92fbe3951a8612 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sat, 2 Oct 2021 17:25:27 -0700 Subject: [PATCH] [FPGA-Verilog] Upgrading verilog testbench generator for QuickLogic memory bank using BL/WL shift register --- .../verilog_top_testbench_memory_bank.cpp | 319 +++++++++++++++++- .../verilog_top_testbench_memory_bank.h | 9 + 2 files changed, 323 insertions(+), 5 deletions(-) diff --git a/openfpga/src/fpga_verilog/verilog_top_testbench_memory_bank.cpp b/openfpga/src/fpga_verilog/verilog_top_testbench_memory_bank.cpp index 7618cd510..b2fa8277b 100644 --- a/openfpga/src/fpga_verilog/verilog_top_testbench_memory_bank.cpp +++ b/openfpga/src/fpga_verilog/verilog_top_testbench_memory_bank.cpp @@ -36,6 +36,17 @@ /* begin namespace openfpga */ namespace openfpga { +constexpr char* TOP_TB_BL_SHIFT_REGISTER_CLOCK_PORT_NAME = "bl_sr_clock"; +constexpr char* TOP_TB_WL_SHIFT_REGISTER_CLOCK_PORT_NAME = "wl_sr_clock"; +constexpr char* TOP_TB_START_BL_SHIFT_REGISTER_PORT_NAME = "start_bl_sr"; +constexpr char* TOP_TB_START_WL_SHIFT_REGISTER_PORT_NAME = "start_wl_sr"; +constexpr char* TOP_TB_BL_SHIFT_REGISTER_COUNT_PORT_NAME = "bl_sr_count"; +constexpr char* TOP_TB_WL_SHIFT_REGISTER_COUNT_PORT_NAME = "wl_sr_count"; +constexpr char* TOP_TB_BITSTREAM_BL_HEAD_WIDTH_VARIABLE = "BITSTREAM_BL_HEAD_WIDTH"; +constexpr char* TOP_TB_BITSTREAM_WL_HEAD_WIDTH_VARIABLE = "BITSTREAM_WL_HEAD_WIDTH"; +constexpr char* TOP_TB_BITSTREAM_BL_WORD_SIZE_VARIABLE = "BITSTREAM_BL_WORD_SIZE"; +constexpr char* TOP_TB_BITSTREAM_WL_WORD_SIZE_VARIABLE = "BITSTREAM_WL_WORD_SIZE"; + void print_verilog_top_testbench_ql_memory_bank_port(std::fstream& fp, const ModuleManager& module_manager, const ModuleId& top_module, @@ -73,6 +84,12 @@ void print_verilog_top_testbench_ql_memory_bank_port(std::fstream& fp, BasicPort sr_tail_port = module_manager.module_port(top_module, sr_tail_port_id); fp << generate_verilog_port(VERILOG_PORT_WIRE, sr_tail_port) << ";" << std::endl; } + + /* BL Shift register clock and registers */ + BasicPort sr_clock_port(std::string(TOP_TB_BL_SHIFT_REGISTER_CLOCK_PORT_NAME), 1); + fp << generate_verilog_port(VERILOG_PORT_WIRE, sr_clock_port) << ";" << std::endl; + BasicPort sr_clock_register_port(std::string(std::string(TOP_TB_BL_SHIFT_REGISTER_CLOCK_PORT_NAME) + std::string(TOP_TB_CLOCK_REG_POSTFIX)), 1); + fp << generate_verilog_port(VERILOG_PORT_REG, sr_clock_register_port) << ";" << std::endl; } /* Print the address port for the Word-Line decoder here */ @@ -105,6 +122,12 @@ void print_verilog_top_testbench_ql_memory_bank_port(std::fstream& fp, BasicPort sr_tail_port = module_manager.module_port(top_module, sr_tail_port_id); fp << generate_verilog_port(VERILOG_PORT_WIRE, sr_tail_port) << ";" << std::endl; } + + /* WL Shift register clock and registers */ + BasicPort sr_clock_port(std::string(TOP_TB_WL_SHIFT_REGISTER_CLOCK_PORT_NAME), 1); + fp << generate_verilog_port(VERILOG_PORT_WIRE, sr_clock_port) << ";" << std::endl; + BasicPort sr_clock_register_port(std::string(std::string(TOP_TB_WL_SHIFT_REGISTER_CLOCK_PORT_NAME) + std::string(TOP_TB_CLOCK_REG_POSTFIX)), 1); + fp << generate_verilog_port(VERILOG_PORT_REG, sr_clock_register_port) << ";" << std::endl; } /* Print the data-input port: only available when BL has a decoder */ @@ -180,11 +203,6 @@ void print_verilog_full_testbench_ql_memory_bank_flatten_bitstream(std::fstream& /* Validate the file stream */ valid_file_stream(fp); - /* No fast configuration available in this configuration protocol. Give a warning */ - if (true == fast_configuration) { - VTR_LOG_WARN("Fast configuration is not available for flatten BL protocol"); - } - /* Reorganize the fabric bitstream by the same address across regions */ MemoryBankFlattenFabricBitstream fabric_bits_by_addr = build_memory_bank_flatten_fabric_bitstream(fabric_bitstream, fast_configuration, @@ -308,6 +326,290 @@ void print_verilog_full_testbench_ql_memory_bank_flatten_bitstream(std::fstream& print_verilog_comment(fp, "----- End bitstream loading during configuration phase -----"); } +/* Verilog codes to load bitstream from a bit file for memory bank using flatten BL/WLs */ +void print_verilog_full_testbench_ql_memory_bank_shift_register_clock_generator(std::fstream& fp, + const BasicPort& prog_clock_port, + const BasicPort& start_sr_port, + const BasicPort& sr_clock_port) { + /* Validate the file stream */ + valid_file_stream(fp); + + fp << "always"; + fp << " @(negedge " << generate_verilog_port(VERILOG_PORT_CONKT, prog_clock_port) << ")"; + fp << " begin"; + fp << std::endl; + + fp << "\t"; + fp << generate_verilog_port_constant_values(sr_clock_port, std::vector(sr_clock_port.get_width(), 0), true); + fp << ";" << std::endl; + + fp << "\t"; + fp << "while (" << generate_verilog_port(VERILOG_PORT_CONKT, start_sr_port) << ") begin"; + fp << std::endl; + + fp << "\t\t"; + fp << "#0.05 "; + print_verilog_register_connection(fp, sr_clock_port, sr_clock_port, true); + fp << ";" << std::endl; + + fp << "\t"; + fp << "end"; + fp << std::endl; + + fp << "\t"; + fp << generate_verilog_port_constant_values(sr_clock_port, std::vector(sr_clock_port.get_width(), 0), true); + fp << ";" << std::endl; +} + +/* Verilog codes to load bitstream from a bit file for memory bank using flatten BL/WLs */ +static +void print_verilog_full_testbench_ql_memory_bank_shift_register_bitstream(std::fstream& fp, + const std::string& bitstream_file, + const bool& fast_configuration, + const bool& bit_value_to_skip, + const ModuleManager& module_manager, + const ModuleId& top_module, + const FabricBitstream& fabric_bitstream) { + /* Validate the file stream */ + valid_file_stream(fp); + + /* Reorganize the fabric bitstream by the same address across regions */ + MemoryBankShiftRegisterFabricBitstream fabric_bits_by_addr = build_memory_bank_shift_register_fabric_bitstream(fabric_bitstream, + fast_configuration, + bit_value_to_skip); + + /* Feed address and data input pair one by one + * Note: the first cycle is reserved for programming reset + * We should give dummy values + */ + std::vector bl_head_ports; + for (const ConfigRegionId& region : module_manager.regions(top_module)) { + ModulePortId cur_bl_head_port_id = module_manager.find_module_port(top_module, + generate_regional_blwl_port_name(std::string(BL_SHIFT_REGISTER_CHAIN_HEAD_NAME), region)); + bl_head_ports.push_back(module_manager.module_port(top_module, cur_bl_head_port_id)); + } + + std::vector wl_head_ports; + for (const ConfigRegionId& region : module_manager.regions(top_module)) { + ModulePortId cur_wl_head_port_id = module_manager.find_module_port(top_module, + generate_regional_blwl_port_name(std::string(WL_SHIFT_REGISTER_CHAIN_HEAD_NAME), region)); + wl_head_ports.push_back(module_manager.module_port(top_module, cur_wl_head_port_id)); + } + + /* Calculate the total size of BL/WL ports */ + size_t bl_head_port_width = 0; + for (const BasicPort& bl_head_port : bl_head_ports) { + bl_head_port_width += bl_head_port.get_width(); + } + VTR_ASSERT(bl_head_port_width == fabric_bits_by_addr.bl_width()); + + size_t wl_head_port_width = 0; + for (const BasicPort& wl_head_port : wl_head_ports) { + wl_head_port_width += wl_head_port.get_width(); + } + VTR_ASSERT(wl_head_port_width == fabric_bits_by_addr.wl_width()); + + std::vector initial_bl_head_values(bl_head_port_width, 0); + std::vector initial_wl_head_values(wl_head_port_width, 0); + + /* Define a constant for the bitstream length */ + print_verilog_define_flag(fp, std::string(TOP_TB_BITSTREAM_LENGTH_VARIABLE), fabric_bits_by_addr.num_words()); + print_verilog_define_flag(fp, std::string(TOP_TB_BITSTREAM_WIDTH_VARIABLE), std::max(bl_head_port_width, wl_head_port_width)); + print_verilog_define_flag(fp, std::string(TOP_TB_BITSTREAM_BL_HEAD_WIDTH_VARIABLE), bl_head_port_width); + print_verilog_define_flag(fp, std::string(TOP_TB_BITSTREAM_WL_HEAD_WIDTH_VARIABLE), wl_head_port_width); + print_verilog_define_flag(fp, std::string(TOP_TB_BITSTREAM_BL_WORD_SIZE_VARIABLE), fabric_bits_by_addr.bl_word_size()); + print_verilog_define_flag(fp, std::string(TOP_TB_BITSTREAM_WL_WORD_SIZE_VARIABLE), fabric_bits_by_addr.wl_word_size()); + + /* Declare local variables for bitstream loading in Verilog */ + print_verilog_comment(fp, "----- Virtual memory to store the bitstream from external file -----"); + fp << "reg [0:`" << TOP_TB_BITSTREAM_WIDTH_VARIABLE << " - 1] "; + fp << TOP_TB_BITSTREAM_MEM_REG_NAME << "[0:`"; + fp << TOP_TB_BITSTREAM_LENGTH_VARIABLE << "*(`" << TOP_TB_BITSTREAM_BL_WORD_SIZE_VARIABLE; + fp << " + `"<< TOP_TB_BITSTREAM_WL_WORD_SIZE_VARIABLE << ") - 1];"; + fp << std::endl; + + fp << "reg [$clog2(`" << TOP_TB_BITSTREAM_LENGTH_VARIABLE << "):0] " << TOP_TB_BITSTREAM_INDEX_REG_NAME << ";" << std::endl; + + /* Register to enable/disable bl/wl shift register clocks */ + BasicPort start_bl_sr_port(TOP_TB_START_BL_SHIFT_REGISTER_PORT_NAME, 1); + fp << generate_verilog_port(VERILOG_PORT_REG, start_bl_sr_port) << ";" << std::endl; + BasicPort start_wl_sr_port(TOP_TB_START_WL_SHIFT_REGISTER_PORT_NAME, 1); + fp << generate_verilog_port(VERILOG_PORT_REG, start_wl_sr_port) << ";" << std::endl; + + /* Register to count bl/wl shift register clocks */ + fp << "integer " << TOP_TB_BL_SHIFT_REGISTER_COUNT_PORT_NAME << ";" << std::endl; + fp << "integer " << TOP_TB_WL_SHIFT_REGISTER_COUNT_PORT_NAME << ";" << std::endl; + + print_verilog_comment(fp, "----- Preload bitstream file to a virtual memory -----"); + fp << "initial begin" << std::endl; + fp << "\t"; + fp << "$readmemb(\"" << bitstream_file << "\", " << TOP_TB_BITSTREAM_MEM_REG_NAME << ");"; + fp << std::endl; + + print_verilog_comment(fp, "----- Bit-Line head port default input -----"); + fp << "\t"; + fp << generate_verilog_ports_constant_values(bl_head_ports, initial_bl_head_values); + fp << ";"; + fp << std::endl; + + print_verilog_comment(fp, "----- Word-Line head port default input -----"); + fp << "\t"; + fp << generate_verilog_ports_constant_values(wl_head_ports, initial_wl_head_values); + fp << ";"; + fp << std::endl; + + fp << "\t"; + fp << TOP_TB_BITSTREAM_INDEX_REG_NAME << " <= 0"; + fp << ";"; + fp << std::endl; + + fp << "\t"; + fp << generate_verilog_port_constant_values(start_bl_sr_port, std::vector(start_bl_sr_port.get_width(), 0), true); + fp << ";"; + fp << std::endl; + + fp << "\t"; + fp << generate_verilog_port_constant_values(start_wl_sr_port, std::vector(start_wl_sr_port.get_width(), 0), true); + fp << ";"; + fp << std::endl; + + fp << "end"; + fp << std::endl; + + + BasicPort prog_clock_port(std::string(TOP_TB_PROG_CLOCK_PORT_NAME) + std::string(TOP_TB_CLOCK_REG_POSTFIX), 1); + BasicPort bl_sr_clock_port(TOP_TB_BL_SHIFT_REGISTER_CLOCK_PORT_NAME, 1); + BasicPort wl_sr_clock_port(TOP_TB_WL_SHIFT_REGISTER_CLOCK_PORT_NAME, 1); + + print_verilog_comment(fp, "----- BL Shift register clock generator -----"); + print_verilog_full_testbench_ql_memory_bank_shift_register_clock_generator(fp, prog_clock_port, start_bl_sr_port, bl_sr_clock_port); + + print_verilog_comment(fp, "----- WL Shift register clock generator -----"); + print_verilog_full_testbench_ql_memory_bank_shift_register_clock_generator(fp, prog_clock_port, start_wl_sr_port, wl_sr_clock_port); + + print_verilog_comment(fp, "----- Begin bitstream loading during configuration phase -----"); + fp << "always"; + fp << " @(negedge " << generate_verilog_port(VERILOG_PORT_CONKT, prog_clock_port) << ")"; + fp << " begin"; + fp << std::endl; + + /* Finished all the configuration words, raise the configuration done signal */ + fp << "\t"; + fp << "if ("; + fp << TOP_TB_BITSTREAM_INDEX_REG_NAME; + fp << " >= "; + fp << "`" << TOP_TB_BITSTREAM_LENGTH_VARIABLE; + fp << ") begin"; + fp << std::endl; + + BasicPort config_done_port(std::string(TOP_TB_CONFIG_DONE_PORT_NAME), 1); + fp << "\t\t"; + std::vector config_done_final_values(config_done_port.get_width(), 1); + fp << generate_verilog_port_constant_values(config_done_port, config_done_final_values, true); + fp << ";" << std::endl; + + fp << "\t"; + fp << "end else begin"; + fp << std::endl; + + /* When there are still configuration words to be load, start the BL and WL shift register clock */ + fp << "\t\t"; + fp << generate_verilog_port_constant_values(start_bl_sr_port, std::vector(start_bl_sr_port.get_width(), 0), true); + fp << ";"; + fp << std::endl; + + fp << "\t\t"; + fp << generate_verilog_port_constant_values(start_wl_sr_port, std::vector(start_wl_sr_port.get_width(), 0), true); + fp << ";"; + fp << std::endl; + + fp << "\t\t"; + fp << TOP_TB_BL_SHIFT_REGISTER_COUNT_PORT_NAME << " = 0;"; + fp << std::endl; + + fp << "\t\t"; + fp << TOP_TB_WL_SHIFT_REGISTER_COUNT_PORT_NAME << " = 0;"; + fp << std::endl; + + fp << "\t\t"; + fp << TOP_TB_BITSTREAM_INDEX_REG_NAME; + fp << " <= "; + fp << TOP_TB_BITSTREAM_INDEX_REG_NAME << " + 1"; + fp << ";" << std::endl; + + fp << "\t"; + fp << "end"; + fp << std::endl; + + fp << "end"; + fp << std::endl; + + /* Load data to BL shift register chains */ + fp << "always"; + fp << " @(negedge " << generate_verilog_port(VERILOG_PORT_CONKT, bl_sr_clock_port) << ")"; + fp << " begin"; + fp << std::endl; + + fp << "\t\t"; + fp << generate_verilog_port_constant_values(start_bl_sr_port, std::vector(start_bl_sr_port.get_width(), 0), true); + fp << std::endl; + + fp << "\t\t"; + fp << TOP_TB_BL_SHIFT_REGISTER_COUNT_PORT_NAME << " = 0;"; + fp << std::endl; + + fp << "\t"; + fp << "end else begin" << std::endl; + + fp << "\t\t"; + fp << generate_verilog_ports(bl_head_ports); + fp << " <= "; + fp << TOP_TB_BITSTREAM_MEM_REG_NAME << "["; + fp << TOP_TB_BITSTREAM_INDEX_REG_NAME << "*(`" << TOP_TB_BITSTREAM_BL_WORD_SIZE_VARIABLE << "+ `" << TOP_TB_BITSTREAM_BL_WORD_SIZE_VARIABLE << ") + " << TOP_TB_BL_SHIFT_REGISTER_COUNT_PORT_NAME; + fp << "];" << std::endl; + + fp << "\t"; + fp << "end"; + fp << std::endl; + + fp << "end"; + fp << std::endl; + + /* Load data to WL shift register chains */ + fp << "always"; + fp << " @(negedge " << generate_verilog_port(VERILOG_PORT_CONKT, wl_sr_clock_port) << ")"; + fp << " begin"; + fp << std::endl; + + fp << "\t\t"; + fp << generate_verilog_port_constant_values(start_wl_sr_port, std::vector(start_wl_sr_port.get_width(), 0), true); + fp << std::endl; + + fp << "\t\t"; + fp << TOP_TB_WL_SHIFT_REGISTER_COUNT_PORT_NAME << " = 0;"; + fp << std::endl; + + fp << "\t"; + fp << "end else begin" << std::endl; + + fp << "\t\t"; + fp << generate_verilog_ports(wl_head_ports); + fp << " <= "; + fp << TOP_TB_BITSTREAM_MEM_REG_NAME << "["; + fp << TOP_TB_BITSTREAM_INDEX_REG_NAME << "*(`" << TOP_TB_BITSTREAM_WL_WORD_SIZE_VARIABLE << "+ `" << TOP_TB_BITSTREAM_WL_WORD_SIZE_VARIABLE << ") + `" << TOP_TB_BITSTREAM_WL_WORD_SIZE_VARIABLE << " + " << TOP_TB_WL_SHIFT_REGISTER_COUNT_PORT_NAME; + fp << "];" << std::endl; + + fp << "\t"; + fp << "end"; + fp << std::endl; + + fp << "end"; + fp << std::endl; + + print_verilog_comment(fp, "----- End bitstream loading during configuration phase -----"); +} + + /* Verilog codes to load bitstream from a bit file for memory bank using BL/WL decoders */ static void print_verilog_full_testbench_ql_memory_bank_decoder_bitstream(std::fstream& fp, @@ -468,6 +770,13 @@ void print_verilog_full_testbench_ql_memory_bank_bitstream(std::fstream& fp, bit_value_to_skip, module_manager, top_module, fabric_bitstream); + } else if ( (BLWL_PROTOCOL_SHIFT_REGISTER == config_protocol.bl_protocol_type()) + && (BLWL_PROTOCOL_SHIFT_REGISTER == config_protocol.wl_protocol_type()) ) { + print_verilog_full_testbench_ql_memory_bank_shift_register_bitstream(fp, bitstream_file, + fast_configuration, + bit_value_to_skip, + module_manager, top_module, + fabric_bitstream); } } diff --git a/openfpga/src/fpga_verilog/verilog_top_testbench_memory_bank.h b/openfpga/src/fpga_verilog/verilog_top_testbench_memory_bank.h index 87ddf3ac5..194f0b86f 100644 --- a/openfpga/src/fpga_verilog/verilog_top_testbench_memory_bank.h +++ b/openfpga/src/fpga_verilog/verilog_top_testbench_memory_bank.h @@ -34,6 +34,15 @@ void print_verilog_top_testbench_ql_memory_bank_port(std::fstream& fp, const ModuleId& top_module, const ConfigProtocol& config_protocol); +/** + * @brief Generate the Verilog codes for a shift register clocks that controls BL/WL protocols + */ +void print_verilog_full_testbench_ql_memory_bank_shift_register_clock_generator(std::fstream& fp, + const BasicPort& prog_clock_port, + const BasicPort& start_sr_port, + const BasicPort& sr_clock_port); + + /** * @brief Print stimulus for a FPGA fabric with a memory bank configuration protocol * where configuration bits are programming in serial (one by one)