Merge pull request #20 from RapidSilicon/qlbank_sr

Support Shift-registers-based QuickLogic's Memory Bank
This commit is contained in:
tangxifan 2021-10-03 17:23:07 -07:00 committed by GitHub
commit 2903f28d24
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
67 changed files with 3033 additions and 87 deletions

View File

@ -326,6 +326,102 @@ size_t check_ccff_circuit_model_ports(const CircuitLibrary& circuit_lib,
return num_err;
}
/************************************************************************
* A function to check the port map of CCFF circuit model used to control BLs
* - Require 1 clock port
* - Require 1 input port as data input (to be driven by other CCFF in a chain)
* - Require 1 output port as data output (to drive other CCFF in a chain)
* - Require 1 BL port as data output / inout (to drive/driven by BLs)
***********************************************************************/
size_t check_bl_ccff_circuit_model_ports(const CircuitLibrary& circuit_lib,
const CircuitModelId& circuit_model) {
size_t num_err = 0;
/* Check the type of circuit model */
VTR_ASSERT(CIRCUIT_MODEL_CCFF == circuit_lib.model_type(circuit_model));
/* Check if we have D, Set and Reset */
/* We can have either 1 input which is D or 2 inputs which are D and scan input */
size_t num_input_ports = circuit_lib.model_ports_by_type(circuit_model, CIRCUIT_MODEL_PORT_INPUT, true).size();
if (1 != num_input_ports) {
VTR_LOG_ERROR("Configuration flip-flop for BL shift register '%s' must have 1 %s port!\n",
circuit_lib.model_name(circuit_model).c_str(),
CIRCUIT_MODEL_PORT_TYPE_STRING[size_t(CIRCUIT_MODEL_PORT_INPUT)]);
num_err++;
}
num_err += check_one_circuit_model_port_type_and_size_required(circuit_lib, circuit_model,
CIRCUIT_MODEL_PORT_INPUT,
num_input_ports, 1, false);
/* Check if we have a clock */
num_err += check_one_circuit_model_port_type_and_size_required(circuit_lib, circuit_model,
CIRCUIT_MODEL_PORT_CLOCK,
1, 1, true);
/* Check if we have 1 output*/
num_err += check_one_circuit_model_port_type_and_size_required(circuit_lib, circuit_model,
CIRCUIT_MODEL_PORT_OUTPUT,
1, 1, false);
/* Check if we have 1 bl port */
num_err += check_one_circuit_model_port_type_and_size_required(circuit_lib, circuit_model,
CIRCUIT_MODEL_PORT_BL,
1, 1, false);
return num_err;
}
/************************************************************************
* A function to check the port map of CCFF circuit model used to control WLs
* - Require 1 clock port
* - Require 1 input port as data input (to be driven by other CCFF in a chain)
* - Require 1 output port as data output (to drive other CCFF in a chain)
* - Require 1 WL port as data output (to drive WLs)
* - Optionally require 1 WLR port as data output (to drive WLRs)
***********************************************************************/
size_t check_wl_ccff_circuit_model_ports(const CircuitLibrary& circuit_lib,
const CircuitModelId& circuit_model) {
size_t num_err = 0;
/* Check the type of circuit model */
VTR_ASSERT(CIRCUIT_MODEL_CCFF == circuit_lib.model_type(circuit_model));
/* Check if we have D, Set and Reset */
/* We can have either 1 input which is D or 2 inputs which are D and scan input */
size_t num_input_ports = circuit_lib.model_ports_by_type(circuit_model, CIRCUIT_MODEL_PORT_INPUT, true).size();
if (1 != num_input_ports) {
VTR_LOG_ERROR("Configuration flip-flop for WL shift register '%s' must have 1 %s port!\n",
circuit_lib.model_name(circuit_model).c_str(),
CIRCUIT_MODEL_PORT_TYPE_STRING[size_t(CIRCUIT_MODEL_PORT_INPUT)]);
num_err++;
}
num_err += check_one_circuit_model_port_type_and_size_required(circuit_lib, circuit_model,
CIRCUIT_MODEL_PORT_INPUT,
num_input_ports, 1, false);
/* Check if we have two clock: 1 for write-enable, 1 for shift register */
num_err += check_one_circuit_model_port_type_and_size_required(circuit_lib, circuit_model,
CIRCUIT_MODEL_PORT_CLOCK,
2, 1, true);
/* Check if we have 1 output*/
num_err += check_one_circuit_model_port_type_and_size_required(circuit_lib, circuit_model,
CIRCUIT_MODEL_PORT_OUTPUT,
1, 1, false);
/* Check if we have 1 wl port */
if (0 < circuit_lib.model_ports_by_type(circuit_model, CIRCUIT_MODEL_PORT_WLR, true).size()) {
num_err += check_one_circuit_model_port_type_and_size_required(circuit_lib, circuit_model,
CIRCUIT_MODEL_PORT_WLR,
1, 1, false);
}
return num_err;
}
/************************************************************************
* A function to check the port map of SRAM circuit model
***********************************************************************/

View File

