[Tool] Support region-based bitstream in fabric bitstream data base and Verilog testbenches

This commit is contained in:
tangxifan 2020-09-29 12:22:10 -06:00
parent 180d72f3e5
commit e988e35f81
6 changed files with 278 additions and 64 deletions

View File

@ -38,29 +38,58 @@ static
void rec_build_module_fabric_dependent_chain_bitstream(const BitstreamManager& bitstream_manager,
const ConfigBlockId& parent_block,
const ModuleManager& module_manager,
const ModuleId& top_module,
const ModuleId& parent_module,
FabricBitstream& fabric_bitstream) {
const ConfigRegionId& config_region,
FabricBitstream& fabric_bitstream,
const FabricBitRegionId& fabric_bitstream_region) {
/* Depth-first search: if we have any children in the parent_block,
* we dive to the next level first!
*/
if (0 < bitstream_manager.block_children(parent_block).size()) {
for (size_t child_id = 0; child_id < module_manager.configurable_children(parent_module).size(); ++child_id) {
ModuleId child_module = module_manager.configurable_children(parent_module)[child_id];
size_t child_instance = module_manager.configurable_child_instances(parent_module)[child_id];
/* Get the instance name and ensure it is not empty */
std::string instance_name = module_manager.instance_name(parent_module, child_module, child_instance);
/* Find the child block that matches the instance name! */
ConfigBlockId child_block = bitstream_manager.find_child_block(parent_block, instance_name);
/* We must have one valid block id! */
if (true != bitstream_manager.valid_block_id(child_block))
VTR_ASSERT(true == bitstream_manager.valid_block_id(child_block));
if (parent_module == top_module) {
for (size_t child_id = 0; child_id < module_manager.region_configurable_children(parent_module, config_region).size(); ++child_id) {
ModuleId child_module = module_manager.region_configurable_children(parent_module, config_region)[child_id];
size_t child_instance = module_manager.region_configurable_child_instances(parent_module, config_region)[child_id];
/* Get the instance name and ensure it is not empty */
std::string instance_name = module_manager.instance_name(parent_module, child_module, child_instance);
/* Find the child block that matches the instance name! */
ConfigBlockId child_block = bitstream_manager.find_child_block(parent_block, instance_name);
/* We must have one valid block id! */
if (true != bitstream_manager.valid_block_id(child_block))
VTR_ASSERT(true == bitstream_manager.valid_block_id(child_block));
/* Go recursively */
rec_build_module_fabric_dependent_chain_bitstream(bitstream_manager, child_block,
module_manager, child_module,
fabric_bitstream);
/* Go recursively */
rec_build_module_fabric_dependent_chain_bitstream(bitstream_manager, child_block,
module_manager, top_module,
child_module,
config_region,
fabric_bitstream,
fabric_bitstream_region);
}
} else {
for (size_t child_id = 0; child_id < module_manager.configurable_children(parent_module).size(); ++child_id) {
ModuleId child_module = module_manager.configurable_children(parent_module)[child_id];
size_t child_instance = module_manager.configurable_child_instances(parent_module)[child_id];
/* Get the instance name and ensure it is not empty */
std::string instance_name = module_manager.instance_name(parent_module, child_module, child_instance);
/* Find the child block that matches the instance name! */
ConfigBlockId child_block = bitstream_manager.find_child_block(parent_block, instance_name);
/* We must have one valid block id! */
if (true != bitstream_manager.valid_block_id(child_block))
VTR_ASSERT(true == bitstream_manager.valid_block_id(child_block));
/* Go recursively */
rec_build_module_fabric_dependent_chain_bitstream(bitstream_manager, child_block,
module_manager, top_module,
child_module,
config_region,
fabric_bitstream,
fabric_bitstream_region);
}
}
/* Ensure that there should be no configuration bits in the parent block */
VTR_ASSERT(0 == bitstream_manager.block_bits(parent_block).size());
@ -71,7 +100,8 @@ void rec_build_module_fabric_dependent_chain_bitstream(const BitstreamManager& b
* And then, we can return
*/
for (const ConfigBitId& config_bit : bitstream_manager.block_bits(parent_block)) {
fabric_bitstream.add_bit(config_bit);
FabricBitId fabric_bit = fabric_bitstream.add_bit(config_bit);
fabric_bitstream.add_bit_to_region(fabric_bitstream_region, fabric_bit);
}
}
@ -382,19 +412,32 @@ void build_module_fabric_dependent_bitstream(const ConfigProtocol& config_protoc
/* Reserve bits before build-up */
fabric_bitstream.reserve_bits(bitstream_manager.num_bits());
rec_build_module_fabric_dependent_chain_bitstream(bitstream_manager, top_block,
module_manager, top_module,
fabric_bitstream);
for (const ConfigRegionId& config_region : module_manager.regions(top_module)) {
FabricBitRegionId fabric_bitstream_region = fabric_bitstream.add_region();
rec_build_module_fabric_dependent_chain_bitstream(bitstream_manager, top_block,
module_manager, top_module,
top_module,
config_region,
fabric_bitstream,
fabric_bitstream_region);
}
break;
}
case CONFIG_MEM_SCAN_CHAIN: {
/* Reserve bits before build-up */
fabric_bitstream.reserve_bits(bitstream_manager.num_bits());
rec_build_module_fabric_dependent_chain_bitstream(bitstream_manager, top_block,
module_manager, top_module,
fabric_bitstream);
fabric_bitstream.reverse();
for (const ConfigRegionId& config_region : module_manager.regions(top_module)) {
FabricBitRegionId fabric_bitstream_region = fabric_bitstream.add_region();
rec_build_module_fabric_dependent_chain_bitstream(bitstream_manager, top_block,
module_manager, top_module,
top_module,
config_region,
fabric_bitstream,
fabric_bitstream_region);
fabric_bitstream.reverse_region_bits(fabric_bitstream_region);
}
break;
}
case CONFIG_MEM_MEMORY_BANK: {

View File

@ -50,7 +50,7 @@ std::vector<FabricBitId> FabricBitstream::region_bits(const FabricBitRegionId& r
/* Ensure a valid id */
VTR_ASSERT(true == valid_region_id(region_id));
return region_bits_[region_id];
return region_bit_ids_[region_id];
}
/******************************************************************************
@ -185,7 +185,24 @@ void FabricBitstream::set_wl_address_length(const size_t& length) {
}
void FabricBitstream::reserve_regions(const size_t& num_regions) {
region_bits_.reserve(num_regions);
region_bit_ids_.reserve(num_regions);
}
FabricBitRegionId FabricBitstream::add_region() {
FabricBitRegionId region = FabricBitRegionId(num_regions_);
/* Add a new bit, and allocate associated data structures */
num_regions_++;
region_bit_ids_.emplace_back();
return region;
}
void FabricBitstream::add_bit_to_region(const FabricBitRegionId& region_id,
const FabricBitId& bit_id) {
VTR_ASSERT(true == valid_region_id(region_id));
VTR_ASSERT(true == valid_bit_id(bit_id));
region_bit_ids_[region_id].push_back(bit_id);
}
void FabricBitstream::reverse() {
@ -204,7 +221,7 @@ void FabricBitstream::reverse() {
void FabricBitstream::reverse_region_bits(const FabricBitRegionId& region_id) {
VTR_ASSERT(true == valid_region_id(region_id));
std::reverse(region_bits_[region_id].begin(), region_bits_[region_id].end());
std::reverse(region_bit_ids_[region_id].begin(), region_bit_ids_[region_id].end());
}
/******************************************************************************

View File

@ -149,6 +149,12 @@ class FabricBitstream {
/* Reserve regions */
void reserve_regions(const size_t& num_regions);
/* Add a new configuration region */
FabricBitRegionId add_region();
void add_bit_to_region(const FabricBitRegionId& region_id,
const FabricBitId& bit_id);
/* Reserve bits by region */
void reverse_region_bits(const FabricBitRegionId& region_id);
@ -182,7 +188,7 @@ class FabricBitstream {
/* Unique id of a region in the Bitstream */
size_t num_regions_;
std::unordered_set<FabricBitRegionId> invalid_region_ids_;
vtr::vector<FabricBitRegionId, std::vector<FabricBitId>> region_bits_;
vtr::vector<FabricBitRegionId, std::vector<FabricBitId>> region_bit_ids_;
/* Unique id of a bit in the Bitstream */
size_t num_bits_;

View File

@ -23,6 +23,8 @@
#include "simulation_utils.h"
#include "openfpga_atom_netlist_utils.h"
#include "fabric_bitstream_utils.h"
#include "verilog_constants.h"
#include "verilog_writer_utils.h"
#include "verilog_testbench_utils.h"
@ -136,18 +138,22 @@ void print_verilog_top_testbench_flatten_memory_port(std::fstream& fp,
* Print local wires for configuration chain protocols
*******************************************************************/
static
void print_verilog_top_testbench_config_chain_port(std::fstream& fp) {
void print_verilog_top_testbench_config_chain_port(std::fstream& fp,
const ModuleManager& module_manager,
const ModuleId& top_module) {
/* Validate the file stream */
valid_file_stream(fp);
/* Print the head of configuraion-chains here */
print_verilog_comment(fp, std::string("---- Configuration-chain head -----"));
BasicPort config_chain_head_port(generate_configuration_chain_head_name(), 1);
ModulePortId cc_head_port_id = module_manager.find_module_port(top_module, generate_configuration_chain_head_name());
BasicPort config_chain_head_port = module_manager.module_port(top_module, cc_head_port_id);
fp << generate_verilog_port(VERILOG_PORT_REG, config_chain_head_port) << ";" << std::endl;
/* Print the tail of configuration-chains here */
print_verilog_comment(fp, std::string("---- Configuration-chain tail -----"));
BasicPort config_chain_tail_port(generate_configuration_chain_tail_name(), 1);
ModulePortId cc_tail_port_id = module_manager.find_module_port(top_module, generate_configuration_chain_tail_name());
BasicPort config_chain_tail_port = module_manager.module_port(top_module, cc_tail_port_id);
fp << generate_verilog_port(VERILOG_PORT_WIRE, config_chain_tail_port) << ";" << std::endl;
}
@ -271,7 +277,7 @@ void print_verilog_top_testbench_config_protocol_port(std::fstream& fp,
print_verilog_top_testbench_flatten_memory_port(fp, module_manager, top_module);
break;
case CONFIG_MEM_SCAN_CHAIN:
print_verilog_top_testbench_config_chain_port(fp);
print_verilog_top_testbench_config_chain_port(fp, module_manager, top_module);
break;
case CONFIG_MEM_MEMORY_BANK:
print_verilog_top_testbench_memory_bank_port(fp, module_manager, top_module);
@ -659,7 +665,10 @@ size_t calculate_num_config_clock_cycles(const e_config_protocol_type& sram_orgz
const bool& bit_value_to_skip,
const BitstreamManager& bitstream_manager,
const FabricBitstream& fabric_bitstream) {
size_t num_config_clock_cycles = 1 + fabric_bitstream.num_bits();
/* Find the longest regional bitstream */
size_t regional_bitstream_max_size = find_fabric_regional_bitstream_max_size(fabric_bitstream);
size_t num_config_clock_cycles = 1 + regional_bitstream_max_size;
/* Branch on the type of configuration protocol */
switch (sram_orgz_type) {
@ -670,23 +679,24 @@ size_t calculate_num_config_clock_cycles(const e_config_protocol_type& sram_orgz
num_config_clock_cycles = 2;
break;
case CONFIG_MEM_SCAN_CHAIN:
/* For fast configuraiton, the bitstream size counts from the first bit '1' */
/* For fast configuration, the bitstream size counts from the first bit '1' */
if (true == fast_configuration) {
size_t full_num_config_clock_cycles = num_config_clock_cycles;
size_t num_bits_to_skip = 0;
for (const FabricBitId& bit_id : fabric_bitstream.bits()) {
if (bit_value_to_skip != bitstream_manager.bit_value(fabric_bitstream.config_bit(bit_id))) {
break;
}
num_bits_to_skip++;
}
/* For fast configuration, the number of bits to be skipped
* depends on each regional bitstream
* For example:
* Region 0: 000000001111101010
* Region 1: 00000011010101
* Region 2: 0010101111000110
* The number of bits that can be skipped is limited by Region 2
*/
size_t num_bits_to_skip = find_configuration_chain_fabric_bitstream_size_to_be_skipped(fabric_bitstream, bitstream_manager, bit_value_to_skip);
num_config_clock_cycles = full_num_config_clock_cycles - num_bits_to_skip;
num_config_clock_cycles = 1 + regional_bitstream_max_size - num_bits_to_skip;
VTR_LOG("Fast configuration reduces number of configuration clock cycles from %lu to %lu (compression_rate = %f%)\n",
full_num_config_clock_cycles,
1 + regional_bitstream_max_size,
num_config_clock_cycles,
100. * ((float)num_config_clock_cycles / (float)full_num_config_clock_cycles - 1.));
100. * ((float)num_config_clock_cycles / (float)(1 + regional_bitstream_max_size) - 1.));
}
break;
case CONFIG_MEM_MEMORY_BANK:
@ -770,14 +780,17 @@ void print_verilog_top_testbench_benchmark_instance(std::fstream& fp,
* During each programming cycle, we feed the input of scan chain with a memory bit
*******************************************************************/
static
void print_verilog_top_testbench_load_bitstream_task_configuration_chain(std::fstream& fp) {
void print_verilog_top_testbench_load_bitstream_task_configuration_chain(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);
BasicPort cc_head_port(generate_configuration_chain_head_name(), 1);
BasicPort cc_head_value(generate_configuration_chain_head_name() + std::string("_val"), 1);
ModulePortId cc_head_port_id = module_manager.find_module_port(top_module, generate_configuration_chain_head_name());
BasicPort cc_head_port = module_manager.module_port(top_module, cc_head_port_id);
BasicPort cc_head_value(generate_configuration_chain_head_name() + std::string("_val"), cc_head_port.get_width());
/* Add an empty line as splitter */
fp << std::endl;
@ -962,7 +975,9 @@ void print_verilog_top_testbench_load_bitstream_task(std::fstream& fp,
/* No need to have a specific task. Loading is done in 1 clock cycle */
break;
case CONFIG_MEM_SCAN_CHAIN:
print_verilog_top_testbench_load_bitstream_task_configuration_chain(fp);
print_verilog_top_testbench_load_bitstream_task_configuration_chain(fp,
module_manager,
top_module);
break;
case CONFIG_MEM_MEMORY_BANK:
print_verilog_top_testbench_load_bitstream_task_memory_bank(fp,
@ -1359,6 +1374,8 @@ static
void print_verilog_top_testbench_configuration_chain_bitstream(std::fstream& fp,
const bool& fast_configuration,
const bool& bit_value_to_skip,
const ModuleManager& module_manager,
const ModuleId& top_module,
const BitstreamManager& bitstream_manager,
const FabricBitstream& fabric_bitstream) {
/* Validate the file stream */
@ -1370,7 +1387,8 @@ void print_verilog_top_testbench_configuration_chain_bitstream(std::fstream& fp,
* We do not care the value of scan_chain head during the first programming cycle
* It is reset anyway
*/
BasicPort config_chain_head_port(generate_configuration_chain_head_name(), 1);
ModulePortId cc_head_port_id = module_manager.find_module_port(top_module, generate_configuration_chain_head_name());
BasicPort config_chain_head_port = module_manager.module_port(top_module, cc_head_port_id);
std::vector<size_t> initial_values(config_chain_head_port.get_width(), 0);
print_verilog_comment(fp, "----- Begin bitstream loading during configuration phase -----");
@ -1383,27 +1401,60 @@ void print_verilog_top_testbench_configuration_chain_bitstream(std::fstream& fp,
fp << std::endl;
/* Find the longest bitstream */
size_t regional_bitstream_max_size = find_fabric_regional_bitstream_max_size(fabric_bitstream);
/* For fast configuration, the bitstream size counts from the first bit '1' */
size_t num_bits_to_skip = 0;
if (true == fast_configuration) {
num_bits_to_skip = find_configuration_chain_fabric_bitstream_size_to_be_skipped(fabric_bitstream, bitstream_manager, bit_value_to_skip);
}
VTR_ASSERT(num_bits_to_skip < regional_bitstream_max_size);
/* Reorganize the regional bitstreams to be the same size */
std::vector<std::vector<bool>> regional_bitstreams;
regional_bitstreams.reserve(fabric_bitstream.regions().size());
for (const FabricBitRegionId& region : fabric_bitstream.regions()) {
std::vector<bool> curr_regional_bitstream;
curr_regional_bitstream.resize(regional_bitstream_max_size, false);
/* Starting index should consider the offset between the current bitstream size and
* the maximum size of regional bitstream
*/
size_t offset = regional_bitstream_max_size - fabric_bitstream.region_bits(region).size();
for (const FabricBitId& bit_id : fabric_bitstream.region_bits(region)) {
curr_regional_bitstream[offset] = bitstream_manager.bit_value(fabric_bitstream.config_bit(bit_id));
offset++;
}
VTR_ASSERT(offset == regional_bitstream_max_size);
/* Add the adapt sub-bitstream */
regional_bitstreams.push_back(curr_regional_bitstream);
}
/* Attention: when the fast configuration is enabled, we will start from the first bit '1'
* This requires a reset signal (as we forced in the first clock cycle)
*
* Note that bitstream may come from different regions
* The bitstream value to be loaded should be organized as follows
*
* cycleA
* |
* Region 0: 0|00000001111101010
* Region 1: | 00000011010101
* Region 2: | 0010101111000110
*
* Zero bits will be added to the head of those bitstreams are shorter
* than the longest bitstream
*/
bool start_config = false;
for (const FabricBitId& bit_id : fabric_bitstream.bits()) {
if ( (false == start_config)
&& (bit_value_to_skip != bitstream_manager.bit_value(fabric_bitstream.config_bit(bit_id)))) {
start_config = true;
}
/* In fast configuration mode, we do not output anything
* until we have to (the first bit '1' detected)
*/
if ( (true == fast_configuration)
&& (false == start_config)) {
continue;
for (size_t ibit = num_bits_to_skip; ibit < regional_bitstream_max_size; ++ibit) {
std::vector<size_t> curr_cc_head_val;
curr_cc_head_val.reserve(fabric_bitstream.regions().size());
for (const auto& region_bitstream : regional_bitstreams) {
curr_cc_head_val.push_back((size_t)region_bitstream[ibit]);
}
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;
fp << "(" << generate_verilog_constant_values(curr_cc_head_val) << ");" << std::endl;
}
/* Raise the flag of configuration done when bitstream loading is complete */
@ -1654,6 +1705,7 @@ void print_verilog_top_testbench_bitstream(std::fstream& fp,
case CONFIG_MEM_SCAN_CHAIN:
print_verilog_top_testbench_configuration_chain_bitstream(fp, fast_configuration,
bit_value_to_skip,
module_manager, top_module,
bitstream_manager, fabric_bitstream);
break;
case CONFIG_MEM_MEMORY_BANK:

View File

@ -0,0 +1,67 @@
/************************************************************************
* Function to perform fundamental operation for fabric bitstream class
* These functions are not universal methods for the FabricBitstream class
* They are made to ease the development in some specific purposes
* Please classify such functions in this file
***********************************************************************/
#include <algorithm>
/* Headers from vtrutil library */
#include "vtr_assert.h"
#include "vtr_log.h"
#include "fabric_bitstream_utils.h"
/* begin namespace openfpga */
namespace openfpga {
/********************************************************************
* Find the longest bitstream size of a fabric bitstream
*******************************************************************/
size_t find_fabric_regional_bitstream_max_size(const FabricBitstream& fabric_bitstream) {
size_t regional_bitstream_max_size = 0;
/* Find the longest regional bitstream */
for (const auto& region : fabric_bitstream.regions()) {
if (regional_bitstream_max_size < fabric_bitstream.region_bits(region).size()) {
regional_bitstream_max_size = fabric_bitstream.region_bits(region).size();
}
}
return regional_bitstream_max_size;
}
/********************************************************************
* For fast configuration, the number of bits to be skipped
* depends on each regional bitstream
* For example:
* Region 0: 000000001111101010
* Region 1: 00000011010101
* Region 2: 0010101111000110
* The number of bits that can be skipped is limited by Region 2
* Find the longest bitstream size of a fabric bitstream
*******************************************************************/
size_t find_configuration_chain_fabric_bitstream_size_to_be_skipped(const FabricBitstream& fabric_bitstream,
const BitstreamManager& bitstream_manager,
const bool& bit_value_to_skip) {
size_t regional_bitstream_max_size = find_fabric_regional_bitstream_max_size(fabric_bitstream);
size_t num_bits_to_skip = size_t(-1);
for (const auto& region : fabric_bitstream.regions()) {
size_t curr_region_num_bits_to_skip = 0;
for (const FabricBitId& bit_id : fabric_bitstream.region_bits(region)) {
if (bit_value_to_skip != bitstream_manager.bit_value(fabric_bitstream.config_bit(bit_id))) {
break;
}
curr_region_num_bits_to_skip++;
}
/* For regional bitstream which is short than the longest region bitstream,
* The number of bits to skip
*/
curr_region_num_bits_to_skip += regional_bitstream_max_size - fabric_bitstream.region_bits(region).size();
num_bits_to_skip = std::min(curr_region_num_bits_to_skip, num_bits_to_skip);
}
return num_bits_to_skip;
}
} /* end namespace openfpga */

View File

@ -0,0 +1,29 @@
/********************************************************************
* Header file for fabric_bitstream_utils.cpp
*******************************************************************/
#ifndef FABRIC_BITSTREAM_UTILS_H
#define FABRIC_BITSTREAM_UTILS_H
/********************************************************************
* Include header files that are required by function declaration
*******************************************************************/
#include <vector>
#include "bitstream_manager.h"
#include "fabric_bitstream.h"
/********************************************************************
* Function declaration
*******************************************************************/
/* begin namespace openfpga */
namespace openfpga {
size_t find_fabric_regional_bitstream_max_size(const FabricBitstream& fabric_bitstream);
size_t find_configuration_chain_fabric_bitstream_size_to_be_skipped(const FabricBitstream& fabric_bitstream,
const BitstreamManager& bitstream_manager,
const bool& bit_value_to_skip);
} /* end namespace openfpga */
#endif