Merge pull request #1756 from chungshien/openfpga-overwrite-bits
Openfpga support setting bitstream bit manually
This commit is contained in:
commit
6ea1feb445
|
@ -16,6 +16,9 @@ This can define a hard-coded bitstream for a reconfigurable resource in FPGA fab
|
|||
<non_fabric name="<string>" file="<string>">
|
||||
<pb name="<string>" type="<string>" content="<string>"/>
|
||||
</non_fabric>
|
||||
<overwrite_bitstream>
|
||||
<bit value="<0 or 1>" path="<string>"/>
|
||||
</overwrite_bitstream>
|
||||
</openfpga_bitstream_setting>
|
||||
|
||||
pb_type-related Settings
|
||||
|
@ -75,7 +78,7 @@ The following syntax are applicable to the XML definition tagged by ``interconne
|
|||
The default path can be either ``iopad.inpad`` or ``ff.Q`` which corresponds to the first input and the second input respectively.
|
||||
|
||||
non_fabric-related Settings
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
This is special syntax to extract PB defined parameter or attribute and save the data into dedicated JSON file outside of fabric bitstream
|
||||
|
||||
|
@ -97,7 +100,7 @@ The following syntax are applicable to the XML definition tagged by ``non_fabric
|
|||
|
||||
file="bram.json"
|
||||
|
||||
.. option:: ``pb`` child element name="<string: pb_type child name>"
|
||||
.. option:: pb child element name="<string: pb_type child name>"
|
||||
|
||||
Together with ``pb_type`` top level name, that is the source of the ``pb_type`` bitstream
|
||||
|
||||
|
@ -112,6 +115,33 @@ The following syntax are applicable to the XML definition tagged by ``non_fabric
|
|||
|
||||
The final ``pb_type`` name is "bram.bram_lr[mem_36K_tdp].mem_36K"
|
||||
|
||||
.. option:: ``pb`` child element content="<string>"
|
||||
.. option:: pb child element content="<string>"
|
||||
|
||||
The content of the ``pb_type`` data to be extracted. For example, ``content=".param INIT_i"`` means that the data will be extracted from the ``.param INIT_i`` line defined under the ``.blif model``.
|
||||
|
||||
overwrite_bitstream-related Settings
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
This is to allow user to set value of a list of bits which is represented using full path in the hierarchy of FPGA fabric
|
||||
|
||||
This ``overwrite_bitstream`` settings has the highest priority than loading any external bitstream file
|
||||
|
||||
Each bit to overwrite is represented by one ``bit`` child node/tag
|
||||
|
||||
The following syntax are applicable to the XML definition tagged by ``bit`` node under ``overwrite_bitstream`` setting.
|
||||
|
||||
.. option:: value="<0 or 1>"
|
||||
|
||||
The boolean ``0`` or ``1`` that will be set. For example,
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
value="0"
|
||||
|
||||
.. option:: path="<string>"
|
||||
|
||||
``path`` represents the location of this block in FPGA fabric, i.e., the full path in the hierarchy of FPGA fabric.
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
path="fpga_top.grid_clb_1__2_.logical_tile_clb_mode_clb__0.mem_fle_9_in_5[0]"
|
||||
|
|
|
@ -24,6 +24,12 @@ BitstreamSetting::interconnect_settings() const {
|
|||
interconnect_setting_ids_.end());
|
||||
}
|
||||
|
||||
BitstreamSetting::overwrite_bitstream_range
|
||||
BitstreamSetting::overwrite_bitstreams() const {
|
||||
return vtr::make_range(overwrite_bitstream_ids_.begin(),
|
||||
overwrite_bitstream_ids_.end());
|
||||
}
|
||||
|
||||
/************************************************************************
|
||||
* Constructors
|
||||
***********************************************************************/
|
||||
|
@ -106,6 +112,18 @@ std::vector<NonFabricBitstreamSetting> BitstreamSetting::non_fabric() const {
|
|||
return non_fabric_;
|
||||
}
|
||||
|
||||
std::string BitstreamSetting::overwrite_bitstream_path(
|
||||
const OverwriteBitstreamId& id) const {
|
||||
VTR_ASSERT(true == valid_overwrite_bitstream_id(id));
|
||||
return overwrite_bitstream_paths_[id];
|
||||
}
|
||||
|
||||
bool BitstreamSetting::overwrite_bitstream_value(
|
||||
const OverwriteBitstreamId& id) const {
|
||||
VTR_ASSERT(true == valid_overwrite_bitstream_id(id));
|
||||
return overwrite_bitstream_values_[id];
|
||||
}
|
||||
|
||||
/************************************************************************
|
||||
* Public Mutators
|
||||
***********************************************************************/
|
||||
|
@ -178,6 +196,21 @@ void BitstreamSetting::add_non_fabric_pb(const std::string& pb,
|
|||
}
|
||||
}
|
||||
|
||||
OverwriteBitstreamId BitstreamSetting::add_overwrite_bitstream(
|
||||
const std::string& path, const bool& value) {
|
||||
VTR_ASSERT(path.size());
|
||||
VTR_ASSERT(overwrite_bitstream_ids_.size() ==
|
||||
overwrite_bitstream_paths_.size());
|
||||
VTR_ASSERT(overwrite_bitstream_paths_.size() ==
|
||||
overwrite_bitstream_values_.size());
|
||||
OverwriteBitstreamId id =
|
||||
OverwriteBitstreamId(overwrite_bitstream_ids_.size());
|
||||
overwrite_bitstream_ids_.push_back(id);
|
||||
overwrite_bitstream_paths_.push_back(path);
|
||||
overwrite_bitstream_values_.push_back(value);
|
||||
return id;
|
||||
}
|
||||
|
||||
/************************************************************************
|
||||
* Public Validators
|
||||
***********************************************************************/
|
||||
|
@ -194,4 +227,14 @@ bool BitstreamSetting::valid_bitstream_interconnect_setting_id(
|
|||
interconnect_setting_ids_[interconnect_setting_id]);
|
||||
}
|
||||
|
||||
bool BitstreamSetting::valid_overwrite_bitstream_id(
|
||||
const OverwriteBitstreamId& id) const {
|
||||
VTR_ASSERT(overwrite_bitstream_ids_.size() ==
|
||||
overwrite_bitstream_paths_.size());
|
||||
VTR_ASSERT(overwrite_bitstream_paths_.size() ==
|
||||
overwrite_bitstream_values_.size());
|
||||
return (size_t(id) < overwrite_bitstream_ids_.size()) &&
|
||||
(id == overwrite_bitstream_ids_[id]);
|
||||
}
|
||||
|
||||
} // namespace openfpga
|
||||
|
|
|
@ -61,11 +61,15 @@ class BitstreamSetting {
|
|||
typedef vtr::vector<BitstreamInterconnectSettingId,
|
||||
BitstreamInterconnectSettingId>::const_iterator
|
||||
bitstream_interconnect_setting_iterator;
|
||||
typedef vtr::vector<OverwriteBitstreamId,
|
||||
OverwriteBitstreamId>::const_iterator
|
||||
overwrite_bitstream_iterator;
|
||||
/* Create range */
|
||||
typedef vtr::Range<bitstream_pb_type_setting_iterator>
|
||||
bitstream_pb_type_setting_range;
|
||||
typedef vtr::Range<bitstream_interconnect_setting_iterator>
|
||||
bitstream_interconnect_setting_range;
|
||||
typedef vtr::Range<overwrite_bitstream_iterator> overwrite_bitstream_range;
|
||||
|
||||
public: /* Constructors */
|
||||
BitstreamSetting();
|
||||
|
@ -73,6 +77,7 @@ class BitstreamSetting {
|
|||
public: /* Accessors: aggregates */
|
||||
bitstream_pb_type_setting_range pb_type_settings() const;
|
||||
bitstream_interconnect_setting_range interconnect_settings() const;
|
||||
overwrite_bitstream_range overwrite_bitstreams() const;
|
||||
|
||||
public: /* Public Accessors */
|
||||
std::string pb_type_name(
|
||||
|
@ -98,6 +103,8 @@ class BitstreamSetting {
|
|||
std::string default_path(
|
||||
const BitstreamInterconnectSettingId& interconnect_setting_id) const;
|
||||
std::vector<NonFabricBitstreamSetting> non_fabric() const;
|
||||
std::string overwrite_bitstream_path(const OverwriteBitstreamId& id) const;
|
||||
bool overwrite_bitstream_value(const OverwriteBitstreamId& id) const;
|
||||
|
||||
public: /* Public Mutators */
|
||||
BitstreamPbTypeSettingId add_bitstream_pb_type_setting(
|
||||
|
@ -120,11 +127,15 @@ class BitstreamSetting {
|
|||
void add_non_fabric(const std::string& name, const std::string& file);
|
||||
void add_non_fabric_pb(const std::string& pb, const std::string& content);
|
||||
|
||||
OverwriteBitstreamId add_overwrite_bitstream(const std::string& path,
|
||||
const bool& value);
|
||||
|
||||
public: /* Public Validators */
|
||||
bool valid_bitstream_pb_type_setting_id(
|
||||
const BitstreamPbTypeSettingId& pb_type_setting_id) const;
|
||||
bool valid_bitstream_interconnect_setting_id(
|
||||
const BitstreamInterconnectSettingId& interconnect_setting_id) const;
|
||||
bool valid_overwrite_bitstream_id(const OverwriteBitstreamId& id) const;
|
||||
|
||||
private: /* Internal data */
|
||||
/* Pb type -related settings
|
||||
|
@ -162,6 +173,10 @@ class BitstreamSetting {
|
|||
vtr::vector<BitstreamInterconnectSettingId, std::string>
|
||||
interconnect_default_paths_;
|
||||
std::vector<NonFabricBitstreamSetting> non_fabric_;
|
||||
vtr::vector<OverwriteBitstreamId, OverwriteBitstreamId>
|
||||
overwrite_bitstream_ids_;
|
||||
vtr::vector<OverwriteBitstreamId, std::string> overwrite_bitstream_paths_;
|
||||
vtr::vector<OverwriteBitstreamId, bool> overwrite_bitstream_values_;
|
||||
};
|
||||
|
||||
} // namespace openfpga
|
||||
|
|
|
@ -15,11 +15,13 @@
|
|||
|
||||
struct bitstream_pb_type_setting_id_tag;
|
||||
struct bitstream_interconnect_setting_id_tag;
|
||||
struct overwrite_bitstream_id_tag;
|
||||
|
||||
typedef vtr::StrongId<bitstream_pb_type_setting_id_tag>
|
||||
BitstreamPbTypeSettingId;
|
||||
typedef vtr::StrongId<bitstream_interconnect_setting_id_tag>
|
||||
BitstreamInterconnectSettingId;
|
||||
typedef vtr::StrongId<overwrite_bitstream_id_tag> OverwriteBitstreamId;
|
||||
|
||||
/* Short declaration of class */
|
||||
class BitstreamSetting;
|
||||
|
|
|
@ -102,6 +102,30 @@ static void read_xml_non_fabric_bitstream_setting(
|
|||
}
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Parse XML description for a bit setting under a <bit> XML node
|
||||
*******************************************************************/
|
||||
static void read_xml_overwrite_bitstream_setting(
|
||||
pugi::xml_node& xml_overwrite_bitstream, const pugiutil::loc_data& loc_data,
|
||||
openfpga::BitstreamSetting& bitstream_setting) {
|
||||
// Loopthrough bit
|
||||
for (pugi::xml_node xml_bit : xml_overwrite_bitstream.children()) {
|
||||
if (xml_bit.name() != std::string("bit")) {
|
||||
bad_tag(xml_bit, loc_data, xml_overwrite_bitstream, {"bit"});
|
||||
}
|
||||
const std::string& path_attr =
|
||||
get_attribute(xml_bit, "path", loc_data).as_string();
|
||||
const std::string& value_attr =
|
||||
get_attribute(xml_bit, "value", loc_data).as_string();
|
||||
if (value_attr != "0" && value_attr != "1") {
|
||||
archfpga_throw(loc_data.filename_c_str(), loc_data.line(xml_bit),
|
||||
"Invalid value of overwrite_bitstream bit. Expect [0|1]");
|
||||
}
|
||||
/* Add to bit */
|
||||
bitstream_setting.add_overwrite_bitstream(path_attr, value_attr == "1");
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Parse XML codes about <openfpga_bitstream_setting> to an object
|
||||
*******************************************************************/
|
||||
|
@ -116,9 +140,10 @@ openfpga::BitstreamSetting read_xml_bitstream_setting(
|
|||
/* Error out if the XML child has an invalid name! */
|
||||
if ((xml_child.name() != std::string("pb_type")) &&
|
||||
(xml_child.name() != std::string("interconnect")) &&
|
||||
(xml_child.name() != std::string("non_fabric"))) {
|
||||
(xml_child.name() != std::string("non_fabric")) &&
|
||||
(xml_child.name() != std::string("overwrite_bitstream"))) {
|
||||
bad_tag(xml_child, loc_data, Node,
|
||||
{"pb_type | interconnect | non_fabric"});
|
||||
{"pb_type | interconnect | non_fabric | overwrite_bitstream"});
|
||||
}
|
||||
|
||||
if (xml_child.name() == std::string("pb_type")) {
|
||||
|
@ -127,10 +152,13 @@ openfpga::BitstreamSetting read_xml_bitstream_setting(
|
|||
} else if (xml_child.name() == std::string("interconnect")) {
|
||||
read_xml_bitstream_interconnect_setting(xml_child, loc_data,
|
||||
bitstream_setting);
|
||||
} else {
|
||||
VTR_ASSERT_SAFE(xml_child.name() == std::string("non_fabric"));
|
||||
} else if (xml_child.name() == std::string("non_fabric")) {
|
||||
read_xml_non_fabric_bitstream_setting(xml_child, loc_data,
|
||||
bitstream_setting);
|
||||
} else {
|
||||
VTR_ASSERT_SAFE(xml_child.name() == std::string("overwrite_bitstream"));
|
||||
read_xml_overwrite_bitstream_setting(xml_child, loc_data,
|
||||
bitstream_setting);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,10 @@
|
|||
|
||||
#include <algorithm>
|
||||
|
||||
#include "arch_error.h"
|
||||
#include "bitstream_manager_utils.h"
|
||||
#include "openfpga_port_parser.h"
|
||||
#include "openfpga_tokenizer.h"
|
||||
#include "vtr_assert.h"
|
||||
#include "vtr_log.h"
|
||||
|
||||
|
@ -296,6 +300,69 @@ void BitstreamManager::add_output_net_id_to_block(
|
|||
block_output_net_ids_[block] = output_net_id;
|
||||
}
|
||||
|
||||
void BitstreamManager::overwrite_bitstream(const std::string& path,
|
||||
const bool& value) {
|
||||
PortParser port_parser(path, PORT_PARSER_SUPPORT_SINGLE_INDEX_FORMAT);
|
||||
if (!port_parser.valid()) {
|
||||
archfpga_throw(__FILE__, __LINE__,
|
||||
"overwrite_bitstream bit path '%s' does not match format "
|
||||
"<full path in the hierarchy of FPGA fabric>[bit index]",
|
||||
path.c_str());
|
||||
} else {
|
||||
BasicPort port = port_parser.port();
|
||||
size_t bit = port.get_lsb();
|
||||
StringToken tokenizer(port.get_name());
|
||||
std::vector<std::string> blocks = tokenizer.split(".");
|
||||
std::vector<ConfigBlockId> block_ids;
|
||||
ConfigBlockId block_id = ConfigBlockId::INVALID();
|
||||
size_t found = 0;
|
||||
for (size_t i = 0; i < blocks.size(); i++) {
|
||||
if (i == 0) {
|
||||
block_ids = find_bitstream_manager_top_blocks(*this);
|
||||
} else {
|
||||
block_ids = block_children(block_id);
|
||||
}
|
||||
// Reset
|
||||
block_id = ConfigBlockId::INVALID();
|
||||
// Find the one from the list that match the name
|
||||
for (auto id : block_ids) {
|
||||
if (block_name(id) == blocks[i]) {
|
||||
block_id = id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (block_id != ConfigBlockId::INVALID()) {
|
||||
// Found one that match the name
|
||||
found++;
|
||||
if (found == blocks.size()) {
|
||||
// Last one, no more child must end here
|
||||
if (block_children(block_id).size() == 0) {
|
||||
std::vector<ConfigBitId> ids = block_bits(block_id);
|
||||
if (bit < ids.size()) {
|
||||
VTR_ASSERT(valid_bit_id(ids[bit]));
|
||||
bit_values_[ids[bit]] = value ? '1' : '0';
|
||||
} else {
|
||||
// No configuration bits at all or out of range, invalidate
|
||||
found = 0;
|
||||
}
|
||||
} else {
|
||||
// There are more child, hence the path still no end, invalidate
|
||||
found = 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Cannot match the name, just stop
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found != blocks.size()) {
|
||||
archfpga_throw(__FILE__, __LINE__,
|
||||
"Failed to find path '%s' to overwrite bitstream",
|
||||
path.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* Public Validators
|
||||
******************************************************************************/
|
||||
|
|
|
@ -213,6 +213,9 @@ class BitstreamManager {
|
|||
void add_output_net_id_to_block(const ConfigBlockId& block,
|
||||
const std::string& output_net_id);
|
||||
|
||||
/* Set bit to the bitstream at the given path */
|
||||
void overwrite_bitstream(const std::string& path, const bool& value);
|
||||
|
||||
public: /* Public Validators */
|
||||
bool valid_bit_id(const ConfigBitId& bit_id) const;
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
#include <cstring>
|
||||
|
||||
#include "arch_error.h"
|
||||
#include "openfpga_tokenizer.h"
|
||||
#include "vtr_assert.h"
|
||||
#include "vtr_geometry.h"
|
||||
|
@ -19,9 +20,10 @@ namespace openfpga {
|
|||
/************************************************************************
|
||||
* Constructors
|
||||
***********************************************************************/
|
||||
PortParser::PortParser(const std::string& data) {
|
||||
PortParser::PortParser(const std::string& data, const int support_format) {
|
||||
set_default_bracket();
|
||||
set_default_delim();
|
||||
set_support_format(support_format);
|
||||
set_data(data);
|
||||
}
|
||||
|
||||
|
@ -33,9 +35,18 @@ std::string PortParser::data() const { return data_; }
|
|||
|
||||
BasicPort PortParser::port() const { return port_; }
|
||||
|
||||
bool PortParser::valid() const { return valid_; }
|
||||
|
||||
/************************************************************************
|
||||
* Public Mutators
|
||||
***********************************************************************/
|
||||
void PortParser::set_support_format(const int support_format) {
|
||||
VTR_ASSERT((support_format & PORT_PARSER_SUPPORT_ALL_FORMAT) != 0);
|
||||
VTR_ASSERT((support_format & ~PORT_PARSER_SUPPORT_ALL_FORMAT) == 0);
|
||||
support_format_ = support_format;
|
||||
return;
|
||||
}
|
||||
|
||||
void PortParser::set_data(const std::string& data) {
|
||||
data_ = data;
|
||||
parse();
|
||||
|
@ -47,6 +58,8 @@ void PortParser::set_data(const std::string& data) {
|
|||
***********************************************************************/
|
||||
/* Parse the data */
|
||||
void PortParser::parse() {
|
||||
valid_ = true;
|
||||
|
||||
/* Create a tokenizer */
|
||||
StringToken tokenizer(data_);
|
||||
|
||||
|
@ -54,11 +67,14 @@ void PortParser::parse() {
|
|||
std::vector<std::string> port_tokens = tokenizer.split(bracket_.x());
|
||||
/* Make sure we have a port name! */
|
||||
VTR_ASSERT_SAFE((1 == port_tokens.size()) || (2 == port_tokens.size()));
|
||||
|
||||
/* Store the port name! */
|
||||
port_.set_name(port_tokens[0]);
|
||||
|
||||
/* If we only have one token */
|
||||
if (1 == port_tokens.size()) {
|
||||
// there is no [
|
||||
valid_ = (support_format_ & PORT_PARSER_SUPPORT_NO_PORT_FORMAT) != 0;
|
||||
port_.set_width(1);
|
||||
return; /* We can finish here */
|
||||
}
|
||||
|
@ -72,19 +88,25 @@ void PortParser::parse() {
|
|||
/* Split the pin string now */
|
||||
tokenizer.set_data(pin_tokens[0]);
|
||||
pin_tokens = tokenizer.split(delim_);
|
||||
VTR_ASSERT_SAFE((1 == pin_tokens.size()) || (2 == pin_tokens.size()));
|
||||
|
||||
/* Check if we have LSB and MSB or just one */
|
||||
if (1 == pin_tokens.size()) {
|
||||
/* Single pin */
|
||||
port_.set_width(std::stoi(pin_tokens[0]), std::stoi(pin_tokens[0]));
|
||||
valid_ = (support_format_ & PORT_PARSER_SUPPORT_SINGLE_INDEX_FORMAT) != 0;
|
||||
size_t temp = string_to_number(pin_tokens[0]);
|
||||
port_.set_width(temp, temp);
|
||||
} else if (2 == pin_tokens.size()) {
|
||||
/* A number of pins.
|
||||
* Note that we always use the LSB for token[0] and MSB for token[1]
|
||||
*/
|
||||
if (std::stoi(pin_tokens[1]) < std::stoi(pin_tokens[0])) {
|
||||
port_.set_width(std::stoi(pin_tokens[1]), std::stoi(pin_tokens[0]));
|
||||
valid_ = (support_format_ & PORT_PARSER_SUPPORT_RANGE_FORMAT) != 0;
|
||||
size_t temp0 = string_to_number(pin_tokens[0]);
|
||||
size_t temp1 = string_to_number(pin_tokens[1]);
|
||||
if (temp1 < temp0) {
|
||||
port_.set_width(temp1, temp0);
|
||||
} else {
|
||||
port_.set_width(std::stoi(pin_tokens[0]), std::stoi(pin_tokens[1]));
|
||||
port_.set_width(temp0, temp1);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -102,6 +124,24 @@ void PortParser::set_default_delim() {
|
|||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
Make sure string is not empty and is all digit before stoi
|
||||
*/
|
||||
size_t PortParser::string_to_number(const std::string& str) {
|
||||
bool bad_format = str.empty();
|
||||
for (auto& chr : str) {
|
||||
if (!std::isdigit(chr)) {
|
||||
bad_format = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (bad_format) {
|
||||
archfpga_throw(__FILE__, __LINE__,
|
||||
"Invalid string '%s' to call std::stoi()", str.c_str());
|
||||
}
|
||||
return (size_t)(std::stoi(str));
|
||||
}
|
||||
|
||||
/************************************************************************
|
||||
* Member functions for MultiPortParser class
|
||||
***********************************************************************/
|
||||
|
|
|
@ -21,37 +21,51 @@
|
|||
/* namespace openfpga begins */
|
||||
namespace openfpga {
|
||||
|
||||
constexpr int PORT_PARSER_SUPPORT_NO_PORT_FORMAT = (1 << 0); // (5) below
|
||||
constexpr int PORT_PARSER_SUPPORT_SINGLE_INDEX_FORMAT = (1 << 1); // (3) below
|
||||
constexpr int PORT_PARSER_SUPPORT_RANGE_FORMAT = (1 << 2); // (1) and (2) below
|
||||
constexpr int PORT_PARSER_SUPPORT_ALL_FORMAT = ((1 << 3) - 1);
|
||||
|
||||
/************************************************************************
|
||||
* Class PortParser: single port parser
|
||||
* Supported port definition:
|
||||
* 1. <port_name>[<LSB>:<MSB>]
|
||||
* 2. <port_name>[<MSB>:<LSB>]
|
||||
* 3. <port_name>[<single_pin_index>]
|
||||
* 4. <port_name>[]
|
||||
* 5. <port_name>
|
||||
* (1) <port_name>[<LSB>:<MSB>]
|
||||
* (2) <port_name>[<MSB>:<LSB>]
|
||||
* (3) <port_name>[<single_pin_index>]
|
||||
* (4) <port_name>[] -- this is not currently supported. Two problems:
|
||||
* * tokenizer will error out and
|
||||
* * stoi cannot support empty string, and give
|
||||
* std::invalid_argument error
|
||||
* (5) <port_name>
|
||||
* In case 4 and 5, we will assign (-1,-1) for LSB and MSB
|
||||
***********************************************************************/
|
||||
class PortParser {
|
||||
public: /* Constructors*/
|
||||
PortParser(const std::string& data);
|
||||
PortParser(const std::string& data,
|
||||
const int support_format = PORT_PARSER_SUPPORT_ALL_FORMAT);
|
||||
|
||||
public: /* Public Accessors */
|
||||
std::string data() const;
|
||||
BasicPort port() const;
|
||||
bool valid() const;
|
||||
|
||||
public: /* Public Mutators */
|
||||
void set_support_format(const int support_format);
|
||||
void set_data(const std::string& data);
|
||||
|
||||
private: /* Private Mutators */
|
||||
void parse();
|
||||
void set_default_bracket();
|
||||
void set_default_delim();
|
||||
size_t string_to_number(const std::string& str);
|
||||
|
||||
private: /* Internal data */
|
||||
std::string data_; /* Lines to be splited */
|
||||
int support_format_;
|
||||
vtr::Point<char> bracket_;
|
||||
char delim_;
|
||||
BasicPort port_;
|
||||
bool valid_;
|
||||
};
|
||||
|
||||
/************************************************************************
|
||||
|
|
|
@ -69,7 +69,19 @@ int call_external_command(const Command& cmd,
|
|||
return CMD_EXEC_FATAL_ERROR;
|
||||
}
|
||||
|
||||
return system(cmd_ss.c_str());
|
||||
// Refer https://pubs.opengroup.org/onlinepubs/009695399/functions/system.html
|
||||
// Refer
|
||||
// https://pubs.opengroup.org/onlinepubs/009695399/functions/waitpid.html
|
||||
int status = system(cmd_ss.c_str());
|
||||
if (WIFEXITED(status)) {
|
||||
// This is normal program exit, WEXITSTATUS() will help you shift the status
|
||||
// accordingly (status >> 8)
|
||||
// Becareful if the final status is 2 or beyond, program will not error
|
||||
// as it is treated as CMD_EXEC_MINOR_ERROR
|
||||
return WEXITSTATUS(status);
|
||||
}
|
||||
// Program maybe terminated because of various killed or stopped signal
|
||||
return CMD_EXEC_FATAL_ERROR;
|
||||
}
|
||||
|
||||
} /* end namespace openfpga */
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include "openfpga_digest.h"
|
||||
#include "openfpga_naming.h"
|
||||
#include "openfpga_reserved_words.h"
|
||||
#include "overwrite_bitstream.h"
|
||||
#include "read_xml_arch_bitstream.h"
|
||||
#include "report_bitstream_distribution.h"
|
||||
#include "vtr_log.h"
|
||||
|
@ -47,6 +48,10 @@ int fpga_bitstream_template(T& openfpga_ctx, const Command& cmd,
|
|||
g_vpr_ctx, openfpga_ctx, cmd_context.option_enable(cmd, opt_verbose));
|
||||
}
|
||||
|
||||
overwrite_bitstream(openfpga_ctx.mutable_bitstream_manager(),
|
||||
openfpga_ctx.bitstream_setting(),
|
||||
cmd_context.option_enable(cmd, opt_verbose));
|
||||
|
||||
if (true == cmd_context.option_enable(cmd, opt_write_file)) {
|
||||
std::string src_dir_path =
|
||||
find_path_dir_name(cmd_context.option_value(cmd, opt_write_file));
|
||||
|
|
|
@ -230,6 +230,7 @@ BitstreamManager build_device_bitstream(const VprContext& vpr_ctx,
|
|||
openfpga_ctx.vpr_device_annotation(), openfpga_ctx.vpr_routing_annotation(),
|
||||
vpr_ctx.device().rr_graph, openfpga_ctx.device_rr_gsb(),
|
||||
openfpga_ctx.flow_manager().compress_routing(), verbose);
|
||||
|
||||
VTR_LOGV(verbose, "Done\n");
|
||||
|
||||
VTR_LOGV(verbose, "Decoded %lu configuration bits into %lu blocks\n",
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
/********************************************************************
|
||||
* This file includes functions to build bitstream from a mapped
|
||||
* FPGA fabric.
|
||||
* We decode the bitstream from configuration of routing multiplexers
|
||||
* and Look-Up Tables (LUTs) which locate in CLBs and global routing
|
||||
*architecture
|
||||
*******************************************************************/
|
||||
|
||||
/* Headers from vtrutil library */
|
||||
#include "overwrite_bitstream.h"
|
||||
|
||||
#include "vtr_assert.h"
|
||||
#include "vtr_log.h"
|
||||
#include "vtr_time.h"
|
||||
|
||||
/* begin namespace openfpga */
|
||||
namespace openfpga {
|
||||
|
||||
/********************************************************************
|
||||
* Overwrite bitstream retrieve from bitstream annotation XML which stored in
|
||||
*BitstreamSetting
|
||||
*******************************************************************/
|
||||
void overwrite_bitstream(openfpga::BitstreamManager& bitstream_manager,
|
||||
const openfpga::BitstreamSetting& bitstream_setting,
|
||||
const bool& verbose) {
|
||||
vtr::ScopedStartFinishTimer timer("\nOverwrite Bitstream\n");
|
||||
|
||||
/* Apply overwrite_bitstream bit's path and value */
|
||||
for (auto& id : bitstream_setting.overwrite_bitstreams()) {
|
||||
std::string path = bitstream_setting.overwrite_bitstream_path(id);
|
||||
bool value = bitstream_setting.overwrite_bitstream_value(id);
|
||||
VTR_LOGV(verbose, "Overwrite bitstream path='%s' to value='%d'\n",
|
||||
path.c_str(), value);
|
||||
bitstream_manager.overwrite_bitstream(path, value);
|
||||
}
|
||||
}
|
||||
|
||||
} /* end namespace openfpga */
|
|
@ -0,0 +1,23 @@
|
|||
#ifndef OVERWRITE_BITSTREAM_H
|
||||
#define OVERWRITE_BITSTREAM_H
|
||||
|
||||
/********************************************************************
|
||||
* Include header files that are required by function declaration
|
||||
*******************************************************************/
|
||||
|
||||
#include "openfpga_context.h"
|
||||
|
||||
/********************************************************************
|
||||
* Function declaration
|
||||
*******************************************************************/
|
||||
|
||||
/* begin namespace openfpga */
|
||||
namespace openfpga {
|
||||
|
||||
void overwrite_bitstream(openfpga::BitstreamManager& bitstream_manager,
|
||||
const openfpga::BitstreamSetting& bitstream_setting,
|
||||
const bool& verbose);
|
||||
|
||||
} /* end namespace openfpga */
|
||||
|
||||
#endif
|
|
@ -15,6 +15,9 @@ echo -e "Testing bitstream generation for an 48x48 FPGA device";
|
|||
run-task fpga_bitstream/generate_bitstream/configuration_chain/device_48x48 $@
|
||||
run-task fpga_bitstream/generate_bitstream/ql_memory_bank_shift_register/device_48x48 $@
|
||||
|
||||
echo -e "Testing bitstream generation for an 4x4 FPGA device (randomly overwrite fabric bits)";
|
||||
run-task fpga_bitstream/overwrite_bitstream/device_4x4 $@
|
||||
|
||||
echo -e "Testing bitstream generation for an 96x96 FPGA device";
|
||||
run-task fpga_bitstream/generate_bitstream/configuration_chain/device_96x96 $@
|
||||
run-task fpga_bitstream/generate_bitstream/ql_memory_bank_shift_register/device_72x72 $@
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
|
||||
# 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 = false
|
||||
spice_output=false
|
||||
verilog_output=true
|
||||
timeout_each_job = 20*60
|
||||
fpga_flow=vpr_blif
|
||||
|
||||
[OpenFPGA_SHELL]
|
||||
openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/tasks/fpga_bitstream/overwrite_bitstream/device_4x4/config/test.openfpga
|
||||
openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k6_frac_N10_adder_chain_mem16K_40nm_openfpga.xml
|
||||
openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml
|
||||
openfpga_bitstream_setting_file=bitstream_annotation.xml
|
||||
openfpga_vpr_device_layout=4x4
|
||||
openfpga_ext_exec_python_script=${PATH:TASK_DIR}/config/test.py
|
||||
|
||||
[ARCHITECTURES]
|
||||
arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k6_frac_N10_tileable_thru_channel_adder_chain_wide_mem16K_40nm.xml
|
||||
|
||||
[BENCHMARKS]
|
||||
bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2/and2.blif
|
||||
|
||||
[SYNTHESIS_PARAM]
|
||||
bench0_top = and2
|
||||
bench0_act = ${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2/and2.act
|
||||
bench0_verilog = ${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2/and2.v
|
||||
|
||||
[SCRIPT_PARAM_MIN_ROUTE_CHAN_WIDTH]
|
|
@ -0,0 +1,53 @@
|
|||
# Majority of the content refer to fix_device_example_script.openfpga
|
||||
|
||||
ext_exec --command "python3 ${OPENFPGA_EXT_EXEC_PYTHON_SCRIPT} run_golden ${OPENFPGA_PATH}"
|
||||
|
||||
ext_exec --command "python3 ${OPENFPGA_EXT_EXEC_PYTHON_SCRIPT} generate_testcase"
|
||||
|
||||
# Run VPR for the 'and' design
|
||||
#--write_rr_graph example_rr_graph.xml
|
||||
vpr ${VPR_ARCH_FILE} ${VPR_TESTBENCH_BLIF} --clock_modeling route --device ${OPENFPGA_VPR_DEVICE_LAYOUT}
|
||||
|
||||
# Read OpenFPGA architecture definition
|
||||
read_openfpga_arch -f ${OPENFPGA_ARCH_FILE}
|
||||
|
||||
# Read OpenFPGA simulation settings
|
||||
read_openfpga_simulation_setting -f ${OPENFPGA_SIM_SETTING_FILE}
|
||||
|
||||
# Read OpenFPGA bitstream settings
|
||||
read_openfpga_bitstream_setting -f ${OPENFPGA_BITSTREAM_SETTING_FILE}
|
||||
|
||||
# Annotate the OpenFPGA architecture to VPR data base
|
||||
# to debug use --verbose options
|
||||
link_openfpga_arch --activity_file ${ACTIVITY_FILE} --sort_gsb_chan_node_in_edges
|
||||
|
||||
# Check and correct any naming conflicts in the BLIF netlist
|
||||
check_netlist_naming_conflict --fix --report ./netlist_renaming.xml
|
||||
|
||||
# Apply fix-up to Look-Up Table truth tables based on packing results
|
||||
lut_truth_table_fixup
|
||||
|
||||
# Build the module graph
|
||||
# - Enabled compression on routing architecture modules
|
||||
# - Enable pin duplication on grid modules
|
||||
build_fabric --compress_routing #--verbose
|
||||
|
||||
# Repack the netlist to physical pbs
|
||||
# This must be done before bitstream generator and testbench generation
|
||||
# Strongly recommend it is done after all the fix-up have been applied
|
||||
repack #--verbose
|
||||
|
||||
# Build the bitstream
|
||||
# - Output the fabric-independent bitstream to a file
|
||||
build_architecture_bitstream --verbose
|
||||
|
||||
# Build fabric-dependent bitstream
|
||||
build_fabric_bitstream --verbose
|
||||
|
||||
# Write fabric-dependent bitstream
|
||||
write_fabric_bitstream --file fabric_bitstream.xml --format xml
|
||||
|
||||
ext_exec --command "python3 ${OPENFPGA_EXT_EXEC_PYTHON_SCRIPT} validate"
|
||||
|
||||
# Finish and exit OpenFPGA
|
||||
exit
|
|
@ -0,0 +1,120 @@
|
|||
import xml.etree.ElementTree as ET
|
||||
import sys
|
||||
import shutil
|
||||
import os
|
||||
import random
|
||||
|
||||
random.seed()
|
||||
assert len(sys.argv) >= 2
|
||||
assert sys.argv[1] in ["run_golden", "generate_testcase", "validate"]
|
||||
TEST_BIT_COUNT = 200
|
||||
|
||||
def read_fabric_bitstream_xml(file) :
|
||||
|
||||
bit_count = 0
|
||||
tree = ET.parse(file)
|
||||
root = tree.getroot()
|
||||
assert root.tag == "fabric_bitstream", "Root tag is not 'fabric_bitstream', but '%s'" % root.tag
|
||||
for region in root :
|
||||
assert region.tag == "region", "fabric_bitstream child node tag is not 'region', but '%s'" % region.tag
|
||||
for bit in region :
|
||||
assert bit.tag == "bit", "region child node tag is not 'bit', but '%s'" % bit.tag
|
||||
assert "path" in bit.attrib, "Attribute 'path' does not exist in bit node"
|
||||
assert "value" in bit.attrib, "Attribute 'value' does not exist in bit node"
|
||||
assert bit.attrib["value"] in ["0", "1"]
|
||||
bit_count += 1
|
||||
return [tree, bit_count]
|
||||
|
||||
def read_bitstream_annotation_xml(file) :
|
||||
|
||||
xml = {}
|
||||
tree = ET.parse(file)
|
||||
root = tree.getroot()
|
||||
assert root.tag == "openfpga_bitstream_setting", "Root tag is not 'openfpga_bitstream_setting', but '%s'" % root.tag
|
||||
for overwrite_bitstream in root :
|
||||
assert overwrite_bitstream.tag == "overwrite_bitstream", "openfpga_bitstream_setting child node tag is not 'overwrite_bitstream', but '%s'" % overwrite_bitstream.tag
|
||||
for bit in overwrite_bitstream :
|
||||
assert bit.tag == "bit", "overwrite_bitstream child node tag is not 'bit', but '%s'" % bit.tag
|
||||
assert "path" in bit.attrib, "Attribute 'path' does not exist in bit node"
|
||||
assert "value" in bit.attrib, "Attribute 'value' does not exist in bit node"
|
||||
path = bit.attrib["path"]
|
||||
assert path not in xml
|
||||
index = path.rfind("[")
|
||||
assert index != -1
|
||||
path = "%s.mem_out%s" % (path[:index], path[index:])
|
||||
assert path not in xml
|
||||
assert bit.attrib["value"] in ["0", "1"]
|
||||
xml[path] = bit.attrib["value"]
|
||||
return xml
|
||||
|
||||
if sys.argv[1] == "run_golden" :
|
||||
|
||||
assert len(sys.argv) >= 3
|
||||
openfpga_exe = os.path.abspath("%s/build/openfpga/openfpga" % sys.argv[2])
|
||||
assert os.path.exists(openfpga_exe)
|
||||
shutil.rmtree("golden", ignore_errors=True)
|
||||
os.mkdir("golden")
|
||||
original_openfpga = open("and2_run.openfpga")
|
||||
golden_openfpga = open("golden/and2_run.openfpga", "w")
|
||||
for line in original_openfpga :
|
||||
if line.find("ext_exec") == 0 :
|
||||
pass
|
||||
else :
|
||||
golden_openfpga.write(line)
|
||||
golden_openfpga.close()
|
||||
original_openfpga.close()
|
||||
bitstream_annotation = open("golden/bitstream_annotation.xml", "w")
|
||||
bitstream_annotation.write("<openfpga_bitstream_setting/>\n")
|
||||
bitstream_annotation.close()
|
||||
shutil.copyfile("and2.blif", "golden/and2.blif")
|
||||
shutil.copyfile("and2_ace_out.act", "golden/and2_ace_out.act")
|
||||
cmd = "cd golden && %s -batch -f and2_run.openfpga > golden.log" % (openfpga_exe)
|
||||
assert os.system(cmd) == 0
|
||||
|
||||
elif sys.argv[1] == "generate_testcase" :
|
||||
|
||||
(tree, bit_count) = read_fabric_bitstream_xml("golden/fabric_bitstream.xml")
|
||||
random_bits = []
|
||||
while len(random_bits) != TEST_BIT_COUNT :
|
||||
bit = random.randint(0, bit_count - 1)
|
||||
if bit not in random_bits :
|
||||
random_bits.append(bit)
|
||||
bitstream_annotation = open("bitstream_annotation.xml", "w")
|
||||
bitstream_annotation.write("<openfpga_bitstream_setting>\n")
|
||||
bitstream_annotation.write(" <overwrite_bitstream>\n")
|
||||
index = 0
|
||||
for region in tree.getroot() :
|
||||
for bit in region :
|
||||
if index in random_bits :
|
||||
path = bit.attrib["path"]
|
||||
value = bit.attrib["value"]
|
||||
assert value in ["0", "1"]
|
||||
path = path.replace(".mem_out[", "[")
|
||||
bitstream_annotation.write(" <bit value=\"%s\" path=\"%s\"/>\n" % ("1" if value == "0" else "0", path))
|
||||
index += 1
|
||||
bitstream_annotation.write(" </overwrite_bitstream>\n")
|
||||
bitstream_annotation.write("</openfpga_bitstream_setting>\n")
|
||||
bitstream_annotation.close()
|
||||
|
||||
else :
|
||||
|
||||
gtree = ET.parse("golden/fabric_bitstream.xml")
|
||||
tree = ET.parse("fabric_bitstream.xml")
|
||||
bitstream_annotation = read_bitstream_annotation_xml("bitstream_annotation.xml")
|
||||
checked_count = 0
|
||||
for gregion, region in zip(gtree.getroot(), tree.getroot()) :
|
||||
for gbit, bit in zip(gregion, region) :
|
||||
assert bit.attrib["path"] == gbit.attrib["path"]
|
||||
path = bit.attrib["path"]
|
||||
if path in bitstream_annotation :
|
||||
# This is something we want to overwrite, hence the value should
|
||||
# Same in the annotation file
|
||||
# Not same in golden fabric
|
||||
assert bit.attrib["value"] != gbit.attrib["value"]
|
||||
assert bit.attrib["value"] == bitstream_annotation[path]
|
||||
else :
|
||||
# This is not what we want to overwrite
|
||||
# Hence the value should same in golden fabric
|
||||
assert bit.attrib["value"] == gbit.attrib["value"]
|
||||
|
||||
exit(0)
|
Loading…
Reference in New Issue