@ -39,6 +39,12 @@ size_t check_ff_circuit_model_ports(const CircuitLibrary& circuit_lib,
size_t check_ccff_circuit_model_ports(const CircuitLibrary& circuit_lib,
const CircuitModelId& circuit_model);
size_t check_bl_ccff_circuit_model_ports(const CircuitLibrary& circuit_lib,
const CircuitModelId& circuit_model);
size_t check_wl_ccff_circuit_model_ports(const CircuitLibrary& circuit_lib,
const CircuitModelId& circuit_model);
size_t check_sram_circuit_model_ports(const CircuitLibrary& circuit_lib,
const CircuitModelId& circuit_model,
const bool& check_blwl);

View File

@ -949,7 +949,6 @@ bool CircuitLibrary::port_is_config_enable(const CircuitPortId& circuit_port_id)
return port_is_config_enable_[circuit_port_id];
}
/* Return a flag if the port is used during programming a FPGA in a circuit model */
bool CircuitLibrary::port_is_prog(const CircuitPortId& circuit_port_id) const {
/* validate the circuit_port_id */
@ -957,6 +956,13 @@ bool CircuitLibrary::port_is_prog(const CircuitPortId& circuit_port_id) const {
return port_is_prog_[circuit_port_id];
}
/* Return a flag if the port is used by shift register in a circuit model */
bool CircuitLibrary::port_is_shift_register(const CircuitPortId& circuit_port_id) const {
/* validate the circuit_port_id */
VTR_ASSERT(valid_circuit_port_id(circuit_port_id));
return port_is_shift_register_[circuit_port_id];
}
/* Return which level the output port locates at a LUT multiplexing structure */
size_t CircuitLibrary::port_lut_frac_level(const CircuitPortId& circuit_port_id) const {
/* validate the circuit_port_id */
@ -1401,6 +1407,7 @@ CircuitPortId CircuitLibrary::add_model_port(const CircuitModelId& model_id,
port_is_set_.push_back(false);
port_is_config_enable_.push_back(false);
port_is_prog_.push_back(false);
port_is_shift_register_.push_back(false);
port_tri_state_model_names_.emplace_back();
port_tri_state_model_ids_.push_back(CircuitModelId::INVALID());
port_inv_model_names_.emplace_back();
@ -1538,6 +1545,15 @@ void CircuitLibrary::set_port_is_prog(const CircuitPortId& circuit_port_id,
return;
}
/* Set the is_prog for a port of a circuit model */
void CircuitLibrary::set_port_is_shift_register(const CircuitPortId& circuit_port_id,
const bool& is_shift_register) {
/* validate the circuit_port_id */
VTR_ASSERT(valid_circuit_port_id(circuit_port_id));
port_is_shift_register_[circuit_port_id] = is_shift_register;
return;
}
/* Set the model_name for a port of a circuit model */
void CircuitLibrary::set_port_tri_state_model_name(const CircuitPortId& circuit_port_id,
const std::string& model_name) {

View File

@ -288,6 +288,7 @@ class CircuitLibrary {
bool port_is_set(const CircuitPortId& circuit_port_id) const;
bool port_is_config_enable(const CircuitPortId& circuit_port_id) const;
bool port_is_prog(const CircuitPortId& circuit_port_id) const;
bool port_is_shift_register(const CircuitPortId& circuit_port_id) const;
size_t port_lut_frac_level(const CircuitPortId& circuit_port_id) const;
bool port_is_harden_lut_port(const CircuitPortId& circuit_port_id) const;
std::vector<size_t> port_lut_output_mask(const CircuitPortId& circuit_port_id) const;
@ -372,6 +373,8 @@ class CircuitLibrary {
const bool& is_config_enable);
void set_port_is_prog(const CircuitPortId& circuit_port_id,
const bool& is_prog);
void set_port_is_shift_register(const CircuitPortId& circuit_port_id,
const bool& is_shift_register);
void set_port_tri_state_model_name(const CircuitPortId& circuit_port_id,
const std::string& model_name);
void set_port_tri_state_model_id(const CircuitPortId& circuit_port_id,
@ -560,6 +563,7 @@ class CircuitLibrary {
vtr::vector<CircuitPortId, bool> port_is_set_;
vtr::vector<CircuitPortId, bool> port_is_config_enable_;
vtr::vector<CircuitPortId, bool> port_is_prog_;
vtr::vector<CircuitPortId, bool> port_is_shift_register_;
vtr::vector<CircuitPortId, std::string> port_tri_state_model_names_;
vtr::vector<CircuitPortId, CircuitModelId> port_tri_state_model_ids_;
vtr::vector<CircuitPortId, std::string> port_inv_model_names_;

View File

@ -45,6 +45,10 @@ CircuitModelId ConfigProtocol::bl_memory_model() const {
return bl_memory_model_;
}
size_t ConfigProtocol::bl_num_banks() const {
return bl_num_banks_;
}
e_blwl_protocol_type ConfigProtocol::wl_protocol_type() const {
return wl_protocol_type_;
}
@ -57,6 +61,10 @@ CircuitModelId ConfigProtocol::wl_memory_model() const {
return wl_memory_model_;
}
size_t ConfigProtocol::wl_num_banks() const {
return wl_num_banks_;
}
/************************************************************************
* Public Mutators
***********************************************************************/
@ -100,6 +108,15 @@ void ConfigProtocol::set_bl_memory_model(const CircuitModelId& memory_model) {
bl_memory_model_ = memory_model;
}
void ConfigProtocol::set_bl_num_banks(const size_t& num_banks) {
if (BLWL_PROTOCOL_SHIFT_REGISTER != bl_protocol_type_) {
VTR_LOG_ERROR("BL protocol memory model is only applicable when '%d' is defined", BLWL_PROTOCOL_TYPE_STRING[bl_protocol_type_]);
return;
}
bl_num_banks_ = num_banks;
}
void ConfigProtocol::set_wl_protocol_type(const e_blwl_protocol_type& type) {
if (CONFIG_MEM_QL_MEMORY_BANK != type_) {
VTR_LOG_ERROR("WL protocol type is only applicable for configuration protocol '%d'", CONFIG_PROTOCOL_TYPE_STRING[type_]);
@ -123,3 +140,12 @@ void ConfigProtocol::set_wl_memory_model(const CircuitModelId& memory_model) {
}
wl_memory_model_ = memory_model;
}
void ConfigProtocol::set_wl_num_banks(const size_t& num_banks) {
if (BLWL_PROTOCOL_SHIFT_REGISTER != wl_protocol_type_) {
VTR_LOG_ERROR("WL protocol memory model is only applicable when '%d' is defined", BLWL_PROTOCOL_TYPE_STRING[wl_protocol_type_]);
return;
}
wl_num_banks_ = num_banks;
}

View File

@ -29,9 +29,11 @@ class ConfigProtocol {
e_blwl_protocol_type bl_protocol_type() const;
std::string bl_memory_model_name() const;
CircuitModelId bl_memory_model() const;
size_t bl_num_banks() const;
e_blwl_protocol_type wl_protocol_type() const;
std::string wl_memory_model_name() const;
CircuitModelId wl_memory_model() const;
size_t wl_num_banks() const;
public: /* Public Mutators */
void set_type(const e_config_protocol_type& type);
void set_memory_model_name(const std::string& memory_model_name);
@ -41,9 +43,11 @@ class ConfigProtocol {
void set_bl_protocol_type(const e_blwl_protocol_type& type);
void set_bl_memory_model_name(const std::string& memory_model_name);
void set_bl_memory_model(const CircuitModelId& memory_model);
void set_bl_num_banks(const size_t& num_banks);
void set_wl_protocol_type(const e_blwl_protocol_type& type);
void set_wl_memory_model_name(const std::string& memory_model_name);
void set_wl_memory_model(const CircuitModelId& memory_model);
void set_wl_num_banks(const size_t& num_banks);
private: /* Internal data */
/* The type of configuration protocol.
* In other words, it is about how to organize and access each configurable memory
@ -62,13 +66,17 @@ class ConfigProtocol {
* - bl/wl_memory_model: defines the circuit model to be used when building shift register chains for BL/WL configuration.
* It must be a valid CCFF circuit model. This is only applicable when shift-register protocol is selected
* for BL or WL.
* - bl/wl_num_banks: defines the number of independent shift register chains (with separated head and tail ports)
* for a given BL protocol per configuration region
*/
e_blwl_protocol_type bl_protocol_type_ = BLWL_PROTOCOL_DECODER;
std::string bl_memory_model_name_;
CircuitModelId bl_memory_model_;
size_t bl_num_banks_;
e_blwl_protocol_type wl_protocol_type_ = BLWL_PROTOCOL_DECODER;
std::string wl_memory_model_name_;
CircuitModelId wl_memory_model_;
size_t wl_num_banks_;
};
#endif

View File

@ -484,6 +484,9 @@ void read_xml_circuit_port(pugi::xml_node& xml_port,
/* Identify if the port is in programming purpose, by default it is NOT */
circuit_lib.set_port_is_prog(port, get_attribute(xml_port, "is_prog", loc_data, pugiutil::ReqOpt::OPTIONAL).as_bool(false));
/* Identify if the port is in shift register purpose, by default it is NOT */
circuit_lib.set_port_is_shift_register(port, get_attribute(xml_port, "is_shift_register", loc_data, pugiutil::ReqOpt::OPTIONAL).as_bool(false));
/* Identify if the port is to enable programming for FPGAs, by default it is NOT */
circuit_lib.set_port_is_config_enable(port, get_attribute(xml_port, "is_config_enable", loc_data, pugiutil::ReqOpt::OPTIONAL).as_bool(false));

View File

@ -68,9 +68,13 @@ void read_xml_bl_protocol(pugi::xml_node& xml_bl_protocol,
config_protocol.set_bl_protocol_type(blwl_protocol_type);
/* Find the memory model, only applicable to shift-registor protocol */
/* only applicable to shift-registor protocol
* - Find the memory model to build shift register chains
* - Find the number of shift register chains for each protocol
*/
if (BLWL_PROTOCOL_SHIFT_REGISTER == blwl_protocol_type) {
config_protocol.set_bl_memory_model_name(get_attribute(xml_bl_protocol, "circuit_model_name", loc_data).as_string());
config_protocol.set_bl_num_banks(get_attribute(xml_bl_protocol, "num_banks", loc_data, pugiutil::ReqOpt::OPTIONAL).as_int(1));
}
}
@ -94,9 +98,13 @@ void read_xml_wl_protocol(pugi::xml_node& xml_wl_protocol,
config_protocol.set_wl_protocol_type(blwl_protocol_type);
/* Find the memory model, only applicable to shift-registor protocol */
/* only applicable to shift-registor protocol
* - Find the memory model to build shift register chains
* - Find the number of shift register chains for each protocol
*/
if (BLWL_PROTOCOL_SHIFT_REGISTER == blwl_protocol_type) {
config_protocol.set_wl_memory_model_name(get_attribute(xml_wl_protocol, "circuit_model_name", loc_data).as_string());
config_protocol.set_wl_num_banks(get_attribute(xml_wl_protocol, "num_banks", loc_data, pugiutil::ReqOpt::OPTIONAL).as_int(1));
}
}

View File

@ -36,7 +36,7 @@ e_sim_accuracy_type string_to_sim_accuracy_type(const std::string& type_string)
}
/********************************************************************
* Parse XML codes of a <clock> line to an object of simulation setting
* Parse XML codes of a <clock> line under <operating> to an object of simulation setting
*******************************************************************/
static
void read_xml_operating_clock_override_setting(pugi::xml_node& xml_clock_override_setting,
@ -62,6 +62,40 @@ void read_xml_operating_clock_override_setting(pugi::xml_node& xml_clock_overrid
sim_setting.set_clock_frequency(clock_id, get_attribute(xml_clock_override_setting, "frequency", loc_data).as_float(0.));
}
/********************************************************************
* Parse XML codes of a <clock> line under <programming> to an object of simulation setting
*******************************************************************/
static
void read_xml_programming_clock_override_setting(pugi::xml_node& xml_clock_override_setting,
const pugiutil::loc_data& loc_data,
openfpga::SimulationSetting& sim_setting) {
std::string clock_name = get_attribute(xml_clock_override_setting, "name", loc_data).as_string();
/* Create a new clock override object in the sim_setting object with the given name */
SimulationClockId clock_id = sim_setting.create_clock(clock_name);
/* Report if the clock creation failed, this is due to a conflicts in naming*/
if (false == sim_setting.valid_clock_id(clock_id)) {
archfpga_throw(loc_data.filename_c_str(), loc_data.line(xml_clock_override_setting),
"Fail to create simulation clock '%s', it may share the same name as other simulation clock definition!\n",
clock_name.c_str());
}
/* Parse port information */
openfpga::PortParser clock_port_parser(get_attribute(xml_clock_override_setting, "port", loc_data).as_string());
sim_setting.set_clock_port(clock_id, clock_port_parser.port());
/* Parse frequency information */
std::string clock_freq_str = get_attribute(xml_clock_override_setting, "frequency", loc_data).as_string();
if (std::string("auto") != clock_freq_str) {
sim_setting.set_clock_frequency(clock_id, get_attribute(xml_clock_override_setting, "frequency", loc_data).as_float(0.));
}
sim_setting.set_clock_is_programming(clock_id, true);
sim_setting.set_clock_is_shift_register(clock_id, get_attribute(xml_clock_override_setting, "is_shift_register", loc_data).as_bool(false));
}
/********************************************************************
* Parse XML codes of a <clock_setting> to an object of simulation setting
*******************************************************************/
@ -102,6 +136,15 @@ void read_xml_clock_setting(pugi::xml_node& xml_clock_setting,
pugi::xml_node xml_programming_clock_setting = get_single_child(xml_clock_setting, "programming", loc_data);
sim_setting.set_programming_clock_frequency(get_attribute(xml_programming_clock_setting, "frequency", loc_data).as_float(0.));
/* Iterate over multiple operating clock settings and parse one by one */
for (pugi::xml_node xml_clock : xml_programming_clock_setting.children()) {
/* Error out if the XML child has an invalid name! */
if (xml_clock.name() != std::string("clock")) {
bad_tag(xml_clock, loc_data, xml_programming_clock_setting, {"clock"});
}
read_xml_programming_clock_override_setting(xml_clock, loc_data, sim_setting);
}
}
/********************************************************************

View File

@ -16,6 +16,36 @@ SimulationSetting::simulation_clock_range SimulationSetting::clocks() const {
return vtr::make_range(clock_ids_.begin(), clock_ids_.end());
}
std::vector<SimulationClockId> SimulationSetting::operating_clocks() const {
std::vector<SimulationClockId> op_clks;
for (const SimulationClockId& clk : clocks()) {
if (!clock_is_programming(clk)) {
op_clks.push_back(clk);
}
}
return op_clks;
}
std::vector<SimulationClockId> SimulationSetting::programming_clocks() const {
std::vector<SimulationClockId> prog_clks;
for (const SimulationClockId& clk : clocks()) {
if (clock_is_programming(clk)) {
prog_clks.push_back(clk);
}
}
return prog_clks;
}
std::vector<SimulationClockId> SimulationSetting::programming_shift_register_clocks() const {
std::vector<SimulationClockId> prog_clks;
for (const SimulationClockId& clk : clocks()) {
if (clock_is_programming(clk) && clock_is_shift_register(clk)) {
prog_clks.push_back(clk);
}
}
return prog_clks;
}
/************************************************************************
* Constructors
***********************************************************************/
@ -53,6 +83,16 @@ float SimulationSetting::clock_frequency(const SimulationClockId& clock_id) cons
return clock_frequencies_[clock_id];
}
bool SimulationSetting::clock_is_programming(const SimulationClockId& clock_id) const {
VTR_ASSERT(valid_clock_id(clock_id));
return clock_is_programming_[clock_id];
}
bool SimulationSetting::clock_is_shift_register(const SimulationClockId& clock_id) const {
VTR_ASSERT(valid_clock_id(clock_id));
return clock_is_shift_register_[clock_id];
}
bool SimulationSetting::auto_select_num_clock_cycles() const {
return 0 == num_clock_cycles_;
}
@ -157,6 +197,8 @@ SimulationClockId SimulationSetting::create_clock(const std::string& name) {
clock_names_.push_back(name);
clock_ports_.emplace_back();
clock_frequencies_.push_back(0.);
clock_is_programming_.push_back(false);
clock_is_shift_register_.push_back(false);
/* Register in the name-to-id map */
clock_name2ids_[name] = clock_id;
@ -176,6 +218,18 @@ void SimulationSetting::set_clock_frequency(const SimulationClockId& clock_id,
clock_frequencies_[clock_id] = frequency;
}
void SimulationSetting::set_clock_is_programming(const SimulationClockId& clock_id,
const float& is_prog) {
VTR_ASSERT(valid_clock_id(clock_id));
clock_is_programming_[clock_id] = is_prog;
}
void SimulationSetting::set_clock_is_shift_register(const SimulationClockId& clock_id,
const float& is_sr) {
VTR_ASSERT(valid_clock_id(clock_id));
clock_is_shift_register_[clock_id] = is_sr;
}
void SimulationSetting::set_num_clock_cycles(const size_t& num_clk_cycles) {
num_clock_cycles_ = num_clk_cycles;
}

View File

@ -62,6 +62,9 @@ class SimulationSetting {
SimulationSetting();
public: /* Accessors: aggregates */
simulation_clock_range clocks() const;
std::vector<SimulationClockId> operating_clocks() const;
std::vector<SimulationClockId> programming_clocks() const;
std::vector<SimulationClockId> programming_shift_register_clocks() const;
public: /* Public Accessors */
float default_operating_clock_frequency() const;
float programming_clock_frequency() const;
@ -69,6 +72,8 @@ class SimulationSetting {
std::string clock_name(const SimulationClockId& clock_id) const;
BasicPort clock_port(const SimulationClockId& clock_id) const;
float clock_frequency(const SimulationClockId& clock_id) const;
bool clock_is_programming(const SimulationClockId& clock_id) const;
bool clock_is_shift_register(const SimulationClockId& clock_id) const;
bool auto_select_num_clock_cycles() const;
size_t num_clock_cycles() const;
float operating_clock_frequency_slack() const;
@ -102,6 +107,10 @@ class SimulationSetting {
const BasicPort& port);
void set_clock_frequency(const SimulationClockId& clock_id,
const float& frequency);
void set_clock_is_programming(const SimulationClockId& clock_id,
const float& is_prog);
void set_clock_is_shift_register(const SimulationClockId& clock_id,
const float& is_sr);
void set_num_clock_cycles(const size_t& num_clk_cycles);
void set_operating_clock_frequency_slack(const float& op_clk_freq_slack);
void set_simulation_temperature(const float& sim_temp);
@ -150,6 +159,8 @@ class SimulationSetting {
vtr::vector<SimulationClockId, std::string> clock_names_;
vtr::vector<SimulationClockId, BasicPort> clock_ports_;
vtr::vector<SimulationClockId, float> clock_frequencies_;
vtr::vector<SimulationClockId, bool> clock_is_programming_;
vtr::vector<SimulationClockId, bool> clock_is_shift_register_;
/* Fast name-to-id lookup */
std::map<std::string, SimulationClockId> clock_name2ids_;

View File

@ -220,6 +220,10 @@ void write_xml_circuit_port(std::fstream& fp,
write_xml_attribute(fp, "is_prog", "true");
}
if (true == circuit_lib.port_is_shift_register(port)) {
write_xml_attribute(fp, "is_shift_register", "true");
}
if (true == circuit_lib.port_is_config_enable(port)) {
write_xml_attribute(fp, "is_config_enable", "true");
}

View File

@ -26,11 +26,24 @@ void write_xml_config_organization(std::fstream& fp,
openfpga::check_file_stream(fname, fp);
fp << "\t\t" << "<organization";
write_xml_attribute(fp, "type", CONFIG_PROTOCOL_TYPE_STRING[config_protocol.type()]);
write_xml_attribute(fp, "circuit_model_name", circuit_lib.model_name(config_protocol.memory_model()).c_str());
fp << "/>" << "\n";
/* Output BL/WL protocols */
fp << "\t\t\t" << "<bl";
write_xml_attribute(fp, "protocol", BLWL_PROTOCOL_TYPE_STRING[config_protocol.bl_protocol_type()]);
write_xml_attribute(fp, "circuit_model_name", circuit_lib.model_name(config_protocol.bl_memory_model()).c_str());
write_xml_attribute(fp, "num_banks", config_protocol.bl_num_banks());
fp << "/>" << "\n";
fp << "\t\t\t" << "<wl";
write_xml_attribute(fp, "protocol", BLWL_PROTOCOL_TYPE_STRING[config_protocol.wl_protocol_type()]);
write_xml_attribute(fp, "circuit_model_name", circuit_lib.model_name(config_protocol.wl_memory_model()).c_str());
write_xml_attribute(fp, "num_banks", config_protocol.wl_num_banks());
fp << "/>" << "\n";
fp << "\t" << "</organization>" << "\n";
}
/********************************************************************

View File

@ -41,7 +41,7 @@ void write_xml_clock_setting(std::fstream& fp,
fp << ">" << "\n";
/* Output clock information one by one */
for (const SimulationClockId& clock_id : sim_setting.clocks()) {
for (const SimulationClockId& clock_id : sim_setting.operating_clocks()) {
fp << "\t\t\t" << "<clock";
write_xml_attribute(fp, "name", sim_setting.clock_name(clock_id).c_str());
write_xml_attribute(fp, "port", generate_xml_port_name(sim_setting.clock_port(clock_id)).c_str());
@ -52,9 +52,22 @@ void write_xml_clock_setting(std::fstream& fp,
fp << "\t\t" << "</operating";
fp << ">" << "\n";
fp << "\t\t" << "<operating";
fp << "\t\t" << "<programming";
write_xml_attribute(fp, "frequency", sim_setting.programming_clock_frequency());
fp << "/>" << "\n";
fp << ">" << "\n";
/* Output clock information one by one */
for (const SimulationClockId& clock_id : sim_setting.programming_clocks()) {
fp << "\t\t\t" << "<clock";
write_xml_attribute(fp, "name", sim_setting.clock_name(clock_id).c_str());
write_xml_attribute(fp, "port", generate_xml_port_name(sim_setting.clock_port(clock_id)).c_str());
write_xml_attribute(fp, "frequency", std::to_string(sim_setting.clock_frequency(clock_id)).c_str());
write_xml_attribute(fp, "is_shift_register", std::to_string(sim_setting.clock_is_shift_register(clock_id)).c_str());
fp << ">" << "\n";
}
fp << "\t\t" << "</programming";
fp << ">" << "\n";
fp << "\t" << "</clock_setting>" << "\n";
}

View File

@ -13,10 +13,18 @@ namespace openfpga {
/* Top-level module name */
constexpr char* FPGA_TOP_MODULE_NAME = "fpga_top";
/* Configuration chain naming constant strings */
constexpr char* CONFIGURABLE_MEMORY_CHAIN_IN_NAME = "ccff_head";
constexpr char* CONFIGURABLE_MEMORY_CHAIN_OUT_NAME = "ccff_tail";
constexpr char* CONFIGURABLE_MEMORY_DATA_OUT_NAME = "mem_out";
constexpr char* CONFIGURABLE_MEMORY_INVERTED_DATA_OUT_NAME = "mem_outb";
constexpr char* BL_SHIFT_REGISTER_CHAIN_HEAD_NAME = "bl_sr_head";
constexpr char* BL_SHIFT_REGISTER_CHAIN_TAIL_NAME = "bl_sr_tail";
constexpr char* BL_SHIFT_REGISTER_CHAIN_BL_OUT_NAME = "bl_sr_bl_out";
constexpr char* WL_SHIFT_REGISTER_CHAIN_HEAD_NAME = "wl_sr_head";
constexpr char* WL_SHIFT_REGISTER_CHAIN_TAIL_NAME = "wl_sr_tail";
constexpr char* WL_SHIFT_REGISTER_CHAIN_WL_OUT_NAME = "wl_sr_wl_out";
constexpr char* WL_SHIFT_REGISTER_CHAIN_WLR_OUT_NAME = "wl_sr_wlr_out";
/* IO PORT */
/* Prefix of global input, output and inout ports of FPGA fabric */

View File

@ -103,6 +103,7 @@ int build_fabric(OpenfpgaContext& openfpga_ctx,
curr_status = build_device_module_graph(openfpga_ctx.mutable_module_graph(),
openfpga_ctx.mutable_decoder_lib(),
openfpga_ctx.mutable_blwl_shift_register_banks(),
const_cast<const OpenfpgaContext&>(openfpga_ctx),
g_vpr_ctx.device(),
cmd_context.option_enable(cmd, opt_frame_view),
@ -123,6 +124,7 @@ int build_fabric(OpenfpgaContext& openfpga_ctx,
/* Build fabric global port information */
openfpga_ctx.mutable_fabric_global_port_info() = build_fabric_global_port_info(openfpga_ctx.module_graph(),
openfpga_ctx.arch().config_protocol,
openfpga_ctx.arch().tile_annotations,
openfpga_ctx.arch().circuit_lib);

View File

@ -23,6 +23,7 @@
#include "device_rr_gsb.h"
#include "io_location_map.h"
#include "fabric_global_port_info.h"
#include "memory_bank_shift_register_banks.h"
/********************************************************************
* This file includes the declaration of the date structure
@ -65,6 +66,7 @@ class OpenfpgaContext : public Context {
const openfpga::DeviceRRGSB& device_rr_gsb() const { return device_rr_gsb_; }
const openfpga::MuxLibrary& mux_lib() const { return mux_lib_; }
const openfpga::DecoderLibrary& decoder_lib() const { return decoder_lib_; }
const std::array<openfpga::MemoryBankShiftRegisterBanks, 2>& blwl_shift_register_banks() { return blwl_sr_banks_; }
const openfpga::TileDirect& tile_direct() const { return tile_direct_; }
const openfpga::ModuleManager& module_graph() const { return module_graph_; }
const openfpga::FlowManager& flow_manager() const { return flow_manager_; }
@ -87,6 +89,7 @@ class OpenfpgaContext : public Context {
openfpga::DeviceRRGSB& mutable_device_rr_gsb() { return device_rr_gsb_; }
openfpga::MuxLibrary& mutable_mux_lib() { return mux_lib_; }
openfpga::DecoderLibrary& mutable_decoder_lib() { return decoder_lib_; }
std::array<openfpga::MemoryBankShiftRegisterBanks, 2>& mutable_blwl_shift_register_banks() { return blwl_sr_banks_; }
openfpga::TileDirect& mutable_tile_direct() { return tile_direct_; }
openfpga::ModuleManager& mutable_module_graph() { return module_graph_; }
openfpga::FlowManager& mutable_flow_manager() { return flow_manager_; }
@ -132,6 +135,11 @@ class OpenfpgaContext : public Context {
/* Inner/inter-column/row tile direct connections */
openfpga::TileDirect tile_direct_;
/* Library of shift register banks that control BLs and WLs
* @note Only used when memory bank is used as configuration protocol
*/
std::array<openfpga::MemoryBankShiftRegisterBanks, 2> blwl_sr_banks_;
/* Fabric module graph */
openfpga::ModuleManager module_graph_;
openfpga::IoLocationMap io_location_map_;

View File

@ -822,6 +822,23 @@ std::string generate_regional_blwl_port_name(const std::string& blwl_port_prefix
return blwl_port_prefix + std::string("_config_region_") + std::to_string(size_t(region_id));
}
/*********************************************************************
* Generate the module name for a shift register chain which configures BLs
*********************************************************************/
std::string generate_bl_shift_register_module_name(const std::string& memory_model_name,
const size_t& shift_register_size) {
return std::string("bl_shift_register_") + memory_model_name + std::string("_size") + std::to_string(shift_register_size);
}
/*********************************************************************
* Generate the module name for a shift register chain which configures WLs
*********************************************************************/
std::string generate_wl_shift_register_module_name(const std::string& memory_model_name,
const size_t& shift_register_size) {
return std::string("wl_shift_register_") + memory_model_name + std::string("_size") + std::to_string(shift_register_size);
}
/*********************************************************************
* Generate the port name for the input bus of a routing multiplexer
* This is very useful in Verilog code generation where the inputs of

View File

@ -187,6 +187,12 @@ std::string generate_sram_local_port_name(const CircuitLibrary& circuit_lib,
std::string generate_regional_blwl_port_name(const std::string& blwl_port_prefix,
const ConfigRegionId& region_id);
std::string generate_bl_shift_register_module_name(const std::string& memory_model_name,
const size_t& shift_register_size);
std::string generate_wl_shift_register_module_name(const std::string& memory_model_name,
const size_t& shift_register_size);
std::string generate_mux_input_bus_port_name(const CircuitLibrary& circuit_lib,
const CircuitModelId& mux_model,
const size_t& mux_size,

View File

@ -52,6 +52,7 @@ int write_fabric_verilog(OpenfpgaContext& openfpga_ctx,
fpga_fabric_verilog(openfpga_ctx.mutable_module_graph(),
openfpga_ctx.mutable_verilog_netlists(),
openfpga_ctx.blwl_shift_register_banks(),
openfpga_ctx.arch().circuit_lib,
openfpga_ctx.mux_lib(),
openfpga_ctx.decoder_lib(),

View File

@ -31,6 +31,7 @@ namespace openfpga {
*******************************************************************/
int build_device_module_graph(ModuleManager& module_manager,
DecoderLibrary& decoder_lib,
std::array<MemoryBankShiftRegisterBanks, 2>& blwl_sr_banks,
const OpenfpgaContext& openfpga_ctx,
const DeviceContext& vpr_device_ctx,
const bool& frame_view,
@ -112,6 +113,7 @@ int build_device_module_graph(ModuleManager& module_manager,
/* Build FPGA fabric top-level module */
status = build_top_module(module_manager,
decoder_lib,
blwl_sr_banks,
openfpga_ctx.arch().circuit_lib,
openfpga_ctx.vpr_device_annotation(),
vpr_device_ctx.grid,

View File

@ -17,6 +17,7 @@ namespace openfpga {
int build_device_module_graph(ModuleManager& module_manager,
DecoderLibrary& decoder_lib,
std::array<MemoryBankShiftRegisterBanks, 2>& blwl_sr_banks,
const OpenfpgaContext& openfpga_ctx,
const DeviceContext& vpr_device_ctx,
const bool& frame_view,

View File

@ -24,6 +24,7 @@ namespace openfpga {
* and cache their port/pin index in the top-level module
*******************************************************************/
FabricGlobalPortInfo build_fabric_global_port_info(const ModuleManager& module_manager,
const ConfigProtocol& config_protocol,
const TileAnnotation& tile_annotation,
const CircuitLibrary& circuit_lib) {
vtr::ScopedStartFinishTimer timer("Create global port info for top module");
@ -53,8 +54,18 @@ FabricGlobalPortInfo build_fabric_global_port_info(const ModuleManager& module_m
fabric_global_port_info.set_global_port_is_reset(fabric_port, circuit_lib.port_is_reset(global_port));
fabric_global_port_info.set_global_port_is_set(fabric_port, circuit_lib.port_is_set(global_port));
fabric_global_port_info.set_global_port_is_prog(fabric_port, circuit_lib.port_is_prog(global_port));
fabric_global_port_info.set_global_port_is_shift_register(fabric_port, circuit_lib.port_is_shift_register(global_port));
fabric_global_port_info.set_global_port_is_config_enable(fabric_port, circuit_lib.port_is_config_enable(global_port));
fabric_global_port_info.set_global_port_default_value(fabric_port, circuit_lib.port_default_value(global_port));
/* Special for BL/WL shift register models: we should identify which clock belongs to BL and which clock belongs to WL */
if (config_protocol.bl_memory_model() == circuit_lib.port_parent_model(global_port)) {
fabric_global_port_info.set_global_port_is_bl(fabric_port, true);
}
if (config_protocol.wl_memory_model() == circuit_lib.port_parent_model(global_port)) {
fabric_global_port_info.set_global_port_is_wl(fabric_port, true);
}
}
/* Add the global ports from tile annotation */

View File

@ -19,6 +19,7 @@
namespace openfpga {
FabricGlobalPortInfo build_fabric_global_port_info(const ModuleManager& module_manager,
const ConfigProtocol& config_protocol,
const TileAnnotation& tile_annotation,
const CircuitLibrary& circuit_lib);

View File

@ -78,7 +78,6 @@ void add_module_input_nets_to_mem_modules(ModuleManager& module_manager,
* pin of output port of the memory module, where W is the size of port
* 3. It assumes fixed port name for output ports
********************************************************************/
static
std::vector<ModuleNetId> add_module_output_nets_to_chain_mem_modules(ModuleManager& module_manager,
const ModuleId& mem_module,
const std::string& mem_module_output_name,

View File

@ -16,6 +16,15 @@
/* begin namespace openfpga */
namespace openfpga {
std::vector<ModuleNetId> add_module_output_nets_to_chain_mem_modules(ModuleManager& module_manager,
const ModuleId& mem_module,
const std::string& mem_module_output_name,
const CircuitLibrary& circuit_lib,
const CircuitPortId& circuit_port,
const ModuleId& child_module,
const size_t& child_index,
const size_t& child_instance);
void build_memory_modules(ModuleManager& module_manager,
DecoderLibrary& arch_decoder_lib,
const MuxLibrary& mux_lib,

View File

@ -283,6 +283,7 @@ vtr::Matrix<size_t> add_top_module_connection_block_instances(ModuleManager& mod
*******************************************************************/
int build_top_module(ModuleManager& module_manager,
DecoderLibrary& decoder_lib,
std::array<MemoryBankShiftRegisterBanks, 2>& blwl_sr_banks,
const CircuitLibrary& circuit_lib,
const VprDeviceAnnotation& vpr_device_annotation,
const DeviceGrid& grids,
@ -341,12 +342,6 @@ int build_top_module(ModuleManager& module_manager,
tile_direct, arch_direct);
}
/* Add global ports to the pb_module:
* This is a much easier job after adding sub modules (instances),
* we just need to find all the global ports from the child modules and build a list of it
*/
add_module_global_ports_from_child_modules(module_manager, top_module);
/* Add global ports from grid ports that are defined as global in tile annotation */
status = add_top_module_global_ports_from_grid_modules(module_manager, top_module, tile_annotation, vpr_device_annotation, grids, grid_instance_ids);
if (CMD_EXEC_FATAL_ERROR == status) {
@ -420,13 +415,20 @@ int build_top_module(ModuleManager& module_manager,
* This is a one-shot addition that covers all the memory modules in this pb module!
*/
if (0 < module_manager.configurable_children(top_module).size()) {
add_top_module_nets_memory_config_bus(module_manager, decoder_lib,
add_top_module_nets_memory_config_bus(module_manager, decoder_lib, blwl_sr_banks,
top_module,
circuit_lib,
config_protocol, circuit_lib.design_tech_type(sram_model),
top_module_num_config_bits);
}
/* Add global ports to the top module:
* This is a much easier job after adding sub modules (instances),
* we just need to find all the global ports from the child modules and build a list of it
* @note This function is called after the add_top_module_nets_memory_config_bus() because it may add some sub modules
*/
add_module_global_ports_from_child_modules(module_manager, top_module);
return status;
}

View File

@ -19,6 +19,7 @@
#include "config_protocol.h"
#include "module_manager.h"
#include "fabric_key.h"
#include "memory_bank_shift_register_banks.h"
/********************************************************************
* Function declaration
@ -29,6 +30,7 @@ namespace openfpga {
int build_top_module(ModuleManager& module_manager,
DecoderLibrary& decoder_lib,
std::array<MemoryBankShiftRegisterBanks, 2>& blwl_sr_banks,
const CircuitLibrary& circuit_lib,
const VprDeviceAnnotation& vpr_device_annotation,
const DeviceGrid& grids,

View File

@ -1735,6 +1735,7 @@ void add_top_module_nets_cmos_memory_frame_config_bus(ModuleManager& module_mana
static
void add_top_module_nets_cmos_memory_config_bus(ModuleManager& module_manager,
DecoderLibrary& decoder_lib,
std::array<MemoryBankShiftRegisterBanks, 2>& blwl_sr_banks,
const ModuleId& parent_module,
const CircuitLibrary& circuit_lib,
const ConfigProtocol& config_protocol,
@ -1754,7 +1755,8 @@ void add_top_module_nets_cmos_memory_config_bus(ModuleManager& module_manager,
add_top_module_nets_cmos_memory_bank_config_bus(module_manager, decoder_lib, parent_module, num_config_bits);
break;
case CONFIG_MEM_QL_MEMORY_BANK:
add_top_module_nets_cmos_ql_memory_bank_config_bus(module_manager, decoder_lib, parent_module, circuit_lib, config_protocol, num_config_bits);
add_top_module_nets_cmos_ql_memory_bank_config_bus(module_manager, decoder_lib, blwl_sr_banks,
parent_module, circuit_lib, config_protocol, num_config_bits);
break;
case CONFIG_MEM_FRAME_BASED:
add_top_module_nets_cmos_memory_frame_config_bus(module_manager, decoder_lib, parent_module, num_config_bits);
@ -1800,6 +1802,7 @@ void add_top_module_nets_cmos_memory_config_bus(ModuleManager& module_manager,
*******************************************************************/
void add_top_module_nets_memory_config_bus(ModuleManager& module_manager,
DecoderLibrary& decoder_lib,
std::array<MemoryBankShiftRegisterBanks, 2>& blwl_sr_banks,
const ModuleId& parent_module,
const CircuitLibrary& circuit_lib,
const ConfigProtocol& config_protocol,
@ -1811,6 +1814,7 @@ void add_top_module_nets_memory_config_bus(ModuleManager& module_manager,
switch (mem_tech) {
case CIRCUIT_MODEL_DESIGN_CMOS:
add_top_module_nets_cmos_memory_config_bus(module_manager, decoder_lib,
blwl_sr_banks,
parent_module,
circuit_lib,
config_protocol,

View File

@ -18,6 +18,7 @@
#include "device_rr_gsb.h"
#include "fabric_key.h"
#include "config_protocol.h"
#include "memory_bank_shift_register_banks.h"
#include "build_top_module_memory_utils.h"
/********************************************************************
@ -64,6 +65,7 @@ void add_top_module_sram_ports(ModuleManager& module_manager,
void add_top_module_nets_memory_config_bus(ModuleManager& module_manager,
DecoderLibrary& decoder_lib,
std::array<MemoryBankShiftRegisterBanks, 2>& blwl_sr_banks,
const ModuleId& parent_module,
const CircuitLibrary& circuit_lib,
const ConfigProtocol& config_protocol,

View File

@ -24,12 +24,331 @@
#include "decoder_library_utils.h"
#include "module_manager_utils.h"
#include "memory_bank_utils.h"
#include "build_memory_modules.h"
#include "build_decoder_modules.h"
#include "memory_bank_shift_register_banks.h"
#include "build_top_module_memory_bank.h"
/* begin namespace openfpga */
namespace openfpga {
/********************************************************************
* Connect all the memory modules under the parent module in a chain
*
* +--------+ +--------+ +--------+
* ccff_head --->| Memory |--->| Memory |--->... --->| Memory |----> ccff_tail
* | Module | | Module | | Module |
* | [0] | | [1] | | [N-1] |
* +--------+ +--------+ +--------+
* For the 1st memory module:
* net source is the configuration chain head of the primitive module
* net sink is the configuration chain head of the next memory module
*
* For the rest of memory modules:
* net source is the configuration chain tail of the previous memory module
* net sink is the configuration chain head of the next memory module
*
* Note that:
* This function is designed for memory modules ONLY!
* Do not use it to replace the
* add_module_nets_cmos_memory_chain_config_bus() !!!
*********************************************************************/
static
void add_module_nets_to_ql_memory_bank_shift_register_module(ModuleManager& module_manager,
const ModuleId& parent_module,
const CircuitLibrary& circuit_lib,
const CircuitPortId& model_input_port,
const CircuitPortId& model_output_port,
const std::string& chain_head_port_name,
const std::string& chain_tail_port_name) {
for (size_t mem_index = 0; mem_index < module_manager.configurable_children(parent_module).size(); ++mem_index) {
ModuleId net_src_module_id;
size_t net_src_instance_id;
ModulePortId net_src_port_id;
ModuleId net_sink_module_id;
size_t net_sink_instance_id;
ModulePortId net_sink_port_id;
if (0 == mem_index) {
/* Find the port name of configuration chain head */
std::string src_port_name = chain_head_port_name;
net_src_module_id = parent_module;
net_src_instance_id = 0;
net_src_port_id = module_manager.find_module_port(net_src_module_id, src_port_name);
/* Find the port name of next memory module */
std::string sink_port_name = circuit_lib.port_prefix(model_input_port);
net_sink_module_id = module_manager.configurable_children(parent_module)[mem_index];
net_sink_instance_id = module_manager.configurable_child_instances(parent_module)[mem_index];
net_sink_port_id = module_manager.find_module_port(net_sink_module_id, sink_port_name);
} else {
/* Find the port name of previous memory module */
std::string src_port_name = circuit_lib.port_prefix(model_output_port);
net_src_module_id = module_manager.configurable_children(parent_module)[mem_index - 1];
net_src_instance_id = module_manager.configurable_child_instances(parent_module)[mem_index - 1];
net_src_port_id = module_manager.find_module_port(net_src_module_id, src_port_name);
/* Find the port name of next memory module */
std::string sink_port_name = circuit_lib.port_prefix(model_input_port);
net_sink_module_id = module_manager.configurable_children(parent_module)[mem_index];
net_sink_instance_id = module_manager.configurable_child_instances(parent_module)[mem_index];
net_sink_port_id = module_manager.find_module_port(net_sink_module_id, sink_port_name);
}
/* Get the pin id for source port */
BasicPort net_src_port = module_manager.module_port(net_src_module_id, net_src_port_id);
/* Get the pin id for sink port */
BasicPort net_sink_port = module_manager.module_port(net_sink_module_id, net_sink_port_id);
/* Port sizes of source and sink should match */
VTR_ASSERT(net_src_port.get_width() == net_sink_port.get_width());
/* Create a net for each pin */
for (size_t pin_id = 0; pin_id < net_src_port.pins().size(); ++pin_id) {
/* Create a net and add source and sink to it */
ModuleNetId net = create_module_source_pin_net(module_manager, parent_module, net_src_module_id, net_src_instance_id, net_src_port_id, net_src_port.pins()[pin_id]);
/* Add net sink */
module_manager.add_module_net_sink(parent_module, net, net_sink_module_id, net_sink_instance_id, net_sink_port_id, net_sink_port.pins()[pin_id]);
}
}
/* For the last memory module:
* net source is the configuration chain tail of the previous memory module
* net sink is the configuration chain tail of the primitive module
*/
/* Find the port name of previous memory module */
std::string src_port_name = circuit_lib.port_prefix(model_output_port);
ModuleId net_src_module_id = module_manager.configurable_children(parent_module).back();
size_t net_src_instance_id = module_manager.configurable_child_instances(parent_module).back();
ModulePortId net_src_port_id = module_manager.find_module_port(net_src_module_id, src_port_name);
/* Find the port name of next memory module */
std::string sink_port_name = chain_tail_port_name;
ModuleId net_sink_module_id = parent_module;
size_t net_sink_instance_id = 0;
ModulePortId net_sink_port_id = module_manager.find_module_port(net_sink_module_id, sink_port_name);
/* Get the pin id for source port */
BasicPort net_src_port = module_manager.module_port(net_src_module_id, net_src_port_id);
/* Get the pin id for sink port */
BasicPort net_sink_port = module_manager.module_port(net_sink_module_id, net_sink_port_id);
/* Port sizes of source and sink should match */
VTR_ASSERT(net_src_port.get_width() == net_sink_port.get_width());
/* Create a net for each pin */
for (size_t pin_id = 0; pin_id < net_src_port.pins().size(); ++pin_id) {
/* Create a net and add source and sink to it */
ModuleNetId net = create_module_source_pin_net(module_manager, parent_module, net_src_module_id, net_src_instance_id, net_src_port_id, net_src_port.pins()[pin_id]);
/* Add net sink */
module_manager.add_module_net_sink(parent_module, net, net_sink_module_id, net_sink_instance_id, net_sink_port_id, net_sink_port.pins()[pin_id]);
}
}
/*********************************************************************
* BL shift register chain module organization
*
* +-------+ +-------+ +-------+
* chain --->| CCFF |--->| CCFF |--->... --->| CCFF |---->chain
* input&clock | [0] | | [1] | | [N-1] | output
* +-------+ +-------+ +-------+
* | | ... | config-memory output
* v v v
* +-----------------------------------------+
* | BLs of configurable modules |
*
********************************************************************/
static
ModuleId build_bl_shift_register_chain_module(ModuleManager& module_manager,
const CircuitLibrary& circuit_lib,
const std::string& module_name,
const CircuitModelId& sram_model,
const size_t& num_mems) {
/* Get the input ports from the SRAM */
std::vector<CircuitPortId> sram_input_ports = circuit_lib.model_ports_by_type(sram_model, CIRCUIT_MODEL_PORT_INPUT, true);
VTR_ASSERT(1 == sram_input_ports.size());
/* Get the output ports from the SRAM */
std::vector<CircuitPortId> sram_output_ports = circuit_lib.model_ports_by_type(sram_model, CIRCUIT_MODEL_PORT_OUTPUT, true);
VTR_ASSERT(1 == sram_output_ports.size());
/* Get the BL ports from the SRAM */
std::vector<CircuitPortId> sram_bl_ports = circuit_lib.model_ports_by_type(sram_model, CIRCUIT_MODEL_PORT_BL, true);
VTR_ASSERT(1 == sram_bl_ports.size());
/* Create a module and add to the module manager */
ModuleId mem_module = module_manager.add_module(module_name);
VTR_ASSERT(true == module_manager.valid_module_id(mem_module));
/* Label module usage */
module_manager.set_module_usage(mem_module, ModuleManager::MODULE_CONFIG);
/* Add an input port, which is the head of configuration chain in the module */
/* TODO: restriction!!!
* consider only the first input of the CCFF model as the D port,
* which will be connected to the head of the chain
*/
BasicPort chain_head_port(BL_SHIFT_REGISTER_CHAIN_HEAD_NAME,
circuit_lib.port_size(sram_input_ports[0]));
module_manager.add_port(mem_module, chain_head_port, ModuleManager::MODULE_INPUT_PORT);
/* Add an output port, which is the tail of configuration chain in the module */
/* TODO: restriction!!!
* consider only the first output of the CCFF model as the Q port,
* which will be connected to the tail of the chain
*/
BasicPort chain_tail_port(BL_SHIFT_REGISTER_CHAIN_TAIL_NAME,
circuit_lib.port_size(sram_output_ports[0]));
module_manager.add_port(mem_module, chain_tail_port, ModuleManager::MODULE_OUTPUT_PORT);
/* Add the output ports to output BL signals */
BasicPort chain_bl_port(BL_SHIFT_REGISTER_CHAIN_BL_OUT_NAME,
num_mems);
module_manager.add_port(mem_module, chain_bl_port, ModuleManager::MODULE_OUTPUT_PORT);
/* Find the sram module in the module manager */
ModuleId sram_mem_module = module_manager.find_module(circuit_lib.model_name(sram_model));
/* Instanciate each submodule */
for (size_t i = 0; i < num_mems; ++i) {
size_t sram_mem_instance = module_manager.num_instance(mem_module, sram_mem_module);
module_manager.add_child_module(mem_module, sram_mem_module);
module_manager.add_configurable_child(mem_module, sram_mem_module, sram_mem_instance);
/* Build module nets to wire bl outputs of sram modules to BL outputs of memory module */
for (const auto& child_module_output_port : sram_bl_ports) {
std::string chain_output_port_name = std::string(BL_SHIFT_REGISTER_CHAIN_BL_OUT_NAME);
add_module_output_nets_to_chain_mem_modules(module_manager, mem_module,
chain_output_port_name, circuit_lib,
child_module_output_port,
sram_mem_module, i, sram_mem_instance);
}
}
/* Build module nets to wire the configuration chain */
add_module_nets_to_ql_memory_bank_shift_register_module(module_manager, mem_module,
circuit_lib, sram_input_ports[0], sram_output_ports[0],
std::string(BL_SHIFT_REGISTER_CHAIN_HEAD_NAME),
std::string(BL_SHIFT_REGISTER_CHAIN_TAIL_NAME));
/* Add global ports to the pb_module:
* This is a much easier job after adding sub modules (instances),
* we just need to find all the global ports from the child modules and build a list of it
*/
add_module_global_ports_from_child_modules(module_manager, mem_module);
return mem_module;
}
/*********************************************************************
* WL shift register chain module organization
*
* +-------+ +-------+ +-------+
* chain --->| CCFF |--->| CCFF |--->... --->| CCFF |---->chain
* input&clock | [0] | | [1] | | [N-1] | output
* +-------+ +-------+ +-------+
* | | ... | config-memory output
* v v v
* +-----------------------------------------+
* | WL/WLRs of configurable modules |
*
********************************************************************/
static
ModuleId build_wl_shift_register_chain_module(ModuleManager& module_manager,
const CircuitLibrary& circuit_lib,
const std::string& module_name,
const CircuitModelId& sram_model,
const size_t& num_mems) {
/* Get the input ports from the SRAM */
std::vector<CircuitPortId> sram_input_ports = circuit_lib.model_ports_by_type(sram_model, CIRCUIT_MODEL_PORT_INPUT, true);
VTR_ASSERT(1 == sram_input_ports.size());
/* Get the output ports from the SRAM */
std::vector<CircuitPortId> sram_output_ports = circuit_lib.model_ports_by_type(sram_model, CIRCUIT_MODEL_PORT_OUTPUT, true);
VTR_ASSERT(1 == sram_output_ports.size());
/* Get the WL ports from the SRAM */
std::vector<CircuitPortId> sram_wl_ports = circuit_lib.model_ports_by_type(sram_model, CIRCUIT_MODEL_PORT_WL, true);
VTR_ASSERT(1 == sram_wl_ports.size());
/* Get the optional WLR ports from the SRAM */
std::vector<CircuitPortId> sram_wlr_ports = circuit_lib.model_ports_by_type(sram_model, CIRCUIT_MODEL_PORT_WLR, true);
VTR_ASSERT(0 == sram_wlr_ports.size() || 1 == sram_wlr_ports.size());
/* Create a module and add to the module manager */
ModuleId mem_module = module_manager.add_module(module_name);
VTR_ASSERT(true == module_manager.valid_module_id(mem_module));
/* Label module usage */
module_manager.set_module_usage(mem_module, ModuleManager::MODULE_CONFIG);
/* Add an input port, which is the head of configuration chain in the module */
/* TODO: restriction!!!
* consider only the first input of the CCFF model as the D port,
* which will be connected to the head of the chain
*/
BasicPort chain_head_port(WL_SHIFT_REGISTER_CHAIN_HEAD_NAME,
circuit_lib.port_size(sram_input_ports[0]));
module_manager.add_port(mem_module, chain_head_port, ModuleManager::MODULE_INPUT_PORT);
/* Add an output port, which is the tail of configuration chain in the module */
/* TODO: restriction!!!
* consider only the first output of the CCFF model as the Q port,
* which will be connected to the tail of the chain
*/
BasicPort chain_tail_port(WL_SHIFT_REGISTER_CHAIN_TAIL_NAME,
circuit_lib.port_size(sram_output_ports[0]));
module_manager.add_port(mem_module, chain_tail_port, ModuleManager::MODULE_OUTPUT_PORT);
/* Add the output ports to output BL signals */
BasicPort chain_wl_port(WL_SHIFT_REGISTER_CHAIN_WL_OUT_NAME,
num_mems);
module_manager.add_port(mem_module, chain_wl_port, ModuleManager::MODULE_OUTPUT_PORT);
/* Find the sram module in the module manager */
ModuleId sram_mem_module = module_manager.find_module(circuit_lib.model_name(sram_model));
/* Instanciate each submodule */
for (size_t i = 0; i < num_mems; ++i) {
size_t sram_mem_instance = module_manager.num_instance(mem_module, sram_mem_module);
module_manager.add_child_module(mem_module, sram_mem_module);
module_manager.add_configurable_child(mem_module, sram_mem_module, sram_mem_instance);
/* Build module nets to wire wl outputs of sram modules to WL outputs of memory module */
for (const auto& child_module_output_port : sram_wl_ports) {
std::string chain_output_port_name = std::string(WL_SHIFT_REGISTER_CHAIN_WL_OUT_NAME);
add_module_output_nets_to_chain_mem_modules(module_manager, mem_module,
chain_output_port_name, circuit_lib,
child_module_output_port,
sram_mem_module, i, sram_mem_instance);
}
/* Build module nets to wire wlr outputs of sram modules to WLR outputs of memory module */
for (const auto& child_module_output_port : sram_wlr_ports) {
std::string chain_output_port_name = std::string(WL_SHIFT_REGISTER_CHAIN_WLR_OUT_NAME);
add_module_output_nets_to_chain_mem_modules(module_manager, mem_module,
chain_output_port_name, circuit_lib,
child_module_output_port,
sram_mem_module, i, sram_mem_instance);
}
}
/* Build module nets to wire the configuration chain */
add_module_nets_to_ql_memory_bank_shift_register_module(module_manager, mem_module,
circuit_lib, sram_input_ports[0], sram_output_ports[0],
std::string(WL_SHIFT_REGISTER_CHAIN_HEAD_NAME),
std::string(WL_SHIFT_REGISTER_CHAIN_TAIL_NAME));
/* Add global ports to the pb_module:
* This is a much easier job after adding sub modules (instances),
* we just need to find all the global ports from the child modules and build a list of it
*/
add_module_global_ports_from_child_modules(module_manager, mem_module);
return mem_module;
}
/*********************************************************************
* This function to add nets for quicklogic memory banks
* Each configuration region has independent memory bank circuitry
@ -745,6 +1064,396 @@ void add_top_module_nets_cmos_ql_memory_bank_wl_flatten_config_bus(ModuleManager
}
}
/*********************************************************************
* This function to add nets for QuickLogic memory bank
* We build the net connects between the head ports of shift register banks
* and the head ports of top-level module
* @note This function is applicable to both BL and WL shift registers
**********************************************************************/
static
void add_top_module_nets_cmos_ql_memory_bank_shift_register_bank_heads(ModuleManager& module_manager,
const ModuleId& top_module,
const MemoryBankShiftRegisterBanks& sr_banks,
const std::string& head_port_name) {
for (const ConfigRegionId& config_region : module_manager.regions(top_module)) {
ModulePortId blsr_head_port = module_manager.find_module_port(top_module, generate_regional_blwl_port_name(head_port_name, config_region));
BasicPort blsr_head_port_info = module_manager.module_port(top_module, blsr_head_port);
for (size_t iinst = 0; iinst < sr_banks.shift_register_bank_modules(config_region).size(); ++iinst) {
ModuleId sr_bank_module = sr_banks.shift_register_bank_modules(config_region)[iinst];
size_t sr_bank_instance = sr_banks.shift_register_bank_instances(config_region)[iinst];
VTR_ASSERT(sr_bank_module);
ModulePortId sr_module_head_port = module_manager.find_module_port(sr_bank_module, head_port_name);
BasicPort sr_module_head_port_info = module_manager.module_port(sr_bank_module, sr_module_head_port);
VTR_ASSERT(sr_module_head_port_info.get_width() == blsr_head_port_info.get_width());
for (size_t ipin = 0; ipin < sr_module_head_port_info.pins().size(); ++ipin) {
/* Create net */
ModuleNetId net = create_module_source_pin_net(module_manager, top_module,
top_module, 0,
blsr_head_port,
blsr_head_port_info.pins()[ipin]);
VTR_ASSERT(ModuleNetId::INVALID() != net);
/* Add net sink */
module_manager.add_module_net_sink(top_module, net,
sr_bank_module, sr_bank_instance, sr_module_head_port, sr_module_head_port_info.pins()[ipin]);
}
}
}
}
/*********************************************************************
* This function to add nets for QuickLogic memory bank
* We build the net connects between the head ports of shift register banks
* and the head ports of top-level module
* @note This function is applicable to both BL and WL shift registers
**********************************************************************/
static
void add_top_module_nets_cmos_ql_memory_bank_shift_register_bank_tails(ModuleManager& module_manager,
const ModuleId& top_module,
const MemoryBankShiftRegisterBanks& sr_banks,
const std::string& tail_port_name) {
for (const ConfigRegionId& config_region : module_manager.regions(top_module)) {
ModulePortId blsr_tail_port = module_manager.find_module_port(top_module, generate_regional_blwl_port_name(tail_port_name, config_region));
BasicPort blsr_tail_port_info = module_manager.module_port(top_module, blsr_tail_port);
for (size_t iinst = 0; iinst < sr_banks.shift_register_bank_modules(config_region).size(); ++iinst) {
ModuleId sr_bank_module = sr_banks.shift_register_bank_modules(config_region)[iinst];
size_t sr_bank_instance = sr_banks.shift_register_bank_instances(config_region)[iinst];
VTR_ASSERT(sr_bank_module);
ModulePortId sr_module_tail_port = module_manager.find_module_port(sr_bank_module, tail_port_name);
BasicPort sr_module_tail_port_info = module_manager.module_port(sr_bank_module, sr_module_tail_port);
VTR_ASSERT(sr_module_tail_port_info.get_width() == blsr_tail_port_info.get_width());
for (size_t ipin = 0; ipin < sr_module_tail_port_info.pins().size(); ++ipin) {
/* Create net */
ModuleNetId net = create_module_source_pin_net(module_manager, top_module,
sr_bank_module, sr_bank_instance,
sr_module_tail_port, sr_module_tail_port_info.pins()[ipin]);
VTR_ASSERT(ModuleNetId::INVALID() != net);
/* Add net sink */
module_manager.add_module_net_sink(top_module, net,
top_module, 0,
blsr_tail_port, blsr_tail_port_info.pins()[ipin]);
}
}
}
}
/**************************************************************
* Add BL/WL nets from shift register module to each configurable child
* BL pins of shift register module are connected to the BL input pins of each PB/CB/SB
* For all the PB/CB/SB in the same column, they share the same set of BLs
* A quick example
*
* +-----------------------+
* | Shift register chain |
* +-----------------------+
* BL[i .. i + sqrt(N)]
* |
* | CLB[1][H]
* | +---------+
* | | SRAM |
* +-->| [0..N] |
* | +---------+
* |
* ...
* | CLB[1][1]
* | +---------+
* | | SRAM |
* +-->| [0..N] |
* | +---------+
* |
* @note optional BL/WL is applicable to WLR, which may not always exist
*/
static
void add_top_module_nets_cmos_ql_memory_bank_shift_register_bank_blwls(ModuleManager& module_manager,
const ModuleId& top_module,
const MemoryBankShiftRegisterBanks& sr_banks,
const std::string& sr_blwl_port_name,
const std::string& child_blwl_port_name,
const bool& optional_blwl = false) {
for (const ConfigRegionId& config_region : module_manager.regions(top_module)) {
for (size_t iinst = 0; iinst < sr_banks.shift_register_bank_modules(config_region).size(); ++iinst) {
ModuleId sr_bank_module = sr_banks.shift_register_bank_modules(config_region)[iinst];
size_t sr_bank_instance = sr_banks.shift_register_bank_instances(config_region)[iinst];
VTR_ASSERT(sr_bank_module);
ModulePortId sr_module_blwl_port = module_manager.find_module_port(sr_bank_module, sr_blwl_port_name);
if (!sr_module_blwl_port && optional_blwl) {
continue;
}
VTR_ASSERT(sr_module_blwl_port);
BasicPort sr_module_blwl_port_info = module_manager.module_port(sr_bank_module, sr_module_blwl_port);
for (size_t sink_id = 0; sink_id < sr_banks.shift_register_bank_sink_child_ids(config_region, sr_bank_module, sr_bank_instance).size(); ++sink_id) {
size_t child_id = sr_banks.shift_register_bank_sink_child_ids(config_region, sr_bank_module, sr_bank_instance)[sink_id];
ModuleId child_module = module_manager.region_configurable_children(top_module, config_region)[child_id];
size_t child_instance = module_manager.region_configurable_child_instances(top_module, config_region)[child_id];
/* Find the BL port */
ModulePortId child_blwl_port = module_manager.find_module_port(child_module, child_blwl_port_name);
BasicPort child_blwl_port_info = module_manager.module_port(child_module, child_blwl_port);
size_t cur_sr_module_blwl_pin_id = sr_banks.shift_register_bank_source_blwl_ids(config_region, sr_bank_module, sr_bank_instance)[sink_id];
/* Create net */
ModuleNetId net = create_module_source_pin_net(module_manager, top_module,
sr_bank_module, sr_bank_instance,
sr_module_blwl_port,
sr_module_blwl_port_info.pins()[cur_sr_module_blwl_pin_id]);
VTR_ASSERT(ModuleNetId::INVALID() != net);
/* Add net sink */
size_t sink_pin_id = sr_banks.shift_register_bank_sink_pin_ids(config_region, sr_bank_module, sr_bank_instance)[sink_id];
module_manager.add_module_net_sink(top_module, net,
child_module, child_instance, child_blwl_port, sink_pin_id);
}
}
}
}
/*********************************************************************
* This function to add nets for quicklogic memory banks using shift registers to manipulate BL/WLs
* Each configuration region has independent BL/WL shift register banks
* - Find the number of BLs and WLs required for each region
* - Find the number of BL and WL shift register chains required for each region
* - Create the module of shift register chain for each unique size
* - Create nets to connect from top-level module inputs to BL/WL shift register chains
* - Create nets to connect from BL/WL shift register chains to BL/WL of configurable children
*
* @note this function only adds the BL protocol
*
* Detailed schematic of each memory bank:
* @note The numbers are just made to show a simplified example, practical cases are more complicated!
*
* sr_clk sr_head sr_tail
* | | ^
* v v |
* +-------------------------------------------------+
* | Bit Line shift register chains |
* +-------------------------------------------------+
* | | |
* +---------+ BL[0:9] BL[10:17] BL[18:22]
* | | | | |
* | | | | |
* | Word |--WL[0:3]-->-----------+---------------+---- ... |------+-->
* | | | | | | | |
* | Line | | v | v | v
* | | | +-------+ | +-------+ | +------+
* | shift | +-->| SRAM | +-->| SRAM | +->| SRAM |
* | | | | [0:8] | | | [0:5] | ... | | [0:7]|
* | register| | +-------+ | +-------+ | +------+
* | | | | |
* | chains |--WL[4:14] -----------+--------------+--------- | -----+-->
* | | | | | | | |
* | | | v | v | v
* | | | +-------+ | +-------+ | +-------+
* | | +-->| SRAM | | | SRAM | +->| SRAM |
* | | | | [0:80]| | | [0:63]| ... | | [0:31]|
* | | | +-------+ | +-------+ | +-------+
* | | | |
* | | | ... ... ... | ...
* | | | | |
* | |--WL[15:18] -----------+---------------+---- --- | -----+-->
* | | | | | | | |
* | | | v | v | v
* +---------+ | +-------+ | +-------+ | +-------+
* +-->| SRAM | +-->| SRAM | +->| SRAM |
* | |[0:5] | | | [0:8] | ... | | [0:2] |
* | +-------+ | +-------+ | +-------+
* v v v
* WL[0:9] WL[0:7] WL[0:4]
*
**********************************************************************/
static
void add_top_module_nets_cmos_ql_memory_bank_bl_shift_register_config_bus(ModuleManager& module_manager,
MemoryBankShiftRegisterBanks& sr_banks,
const ModuleId& top_module,
const CircuitLibrary& circuit_lib,
const ConfigProtocol& config_protocol) {
CircuitModelId sram_model = config_protocol.memory_model();
CircuitModelId bl_memory_model = config_protocol.bl_memory_model();
/* Find out the unique shift register chain sizes */
vtr::vector<ConfigRegionId, size_t> unique_sr_sizes;
unique_sr_sizes.resize(module_manager.regions(top_module).size());
for (const ConfigRegionId& config_region : module_manager.regions(top_module)) {
/* TODO: Need to find how to cut the BLs when there are multiple banks for shift registers in a region */
size_t num_bls = compute_memory_bank_regional_num_bls(module_manager, top_module,
config_region,
circuit_lib, sram_model);
unique_sr_sizes[config_region] = num_bls;
}
/* Build submodules for shift register chains */
for (const size_t& sr_size : unique_sr_sizes) {
std::string sr_module_name = generate_bl_shift_register_module_name(circuit_lib.model_name(bl_memory_model), sr_size);
build_bl_shift_register_chain_module(module_manager,
circuit_lib,
sr_module_name,
bl_memory_model,
sr_size);
}
/* Instanciate the shift register chains in the top-level module */
sr_banks.resize_regions(module_manager.regions(top_module).size());
for (const ConfigRegionId& config_region : module_manager.regions(top_module)) {
std::string sr_module_name = generate_bl_shift_register_module_name(circuit_lib.model_name(bl_memory_model), unique_sr_sizes[config_region]);
ModuleId sr_bank_module = module_manager.find_module(sr_module_name);
VTR_ASSERT(sr_bank_module);
size_t cur_inst = module_manager.num_instance(top_module, sr_bank_module);
module_manager.add_child_module(top_module, sr_bank_module);
sr_banks.add_shift_register_instance(config_region, sr_bank_module, cur_inst);
/**************************************************************
* Precompute the BLs and WLs distribution across the FPGA fabric
* The distribution is a matrix which contains the starting index of BL/WL for each column or row
*/
std::pair<int, int> child_x_range = compute_memory_bank_regional_configurable_child_x_range(module_manager, top_module, config_region);
std::map<int, size_t> num_bls_per_tile = compute_memory_bank_regional_bitline_numbers_per_tile(module_manager, top_module,
config_region,
circuit_lib, sram_model);
std::map<int, size_t> bl_start_index_per_tile = compute_memory_bank_regional_blwl_start_index_per_tile(child_x_range, num_bls_per_tile);
for (size_t child_id = 0; child_id < module_manager.region_configurable_children(top_module, config_region).size(); ++child_id) {
ModuleId child_module = module_manager.region_configurable_children(top_module, config_region)[child_id];
vtr::Point<int> coord = module_manager.region_configurable_child_coordinates(top_module, config_region)[child_id];
/* Find the BL port */
ModulePortId child_bl_port = module_manager.find_module_port(child_module, std::string(MEMORY_BL_PORT_NAME));
BasicPort child_bl_port_info = module_manager.module_port(child_module, child_bl_port);
size_t cur_bl_index = 0;
for (const size_t& sink_bl_pin : child_bl_port_info.pins()) {
size_t bl_pin_id = bl_start_index_per_tile[coord.x()] + cur_bl_index;
sr_banks.add_shift_register_sink_nodes(config_region, sr_bank_module, cur_inst, child_id, sink_bl_pin);
sr_banks.add_shift_register_source_blwls(config_region, sr_bank_module, cur_inst, bl_pin_id);
cur_bl_index++;
}
}
}
/* Create connections between top-level module and the BL shift register banks
* - Connect the head port from top-level module to each shift register bank
* - Connect the tail port from each shift register bank to top-level module
*/
add_top_module_nets_cmos_ql_memory_bank_shift_register_bank_heads(module_manager, top_module, sr_banks,
std::string(BL_SHIFT_REGISTER_CHAIN_HEAD_NAME));
add_top_module_nets_cmos_ql_memory_bank_shift_register_bank_tails(module_manager, top_module, sr_banks,
std::string(BL_SHIFT_REGISTER_CHAIN_TAIL_NAME));
/* Create connections between BLs of top-level module and BLs of child modules for each region */
add_top_module_nets_cmos_ql_memory_bank_shift_register_bank_blwls(module_manager, top_module, sr_banks,
std::string(BL_SHIFT_REGISTER_CHAIN_BL_OUT_NAME),
std::string(MEMORY_BL_PORT_NAME));
}
/*********************************************************************
* Top-level function to add nets for quicklogic memory banks using shift registers to control BL/WLs
*
* @note this function only adds the WL configuration bus
*
* @note see detailed explanation on the bus connection in function add_top_module_nets_cmos_ql_memory_bank_bl_shift_register_config_bus()
**********************************************************************/
static
void add_top_module_nets_cmos_ql_memory_bank_wl_shift_register_config_bus(ModuleManager& module_manager,
MemoryBankShiftRegisterBanks& sr_banks,
const ModuleId& top_module,
const CircuitLibrary& circuit_lib,
const ConfigProtocol& config_protocol) {
CircuitModelId sram_model = config_protocol.memory_model();
CircuitModelId wl_memory_model = config_protocol.wl_memory_model();
/* Find out the unique shift register chain sizes */
vtr::vector<ConfigRegionId, size_t> unique_sr_sizes;
for (const ConfigRegionId& config_region : module_manager.regions(top_module)) {
/* TODO: Need to find how to cut the BLs when there are multiple banks for shift registers in a region */
size_t num_wls = compute_memory_bank_regional_num_wls(module_manager, top_module,
config_region,
circuit_lib, sram_model);
unique_sr_sizes.push_back(num_wls);
}
/* TODO: Build submodules for shift register chains */
for (const size_t& sr_size : unique_sr_sizes) {
std::string sr_module_name = generate_wl_shift_register_module_name(circuit_lib.model_name(wl_memory_model), sr_size);
build_wl_shift_register_chain_module(module_manager,
circuit_lib,
sr_module_name,
wl_memory_model,
sr_size);
}
/* Instanciate the shift register chains in the top-level module */
sr_banks.resize_regions(module_manager.regions(top_module).size());
for (const ConfigRegionId& config_region : module_manager.regions(top_module)) {
std::string sr_module_name = generate_wl_shift_register_module_name(circuit_lib.model_name(wl_memory_model), unique_sr_sizes[config_region]);
ModuleId sr_bank_module = module_manager.find_module(sr_module_name);
VTR_ASSERT(sr_bank_module);
size_t cur_inst = module_manager.num_instance(top_module, sr_bank_module);
module_manager.add_child_module(top_module, sr_bank_module);
sr_banks.add_shift_register_instance(config_region, sr_bank_module, cur_inst);
/**************************************************************
* Precompute the BLs and WLs distribution across the FPGA fabric
* The distribution is a matrix which contains the starting index of BL/WL for each column or row
*/
std::pair<int, int> child_y_range = compute_memory_bank_regional_configurable_child_y_range(module_manager, top_module, config_region);
std::map<int, size_t> num_wls_per_tile = compute_memory_bank_regional_wordline_numbers_per_tile(module_manager, top_module,
config_region,
circuit_lib, sram_model);
std::map<int, size_t> wl_start_index_per_tile = compute_memory_bank_regional_blwl_start_index_per_tile(child_y_range, num_wls_per_tile);
for (size_t child_id = 0; child_id < module_manager.region_configurable_children(top_module, config_region).size(); ++child_id) {
ModuleId child_module = module_manager.region_configurable_children(top_module, config_region)[child_id];
vtr::Point<int> coord = module_manager.region_configurable_child_coordinates(top_module, config_region)[child_id];
size_t cur_wl_index = 0;
/* Find the WL port */
ModulePortId child_wl_port = module_manager.find_module_port(child_module, std::string(MEMORY_WL_PORT_NAME));
BasicPort child_wl_port_info = module_manager.module_port(child_module, child_wl_port);
for (const size_t& sink_wl_pin : child_wl_port_info.pins()) {
size_t wl_pin_id = wl_start_index_per_tile[coord.y()] + cur_wl_index;
sr_banks.add_shift_register_sink_nodes(config_region, sr_bank_module, cur_inst, child_id, sink_wl_pin);
sr_banks.add_shift_register_source_blwls(config_region, sr_bank_module, cur_inst, wl_pin_id);
cur_wl_index++;
}
}
}
/* Create connections between top-level module and the BL shift register banks
* - Connect the head port from top-level module to each shift register bank
* - Connect the tail port from each shift register bank to top-level module
*/
add_top_module_nets_cmos_ql_memory_bank_shift_register_bank_heads(module_manager, top_module, sr_banks,
std::string(WL_SHIFT_REGISTER_CHAIN_HEAD_NAME));
add_top_module_nets_cmos_ql_memory_bank_shift_register_bank_tails(module_manager, top_module, sr_banks,
std::string(WL_SHIFT_REGISTER_CHAIN_TAIL_NAME));
/* Create connections between BLs of top-level module and BLs of child modules for each region */
add_top_module_nets_cmos_ql_memory_bank_shift_register_bank_blwls(module_manager, top_module, sr_banks,
std::string(WL_SHIFT_REGISTER_CHAIN_WL_OUT_NAME),
std::string(MEMORY_WL_PORT_NAME));
add_top_module_nets_cmos_ql_memory_bank_shift_register_bank_blwls(module_manager, top_module, sr_banks,
std::string(WL_SHIFT_REGISTER_CHAIN_WLR_OUT_NAME),
std::string(MEMORY_WLR_PORT_NAME),
true);
}
/*********************************************************************
* Top-level function to add nets for quicklogic memory banks
* - Each configuration region has independent memory bank circuitry
@ -761,6 +1470,7 @@ void add_top_module_nets_cmos_ql_memory_bank_wl_flatten_config_bus(ModuleManager
********************************************************************/
void add_top_module_nets_cmos_ql_memory_bank_config_bus(ModuleManager& module_manager,
DecoderLibrary& decoder_lib,
std::array<MemoryBankShiftRegisterBanks, 2>& blwl_sr_banks,
const ModuleId& top_module,
const CircuitLibrary& circuit_lib,
const ConfigProtocol& config_protocol,
@ -778,7 +1488,7 @@ void add_top_module_nets_cmos_ql_memory_bank_config_bus(ModuleManager& module_ma
break;
}
case BLWL_PROTOCOL_SHIFT_REGISTER: {
/* TODO */
add_top_module_nets_cmos_ql_memory_bank_bl_shift_register_config_bus(module_manager, blwl_sr_banks[0], top_module, circuit_lib, config_protocol);
break;
}
default: {
@ -797,7 +1507,7 @@ void add_top_module_nets_cmos_ql_memory_bank_config_bus(ModuleManager& module_ma
break;
}
case BLWL_PROTOCOL_SHIFT_REGISTER: {
/* TODO */
add_top_module_nets_cmos_ql_memory_bank_wl_shift_register_config_bus(module_manager, blwl_sr_banks[1], top_module, circuit_lib, config_protocol);
break;
}
default: {
@ -872,7 +1582,14 @@ void add_top_module_ql_memory_bank_sram_ports(ModuleManager& module_manager,
break;
}
case BLWL_PROTOCOL_SHIFT_REGISTER: {
/* TODO */
/* Each region will have independent shift register banks */
for (const ConfigRegionId& config_region : module_manager.regions(module_id)) {
size_t num_heads = config_protocol.bl_num_banks();
BasicPort blsr_head_port(generate_regional_blwl_port_name(std::string(BL_SHIFT_REGISTER_CHAIN_HEAD_NAME), config_region), num_heads);
module_manager.add_port(module_id, blsr_head_port, ModuleManager::MODULE_INPUT_PORT);
BasicPort blsr_tail_port(generate_regional_blwl_port_name(std::string(BL_SHIFT_REGISTER_CHAIN_TAIL_NAME), config_region), num_heads);
module_manager.add_port(module_id, blsr_tail_port, ModuleManager::MODULE_OUTPUT_PORT);
}
break;
}
default: {
@ -914,7 +1631,14 @@ void add_top_module_ql_memory_bank_sram_ports(ModuleManager& module_manager,
break;
}
case BLWL_PROTOCOL_SHIFT_REGISTER: {
/* TODO */
/* Each region will have independent shift register banks */
for (const ConfigRegionId& config_region : module_manager.regions(module_id)) {
size_t num_heads = config_protocol.wl_num_banks();
BasicPort wlsr_head_port(generate_regional_blwl_port_name(std::string(WL_SHIFT_REGISTER_CHAIN_HEAD_NAME), config_region), num_heads);
module_manager.add_port(module_id, wlsr_head_port, ModuleManager::MODULE_INPUT_PORT);
BasicPort wlsr_tail_port(generate_regional_blwl_port_name(std::string(WL_SHIFT_REGISTER_CHAIN_TAIL_NAME), config_region), num_heads);
module_manager.add_port(module_id, wlsr_tail_port, ModuleManager::MODULE_OUTPUT_PORT);
}
break;
}
default: {

View File

@ -7,12 +7,14 @@
#include <vector>
#include <map>
#include <array>
#include "vtr_vector.h"
#include "vtr_ndmatrix.h"
#include "module_manager.h"
#include "config_protocol.h"
#include "circuit_library.h"
#include "decoder_library.h"
#include "memory_bank_shift_register_banks.h"
#include "build_top_module_memory_utils.h"
/********************************************************************
@ -24,6 +26,7 @@ namespace openfpga {
void add_top_module_nets_cmos_ql_memory_bank_config_bus(ModuleManager& module_manager,
DecoderLibrary& decoder_lib,
std::array<MemoryBankShiftRegisterBanks, 2>& blwl_sr_banks,
const ModuleId& top_module,
const CircuitLibrary& circuit_lib,
const ConfigProtocol& config_protocol,

View File

@ -50,6 +50,21 @@ bool FabricGlobalPortInfo::global_port_is_prog(const FabricGlobalPortId& global_
return global_port_is_prog_[global_port_id];
}
bool FabricGlobalPortInfo::global_port_is_shift_register(const FabricGlobalPortId& global_port_id) const {
VTR_ASSERT(valid_global_port_id(global_port_id));
return global_port_is_shift_register_[global_port_id];
}
bool FabricGlobalPortInfo::global_port_is_bl(const FabricGlobalPortId& global_port_id) const {
VTR_ASSERT(valid_global_port_id(global_port_id));
return global_port_is_bl_[global_port_id];
}
bool FabricGlobalPortInfo::global_port_is_wl(const FabricGlobalPortId& global_port_id) const {
VTR_ASSERT(valid_global_port_id(global_port_id));
return global_port_is_wl_[global_port_id];
}
bool FabricGlobalPortInfo::global_port_is_config_enable(const FabricGlobalPortId& global_port_id) const {
VTR_ASSERT(valid_global_port_id(global_port_id));
return global_port_is_config_enable_[global_port_id];
@ -77,6 +92,9 @@ FabricGlobalPortId FabricGlobalPortInfo::create_global_port(const ModulePortId&
global_port_is_set_.push_back(false);
global_port_is_reset_.push_back(false);
global_port_is_prog_.push_back(false);
global_port_is_shift_register_.push_back(false);
global_port_is_bl_.push_back(false);
global_port_is_wl_.push_back(false);
global_port_is_io_.push_back(false);
global_port_is_config_enable_.push_back(false);
global_port_default_values_.push_back(0);
@ -108,6 +126,24 @@ void FabricGlobalPortInfo::set_global_port_is_prog(const FabricGlobalPortId& glo
global_port_is_prog_[global_port_id] = is_prog;
}
void FabricGlobalPortInfo::set_global_port_is_shift_register(const FabricGlobalPortId& global_port_id,
const bool& is_shift_register) {
VTR_ASSERT(valid_global_port_id(global_port_id));
global_port_is_shift_register_[global_port_id] = is_shift_register;
}
void FabricGlobalPortInfo::set_global_port_is_bl(const FabricGlobalPortId& global_port_id,
const bool& is_bl) {
VTR_ASSERT(valid_global_port_id(global_port_id));
global_port_is_bl_[global_port_id] = is_bl;
}
void FabricGlobalPortInfo::set_global_port_is_wl(const FabricGlobalPortId& global_port_id,
const bool& is_wl) {
VTR_ASSERT(valid_global_port_id(global_port_id));
global_port_is_wl_[global_port_id] = is_wl;
}
void FabricGlobalPortInfo::set_global_port_is_config_enable(const FabricGlobalPortId& global_port_id,
const bool& is_config_enable) {
VTR_ASSERT(valid_global_port_id(global_port_id));

View File

@ -36,6 +36,9 @@ class FabricGlobalPortInfo {
bool global_port_is_set(const FabricGlobalPortId& global_port_id) const;
bool global_port_is_reset(const FabricGlobalPortId& global_port_id) const;
bool global_port_is_prog(const FabricGlobalPortId& global_port_id) const;
bool global_port_is_bl(const FabricGlobalPortId& global_port_id) const;
bool global_port_is_wl(const FabricGlobalPortId& global_port_id) const;
bool global_port_is_shift_register(const FabricGlobalPortId& global_port_id) const;
bool global_port_is_config_enable(const FabricGlobalPortId& global_port_id) const;
bool global_port_is_io(const FabricGlobalPortId& global_port_id) const;
size_t global_port_default_value(const FabricGlobalPortId& global_port_id) const;
@ -52,6 +55,12 @@ class FabricGlobalPortInfo {
const bool& is_reset);
void set_global_port_is_prog(const FabricGlobalPortId& global_port_id,
const bool& is_prog);
void set_global_port_is_shift_register(const FabricGlobalPortId& global_port_id,
const bool& is_shift_register);
void set_global_port_is_bl(const FabricGlobalPortId& global_port_id,
const bool& is_bl);
void set_global_port_is_wl(const FabricGlobalPortId& global_port_id,
const bool& is_wl);
void set_global_port_is_config_enable(const FabricGlobalPortId& global_port_id,
const bool& is_config_enable);
void set_global_port_is_io(const FabricGlobalPortId& global_port_id,
@ -68,6 +77,9 @@ class FabricGlobalPortInfo {
vtr::vector<FabricGlobalPortId, bool> global_port_is_reset_;
vtr::vector<FabricGlobalPortId, bool> global_port_is_set_;
vtr::vector<FabricGlobalPortId, bool> global_port_is_prog_;
vtr::vector<FabricGlobalPortId, bool> global_port_is_shift_register_;
vtr::vector<FabricGlobalPortId, bool> global_port_is_bl_;
vtr::vector<FabricGlobalPortId, bool> global_port_is_wl_;
vtr::vector<FabricGlobalPortId, bool> global_port_is_config_enable_;
vtr::vector<FabricGlobalPortId, bool> global_port_is_io_;
vtr::vector<FabricGlobalPortId, size_t> global_port_default_values_;

View File

@ -0,0 +1,114 @@
#include <algorithm>
#include "vtr_assert.h"
#include "memory_bank_shift_register_banks.h"
/* begin namespace openfpga */
namespace openfpga {
std::vector<ModuleId> MemoryBankShiftRegisterBanks::shift_register_bank_unique_modules() const {
std::vector<ModuleId> sr_bank_modules;
for (const auto& region : sr_instance_sink_child_ids_) {
for (const auto& pair : region) {
if (sr_bank_modules.end() == std::find(sr_bank_modules.begin(), sr_bank_modules.end(), pair.first.first)) {
sr_bank_modules.push_back(pair.first.first);
}
}
}
return sr_bank_modules;
}
std::vector<ModuleId> MemoryBankShiftRegisterBanks::shift_register_bank_modules(const ConfigRegionId& region) const {
std::vector<ModuleId> sr_bank_modules;
VTR_ASSERT(valid_region_id(region));
for (const auto& pair : sr_instance_sink_child_ids_[region]) {
sr_bank_modules.push_back(pair.first.first);
}
return sr_bank_modules;
}
std::vector<size_t> MemoryBankShiftRegisterBanks::shift_register_bank_instances(const ConfigRegionId& region) const {
std::vector<size_t> sr_bank_instances;
VTR_ASSERT(valid_region_id(region));
for (const auto& pair : sr_instance_sink_child_ids_[region]) {
sr_bank_instances.push_back(pair.first.second);
}
return sr_bank_instances;
}
std::vector<size_t> MemoryBankShiftRegisterBanks::shift_register_bank_sink_child_ids(const ConfigRegionId& region,
const ModuleId& sr_module,
const size_t& sr_instance) const {
VTR_ASSERT(valid_region_id(region));
auto result = sr_instance_sink_child_ids_[region].find(std::make_pair(sr_module, sr_instance));
/* Return an empty vector if not found */
if (result != sr_instance_sink_child_ids_[region].end()) {
return result->second;
}
return std::vector<size_t>();
}
std::vector<size_t> MemoryBankShiftRegisterBanks::shift_register_bank_sink_pin_ids(const ConfigRegionId& region,
const ModuleId& sr_module,
const size_t& sr_instance) const {
VTR_ASSERT(valid_region_id(region));
auto result = sr_instance_sink_child_pin_ids_[region].find(std::make_pair(sr_module, sr_instance));
/* Return an empty vector if not found */
if (result != sr_instance_sink_child_pin_ids_[region].end()) {
return result->second;
}
return std::vector<size_t>();
}
std::vector<size_t> MemoryBankShiftRegisterBanks::shift_register_bank_source_blwl_ids(const ConfigRegionId& region,
const ModuleId& sr_module,
const size_t& sr_instance) const {
VTR_ASSERT(valid_region_id(region));
auto result = sr_instance_source_blwl_ids_[region].find(std::make_pair(sr_module, sr_instance));
/* Return an empty vector if not found */
if (result != sr_instance_source_blwl_ids_[region].end()) {
return result->second;
}
return std::vector<size_t>();
}
void MemoryBankShiftRegisterBanks::resize_regions(const size_t& num_regions) {
sr_instance_sink_child_ids_.resize(num_regions);
sr_instance_sink_child_pin_ids_.resize(num_regions);
sr_instance_source_blwl_ids_.resize(num_regions);
}
void MemoryBankShiftRegisterBanks::add_shift_register_instance(const ConfigRegionId& region,
const ModuleId& sr_module,
const size_t& sr_instance) {
VTR_ASSERT(valid_region_id(region));
sr_instance_sink_child_ids_[region][std::make_pair(sr_module, sr_instance)];
sr_instance_sink_child_pin_ids_[region][std::make_pair(sr_module, sr_instance)];
sr_instance_source_blwl_ids_[region][std::make_pair(sr_module, sr_instance)];
}
void MemoryBankShiftRegisterBanks::add_shift_register_sink_nodes(const ConfigRegionId& region,
const ModuleId& sr_module,
const size_t& sr_instance,
const size_t& sink_child_id,
const size_t& sink_child_pin_id) {
VTR_ASSERT(valid_region_id(region));
sr_instance_sink_child_ids_[region][std::make_pair(sr_module, sr_instance)].push_back(sink_child_id);
sr_instance_sink_child_pin_ids_[region][std::make_pair(sr_module, sr_instance)].push_back(sink_child_pin_id);
}
void MemoryBankShiftRegisterBanks::add_shift_register_source_blwls(const ConfigRegionId& region,
const ModuleId& sr_module,
const size_t& sr_instance,
const size_t& sink_blwl_id) {
VTR_ASSERT(valid_region_id(region));
sr_instance_source_blwl_ids_[region][std::make_pair(sr_module, sr_instance)].push_back(sink_blwl_id);
}
bool MemoryBankShiftRegisterBanks::valid_region_id(const ConfigRegionId& region) const {
return size_t(region) < sr_instance_sink_child_ids_.size();
}
} /* end namespace openfpga */

View File

@ -0,0 +1,87 @@
#ifndef MEMORY_BANK_SHIFT_REGISTER_BANKS_H
#define MEMORY_BANK_SHIFT_REGISTER_BANKS_H
#include <map>
#include <vector>
#include "vtr_vector.h"
#include "module_manager.h"
/* begin namespace openfpga */
namespace openfpga {
/******************************************************************************
* This files includes data structures that stores detailed information about
* shift register banks for each configuration region in the top-level module, including
* - Module id of each shift register bank
* - Instance id of each shift register bank
* - The connectivity of of each shift register bank to reconfigurable child under a configuration region
* - The ids of each child to be connected
* - The Bit Line (BL) or Word Line (WL) pin id of each child
* @note This data structure is mainly used as a database for adding connections around shift register banks in top-level module
******************************************************************************/
class MemoryBankShiftRegisterBanks {
public: /* Accessors */
/* @brief Return a list of modules of unique shift register banks across all the regions */
std::vector<ModuleId> shift_register_bank_unique_modules() const;
/* @brief Return a list of modules of shift register banks under a specific configuration region of top-level module */
std::vector<ModuleId> shift_register_bank_modules(const ConfigRegionId& region) const;
/* @brief Return a list of instances of shift register banks under a specific configuration region of top-level module */
std::vector<size_t> shift_register_bank_instances(const ConfigRegionId& region) const;
/* @brief Return a list of ids of reconfigurable children for a given instance of shift register bank
* under a specific configuration region of top-level module
*/
std::vector<size_t> shift_register_bank_sink_child_ids(const ConfigRegionId& region,
const ModuleId& sr_module,
const size_t& sr_instance) const;
/* @brief Return a list of BL/WL ids of reconfigurable children for a given instance of shift register bank
* under a specific configuration region of top-level module
*/
std::vector<size_t> shift_register_bank_sink_pin_ids(const ConfigRegionId& region,
const ModuleId& sr_module,
const size_t& sr_instance) const;
/* @brief Return a list of BL/WL ids of a given instance of shift register bank
* under a specific configuration region of top-level module
*/
std::vector<size_t> shift_register_bank_source_blwl_ids(const ConfigRegionId& region,
const ModuleId& sr_module,
const size_t& sr_instance) const;
public: /* Mutators */
void resize_regions(const size_t& num_regions);
/* @brief Add the module id and instance id of a shift register under a specific configuration region of top-level module */
void add_shift_register_instance(const ConfigRegionId& region,
const ModuleId& sr_module,
const size_t& sr_instance);
/* @brief Add the child id and pin id of BL/WL to which a shift register is connected to under a specific configuration region of top-level module */
void add_shift_register_sink_nodes(const ConfigRegionId& region,
const ModuleId& sr_module,
const size_t& sr_instance,
const size_t& sink_child_id,
const size_t& sink_child_pin_id);
/* @brief Add the BL/WL id under a specific configuration region of top-level module to which a shift register is connected to */
void add_shift_register_source_blwls(const ConfigRegionId& region,
const ModuleId& sr_module,
const size_t& sr_instance,
const size_t& sink_blwl_id);
public: /* Validators */
bool valid_region_id(const ConfigRegionId& region) const;
private: /* Internal data */
/* [config_region][(shift_register_module, shift_register_instance)][i] = (reconfigurable_child_id, blwl_port_pin_index)*/
vtr::vector<ConfigRegionId, std::map<std::pair<ModuleId, size_t>, std::vector<size_t>>> sr_instance_sink_child_ids_;
vtr::vector<ConfigRegionId, std::map<std::pair<ModuleId, size_t>, std::vector<size_t>>> sr_instance_sink_child_pin_ids_;
vtr::vector<ConfigRegionId, std::map<std::pair<ModuleId, size_t>, std::vector<size_t>>> sr_instance_source_blwl_ids_;
};
} /* end namespace openfpga */
#endif

View File

@ -249,8 +249,12 @@ void build_module_fabric_dependent_bitstream_ql_memory_bank(const ConfigProtocol
}
}
} else {
/* TODO */
VTR_ASSERT(BLWL_PROTOCOL_SHIFT_REGISTER == config_protocol.bl_protocol_type());
bl_addr_port_info.set_width(1); /* Deposit minimum width */
for (const ConfigRegionId& config_region : module_manager.regions(top_module)) {
size_t num_bls = compute_memory_bank_regional_num_bls(module_manager, top_module, config_region, circuit_lib, config_protocol.memory_model());
bl_addr_port_info.set_width(std::max(num_bls, bl_addr_port_info.get_width()));
}
}
/* For different WL control protocol, the address ports are different
@ -275,8 +279,12 @@ void build_module_fabric_dependent_bitstream_ql_memory_bank(const ConfigProtocol
}
}
} else {
/* TODO */
VTR_ASSERT(BLWL_PROTOCOL_SHIFT_REGISTER == config_protocol.wl_protocol_type());
wl_addr_port_info.set_width(1); /* Deposit minimum width */
for (const ConfigRegionId& config_region : module_manager.regions(top_module)) {
size_t num_wls = compute_memory_bank_regional_num_wls(module_manager, top_module, config_region, circuit_lib, config_protocol.memory_model());
wl_addr_port_info.set_width(std::max(num_wls, wl_addr_port_info.get_width()));
}
}
/* Reserve bits before build-up */
@ -297,26 +305,30 @@ void build_module_fabric_dependent_bitstream_ql_memory_bank(const ConfigProtocol
/* Find the BL/WL port (different region may have different sizes of BL/WLs) */
ModulePortId cur_bl_addr_port;
BasicPort cur_bl_addr_port_info;
if (BLWL_PROTOCOL_DECODER == config_protocol.bl_protocol_type()) {
cur_bl_addr_port = module_manager.find_module_port(top_module, std::string(DECODER_BL_ADDRESS_PORT_NAME));
cur_bl_addr_port_info = module_manager.module_port(top_module, cur_bl_addr_port);
} else if (BLWL_PROTOCOL_FLATTEN == config_protocol.bl_protocol_type()) {
cur_bl_addr_port = module_manager.find_module_port(top_module, generate_regional_blwl_port_name(std::string(MEMORY_BL_PORT_NAME), config_region));
cur_bl_addr_port_info = module_manager.module_port(top_module, cur_bl_addr_port);
} else {
/* TODO */
VTR_ASSERT(BLWL_PROTOCOL_SHIFT_REGISTER == config_protocol.bl_protocol_type());
cur_bl_addr_port_info.set_width(compute_memory_bank_regional_num_bls(module_manager, top_module, config_region, circuit_lib, config_protocol.memory_model()));
}
BasicPort cur_bl_addr_port_info = module_manager.module_port(top_module, cur_bl_addr_port);
ModulePortId cur_wl_addr_port;
BasicPort cur_wl_addr_port_info;
if (BLWL_PROTOCOL_DECODER == config_protocol.wl_protocol_type()) {
cur_wl_addr_port = module_manager.find_module_port(top_module, std::string(DECODER_WL_ADDRESS_PORT_NAME));
cur_wl_addr_port_info = module_manager.module_port(top_module, cur_wl_addr_port);
} else if (BLWL_PROTOCOL_FLATTEN == config_protocol.wl_protocol_type()) {
cur_wl_addr_port = module_manager.find_module_port(top_module, generate_regional_blwl_port_name(std::string(MEMORY_WL_PORT_NAME), config_region));
cur_wl_addr_port_info = module_manager.module_port(top_module, cur_wl_addr_port);
} else {
/* TODO */
VTR_ASSERT(BLWL_PROTOCOL_SHIFT_REGISTER == config_protocol.wl_protocol_type());
cur_wl_addr_port_info.set_width(compute_memory_bank_regional_num_wls(module_manager, top_module, config_region, circuit_lib, config_protocol.memory_model()));
}
BasicPort cur_wl_addr_port_info = module_manager.module_port(top_module, cur_wl_addr_port);
/**************************************************************
* Precompute the BLs and WLs distribution across the FPGA fabric

View File

@ -0,0 +1,50 @@
#include "memory_bank_flatten_fabric_bitstream.h"
/* begin namespace openfpga */
namespace openfpga {
size_t MemoryBankFlattenFabricBitstream::size() const {
return bitstream_.size();
}
size_t MemoryBankFlattenFabricBitstream::bl_vector_size() const {
/* The address sizes and data input sizes are the same across any element,
* just get it from the 1st element to save runtime
*/
size_t bl_vec_size = 0;
for (const auto& bl_vec : bitstream_.begin()->second) {
bl_vec_size += bl_vec.size();
}
return bl_vec_size;
}
size_t MemoryBankFlattenFabricBitstream::wl_vector_size() const {
/* The address sizes and data input sizes are the same across any element,
* just get it from the 1st element to save runtime
*/
size_t wl_vec_size = 0;
for (const auto& wl_vec : bitstream_.begin()->first) {
wl_vec_size += wl_vec.size();
}
return wl_vec_size;
}
std::vector<std::string> MemoryBankFlattenFabricBitstream::bl_vector(const std::vector<std::string>& wl_vec) const {
return bitstream_.at(wl_vec);
}
std::vector<std::vector<std::string>> MemoryBankFlattenFabricBitstream::wl_vectors() const {
std::vector<std::vector<std::string>> wl_vecs;
for (const auto& pair : bitstream_) {
wl_vecs.push_back(pair.first);
}
return wl_vecs;
}
void MemoryBankFlattenFabricBitstream::add_blwl_vectors(const std::vector<std::string>& bl_vec,
const std::vector<std::string>& wl_vec) {
/* TODO: Add sanity check. Give a warning if the wl vector is already there */
bitstream_[wl_vec] = bl_vec;
}
} /* end namespace openfpga */

View File

@ -0,0 +1,49 @@
#ifndef MEMORY_BANK_FLATTEN_FABRIC_BITSTREAM_H
#define MEMORY_BANK_FLATTEN_FABRIC_BITSTREAM_H
#include <string>
#include <map>
#include <vector>
#include "vtr_vector.h"
/* begin namespace openfpga */
namespace openfpga {
/******************************************************************************
* This files includes data structures that stores a downloadable format of fabric bitstream
* which is compatible with memory bank configuration protocol using flatten BL/WL buses
* @note This data structure is mainly used to output bitstream file for compatible protocols
******************************************************************************/
class MemoryBankFlattenFabricBitstream {
public: /* Accessors */
/* @brief Return the length of bitstream */
size_t size() const;
/* @brief Return the BL address size */
size_t bl_vector_size() const;
/* @brief Return the WL address size */
size_t wl_vector_size() const;
/* @brief Return the BL vectors with a given WL key */
std::vector<std::string> bl_vector(const std::vector<std::string>& wl_vec) const;
/* @brief Return all the WL vectors in a downloaded sequence */
std::vector<std::vector<std::string>> wl_vectors() const;
public: /* Mutators */
/* @brief add a pair of BL/WL vectors to the bitstream database */
void add_blwl_vectors(const std::vector<std::string>& bl_vec,
const std::vector<std::string>& wl_vec);
public: /* Validators */
private: /* Internal data */
/* [(wl_bank0, wl_bank1, ...)] = [(bl_bank0, bl_bank1, ...)]
* Must use (WL, BL) as pairs in the map!!!
* This is because BL data may not be unique while WL must be unique
*/
std::map<std::vector<std::string>, std::vector<std::string>> bitstream_;
};
} /* end namespace openfpga */
#endif

View File

@ -0,0 +1,82 @@
#include "vtr_assert.h"
#include "memory_bank_shift_register_fabric_bitstream.h"
/* begin namespace openfpga */
namespace openfpga {
MemoryBankShiftRegisterFabricBitstream::word_range MemoryBankShiftRegisterFabricBitstream::words() const {
return vtr::make_range(bitstream_word_ids_.begin(), bitstream_word_ids_.end());
}
size_t MemoryBankShiftRegisterFabricBitstream::num_words() const {
return bitstream_word_ids_.size();
}
size_t MemoryBankShiftRegisterFabricBitstream::bl_word_size() const {
/* For a fast runtime, we just inspect the last element
* It is the validator which should ensure all the words have a uniform size
*/
return bitstream_word_bls_[bitstream_word_ids_.back()].size();
}
size_t MemoryBankShiftRegisterFabricBitstream::wl_word_size() const {
/* For a fast runtime, we just inspect the last element
* It is the validator which should ensure all the words have a uniform size
*/
return bitstream_word_wls_[bitstream_word_ids_.back()].size();
}
size_t MemoryBankShiftRegisterFabricBitstream::bl_width() const {
/* For a fast runtime, we just inspect the last element
* It is the validator which should ensure all the words have a uniform size
*/
return bitstream_word_bls_[bitstream_word_ids_.back()].back().size();
}
size_t MemoryBankShiftRegisterFabricBitstream::wl_width() const {
/* For a fast runtime, we just inspect the last element
* It is the validator which should ensure all the words have a uniform size
*/
return bitstream_word_wls_[bitstream_word_ids_.back()].back().size();
}
std::vector<std::string> MemoryBankShiftRegisterFabricBitstream::bl_vectors(const MemoryBankShiftRegisterFabricBitstreamWordId& word_id) const {
VTR_ASSERT(valid_word_id(word_id));
return bitstream_word_bls_[word_id];
}
std::vector<std::string> MemoryBankShiftRegisterFabricBitstream::wl_vectors(const MemoryBankShiftRegisterFabricBitstreamWordId& word_id) const {
VTR_ASSERT(valid_word_id(word_id));
return bitstream_word_wls_[word_id];
}
MemoryBankShiftRegisterFabricBitstreamWordId MemoryBankShiftRegisterFabricBitstream::create_word() {
/* Create a new id*/
MemoryBankShiftRegisterFabricBitstreamWordId word_id = MemoryBankShiftRegisterFabricBitstreamWordId(bitstream_word_ids_.size());
/* Update the id list */
bitstream_word_ids_.push_back(word_id);
/* Initialize other attributes */
bitstream_word_bls_.emplace_back();
bitstream_word_wls_.emplace_back();
return word_id;
}
void MemoryBankShiftRegisterFabricBitstream::add_bl_vectors(const MemoryBankShiftRegisterFabricBitstreamWordId& word_id,
const std::string& bl_vec) {
VTR_ASSERT(valid_word_id(word_id));
return bitstream_word_bls_[word_id].push_back(bl_vec);
}
void MemoryBankShiftRegisterFabricBitstream::add_wl_vectors(const MemoryBankShiftRegisterFabricBitstreamWordId& word_id,
const std::string& wl_vec) {
VTR_ASSERT(valid_word_id(word_id));
return bitstream_word_wls_[word_id].push_back(wl_vec);
}
bool MemoryBankShiftRegisterFabricBitstream::valid_word_id(const MemoryBankShiftRegisterFabricBitstreamWordId& word_id) const {
return ( size_t(word_id) < bitstream_word_ids_.size() ) && ( word_id == bitstream_word_ids_[word_id] );
}
} /* end namespace openfpga */

View File

@ -0,0 +1,96 @@
#ifndef MEMORY_BANK_SHIFT_REGISTER_FABRIC_BITSTREAM_H
#define MEMORY_BANK_SHIFT_REGISTER_FABRIC_BITSTREAM_H
#include <string>
#include <map>
#include <vector>
#include "vtr_vector.h"
#include "memory_bank_shift_register_fabric_bitstream_fwd.h"
/* begin namespace openfpga */
namespace openfpga {
/******************************************************************************
* This files includes data structures that stores a downloadable format of fabric bitstream
* which is compatible with memory bank configuration protocol using shift register to control BL/WLs
* @note This data structure is mainly used to output bitstream file for compatible protocols
******************************************************************************/
class MemoryBankShiftRegisterFabricBitstream {
public: /* Types */
typedef vtr::vector<MemoryBankShiftRegisterFabricBitstreamWordId, MemoryBankShiftRegisterFabricBitstreamWordId>::const_iterator word_iterator;
/* Create range */
typedef vtr::Range<word_iterator> word_range;
public: /* Accessors: aggregates */
word_range words() const;
public: /* Accessors */
/* @brief Return the length of bitstream */
size_t num_words() const;
/* @brief Return the length of BL part of each word. All the word should have a uniform size */
size_t bl_word_size() const;
/* @brief Return the length of WL part of each word. All the word should have a uniform size */
size_t wl_word_size() const;
/* @brief Return the width of each BL word, which is the number of heads through which a BL word can be loaded in parallel */
size_t bl_width() const;
/* @brief Return the width of each WL word, which is the number of heads through which a WL word can be loaded in parallel */
size_t wl_width() const;
/* @brief Return the BL vectors with a given word id*/
std::vector<std::string> bl_vectors(const MemoryBankShiftRegisterFabricBitstreamWordId& word_id) const;
/* @brief Return the WL vectors in a given word id */
std::vector<std::string> wl_vectors(const MemoryBankShiftRegisterFabricBitstreamWordId& word_id) const;
public: /* Mutators */
/* @brief Create a new word */
MemoryBankShiftRegisterFabricBitstreamWordId create_word();
/* @brief Add BLs to a given word */
void add_bl_vectors(const MemoryBankShiftRegisterFabricBitstreamWordId& word_id,
const std::string& bl_vec);
/* @brief Add WLs to a given word */
void add_wl_vectors(const MemoryBankShiftRegisterFabricBitstreamWordId& word_id,
const std::string& wl_vec);
public: /* Validators */
bool valid_word_id(const MemoryBankShiftRegisterFabricBitstreamWordId& word_id) const;
private: /* Internal data */
/* Organization of the bitstream
*
* ============= Begin of Word 1 ==============
* |<--No of -->|<-- No of -->|
* | BL heads | WL heads |
* 010101 .. 101 101110 .. 001 ----
* ... ... ^
* |
* max. shift register length per word
* |
* v
* 110001 .. 111 100100 .. 110 ----
* ============= End of Word 1 ==============
* ============= Begin of Word 2 ==============
* 010101 .. 101 101110 .. 001 ----
* ... ... ^
* |
* max. shift register length per word
* |
* v
* 110001 .. 111 100100 .. 110 ----
* ============= End of Word 2 ==============
* .... more words
*/
vtr::vector<MemoryBankShiftRegisterFabricBitstreamWordId, MemoryBankShiftRegisterFabricBitstreamWordId> bitstream_word_ids_;
vtr::vector<MemoryBankShiftRegisterFabricBitstreamWordId, std::vector<std::string>> bitstream_word_bls_;
vtr::vector<MemoryBankShiftRegisterFabricBitstreamWordId, std::vector<std::string>> bitstream_word_wls_;
};
} /* end namespace openfpga */
#endif

View File

@ -0,0 +1,23 @@
/**************************************************
* This file includes only declarations for
* the data structures for MemoryBankShiftRegisterFabricBitstream
* Please refer to memory_bank_shift_register_fabric_bitstream.h for more details
*************************************************/
#ifndef MEMORY_BANK_SHIFT_REGISTER_FABRIC_BITSTREAM_FWD_H
#define MEMORY_BANK_SHIFT_REGISTER_FABRIC_BITSTREAM_FWD_H
#include "vtr_strong_id.h"
/* begin namespace openfpga */
namespace openfpga {
/* Strong Ids for ModuleManager */
struct memory_bank_shift_register_fabric_bitstream_word_id_tag;
typedef vtr::StrongId<memory_bank_shift_register_fabric_bitstream_word_id_tag> MemoryBankShiftRegisterFabricBitstreamWordId;
class MemoryBankShiftRegisterFabricBitstream;
} /* end namespace openfpga */
#endif

View File

@ -190,23 +190,18 @@ int write_memory_bank_fabric_bitstream_to_text_file(std::fstream& fp,
*******************************************************************/
static
int write_memory_bank_flatten_fabric_bitstream_to_text_file(std::fstream& fp,
const bool& fast_configuration,
const bool& bit_value_to_skip,
const FabricBitstream& fabric_bitstream) {
int status = 0;
MemoryBankFlattenFabricBitstream fabric_bits = build_memory_bank_flatten_fabric_bitstream(fabric_bitstream, bit_value_to_skip);
MemoryBankFlattenFabricBitstream fabric_bits = build_memory_bank_flatten_fabric_bitstream(fabric_bitstream, fast_configuration, bit_value_to_skip);
/* The address sizes and data input sizes are the same across any element,
* just get it from the 1st element to save runtime
*/
size_t bl_addr_size = 0;
for (const auto& bl_vec : fabric_bits.begin()->second) {
bl_addr_size += bl_vec.size();
}
size_t wl_addr_size = 0;
for (const auto& wl_vec : fabric_bits.begin()->first) {
wl_addr_size += wl_vec.size();
}
size_t bl_addr_size = fabric_bits.bl_vector_size();
size_t wl_addr_size = fabric_bits.wl_vector_size();
/* Output information about how to intepret the bitstream */
fp << "// Bitstream length: " << fabric_bits.size() << std::endl;
@ -215,14 +210,14 @@ int write_memory_bank_flatten_fabric_bitstream_to_text_file(std::fstream& fp,
fp << "<wl_address " << wl_addr_size << " bits>";
fp << std::endl;
for (const auto& addr_pair : fabric_bits) {
for (const auto& wl_vec : fabric_bits.wl_vectors()) {
/* Write BL address code */
for (const auto& bl_vec : addr_pair.second) {
fp << bl_vec;
for (const auto& bl_unit : fabric_bits.bl_vector(wl_vec)) {
fp << bl_unit;
}
/* Write WL address code */
for (const auto& wl_vec : addr_pair.first) {
fp << wl_vec;
for (const auto& wl_unit : wl_vec) {
fp << wl_unit;
}
fp << std::endl;
}
@ -230,6 +225,57 @@ int write_memory_bank_flatten_fabric_bitstream_to_text_file(std::fstream& fp,
return status;
}
/********************************************************************
* Write the fabric bitstream fitting a memory bank protocol
* to a plain text file
*
* Return:
* - 0 if succeed
* - 1 if critical errors occured
*******************************************************************/
static
int write_memory_bank_shift_register_fabric_bitstream_to_text_file(std::fstream& fp,
const bool& fast_configuration,
const bool& bit_value_to_skip,
const FabricBitstream& fabric_bitstream) {
int status = 0;
MemoryBankShiftRegisterFabricBitstream fabric_bits = build_memory_bank_shift_register_fabric_bitstream(fabric_bitstream, fast_configuration, bit_value_to_skip);
/* Output information about how to intepret the bitstream */
fp << "// Bitstream word count: " << fabric_bits.num_words() << std::endl;
fp << "// Bitstream bl word size: " << fabric_bits.bl_word_size() << std::endl;
fp << "// Bitstream wl word size: " << fabric_bits.wl_word_size() << std::endl;
fp << "// Bitstream width (LSB -> MSB): ";
fp << "<bl shift register heads " << fabric_bits.bl_width() << " bits>";
fp << "<wl shift register heads " << fabric_bits.wl_width() << " bits>";
fp << std::endl;
size_t word_cnt = 0;
for (const auto& word : fabric_bits.words()) {
fp << "// Word " << word_cnt << std::endl;
/* Write BL address code */
fp << "// BL part " << std::endl;
for (const auto& bl_vec : fabric_bits.bl_vectors(word)) {
fp << bl_vec;
fp << std::endl;
}
/* Write WL address code */
fp << "// WL part " << std::endl;
for (const auto& wl_vec : fabric_bits.wl_vectors(word)) {
fp << wl_vec;
fp << std::endl;
}
word_cnt++;
}
return status;
}
/********************************************************************
* Write the fabric bitstream fitting a frame-based protocol
* to a plain text file
@ -369,10 +415,15 @@ int write_fabric_bitstream_to_text_file(const BitstreamManager& bitstream_manage
apply_fast_configuration,
bit_value_to_skip,
fabric_bitstream);
} else {
VTR_ASSERT(BLWL_PROTOCOL_FLATTEN == config_protocol.bl_protocol_type()
|| BLWL_PROTOCOL_SHIFT_REGISTER == config_protocol.bl_protocol_type());
} else if (BLWL_PROTOCOL_FLATTEN == config_protocol.bl_protocol_type()) {
status = write_memory_bank_flatten_fabric_bitstream_to_text_file(fp,
apply_fast_configuration,
bit_value_to_skip,
fabric_bitstream);
} else {
VTR_ASSERT(BLWL_PROTOCOL_SHIFT_REGISTER == config_protocol.bl_protocol_type());
status = write_memory_bank_shift_register_fabric_bitstream_to_text_file(fp,
apply_fast_configuration,
bit_value_to_skip,
fabric_bitstream);
}

View File

@ -97,7 +97,7 @@ void print_pnr_sdc_global_clock_ports(std::fstream& fp,
/* Should try to find a port defintion from simulation parameters
* If found, it means that we need to use special clock name!
*/
for (const SimulationClockId& sim_clock : sim_setting.clocks()) {
for (const SimulationClockId& sim_clock : sim_setting.operating_clocks()) {
if (port_to_constrain == sim_setting.clock_port(sim_clock)) {
clock_period = 1./sim_setting.clock_frequency(sim_clock);
}

View File

@ -55,6 +55,7 @@ namespace openfpga {
********************************************************************/
void fpga_fabric_verilog(ModuleManager &module_manager,
NetlistManager &netlist_manager,
const std::array<MemoryBankShiftRegisterBanks, 2>& blwl_sr_banks,
const CircuitLibrary &circuit_lib,
const MuxLibrary &mux_lib,
const DecoderLibrary &decoder_lib,
@ -94,6 +95,7 @@ void fpga_fabric_verilog(ModuleManager &module_manager,
* Without the modules in the module manager, core logic generation is not possible!!!
*/
print_verilog_submodule(module_manager, netlist_manager,
blwl_sr_banks,
mux_lib, decoder_lib, circuit_lib,
submodule_dir_path,
options);

View File

@ -23,6 +23,7 @@
#include "io_location_map.h"
#include "fabric_global_port_info.h"
#include "vpr_netlist_annotation.h"
#include "memory_bank_shift_register_banks.h"
#include "fabric_verilog_options.h"
#include "verilog_testbench_options.h"
@ -35,6 +36,7 @@ namespace openfpga {
void fpga_fabric_verilog(ModuleManager& module_manager,
NetlistManager& netlist_manager,
const std::array<MemoryBankShiftRegisterBanks, 2>& blwl_sr_banks,
const CircuitLibrary& circuit_lib,
const MuxLibrary& mux_lib,
const DecoderLibrary& decoder_lib,

View File

@ -26,6 +26,7 @@ constexpr char* MUXES_VERILOG_FILE_NAME = "muxes.v";
constexpr char* LOCAL_ENCODER_VERILOG_FILE_NAME = "local_encoder.v";
constexpr char* ARCH_ENCODER_VERILOG_FILE_NAME = "arch_encoder.v";
constexpr char* MEMORIES_VERILOG_FILE_NAME = "memories.v";
constexpr char* SHIFT_REGISTER_BANKS_VERILOG_FILE_NAME = "shift_register_banks.v";
constexpr char* WIRES_VERILOG_FILE_NAME = "wires.v";
constexpr char* ESSENTIALS_VERILOG_FILE_NAME = "inv_buf_passgate.v";
constexpr char* CONFIG_PERIPHERAL_VERILOG_FILE_NAME = "config_peripherals.v";

View File

@ -0,0 +1,81 @@
/*********************************************************************
* This file includes functions to generate Verilog submodules for
* the memories that are affiliated to multiplexers and other programmable
* circuit models, such as IOPADs, LUTs, etc.
********************************************************************/
#include <string>
#include <algorithm>
/* Headers from vtrutil library */
#include "vtr_assert.h"
#include "vtr_log.h"
/* Headers from openfpgautil library */
#include "openfpga_digest.h"
#include "mux_graph.h"
#include "module_manager.h"
#include "circuit_library_utils.h"
#include "mux_utils.h"
#include "openfpga_naming.h"
#include "verilog_constants.h"
#include "verilog_writer_utils.h"
#include "verilog_module_writer.h"
#include "verilog_shift_register_banks.h"
/* begin namespace openfpga */
namespace openfpga {
/*********************************************************************
* Generate Verilog modules for
* the shift register banks that are used to control BL/WLs
********************************************************************/
void print_verilog_submodule_shift_register_banks(const ModuleManager& module_manager,
NetlistManager& netlist_manager,
const std::array<MemoryBankShiftRegisterBanks, 2>& blwl_sr_banks,
const std::string& submodule_dir,
const FabricVerilogOption& options) {
/* Plug in with the mux subckt */
std::string verilog_fname(submodule_dir + std::string(SHIFT_REGISTER_BANKS_VERILOG_FILE_NAME));
/* Create the file stream */
std::fstream fp;
fp.open(verilog_fname, std::fstream::out | std::fstream::trunc);
check_file_stream(verilog_fname.c_str(), fp);
/* Print out debugging information for if the file is not opened/created properly */
VTR_LOG("Writing Verilog netlist for shift register banks '%s' ...",
verilog_fname.c_str());
print_verilog_file_header(fp, "Shift register banks used in FPGA");
/* Create the memory circuits for the multiplexer */
for (const auto& sr_bank : blwl_sr_banks) {
for (const ModuleId& sr_module : sr_bank.shift_register_bank_unique_modules()) {
VTR_ASSERT(true == module_manager.valid_module_id(sr_module));
/* Write the module content in Verilog format */
write_verilog_module_to_file(fp, module_manager, sr_module,
options.explicit_port_mapping(),
options.default_net_type());
/* Add an empty line as a splitter */
fp << std::endl;
}
}
/* Close the file stream */
fp.close();
/* Add fname to the netlist name list */
NetlistId nlist_id = netlist_manager.add_netlist(verilog_fname);
VTR_ASSERT(NetlistId::INVALID() != nlist_id);
netlist_manager.set_netlist_type(nlist_id, NetlistManager::SUBMODULE_NETLIST);
VTR_LOG("Done\n");
}
} /* end namespace openfpga */

View File

@ -0,0 +1,29 @@
#ifndef VERILOG_SHIFT_REGISTER_BANKS_H
#define VERILOG_SHIFT_REGISTER_BANKS_H
/********************************************************************
* Include header files that are required by function declaration
*******************************************************************/
#include <fstream>
#include "memory_bank_shift_register_banks.h"
#include "module_manager.h"
#include "netlist_manager.h"
#include "fabric_verilog_options.h"
/********************************************************************
* Function declaration
*******************************************************************/
/* begin namespace openfpga */
namespace openfpga {
void print_verilog_submodule_shift_register_banks(const ModuleManager& module_manager,
NetlistManager& netlist_manager,
const std::array<MemoryBankShiftRegisterBanks, 2>& blwl_sr_banks,
const std::string& submodule_dir,
const FabricVerilogOption& options);
} /* end namespace openfpga */
#endif

View File

@ -14,6 +14,7 @@
#include "verilog_lut.h"
#include "verilog_wire.h"
#include "verilog_memory.h"
#include "verilog_shift_register_banks.h"
#include "verilog_writer_utils.h"
#include "verilog_constants.h"
@ -33,6 +34,7 @@ namespace openfpga {
********************************************************************/
void print_verilog_submodule(ModuleManager& module_manager,
NetlistManager& netlist_manager,
const std::array<MemoryBankShiftRegisterBanks, 2>& blwl_sr_banks,
const MuxLibrary& mux_lib,
const DecoderLibrary& decoder_lib,
const CircuitLibrary& circuit_lib,
@ -84,14 +86,22 @@ void print_verilog_submodule(ModuleManager& module_manager,
submodule_dir,
fpga_verilog_opts.default_net_type());
/* 4. Memories */
/* Memories */
print_verilog_submodule_memories(const_cast<const ModuleManager&>(module_manager),
netlist_manager,
mux_lib, circuit_lib,
submodule_dir,
fpga_verilog_opts);
/* 5. Dump template for all the modules */
/* Shift register banks */
print_verilog_submodule_shift_register_banks(const_cast<const ModuleManager&>(module_manager),
netlist_manager,
blwl_sr_banks,
submodule_dir,
fpga_verilog_opts);
/* Dump template for all the modules */
if (true == fpga_verilog_opts.print_user_defined_template()) {
print_verilog_submodule_templates(const_cast<const ModuleManager&>(module_manager),
circuit_lib,

View File

@ -8,6 +8,7 @@
#include "netlist_manager.h"
#include "mux_library.h"
#include "decoder_library.h"
#include "memory_bank_shift_register_banks.h"
#include "fabric_verilog_options.h"
/********************************************************************
@ -19,6 +20,7 @@ namespace openfpga {
void print_verilog_submodule(ModuleManager& module_manager,
NetlistManager& netlist_manager,
const std::array<MemoryBankShiftRegisterBanks, 2>& blwl_sr_banks,
const MuxLibrary& mux_lib,
const DecoderLibrary& decoder_lib,
const CircuitLibrary& circuit_lib,

View File

@ -515,7 +515,7 @@ void print_verilog_testbench_clock_stimuli(std::fstream& fp,
/* Skip all the unrelated pin constraints */
VTR_ASSERT(clock_port.get_name() == pin_constraints.net(pin_constraint));
/* Try to find which clock source is considered in simulation settings for this pin */
for (const SimulationClockId& sim_clock_id : simulation_parameters.clocks()) {
for (const SimulationClockId& sim_clock_id : simulation_parameters.operating_clocks()) {
if (pin_constraints.pin(pin_constraint) == simulation_parameters.clock_port(sim_clock_id)) {
clk_freq_to_use = (0.5 / simulation_parameters.clock_frequency(sim_clock_id)) / VERILOG_SIM_TIMESCALE;
}

View File

@ -272,6 +272,11 @@ void print_verilog_top_testbench_global_clock_ports_stimuli(std::fstream& fp,
ModulePortId module_global_port = fabric_global_port_info.global_module_port(fabric_global_port);
VTR_ASSERT(true == module_manager.valid_module_port_id(top_module, module_global_port));
/* Skip shift register clocks, they are handled in another way */
if (true == fabric_global_port_info.global_port_is_shift_register(fabric_global_port)) {
continue;
}
BasicPort stimuli_clock_port;
if (true == fabric_global_port_info.global_port_is_prog(fabric_global_port)) {
stimuli_clock_port.set_name(std::string(TOP_TB_PROG_CLOCK_PORT_NAME));
@ -290,7 +295,7 @@ void print_verilog_top_testbench_global_clock_ports_stimuli(std::fstream& fp,
/* Should try to find a port defintion from simulation parameters
* If found, it means that we need to use special clock name!
*/
for (const SimulationClockId& sim_clock : simulation_parameters.clocks()) {
for (const SimulationClockId& sim_clock : simulation_parameters.operating_clocks()) {
if (global_port_to_connect == simulation_parameters.clock_port(sim_clock)) {
stimuli_clock_port.set_name(generate_top_testbench_clock_name(std::string(TOP_TB_OP_CLOCK_PORT_PREFIX), simulation_parameters.clock_name(sim_clock)));
}
@ -563,6 +568,11 @@ void print_verilog_top_testbench_global_ports_stimuli(std::fstream& fp,
fabric_global_port_info,
simulation_parameters);
print_verilog_top_testbench_global_shift_register_clock_ports_stimuli(fp,
module_manager,
top_module,
fabric_global_port_info);
print_verilog_top_testbench_global_config_done_ports_stimuli(fp,
module_manager,
top_module,
@ -640,7 +650,7 @@ void print_verilog_top_testbench_benchmark_clock_ports(std::fstream& fp,
/* Skip all the unrelated pin constraints */
VTR_ASSERT(clock_port_name == pin_constraints.net(pin_constraint));
/* Try to find which clock source is considered in simulation settings for this pin */
for (const SimulationClockId& sim_clock_id : simulation_parameters.clocks()) {
for (const SimulationClockId& sim_clock_id : simulation_parameters.operating_clocks()) {
if (pin_constraints.pin(pin_constraint) == simulation_parameters.clock_port(sim_clock_id)) {
std::string sim_clock_port_name = generate_top_testbench_clock_name(std::string(TOP_TB_OP_CLOCK_PORT_PREFIX), simulation_parameters.clock_name(sim_clock_id));
clock_source_to_connect = BasicPort(sim_clock_port_name, 1);
@ -742,7 +752,7 @@ void print_verilog_top_testbench_ports(std::fstream& fp,
fp << generate_verilog_port(VERILOG_PORT_REG, prog_clock_register_port) << ";" << std::endl;
/* Multiple operating clocks based on the simulation settings */
for (const SimulationClockId& sim_clock : simulation_parameters.clocks()) {
for (const SimulationClockId& sim_clock : simulation_parameters.operating_clocks()) {
std::string sim_clock_port_name = generate_top_testbench_clock_name(std::string(TOP_TB_OP_CLOCK_PORT_PREFIX), simulation_parameters.clock_name(sim_clock));
BasicPort sim_clock_port(sim_clock_port_name, 1);
fp << generate_verilog_port(VERILOG_PORT_WIRE, sim_clock_port) << ";" << std::endl;
@ -862,9 +872,9 @@ size_t calculate_num_config_clock_cycles(const ConfigProtocol& config_protocol,
100. * ((float)num_config_clock_cycles / (float)full_num_config_clock_cycles - 1.));
}
} else if (BLWL_PROTOCOL_FLATTEN == config_protocol.bl_protocol_type()) {
num_config_clock_cycles = 1 + build_memory_bank_flatten_fabric_bitstream(fabric_bitstream, bit_value_to_skip).size();
num_config_clock_cycles = 1 + build_memory_bank_flatten_fabric_bitstream(fabric_bitstream, fast_configuration, bit_value_to_skip).size();
} else if (BLWL_PROTOCOL_SHIFT_REGISTER == config_protocol.bl_protocol_type()) {
/* TODO */
num_config_clock_cycles = 1 + build_memory_bank_flatten_fabric_bitstream(fabric_bitstream, fast_configuration, bit_value_to_skip).size();
}
break;
}
@ -1010,7 +1020,7 @@ void print_verilog_top_testbench_generic_stimulus(std::fstream& fp,
fp << std::endl;
/* Generate stimuli waveform for multiple user-defined operating clock signals */
for (const SimulationClockId& sim_clock : simulation_parameters.clocks()) {
for (const SimulationClockId& sim_clock : simulation_parameters.operating_clocks()) {
print_verilog_comment(fp, "----- Begin raw operating clock signal '" + simulation_parameters.clock_name(sim_clock) + "' generation -----");
std::string sim_clock_port_name = generate_top_testbench_clock_name(std::string(TOP_TB_OP_CLOCK_PORT_PREFIX), simulation_parameters.clock_name(sim_clock));
BasicPort sim_clock_port(sim_clock_port_name, 1);
@ -1112,21 +1122,30 @@ void print_verilog_top_testbench_generic_stimulus(std::fstream& fp,
*******************************************************************/
static
void print_verilog_top_testbench_configuration_protocol_stimulus(std::fstream& fp,
const e_config_protocol_type& config_protocol_type,
const ConfigProtocol& config_protocol,
const ModuleManager& module_manager,
const ModuleId& top_module,
const bool& fast_configuration,
const bool& bit_value_to_skip,
const FabricBitstream& fabric_bitstream,
const float& prog_clock_period,
const float& timescale) {
/* Validate the file stream */
valid_file_stream(fp);
/* Branch on the type of configuration protocol */
switch (config_protocol_type) {
switch (config_protocol.type()) {
case CONFIG_MEM_STANDALONE:
break;
case CONFIG_MEM_SCAN_CHAIN:
break;
case CONFIG_MEM_QL_MEMORY_BANK:
print_verilog_top_testbench_configuration_protocol_ql_memory_bank_stimulus(fp,
config_protocol,
module_manager,top_module,
fast_configuration, bit_value_to_skip, fabric_bitstream,
prog_clock_period, timescale);
break;
case CONFIG_MEM_MEMORY_BANK:
case CONFIG_MEM_FRAME_BASED: {
ModulePortId en_port_id = module_manager.find_module_port(top_module,
@ -1935,7 +1954,7 @@ int print_verilog_full_testbench(const ModuleManager& module_manager,
float prog_clock_period = (1./simulation_parameters.programming_clock_frequency());
float default_op_clock_period = (1./simulation_parameters.default_operating_clock_frequency());
float max_op_clock_period = 0.;
for (const SimulationClockId& clock_id : simulation_parameters.clocks()) {
for (const SimulationClockId& clock_id : simulation_parameters.operating_clocks()) {
max_op_clock_period = std::max(max_op_clock_period, (float)(1./simulation_parameters.clock_frequency(clock_id)));
}
@ -1956,8 +1975,9 @@ int print_verilog_full_testbench(const ModuleManager& module_manager,
/* Generate stimuli for programming interface */
print_verilog_top_testbench_configuration_protocol_stimulus(fp,
config_protocol.type(),
config_protocol,
module_manager, top_module,
fast_configuration, bit_value_to_skip, fabric_bitstream,
prog_clock_period,
VERILOG_SIM_TIMESCALE);

View File

@ -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,
@ -61,7 +72,28 @@ void print_verilog_top_testbench_ql_memory_bank_port(std::fstream& fp,
}
} else {
VTR_ASSERT(BLWL_PROTOCOL_SHIFT_REGISTER == config_protocol.bl_protocol_type());
/* TODO */
print_verilog_comment(fp, std::string("---- Bit-Line ports -----"));
for (const ConfigRegionId& region : module_manager.regions(top_module)) {
ModulePortId sr_head_port_id = module_manager.find_module_port(top_module,
generate_regional_blwl_port_name(std::string(BL_SHIFT_REGISTER_CHAIN_HEAD_NAME), region));
BasicPort sr_head_port = module_manager.module_port(top_module, sr_head_port_id);
fp << generate_verilog_port(VERILOG_PORT_REG, sr_head_port) << ";" << std::endl;
ModulePortId sr_tail_port_id = module_manager.find_module_port(top_module,
generate_regional_blwl_port_name(std::string(BL_SHIFT_REGISTER_CHAIN_TAIL_NAME), region));
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_REG, sr_clock_port) << ";" << 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;
/* Register to count bl/wl shift register clocks */
fp << "integer " << TOP_TB_BL_SHIFT_REGISTER_COUNT_PORT_NAME << ";" << std::endl;
}
/* Print the address port for the Word-Line decoder here */
@ -82,7 +114,28 @@ void print_verilog_top_testbench_ql_memory_bank_port(std::fstream& fp,
}
} else {
VTR_ASSERT(BLWL_PROTOCOL_SHIFT_REGISTER == config_protocol.wl_protocol_type());
/* TODO */
print_verilog_comment(fp, std::string("---- Word-Line ports -----"));
for (const ConfigRegionId& region : module_manager.regions(top_module)) {
ModulePortId sr_head_port_id = module_manager.find_module_port(top_module,
generate_regional_blwl_port_name(std::string(WL_SHIFT_REGISTER_CHAIN_HEAD_NAME), region));
BasicPort sr_head_port = module_manager.module_port(top_module, sr_head_port_id);
fp << generate_verilog_port(VERILOG_PORT_REG, sr_head_port) << ";" << std::endl;
ModulePortId sr_tail_port_id = module_manager.find_module_port(top_module,
generate_regional_blwl_port_name(std::string(WL_SHIFT_REGISTER_CHAIN_TAIL_NAME), region));
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_REG, sr_clock_port) << ";" << std::endl;
/* Register to enable/disable bl/wl shift register clocks */
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_WL_SHIFT_REGISTER_COUNT_PORT_NAME << ";" << std::endl;
}
/* Print the data-input port: only available when BL has a decoder */
@ -146,6 +199,139 @@ void print_verilog_top_testbench_ql_memory_bank_port(std::fstream& fp,
fp << ";" << std::endl;
}
void print_verilog_top_testbench_global_shift_register_clock_ports_stimuli(std::fstream& fp,
const ModuleManager& module_manager,
const ModuleId& top_module,
const FabricGlobalPortInfo& fabric_global_port_info) {
/* Validate the file stream */
valid_file_stream(fp);
/* Connect global clock ports to shift-register programming clock signal */
for (const FabricGlobalPortId& fabric_global_port : fabric_global_port_info.global_ports()) {
/* Only care shift register clocks */
if (false == fabric_global_port_info.global_port_is_clock(fabric_global_port)
|| false == fabric_global_port_info.global_port_is_shift_register(fabric_global_port)
|| false == fabric_global_port_info.global_port_is_prog(fabric_global_port)) {
continue;
}
/* Find the module port */
ModulePortId module_global_port = fabric_global_port_info.global_module_port(fabric_global_port);
VTR_ASSERT(true == module_manager.valid_module_port_id(top_module, module_global_port));
BasicPort stimuli_clock_port;
if (true == fabric_global_port_info.global_port_is_bl(fabric_global_port)) {
stimuli_clock_port.set_name(std::string(TOP_TB_BL_SHIFT_REGISTER_CLOCK_PORT_NAME));
stimuli_clock_port.set_width(1);
} else {
VTR_ASSERT(true == fabric_global_port_info.global_port_is_wl(fabric_global_port));
stimuli_clock_port.set_name(std::string(TOP_TB_WL_SHIFT_REGISTER_CLOCK_PORT_NAME));
stimuli_clock_port.set_width(1);
}
for (const size_t& pin : module_manager.module_port(top_module, module_global_port).pins()) {
BasicPort global_port_to_connect(module_manager.module_port(top_module, module_global_port).get_name(), pin, pin);
print_verilog_wire_connection(fp, global_port_to_connect,
stimuli_clock_port,
1 == fabric_global_port_info.global_port_default_value(fabric_global_port));
}
}
}
/**
* @brief Generate the Verilog codes for a shift register clocks that controls BL/WL protocols
*/
static
void print_verilog_full_testbench_ql_memory_bank_shift_register_clock_generator(std::fstream& fp,
const BasicPort& start_sr_port,
const BasicPort& sr_clock_port,
const float& sr_clock_period) {
/* Validate the file stream */
valid_file_stream(fp);
fp << "always";
fp << " @(posedge " << generate_verilog_port(VERILOG_PORT_CONKT, start_sr_port) << ")";
fp << " begin";
fp << std::endl;
fp << "\t";
fp << generate_verilog_port_constant_values(sr_clock_port, std::vector<size_t>(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 << "#" << sr_clock_period << " ";
print_verilog_register_connection(fp, sr_clock_port, sr_clock_port, true);
fp << "\t";
fp << "end";
fp << std::endl;
fp << "\t";
fp << generate_verilog_port_constant_values(sr_clock_port, std::vector<size_t>(sr_clock_port.get_width(), 0), true);
fp << ";" << std::endl;
fp << "end";
fp << std::endl;
}
void print_verilog_top_testbench_configuration_protocol_ql_memory_bank_stimulus(std::fstream& fp,
const ConfigProtocol& config_protocol,
const ModuleManager& module_manager,
const ModuleId& top_module,
const bool& fast_configuration,
const bool& bit_value_to_skip,
const FabricBitstream& fabric_bitstream,
const float& prog_clock_period,
const float& timescale) {
ModulePortId en_port_id = module_manager.find_module_port(top_module,
std::string(DECODER_ENABLE_PORT_NAME));
BasicPort en_port(std::string(DECODER_ENABLE_PORT_NAME), 1);
if (en_port_id) {
en_port = module_manager.module_port(top_module, en_port_id);
}
BasicPort en_register_port(std::string(en_port.get_name() + std::string(TOP_TB_CLOCK_REG_POSTFIX)), 1);
print_verilog_comment(fp, std::string("---- Generate enable signal waveform -----"));
print_verilog_shifted_clock_stimuli(fp, en_register_port,
0.25 * prog_clock_period / timescale,
0.5 * prog_clock_period / timescale, 0);
/* Stimulus only for shift-register-based BL/WL protocols */
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);
BasicPort start_bl_sr_port(TOP_TB_START_BL_SHIFT_REGISTER_PORT_NAME, 1);
BasicPort start_wl_sr_port(TOP_TB_START_WL_SHIFT_REGISTER_PORT_NAME, 1);
/* Reorganize the fabric bitstream by the same address across regions */
if (CONFIG_MEM_QL_MEMORY_BANK == config_protocol.type()) {
MemoryBankShiftRegisterFabricBitstream fabric_bits_by_addr = build_memory_bank_shift_register_fabric_bitstream(fabric_bitstream,
fast_configuration,
bit_value_to_skip);
/* TODO: Consider auto-tuned clock period for now:
* - the BL/WL shift register clock only works in the second half of the programming clock period
* - add 2 idle clocks to avoid racing between programming clock and shift register clocks at edge
*/
float bl_sr_clock_period = 0.25 * prog_clock_period / (fabric_bits_by_addr.bl_word_size() + 2) / timescale;
float wl_sr_clock_period = 0.25 * prog_clock_period / (fabric_bits_by_addr.wl_word_size() + 2) / timescale;
if (BLWL_PROTOCOL_SHIFT_REGISTER == config_protocol.bl_protocol_type()) {
print_verilog_comment(fp, "----- BL Shift register clock generator -----");
print_verilog_full_testbench_ql_memory_bank_shift_register_clock_generator(fp, start_bl_sr_port, bl_sr_clock_port, bl_sr_clock_period);
}
if (BLWL_PROTOCOL_SHIFT_REGISTER == config_protocol.wl_protocol_type()) {
print_verilog_comment(fp, "----- WL Shift register clock generator -----");
print_verilog_full_testbench_ql_memory_bank_shift_register_clock_generator(fp, start_wl_sr_port, wl_sr_clock_port, wl_sr_clock_period);
}
}
}
/* 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_flatten_bitstream(std::fstream& fp,
@ -158,13 +344,9 @@ 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,
bit_value_to_skip);
/* Feed address and data input pair one by one
@ -285,6 +467,268 @@ 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 */
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<BasicPort> 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<BasicPort> 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<size_t> initial_bl_head_values(bl_head_port_width, 0);
std::vector<size_t> 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;
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;
BasicPort start_bl_sr_port(TOP_TB_START_BL_SHIFT_REGISTER_PORT_NAME, 1);
BasicPort start_wl_sr_port(TOP_TB_START_WL_SHIFT_REGISTER_PORT_NAME, 1);
fp << "\t";
fp << generate_verilog_port_constant_values(start_bl_sr_port, std::vector<size_t>(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<size_t>(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, "----- 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<size_t> 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<size_t>(start_bl_sr_port.get_width(), 1), true);
fp << ";";
fp << std::endl;
fp << "\t\t";
fp << generate_verilog_port_constant_values(start_wl_sr_port, std::vector<size_t>(start_wl_sr_port.get_width(), 1), 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";
fp << "if (";
fp << TOP_TB_BL_SHIFT_REGISTER_COUNT_PORT_NAME;
fp << " >= ";
fp << "`" << TOP_TB_BITSTREAM_BL_WORD_SIZE_VARIABLE << " - 1";
fp << ") begin";
fp << std::endl;
fp << "\t\t";
fp << generate_verilog_port_constant_values(start_bl_sr_port, std::vector<size_t>(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 << "-1)*(`" << TOP_TB_BITSTREAM_BL_WORD_SIZE_VARIABLE << " + `" << TOP_TB_BITSTREAM_WL_WORD_SIZE_VARIABLE << ") + " << TOP_TB_BL_SHIFT_REGISTER_COUNT_PORT_NAME;
fp << "];" << std::endl;
fp << "\t\t";
fp << TOP_TB_BL_SHIFT_REGISTER_COUNT_PORT_NAME << " = ";
fp << TOP_TB_BL_SHIFT_REGISTER_COUNT_PORT_NAME << " + 1;";
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";
fp << "if (";
fp << TOP_TB_WL_SHIFT_REGISTER_COUNT_PORT_NAME;
fp << " >= ";
fp << "`" << TOP_TB_BITSTREAM_WL_WORD_SIZE_VARIABLE << " - 1";
fp << ") begin";
fp << std::endl;
fp << "\t\t";
fp << generate_verilog_port_constant_values(start_wl_sr_port, std::vector<size_t>(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 << "-1)*(`" << TOP_TB_BITSTREAM_BL_WORD_SIZE_VARIABLE << " + `" << TOP_TB_BITSTREAM_WL_WORD_SIZE_VARIABLE << ") + `" << TOP_TB_BITSTREAM_BL_WORD_SIZE_VARIABLE << " + " << TOP_TB_WL_SHIFT_REGISTER_COUNT_PORT_NAME;
fp << "];" << std::endl;
fp << "\t\t";
fp << TOP_TB_WL_SHIFT_REGISTER_COUNT_PORT_NAME << " = ";
fp << TOP_TB_WL_SHIFT_REGISTER_COUNT_PORT_NAME << " + 1;";
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,
@ -445,6 +889,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);
}
}

View File

@ -34,6 +34,27 @@ 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 that connect shift register clock stimuli to FPGA ports
*/
void print_verilog_top_testbench_global_shift_register_clock_ports_stimuli(std::fstream& fp,
const ModuleManager& module_manager,
const ModuleId& top_module,
const FabricGlobalPortInfo& fabric_global_port_info);
/**
* @brief Generate the Verilog codes that generate stimuli waveforms for BL/WL protocols
*/
void print_verilog_top_testbench_configuration_protocol_ql_memory_bank_stimulus(std::fstream& fp,
const ConfigProtocol& config_protocol,
const ModuleManager& module_manager,
const ModuleId& top_module,
const bool& fast_configuration,
const bool& bit_value_to_skip,
const FabricBitstream& fabric_bitstream,
const float& prog_clock_period,
const float& timescale);
/**
* @brief Print stimulus for a FPGA fabric with a memory bank configuration protocol
* where configuration bits are programming in serial (one by one)

View File

@ -305,7 +305,7 @@ bool check_configurable_memory_circuit_model(const ConfigProtocol& config_protoc
num_err++;
}
if (bl_memory_model) {
num_err += check_ccff_circuit_model_ports(circuit_lib,
num_err += check_bl_ccff_circuit_model_ports(circuit_lib,
bl_memory_model);
}
@ -317,7 +317,7 @@ bool check_configurable_memory_circuit_model(const ConfigProtocol& config_protoc
num_err++;
}
if (wl_memory_model) {
num_err += check_ccff_circuit_model_ports(circuit_lib,
num_err += check_wl_ccff_circuit_model_ports(circuit_lib,
wl_memory_model);
}
break;

View File

@ -233,7 +233,29 @@ MemoryBankFabricBitstream build_memory_bank_fabric_bitstream_by_address(const Fa
}
MemoryBankFlattenFabricBitstream build_memory_bank_flatten_fabric_bitstream(const FabricBitstream& fabric_bitstream,
const bool& fast_configuration,
const bool& bit_value_to_skip) {
/* If fast configuration is not enabled, we need all the wl address even some of them have all-zero BLs */
if (!fast_configuration) {
vtr::vector<FabricBitRegionId, std::map<std::string, std::string>> fabric_bits_per_region;
fabric_bits_per_region.resize(fabric_bitstream.num_regions());
for (const FabricBitRegionId& region : fabric_bitstream.regions()) {
for (const FabricBitId& bit_id : fabric_bitstream.region_bits(region)) {
/* Create string for BL address */
std::string bl_addr_str(fabric_bitstream.bit_bl_address(bit_id).size(), bit_value_to_skip);
/* Create string for WL address */
std::string wl_addr_str;
for (const char& addr_bit : fabric_bitstream.bit_wl_address(bit_id)) {
wl_addr_str.push_back(addr_bit);
}
/* Deposit the config bit */
fabric_bits_per_region[region][wl_addr_str] = bl_addr_str;
}
}
}
/* Build the bitstream by each region, here we use (WL, BL) pairs when storing bitstreams */
vtr::vector<FabricBitRegionId, std::map<std::string, std::string>> fabric_bits_per_region;
fabric_bits_per_region.resize(fabric_bitstream.num_regions());
@ -313,12 +335,98 @@ MemoryBankFlattenFabricBitstream build_memory_bank_flatten_fabric_bitstream(cons
}
}
/* Add the pair to std map */
fabric_bits[cur_wl_vectors] = cur_bl_vectors;
fabric_bits.add_blwl_vectors(cur_bl_vectors, cur_wl_vectors);
}
return fabric_bits;
}
/********************************************************************
* Reshape a list of vectors by aligning all of them to the last element
* For example:
* - Align vectors to the last element
*
* index ---------------------->
* vector 0: 000000001111101010
* vector 1: 00000011010101
* vector 2: 0010101111000110
*
* - Fill void in each vector with desired bits (Here assume fill 'x'
* index ---------------------->
* vector 0: 000000001111101010
* vector 1: xxxx00000011010101
* vector 2: xx0010101111000110
*
* - Rotate the array by 90 degree
* index ----------------------->
* vector 0: 0xx
* vector 1: 0xx
* ...
* vector N: 010
*
*******************************************************************/
static
std::vector<std::string> reshape_bitstream_vectors_to_first_element(const std::vector<std::string>& bitstream_vectors,
const char& default_bit_to_fill) {
/* Find the max sizes of BL bits, this determines the size of shift register chain */
size_t max_vec_size = 0;
for (const auto& vec : bitstream_vectors) {
max_vec_size = std::max(max_vec_size, vec.size());
}
/* Reshape the BL vectors */
std::vector<std::string> reshaped_vectors(bitstream_vectors.size(), std::string());
size_t col_cnt = 0;
for (const auto& vec : bitstream_vectors) {
reshaped_vectors[col_cnt] += vec;
reshaped_vectors[col_cnt] += std::string(max_vec_size - vec.size(), default_bit_to_fill);
col_cnt++;
}
/* Add the BL word to final bitstream */
std::vector<std::string> rotated_vectors;
for (size_t irow = 0; irow < max_vec_size; ++irow) {
std::string cur_vec;
for (size_t icol = 0; icol < reshaped_vectors.size(); ++icol) {
cur_vec.push_back(reshaped_vectors[icol][irow]);
}
rotated_vectors.push_back(cur_vec);
}
return rotated_vectors;
}
MemoryBankShiftRegisterFabricBitstream build_memory_bank_shift_register_fabric_bitstream(const FabricBitstream& fabric_bitstream,
const bool& fast_configuration,
//const std::array<MemoryBankShiftRegisterBanks, 2>& blwl_sr_banks,
const bool& bit_value_to_skip) {
MemoryBankFlattenFabricBitstream raw_fabric_bits = build_memory_bank_flatten_fabric_bitstream(fabric_bitstream, fast_configuration, bit_value_to_skip);
MemoryBankShiftRegisterFabricBitstream fabric_bits;
/* Iterate over each word */
for (const auto& wl_vec : raw_fabric_bits.wl_vectors()) {
std::vector<std::string> bl_vec = raw_fabric_bits.bl_vector(wl_vec);
MemoryBankShiftRegisterFabricBitstreamWordId word_id = fabric_bits.create_word();
std::vector<std::string> reshaped_bl_vectors = reshape_bitstream_vectors_to_first_element(bl_vec, '0');
/* Reverse the vectors due to the shift register chain nature: first-in first-out */
std::reverse(reshaped_bl_vectors.begin(), reshaped_bl_vectors.end());
/* Add the BL word to final bitstream */
for (const auto& reshaped_bl_vec : reshaped_bl_vectors) {
fabric_bits.add_bl_vectors(word_id, reshaped_bl_vec);
}
std::vector<std::string> reshaped_wl_vectors = reshape_bitstream_vectors_to_first_element(wl_vec, '0');
/* Reverse the vectors due to the shift register chain nature: first-in first-out */
std::reverse(reshaped_wl_vectors.begin(), reshaped_wl_vectors.end());
/* Add the BL word to final bitstream */
for (const auto& reshaped_wl_vec : reshaped_wl_vectors) {
fabric_bits.add_wl_vectors(word_id, reshaped_wl_vec);
}
}
return fabric_bits;
}
/********************************************************************
* For fast configuration, the number of bits to be skipped

View File

@ -9,7 +9,11 @@
*******************************************************************/
#include <vector>
#include <map>
#include <array>
#include "bitstream_manager.h"
#include "memory_bank_flatten_fabric_bitstream.h"
#include "memory_bank_shift_register_banks.h"
#include "memory_bank_shift_register_fabric_bitstream.h"
#include "fabric_bitstream.h"
/********************************************************************
@ -37,12 +41,8 @@ FrameFabricBitstream build_frame_based_fabric_bitstream_by_address(const FabricB
size_t find_frame_based_fast_configuration_fabric_bitstream_size(const FabricBitstream& fabric_bitstream,
const bool& bit_value_to_skip);
/* Must use (WL, BL) as pairs in the map!!!
* This is because BL data may not be unique while WL must be unique
*/
typedef std::map<std::vector<std::string>, std::vector<std::string>> MemoryBankFlattenFabricBitstream;
/********************************************************************
* @ brief Reorganize the fabric bitstream for memory banks which use flatten or shift register to manipulate BL and WLs
* @ brief Reorganize the fabric bitstream for memory banks which use flatten BL and WLs
* For each configuration region, we will merge BL address (which are 1-hot codes) under the same WL address
*
* Quick Example
@ -62,6 +62,40 @@ typedef std::map<std::vector<std::string>, std::vector<std::string>> MemoryBankF
* @note the std::map may cause large memory footprint for large bitstream databases!
*******************************************************************/
MemoryBankFlattenFabricBitstream build_memory_bank_flatten_fabric_bitstream(const FabricBitstream& fabric_bitstream,
const bool& fast_configuration,
const bool& bit_value_to_skip);
/********************************************************************
* @ brief Reorganize the fabric bitstream for memory banks which use shift register to manipulate BL and WLs
* For each configuration region, we will merge BL address (which are 1-hot codes) under the same WL address
*
* Quick Example
* <bl of region A>_<bl of region B> <wl of region A>_<wl of region B>
* An example:
* 010_111 000_101
*
* Note that all the BL/WLs across configuration regions are independent. We will combine them together
* Quick Example
* <bl of region A>_<bl of region B> <wl of region A>_<wl of region B>
* 001_010 000_000
* 100_100 000_000
*
* the bitstream will be merged as
* 101_110 000_000
*
* Because that the BL/WL are loaded through shift registers (perhaps using multiple heads), the bitstream will be reorganized as
* Considering single head:
*
* <bl of region A>_<bl of region B> <wl of region A>_<wl of region B>
* 1_1 0_0
* 0_1 0_0
* 1_0 0_0
*
* @note the std::map may cause large memory footprint for large bitstream databases!
*******************************************************************/
MemoryBankShiftRegisterFabricBitstream build_memory_bank_shift_register_fabric_bitstream(const FabricBitstream& fabric_bitstream,
const bool& fast_configuration,
//const std::array<MemoryBankShiftRegisterBanks, 2>& blwl_sr_banks,
const bool& bit_value_to_skip);
/* Alias to a specific organization of bitstreams for memory bank configuration protocol */

View File

@ -84,6 +84,21 @@ size_t find_module_ql_memory_bank_num_blwls(const ModuleManager& module_manager,
return num_blwls;
}
size_t compute_memory_bank_regional_num_bls(const ModuleManager& module_manager,
const ModuleId& top_module,
const ConfigRegionId& config_region,
const CircuitLibrary& circuit_lib,
const CircuitModelId& sram_model) {
size_t num_bls = 0;
std::map<int, size_t> num_bls_per_tile = compute_memory_bank_regional_bitline_numbers_per_tile(module_manager, top_module,
config_region, circuit_lib, sram_model);
for (const auto& pair : num_bls_per_tile) {
num_bls += pair.second;
}
return num_bls;
}
std::map<int, size_t> compute_memory_bank_regional_bitline_numbers_per_tile(const ModuleManager& module_manager,
const ModuleId& top_module,
const ConfigRegionId& config_region,
@ -100,6 +115,22 @@ std::map<int, size_t> compute_memory_bank_regional_bitline_numbers_per_tile(cons
return num_bls_per_tile;
}
size_t compute_memory_bank_regional_num_wls(const ModuleManager& module_manager,
const ModuleId& top_module,
const ConfigRegionId& config_region,
const CircuitLibrary& circuit_lib,
const CircuitModelId& sram_model) {
size_t num_wls = 0;
std::map<int, size_t> num_wls_per_tile = compute_memory_bank_regional_wordline_numbers_per_tile(module_manager, top_module,
config_region, circuit_lib, sram_model);
for (const auto& pair : num_wls_per_tile) {
num_wls += pair.second;
}
return num_wls;
}
std::map<int, size_t> compute_memory_bank_regional_wordline_numbers_per_tile(const ModuleManager& module_manager,
const ModuleId& top_module,
const ConfigRegionId& config_region,

View File

@ -48,6 +48,14 @@ size_t find_module_ql_memory_bank_num_blwls(const ModuleManager& module_manager,
const CircuitModelId& sram_model,
const e_config_protocol_type& sram_orgz_type,
const e_circuit_model_port_type& circuit_port_type);
/**
* @brief Precompute the total number of bit lines required by a specific configuration region
*/
size_t compute_memory_bank_regional_num_bls(const ModuleManager& module_manager,
const ModuleId& top_module,
const ConfigRegionId& config_region,
const CircuitLibrary& circuit_lib,
const CircuitModelId& sram_model);
/**
* @brief Precompute the number of bit lines required by each tile under a specific configuration region
@ -60,6 +68,15 @@ std::map<int, size_t> compute_memory_bank_regional_bitline_numbers_per_tile(cons
const ConfigRegionId& config_region,
const CircuitLibrary& circuit_lib,
const CircuitModelId& sram_model);
/**
* @brief Precompute the total number of word lines required by a specific configuration region
*/
size_t compute_memory_bank_regional_num_wls(const ModuleManager& module_manager,
const ModuleId& top_module,
const ConfigRegionId& config_region,
const CircuitLibrary& circuit_lib,
const CircuitModelId& sram_model);
/**
* @brief Precompute the number of word lines required by each tile under a specific configuration region
* @note

View File

@ -447,11 +447,15 @@ size_t estimate_num_configurable_children_to_skip_by_config_protocol(const Confi
|| CONFIG_MEM_QL_MEMORY_BANK == config_protocol.type()) {
VTR_ASSERT(2 <= curr_region_num_config_child);
num_child_to_skip = 2;
/* If flatten bus is used, BL/WL may not need decoders */
if (BLWL_PROTOCOL_FLATTEN == config_protocol.bl_protocol_type()) {
/* - If flatten bus is used, BL/WL may not need decoders
* - If shift registers are used, BL/WLs do not need decoders. And shift registers are not counted as configurable children
*/
if ( BLWL_PROTOCOL_FLATTEN == config_protocol.bl_protocol_type()
|| BLWL_PROTOCOL_SHIFT_REGISTER == config_protocol.bl_protocol_type() ) {
num_child_to_skip--;
}
if (BLWL_PROTOCOL_FLATTEN == config_protocol.wl_protocol_type()) {
if ( BLWL_PROTOCOL_FLATTEN == config_protocol.wl_protocol_type()
|| BLWL_PROTOCOL_SHIFT_REGISTER == config_protocol.wl_protocol_type() ) {
num_child_to_skip--;
}
}

View File

@ -165,21 +165,33 @@
<port type="output" prefix="inpad" lib_name="Y" size="1"/>
</circuit_model>
<!-- The following flip-flop is used to build the shift register chains for configuring memory banks -->
<circuit_model type="ccff" name="DFFR" prefix="DFFR" spice_netlist="${OPENFPGA_PATH}/openfpga_flow/openfpga_cell_library/spice/dff.sp" verilog_netlist="${OPENFPGA_PATH}/openfpga_flow/openfpga_cell_library/verilog/dff.v">
<circuit_model type="ccff" name="BL_DFFRQ" prefix="BL_DFFRQ" spice_netlist="${OPENFPGA_PATH}/openfpga_flow/openfpga_cell_library/spice/dff.sp" verilog_netlist="${OPENFPGA_PATH}/openfpga_flow/openfpga_cell_library/verilog/dff.v" is_default="true">
<design_technology type="cmos"/>
<input_buffer exist="true" circuit_model_name="INVTX1"/>
<output_buffer exist="true" circuit_model_name="INVTX1"/>
<port type="input" prefix="srReset" lib_name="RST" size="1" is_global="true" default_val="0" is_reset="true" is_prog="true"/>
<port type="input" prefix="D" size="1"/>
<port type="output" prefix="Q" size="1"/>
<port type="output" prefix="QN" size="1"/>
<port type="clock" prefix="sr_clk" lib_name="CK" size="1" is_global="true" default_val="0" is_prog="true"/>
<port type="input" prefix="D" lib_name="SIN" size="1"/>
<port type="output" prefix="Q" lib_name="SOUT" size="1"/>
<port type="bl" prefix="BL" lib_name="BL" size="1"/>
<port type="clock" prefix="bl_sr_clk" lib_name="CK" size="1" is_global="true" default_val="0" is_prog="true" is_shift_register="true"/>
</circuit_model>
<!-- The following flip-flop is used to build the shift register chains for configuring memory banks -->
<circuit_model type="ccff" name="WL_DFFRQ" prefix="WL_DFFRQ" spice_netlist="${OPENFPGA_PATH}/openfpga_flow/openfpga_cell_library/spice/dff.sp" verilog_netlist="${OPENFPGA_PATH}/openfpga_flow/openfpga_cell_library/verilog/dff.v">
<design_technology type="cmos"/>
<input_buffer exist="true" circuit_model_name="INVTX1"/>
<output_buffer exist="true" circuit_model_name="INVTX1"/>
<port type="input" prefix="srReset" lib_name="RST" size="1" is_global="true" default_val="0" is_reset="true" is_prog="true"/>
<port type="input" prefix="D" lib_name="SIN" size="1"/>
<port type="output" prefix="Q" lib_name="SOUT" size="1"/>
<port type="wl" prefix="wl" lib_name="WLW" size="1"/>
<port type="clock" prefix="wl_en" lib_name="WEN" size="1" is_global="true" default_val="0" is_prog="true"/>
<port type="clock" prefix="wl_sr_clk" lib_name="CK" size="1" is_global="true" default_val="0" is_prog="true" is_shift_register="true"/>
</circuit_model>
</circuit_library>
<configuration_protocol>
<organization type="ql_memory_bank" circuit_model_name="SRAM">
<bl protocol="shift_register" circuit_model_name="DFFR"/>
<wl protocol="shift_register" circuit_model_name="DFFR"/>
<bl protocol="shift_register" circuit_model_name="BL_DFFRQ"/>
<wl protocol="shift_register" circuit_model_name="WL_DFFRQ"/>
</organization>
</configuration_protocol>
<connection_block>

View File

@ -0,0 +1,226 @@
<!-- Architecture annotation for OpenFPGA framework
This annotation supports the k6_N10_40nm.xml
- General purpose logic block
- K = 6, N = 10, I = 40
- Single mode
- Routing architecture
- L = 4, fc_in = 0.15, fc_out = 0.1
-->
<openfpga_architecture>
<technology_library>
<device_library>
<device_model name="logic" type="transistor">
<lib type="industry" corner="TOP_TT" ref="M" path="${OPENFPGA_PATH}/openfpga_flow/tech/PTM_45nm/45nm.pm"/>
<design vdd="0.9" pn_ratio="2"/>
<pmos name="pch" chan_length="40e-9" min_width="140e-9" variation="logic_transistor_var"/>
<nmos name="nch" chan_length="40e-9" min_width="140e-9" variation="logic_transistor_var"/>
</device_model>
<device_model name="io" type="transistor">
<lib type="academia" ref="M" path="${OPENFPGA_PATH}/openfpga_flow/tech/PTM_45nm/45nm.pm"/>
<design vdd="2.5" pn_ratio="3"/>
<pmos name="pch_25" chan_length="270e-9" min_width="320e-9" variation="io_transistor_var"/>
<nmos name="nch_25" chan_length="270e-9" min_width="320e-9" variation="io_transistor_var"/>
</device_model>
</device_library>
<variation_library>
<variation name="logic_transistor_var" abs_deviation="0.1" num_sigma="3"/>
<variation name="io_transistor_var" abs_deviation="0.1" num_sigma="3"/>
</variation_library>
</technology_library>
<circuit_library>
<circuit_model type="inv_buf" name="INVTX1" prefix="INVTX1" is_default="true">
<design_technology type="cmos" topology="inverter" size="1"/>
<device_technology device_model_name="logic"/>
<port type="input" prefix="in" size="1"/>
<port type="output" prefix="out" size="1"/>
<delay_matrix type="rise" in_port="in" out_port="out">
10e-12
</delay_matrix>
<delay_matrix type="fall" in_port="in" out_port="out">
10e-12
</delay_matrix>
</circuit_model>
<circuit_model type="inv_buf" name="buf4" prefix="buf4" is_default="false">
<design_technology type="cmos" topology="buffer" size="1" num_level="2" f_per_stage="4"/>
<device_technology device_model_name="logic"/>
<port type="input" prefix="in" size="1"/>
<port type="output" prefix="out" size="1"/>
<delay_matrix type="rise" in_port="in" out_port="out">
10e-12
</delay_matrix>
<delay_matrix type="fall" in_port="in" out_port="out">
10e-12
</delay_matrix>
</circuit_model>
<circuit_model type="inv_buf" name="tap_buf4" prefix="tap_buf4" is_default="false">
<design_technology type="cmos" topology="buffer" size="1" num_level="3" f_per_stage="4"/>
<device_technology device_model_name="logic"/>
<port type="input" prefix="in" size="1"/>
<port type="output" prefix="out" size="1"/>
<delay_matrix type="rise" in_port="in" out_port="out">
10e-12
</delay_matrix>
<delay_matrix type="fall" in_port="in" out_port="out">
10e-12
</delay_matrix>
</circuit_model>
<circuit_model type="pass_gate" name="TGATE" prefix="TGATE" is_default="true">
<design_technology type="cmos" topology="transmission_gate" nmos_size="1" pmos_size="2"/>
<device_technology device_model_name="logic"/>
<input_buffer exist="false"/>
<output_buffer exist="false"/>
<port type="input" prefix="in" size="1"/>
<port type="input" prefix="sel" size="1"/>
<port type="input" prefix="selb" size="1"/>
<port type="output" prefix="out" size="1"/>
<delay_matrix type="rise" in_port="in sel selb" out_port="out">
10e-12 5e-12 5e-12
</delay_matrix>
<delay_matrix type="fall" in_port="in sel selb" out_port="out">
10e-12 5e-12 5e-12
</delay_matrix>
</circuit_model>
<circuit_model type="chan_wire" name="chan_segment" prefix="track_seg" is_default="true">
<design_technology type="cmos"/>
<input_buffer exist="false"/>
<output_buffer exist="false"/>
<port type="input" prefix="in" size="1"/>
<port type="output" prefix="out" size="1"/>
<wire_param model_type="pi" R="101" C="22.5e-15" num_level="1"/> <!-- model_type could be T, res_val and cap_val DON'T CARE -->
</circuit_model>
<circuit_model type="wire" name="direct_interc" prefix="direct_interc" is_default="true">
<design_technology type="cmos"/>
<input_buffer exist="false"/>
<output_buffer exist="false"/>
<port type="input" prefix="in" size="1"/>
<port type="output" prefix="out" size="1"/>
<wire_param model_type="pi" R="0" C="0" num_level="1"/> <!-- model_type could be T, res_val cap_val should be defined -->
</circuit_model>
<circuit_model type="mux" name="mux_2level" prefix="mux_2level" dump_structural_verilog="true">
<design_technology type="cmos" structure="multi_level" num_level="2" add_const_input="true" const_input_val="1"/>
<input_buffer exist="true" circuit_model_name="INVTX1"/>
<output_buffer exist="true" circuit_model_name="INVTX1"/>
<pass_gate_logic circuit_model_name="TGATE"/>
<port type="input" prefix="in" size="1"/>
<port type="output" prefix="out" size="1"/>
<port type="sram" prefix="sram" size="1"/>
</circuit_model>
<circuit_model type="mux" name="mux_2level_tapbuf" prefix="mux_2level_tapbuf" dump_structural_verilog="true">
<design_technology type="cmos" structure="multi_level" num_level="2" add_const_input="true" const_input_val="1"/>
<input_buffer exist="true" circuit_model_name="INVTX1"/>
<output_buffer exist="true" circuit_model_name="tap_buf4"/>
<pass_gate_logic circuit_model_name="TGATE"/>
<port type="input" prefix="in" size="1"/>
<port type="output" prefix="out" size="1"/>
<port type="sram" prefix="sram" size="1"/>
</circuit_model>
<circuit_model type="mux" name="mux_1level_tapbuf" prefix="mux_1level_tapbuf" is_default="true" dump_structural_verilog="true">
<design_technology type="cmos" structure="one_level" add_const_input="true" const_input_val="1"/>
<input_buffer exist="true" circuit_model_name="INVTX1"/>
<output_buffer exist="true" circuit_model_name="tap_buf4"/>
<pass_gate_logic circuit_model_name="TGATE"/>
<port type="input" prefix="in" size="1"/>
<port type="output" prefix="out" size="1"/>
<port type="sram" prefix="sram" size="1"/>
</circuit_model>
<!--DFF subckt ports should be defined as <D> <Q> <CLK> <RESET> <SET> -->
<circuit_model type="ff" name="DFFSRQ" prefix="DFFSRQ" spice_netlist="${OPENFPGA_PATH}/openfpga_flow/openfpga_cell_library/spice/dff.sp" verilog_netlist="${OPENFPGA_PATH}/openfpga_flow/openfpga_cell_library/verilog/dff.v">
<design_technology type="cmos"/>
<input_buffer exist="true" circuit_model_name="INVTX1"/>
<output_buffer exist="true" circuit_model_name="INVTX1"/>
<port type="input" prefix="D" lib_name="D" size="1"/>
<port type="input" prefix="set" lib_name="SET" size="1" is_global="true" default_val="0" is_set="true"/>
<port type="input" prefix="reset" lib_name="RST" size="1" is_global="true" default_val="0" is_reset="true"/>
<port type="output" prefix="Q" lib_name="Q" size="1"/>
<port type="clock" prefix="clk" lib_name="CK" size="1" is_global="true" default_val="0" />
</circuit_model>
<circuit_model type="lut" name="lut4" prefix="lut4" dump_structural_verilog="true">
<design_technology type="cmos"/>
<input_buffer exist="true" circuit_model_name="INVTX1"/>
<output_buffer exist="true" circuit_model_name="INVTX1"/>
<lut_input_inverter exist="true" circuit_model_name="INVTX1"/>
<lut_input_buffer exist="true" circuit_model_name="buf4"/>
<pass_gate_logic circuit_model_name="TGATE"/>
<port type="input" prefix="in" size="4"/>
<port type="output" prefix="out" size="1"/>
<port type="sram" prefix="sram" size="16"/>
</circuit_model>
<!--Scan-chain DFF subckt ports should be defined as <D> <Q> <Qb> <CLK> <RESET> <SET> -->
<circuit_model type="sram" name="SRAM_RE" prefix="SRAM_RE" spice_netlist="${OPENFPGA_PATH}/openfpga_flow/openfpga_cell_library/spice/sram.sp" verilog_netlist="${OPENFPGA_PATH}/openfpga_flow/openfpga_cell_library/verilog/sram.v">
<design_technology type="cmos"/>
<input_buffer exist="true" circuit_model_name="INVTX1"/>
<output_buffer exist="true" circuit_model_name="INVTX1"/>
<port type="bl" prefix="bl" lib_name="D" size="1"/>
<port type="wl" prefix="wl" lib_name="WE" size="1"/>
<port type="wlr" prefix="wlr" lib_name="RE" size="1"/>
<port type="output" prefix="out" lib_name="Q" size="1"/>
<port type="output" prefix="outb" lib_name="QN" size="1"/>
</circuit_model>
<circuit_model type="iopad" name="GPIO" prefix="GPIO" spice_netlist="${OPENFPGA_PATH}/openfpga_flow/openfpga_cell_library/spice/gpio.sp" verilog_netlist="${OPENFPGA_PATH}/openfpga_flow/openfpga_cell_library/verilog/gpio.v">
<design_technology type="cmos"/>
<input_buffer exist="true" circuit_model_name="INVTX1"/>
<output_buffer exist="true" circuit_model_name="INVTX1"/>
<port type="inout" prefix="PAD" size="1" is_global="true" is_io="true" is_data_io="true"/>
<port type="sram" prefix="DIR" size="1" mode_select="true" circuit_model_name="SRAM_RE" default_val="1"/>
<port type="input" prefix="outpad" lib_name="A" size="1"/>
<port type="output" prefix="inpad" lib_name="Y" size="1"/>
</circuit_model>
<!-- The following flip-flop is used to build the shift register chains for configuring memory banks -->
<circuit_model type="ccff" name="BL_DFFRQ" prefix="BL_DFFRQ" spice_netlist="${OPENFPGA_PATH}/openfpga_flow/openfpga_cell_library/spice/dff.sp" verilog_netlist="${OPENFPGA_PATH}/openfpga_flow/openfpga_cell_library/verilog/dff.v">
<design_technology type="cmos"/>
<input_buffer exist="true" circuit_model_name="INVTX1"/>
<output_buffer exist="true" circuit_model_name="INVTX1"/>
<port type="input" prefix="srReset" lib_name="RST" size="1" is_global="true" default_val="0" is_reset="true" is_prog="true" is_shift_register="true"/>
<port type="input" prefix="D" lib_name="SIN" size="1"/>
<port type="output" prefix="Q" lib_name="SOUT" size="1"/>
<port type="bl" prefix="BL" lib_name="BL" size="1"/>
<port type="clock" prefix="bl_sr_clk" lib_name="CK" size="1" is_global="true" default_val="0" is_prog="true" is_shift_register="true"/>
</circuit_model>
<!-- The following flip-flop is used to build the shift register chains for configuring memory banks -->
<circuit_model type="ccff" name="WLR_DFFRQ" prefix="WLR_DFFRQ" spice_netlist="${OPENFPGA_PATH}/openfpga_flow/openfpga_cell_library/spice/dff.sp" verilog_netlist="${OPENFPGA_PATH}/openfpga_flow/openfpga_cell_library/verilog/dff.v">
<design_technology type="cmos"/>
<input_buffer exist="true" circuit_model_name="INVTX1"/>
<output_buffer exist="true" circuit_model_name="INVTX1"/>
<port type="input" prefix="srReset" lib_name="RST" size="1" is_global="true" default_val="0" is_reset="true" is_prog="true" is_shift_register="true"/>
<port type="input" prefix="D" lib_name="SIN" size="1"/>
<port type="output" prefix="Q" lib_name="SOUT" size="1"/>
<port type="wl" prefix="wl" lib_name="WLW" size="1"/>
<port type="wlr" prefix="wlr" lib_name="WLR" size="1"/>
<port type="clock" prefix="wl_en" lib_name="WEN" size="1" is_global="true" default_val="0" is_prog="true"/>
<port type="clock" prefix="wl_sr_clk" lib_name="CK" size="1" is_global="true" default_val="0" is_prog="true" is_shift_register="true"/>
</circuit_model>
</circuit_library>
<configuration_protocol>
<organization type="ql_memory_bank" circuit_model_name="SRAM_RE">
<bl protocol="shift_register" circuit_model_name="BL_DFFRQ"/>
<wl protocol="shift_register" circuit_model_name="WLR_DFFRQ"/>
</organization>
</configuration_protocol>
<connection_block>
<switch name="ipin_cblock" circuit_model_name="mux_2level_tapbuf"/>
</connection_block>
<switch_block>
<switch name="0" circuit_model_name="mux_2level_tapbuf"/>
</switch_block>
<routing_segment>
<segment name="L4" circuit_model_name="chan_segment"/>
</routing_segment>
<pb_type_annotations>
<!-- physical pb_type binding in complex block IO -->
<pb_type name="io" physical_mode_name="physical" idle_mode_name="inpad"/>
<pb_type name="io[physical].iopad" circuit_model_name="GPIO" mode_bits="1"/>
<pb_type name="io[inpad].inpad" physical_pb_type_name="io[physical].iopad" mode_bits="1"/>
<pb_type name="io[outpad].outpad" physical_pb_type_name="io[physical].iopad" mode_bits="0"/>
<!-- End physical pb_type binding in complex block IO -->
<!-- physical pb_type binding in complex block CLB -->
<!-- physical mode will be the default mode if not specified -->
<pb_type name="clb">
<!-- Binding interconnect to circuit models as their physical implementation, if not defined, we use the default model -->
<interconnect name="crossbar" circuit_model_name="mux_2level"/>
</pb_type>
<pb_type name="clb.fle[n1_lut4].ble4.lut4" circuit_model_name="lut4"/>
<pb_type name="clb.fle[n1_lut4].ble4.ff" circuit_model_name="DFFSRQ"/>
<!-- End physical pb_type binding in complex block IO -->
</pb_type_annotations>
</openfpga_architecture>

View File

@ -437,3 +437,91 @@ assign Q = q_reg;
assign QN = !Q;
endmodule //End Of Module
//-----------------------------------------------------
// Function : D-type flip-flop with
// - asynchronous active high reset
// @note This DFF is designed to drive BLs when shift registers are used
//-----------------------------------------------------
module BL_DFFRQ (
input RST, // Reset input
input CK, // Clock Input
input SIN, // Data Input
output SOUT, // Q output
output BL // BL output
);
//------------Internal Variables--------
reg q_reg;
//-------------Code Starts Here---------
always @ ( posedge CK or posedge RST)
if (RST) begin
q_reg <= 1'b0;
end else begin
q_reg <= SIN;
end
assign SOUT = q_reg;
assign BL = q_reg;
endmodule //End Of Module
//-----------------------------------------------------
// Function : D-type flip-flop with
// - asynchronous active high reset
// @note This DFF is designed to drive WLs when shift registers are used
//-----------------------------------------------------
module WL_DFFRQ (
input RST, // Reset input
input CK, // Clock Input
input SIN, // Data Input
input WEN, // Write-enable
output SOUT, // Q output
output WLW // Drive WL write signals
);
//------------Internal Variables--------
reg q_reg;
//-------------Code Starts Here---------
always @ ( posedge CK or posedge RST)
if (RST) begin
q_reg <= 1'b0;
end else begin
q_reg <= SIN;
end
assign SOUT = q_reg;
assign WLW = WEN ? q_reg : 1'b0;
endmodule //End Of Module
//-----------------------------------------------------
// Function : D-type flip-flop with
// - asynchronous active high reset
// @note This DFF is designed to drive WLs and WLRs when shift registers are used
//-----------------------------------------------------
module WLR_DFFRQ (
input RST, // Reset input
input CK, // Clock Input
input SIN, // Data Input
input WEN, // Write-enable
output SOUT, // Q output
output WLW, // Drive WL write signals
output WLR // Drive WL read signals
);
//------------Internal Variables--------
reg q_reg;
//-------------Code Starts Here---------
always @ ( posedge CK or posedge RST)
if (RST) begin
q_reg <= 1'b0;
end else begin
q_reg <= SIN;
end
assign SOUT = q_reg;
assign WLW = WEN ? q_reg : 1'b0;
assign WLR = 1'b0; // Use a constant output just for simple testing
endmodule //End Of Module

View File

@ -0,0 +1,42 @@
<!-- Simulation Setting for OpenFPGA framework
This file will use automatic inference for any settings
including:
- auto select the number of simulation cycles
- auto select the simulation clock frequency from VPR results
-->
<openfpga_simulation_setting>
<clock_setting>
<operating frequency="auto" num_cycles="auto" slack="0.2"/>
<programming frequency="100e6">
<clock name="bl_shift_register_clk" port="bl_sr_clk[0:0]" frequency="auto" is_shift_register="true"/>
<clock name="wl_shift_register_clk" port="wl_sr_clk[0:0]" frequency="auto" is_shift_register="true"/>
</programming>
</clock_setting>
<simulator_option>
<operating_condition temperature="25"/>
<output_log verbose="false" captab="false"/>
<accuracy type="abs" value="1e-13"/>
<runtime fast_simulation="true"/>
</simulator_option>
<monte_carlo num_simulation_points="2"/>
<measurement_setting>
<slew>
<rise upper_thres_pct="0.95" lower_thres_pct="0.05"/>
<fall upper_thres_pct="0.05" lower_thres_pct="0.95"/>
</slew>
<delay>
<rise input_thres_pct="0.5" output_thres_pct="0.5"/>
<fall input_thres_pct="0.5" output_thres_pct="0.5"/>
</delay>
</measurement_setting>
<stimulus>
<clock>
<rise slew_type="abs" slew_time="20e-12" />
<fall slew_type="abs" slew_time="20e-12" />
</clock>
<input>
<rise slew_type="abs" slew_time="25e-12" />
<fall slew_type="abs" slew_time="25e-12" />
</input>
</stimulus>
</openfpga_simulation_setting>

View File

@ -59,6 +59,7 @@ run-task basic_tests/full_testbench/ql_memory_bank_use_wlr --debug --show_thread
run-task basic_tests/full_testbench/multi_region_ql_memory_bank --debug --show_thread_logs
run-task basic_tests/full_testbench/ql_memory_bank_flatten --debug --show_thread_logs
run-task basic_tests/full_testbench/ql_memory_bank_flatten_use_wlr --debug --show_thread_logs
run-task basic_tests/full_testbench/ql_memory_bank_shift_register --debug --show_thread_logs
echo -e "Testing testbenches without self checking features";
run-task basic_tests/full_testbench/full_testbench_without_self_checking --debug --show_thread_logs

View File

@ -0,0 +1,45 @@
# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
# Configuration file for running experiments
# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
# timeout_each_job : FPGA Task script splits fpga flow into multiple jobs
# Each job execute fpga_flow script on combination of architecture & benchmark
# timeout_each_job is timeout for each job
# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
[GENERAL]
run_engine=openfpga_shell
power_tech_file = ${PATH:OPENFPGA_PATH}/openfpga_flow/tech/PTM_45nm/45nm.xml
power_analysis = true
spice_output=false
verilog_output=true
timeout_each_job = 20*60
fpga_flow=yosys_vpr
[OpenFPGA_SHELL]
openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/write_full_testbench_example_script.openfpga
openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_N4_40nm_qlbanksr_openfpga.xml
openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_shift_register_sim_openfpga.xml
openfpga_vpr_device_layout=
openfpga_fast_configuration=
[ARCHITECTURES]
arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml
[BENCHMARKS]
bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2/and2.v
bench1=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/or2/or2.v
bench2=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2_latch/and2_latch.v
[SYNTHESIS_PARAM]
bench0_top = and2
bench0_chan_width = 300
bench1_top = or2
bench1_chan_width = 300
bench2_top = and2_latch
bench2_chan_width = 300
[SCRIPT_PARAM_MIN_ROUTE_CHAN_WIDTH]
end_flow_with_test=