[core] developing memory group modules in grid modules

This commit is contained in:
tangxifan 2023-08-01 17:50:03 -07:00
parent 23643f3fb1
commit 53050b94ab
9 changed files with 441 additions and 7 deletions

View File

@ -41,6 +41,7 @@ constexpr const char* GRID_MEM_INSTANCE_PREFIX = "mem_";
constexpr const char* SWITCH_BLOCK_MEM_INSTANCE_PREFIX = "mem_";
constexpr const char* CONNECTION_BLOCK_MEM_INSTANCE_PREFIX = "mem_";
constexpr const char* MEMORY_MODULE_POSTFIX = "_mem";
constexpr const char* MEMORY_FEEDTHROUGH_MODULE_POSTFIX = "_feedthrough_mem";
constexpr const char* MEMORY_FEEDTHROUGH_DATA_IN_PORT_NAME = "feedthrough_mem_in";
constexpr const char* MEMORY_FEEDTHROUGH_DATA_IN_INV_PORT_NAME = "feedthrough_mem_inb";
constexpr const char* MEMORY_BL_PORT_NAME = "bl";

View File

@ -528,6 +528,13 @@ std::string generate_tile_module_netlist_name(const std::string& block_name,
return block_name + postfix;
}
/*********************************************************************
* Generate the module name of a physical memory module
**********************************************************************/
std::string generate_physical_memory_module_name(const size_t& mem_size) {
return std::string("physical_config_mem_size") + std::to_string(mem_size);
}
/*********************************************************************
* Generate the module name for a connection block with a given coordinate
*********************************************************************/

View File

@ -119,6 +119,8 @@ std::string generate_tile_module_port_name(const std::string& prefix,
std::string generate_tile_module_netlist_name(const std::string& block_name,
const std::string& postfix);
std::string generate_physical_memory_module_name(const size_t& mem_size);
std::string generate_sb_mux_instance_name(const std::string& prefix,
const e_side& sb_side,
const size_t& track_id,

View File

@ -1069,6 +1069,103 @@ static void rec_build_logical_tile_modules(
VTR_LOGV(verbose, "Done\n");
}
/*****************************************************************************
* This function creates a physical memory module and add it the current module
* The following tasks will be accomplished:
* - Traverse all the logical configurable children in the module tree, starting from the current module
* - Build a list of the leaf logical configurable children and count the total memory sizes, the memory size for each physical memory submodule. Note that the physical memory submodule should be cached already in each leaf logical configurable children
* - Get the physical memory module required by each leaf logical configurable child
* - Create a dedicated module name for the physical memory (check if already exists, if yes, skip creating a new module)
* - Instanciate the module
* - Built nets. Note that only the output ports of the physical memory block is required, since they should drive the dedicated memory ports of logical configurable children
*****************************************************************************/
static int add_physical_memory_module(ModuleManager& module_manager,
DecoderLibrary& decoder_lib,
const ModuleId& curr_module,
const CircuitLibrary& circuit_lib,
const e_config_protocol_type& sram_orgz_type,
const CircuitModelId& sram_model) {
int status = CMD_EXEC_SUCCESS;
std::vector<ModuleId> required_phy_mem_modules;
status = rec_find_physical_memory_children(static_cast<const ModuleManager&>(module_manager), curr_module, required_phy_mem_modules);
if (status != CMD_EXEC_SUCCESS) {
return CMD_EXEC_FATAL_ERROR;
}
size_t module_num_config_bits =
find_module_num_config_bits_from_child_modules(
module_manager, curr_module, circuit_lib, sram_model, CONFIG_MEM_FEEDTHROUGH);
std::string phy_mem_module_name = generate_physical_memory_module_name(module_num_config_bits);
ModuleId phy_mem_module = module_manager.find_module(phy_mem_module_name);
if (!module_manager.valid_module_id(phy_mem_module)) {
status = build_memory_group_module(module_manager, decode_lib, circuit_lib, sram_orgz_type, phy_mem_module_name, sram_model, required_phy_mem_modules);
}
if (status != CMD_EXEC_SUCCESS) {
VTR_LOG_ERROR("Failed to create the physical memory module '%s'!\n", phy_mem_module_name.c_str());
return CMD_EXEC_FATAL_ERROR;
}
phy_mem_module = module_manager.find_module(phy_mem_module_name);
if (!module_manager.valid_module_id(phy_mem_module)) {
VTR_LOG_ERROR("Failed to create the physical memory module '%s'!\n", phy_mem_module_name.c_str());
return CMD_EXEC_FATAL_ERROR;
}
/* Add the physical memory module to the current module */
size_t phy_mem_instance = module_manager.num_instance(curr_module, phy_mem_module);
module_manager.add_child_module(curr_module, phy_mem_module, false);
/* Register in the physical configurable children list */
module_manager.add_physical_configurable_child(curr_module, phy_mem_module, phy_mem_instance, curr_module);
/* Build nets between the data output of the physical memory module and the outputs of the logical configurable children */
size_t curr_mem_pin_index = 0;
std::map<CircuitPortType, std::string> mem2mem_port_map;
mem2mem_port_map[CIRCUIT_MODEL_PORT_BL] = std::string(CONFIGURABLE_MEMORY_DATA_OUT_NAME);
mem2mem_port_map[CIRCUIT_MODEL_PORT_BLB] = std::string(CONFIGURABLE_MEMORY_INVERTED_DATA_OUT_NAME);
for (size_t ichild = 0; ichild < module_manager.logical_configurable_children(curr_module).size(); ++ichild) {
for (CircuitPortType port_type : {CIRCUIT_MODEL_PORT_BL, CIRCUIT_MODEL_PORT_BLB}) {
std::string src_port_name = mem2mem_port_map[port_type];
std::string des_port_name =
generate_sram_port_name(CONFIG_MEM_FEEDTHROUGH, port_type);
/* Try to find these ports in the module manager */
ModulePortId src_port_id =
module_manager.find_module_port(phy_mem_module, src_port_name);
if (!module_manager.valid_module_port_id(phy_mem_module, src_port_id)) {
return CMD_EXEC_FATAL_ERROR;
}
BasicPort src_port =
module_manager.module_port(phy_mem_module, src_port_id);
ModuleId des_module = module_manager.logical_configurable_children(curr_module)[ichild];
size_t des_instance = module_manager.logical_configurable_child_instances(curr_module)[ichild];
ModulePortId des_port_id =
module_manager.find_module_port(des_module, des_port_name);
if (!module_manager.valid_module_port_id(des_module, des_port_id)) {
return CMD_EXEC_FATAL_ERROR;
}
BasicPort des_port =
module_manager.module_port(des_module, des_port_id);
/* Build nets */
for (size_t ipin = 0; ipin < des_port.pins().size(); ++ipin) {
/* Create a net and add source and sink to it */
ModuleNetId net = create_module_source_pin_net(
module_manager, curr_module, phy_mem_module, phy_mem_instance,
src_port_id, src_port.pins()[cur_mem_pin_index]);
if (module_manager.valid_module_net_id(curr_module, net)) {
return CMD_EXEC_FATAL_ERROR;
}
/* Add net sink */
module_manager.add_module_net_sink(curr_module, net, des_module,
des_instance, des_port_id,
des_port.pins()[ipin]);
curr_mem_pin_index++;
}
}
}
return status;
}
/*****************************************************************************
* This function will create a Verilog file and print out a Verilog netlist
* for a type of physical block
@ -1140,13 +1237,15 @@ static void build_physical_tile_module(
group_config_block ? CONFIG_MEM_FEEDTHROUGH : sram_orgz_type)) {
/* Only add logical configurable children here. Since we will add a physical memory block at this level */
module_manager.add_configurable_child(grid_module, pb_module,
pb_instance_id, true);
pb_instance_id, group_config_block);
}
}
}
/* TODO: Add a physical memory block */
add_physical_memory_module(module_manager, grid_module);
if (group_config_block) {
add_physical_memory_module(module_manager, decoder_lib, grid_module, circuit_lib, sram_orgz_type, sram_model);
}
/* Add grid ports(pins) to the module */
if (false == duplicate_grid_pin) {

View File

@ -6,8 +6,7 @@
#include <algorithm>
#include <ctime>
#include <string>
/* Headers from vtrutil library */
#include "command_exit_codes.h"
#include "build_decoder_modules.h"
#include "build_memory_modules.h"
#include "circuit_library_utils.h"
@ -932,6 +931,79 @@ static void build_memory_module(ModuleManager& module_manager,
}
}
/*********************************************************************
* Generate Verilog modules for the feedthrough memories that are used
* by a circuit model
* mem_out mem_outb
* | |
* v v
* +------------------------------------+
* | |
* | |
* | |
* +------------------------------------+
* | |
* | mem_in | mem_inb
* v v
* +------------------------------------+
* | Multiplexer Configuration port |
*
********************************************************************/
static int build_feedthrough_memory_module(ModuleManager& module_manager,
const std::string& module_name,
const size_t& num_mems) {
/* Create a module and add to the module manager */
ModuleId mem_module = module_manager.add_module(module_name);
if (!module_manager.valid_module_id(mem_module)) {
return CMD_EXEC_FATAL_ERROR;
}
/* Label module usage */
module_manager.set_module_usage(mem_module, ModuleManager::MODULE_CONFIG);
/* Add module ports */
/* Input: memory inputs */
BasicPort in_port(std::string(MEMORY_FEEDTHROUGH_DATA_IN_PORT_NAME), num_mems);
ModulePortId mem_in_port = module_manager.add_port(
mem_module, in_port, ModuleManager::MODULE_INPUT_PORT);
BasicPort inb_port(std::string(MEMORY_FEEDTHROUGH_DATA_IN_INV_PORT_NAME), num_mems);
ModulePortId mem_inb_port = module_manager.add_port(
mem_module, inb_port, ModuleManager::MODULE_INPUT_PORT);
/* Add each output port */
BasicPort out_port(std::string(CONFIGURABLE_MEMORY_DATA_OUT_NAME), num_mems);
ModulePortId mem_out_port = module_manager.add_port(
mem_module, out_port, ModuleManager::MODULE_OUTPUT_PORT);
BasicPort outb_port(std::string(CONFIGURABLE_MEMORY_INVERTED_DATA_OUT_NAME), num_mems);
ModulePortId mem_outb_port = module_manager.add_port(
mem_module, outb_port, ModuleManager::MODULE_OUTPUT_PORT);
/* Build feedthrough nets */
for (size_t pin_id = 0; pin_id < in_port.pins().size(); ++pin_id) {
ModuleNetId net = module_manager.create_module_net(mem_module);
if (!module_manager.valid_module_net_id(mem_module, net)) {
return CMD_EXEC_FATAL_ERROR;
}
module_manager.add_module_net_source(mem_module, net, mem_module, 0,
mem_in_port, in_port.pins()[pin_id]);
module_manager.add_module_net_sink(
mem_module, net, mem_module, 0, mem_out_port, out_port.pins()[pin_id]);
}
for (size_t pin_id = 0; pin_id < inb_port.pins().size(); ++pin_id) {
ModuleNetId net = module_manager.create_module_net(mem_module);
if (!module_manager.valid_module_net_id(mem_module, net)) {
return CMD_EXEC_FATAL_ERROR;
}
module_manager.add_module_net_source(mem_module, net, mem_module, 0,
mem_inb_port, inb_port.pins()[pin_id]);
module_manager.add_module_net_sink(
mem_module, net, mem_module, 0, mem_outb_port, outb_port.pins()[pin_id]);
}
return CMD_EXEC_SUCCESS;
}
/*********************************************************************
* Generate Verilog modules for the memories that are used
* by multiplexers
@ -990,6 +1062,62 @@ static void build_mux_memory_module(
}
}
/*********************************************************************
* Generate Verilog modules for the feedthrough memories that are used
* by multiplexers
* SRAM ports as feedthrough (driven by physical memory blocks)
* | | | |
* v v ... v v
* +----------------+
* | Memory Module |
* +----------------+
* | | ... | |
* v v v v SRAM ports of multiplexer
* +---------------------+
* in--->| Multiplexer Module |---> out
* +---------------------+
********************************************************************/
static int build_mux_feedthrough_memory_module(
ModuleManager& module_manager,
const CircuitLibrary& circuit_lib,
const e_config_protocol_type& sram_orgz_type, const CircuitModelId& mux_model,
const MuxGraph& mux_graph) {
int status = CMD_EXEC_SUCCESS;
/* Find the actual number of configuration bits, based on the mux graph
* Due to the use of local decoders inside mux, this may be
*/
size_t num_config_bits =
find_mux_num_config_bits(circuit_lib, mux_model, mux_graph, sram_orgz_type);
/* Multiplexers built with different technology is in different organization
*/
switch (circuit_lib.design_tech_type(mux_model)) {
case CIRCUIT_MODEL_DESIGN_CMOS: {
/* Generate module name */
std::string module_name = generate_mux_subckt_name(
circuit_lib, mux_model,
find_mux_num_datapath_inputs(circuit_lib, mux_model,
mux_graph.num_inputs()),
std::string(MEMORY_FEEDTHROUGH_MODULE_POSTFIX));
status = build_feedthrough_memory_module(module_manager, module_name, num_config_bits);
break;
}
case CIRCUIT_MODEL_DESIGN_RRAM:
/* We do not need a memory submodule for RRAM MUX,
* RRAM are embedded in the datapath
* TODO: generate local encoders for RRAM-based multiplexers here!!!
*/
break;
default:
VTR_LOGF_ERROR(__FILE__, __LINE__,
"Invalid design technology of multiplexer '%s'\n",
circuit_lib.model_name(mux_model).c_str());
exit(1);
}
return status;
}
/*********************************************************************
* Build modules for
* the memories that are affiliated to multiplexers and other programmable
@ -1007,12 +1135,13 @@ static void build_mux_memory_module(
* memory-bank organization for the memories.
* If we need feedthrough memory blocks, build the memory modules which contain only feedthrough wires
********************************************************************/
void build_memory_modules(ModuleManager& module_manager,
int build_memory_modules(ModuleManager& module_manager,
DecoderLibrary& arch_decoder_lib,
const MuxLibrary& mux_lib,
const CircuitLibrary& circuit_lib,
const e_config_protocol_type& sram_orgz_type,
const bool& require_feedthrough_memory) {
int status = CMD_EXEC_SUCCESS;
vtr::ScopedStartFinishTimer timer("Build memory modules");
/* Create the memory circuits for the multiplexer */
@ -1029,7 +1158,14 @@ void build_memory_modules(ModuleManager& module_manager,
/* Create a Verilog module for the memories used by the multiplexer */
build_mux_memory_module(module_manager, arch_decoder_lib, circuit_lib,
sram_orgz_type, mux_model, mux_graph);
/* TODO: Create feedthrough memory module */
/* Create feedthrough memory module */
if (require_feedthrough_memory) {
status = build_mux_feedthrough_memory_module(module_manager, circuit_lib,
sram_orgz_type, mux_model, mux_graph);
if (status != CMD_EXEC_SUCCESS) {
return CMD_EXEC_FATAL_ERROR;
}
}
}
/* Create the memory circuits for non-MUX circuit models.
@ -1066,8 +1202,165 @@ void build_memory_modules(ModuleManager& module_manager,
/* Create a Verilog module for the memories used by the circuit model */
build_memory_module(module_manager, arch_decoder_lib, circuit_lib,
sram_orgz_type, module_name, sram_models[0], num_mems);
/* TODO: Create feedthrough memory module */
/* Create feedthrough memory module */
if (require_feedthrough_memory) {
status = build_feedthrough_memory_module(module_manager, module_name, num_mems);
if (status != CMD_EXEC_SUCCESS) {
return CMD_EXEC_FATAL_ERROR;
}
}
}
return status;
}
/*********************************************************************
* Add module nets to connect an output port of a configuration-chain
* memory module to an output port of its child module
* Restriction: this function is really designed for memory modules
* 1. It assumes that output port name of child module is the same as memory
*module
* 2. It assumes exact pin-to-pin mapping:
* j-th pin of output port of the i-th child module is wired to the j + i*W
*-th pin of output port of the memory module, where W is the size of port
* 3. It assumes fixed port name for output ports
********************************************************************/
void add_module_output_nets_to_memory_group_module(
ModuleManager& module_manager, const ModuleId& mem_module,
const std::string& mem_module_output_name, const ModuleId& child_module,
const size_t& output_pin_start_index, const size_t& child_instance) {
/* Wire inputs of parent module to inputs of child modules */
ModulePortId src_port_id = module_manager.find_module_port(
child_module, mem_module_output_name);
ModulePortId sink_port_id =
module_manager.find_module_port(mem_module, mem_module_output_name);
for (size_t pin_id = 0;
pin_id <
module_manager.module_port(child_module, src_port_id).pins().size();
++pin_id) {
ModuleNetId net = module_manager.create_module_net(mem_module);
/* Source pin is shifted by the number of memories */
size_t src_pin_id =
module_manager.module_port(child_module, src_port_id).pins()[pin_id];
/* Source node of the input net is the input of memory module */
module_manager.add_module_net_source(
mem_module, net, child_module, child_instance, src_port_id, src_pin_id);
/* Sink node of the input net is the input of sram module */
size_t sink_pin_id =
output_pin_start_index +
module_manager.module_port(mem_module, sink_port_id).pins()[pin_id];
module_manager.add_module_net_sink(mem_module, net, mem_module, 0,
sink_port_id, sink_pin_id);
}
}
/*********************************************************************
* Build a grouped memory module based on existing memory modules
* - Create the module
* - Add dedicated instance
* - Add ports
* - Add nets
********************************************************************/
int build_memory_group_module(ModuleManager& module_manager,
DecoderLibrary& decoder_lib,
const CircuitLibrary& circuit_lib,
const e_config_protocol_type& sram_orgz_type,
const std::string& module_name,
const CircuitModelId& sram_model,
const std::vector<ModuleId>& child_modules,
const size_t& num_mems) {
ModuleId mem_module = module_manager.add_module(module_name);
if (!module_manager.valid_module_id(mem_module)) {
return CMD_EXEC_FATAL_ERROR;
}
/* Add output ports */
std::string out_port_name = generate_configurable_memory_data_out_name();
BasicPort out_port(out_port_name, num_mems);
module_manager.add_port(mem_module, out_port,
ModuleManager::MODULE_OUTPUT_PORT);
std::string outb_port_name = generate_configurable_memory_inverted_data_out_name();
BasicPort outb_port(outb_port_name, num_mems);
module_manager.add_port(mem_module, outb_port,
ModuleManager::MODULE_OUTPUT_PORT);
/* Add nets between child module outputs and memory modules */
size_t mem_out_pin_start_index = 0;
size_t mem_outb_pin_start_index = 0;
for (size_t ichild = 0; ichild < child_modules.size(); ++ichild) {
ModuleId child_module = child_modules[ichild];
size_t child_instance = module_manager.num_instance(mem_module, child_module);
module_manager.add_child_module(mem_module, child_module, false);
module_manager.add_configurable_child(mem_module, child_module, child_instance, false);
/* Wire outputs of child module to outputs of parent module */
add_module_output_nets_to_memory_group_module(
module_manager, mem_module, out_port_name,
child_module, mem_out_pin_start_index, child_instance);
add_module_output_nets_to_memory_group_module(
module_manager, mem_module, outb_port_name,
child_module, mem_outb_pin_start_index, child_instance);
/* Update pin counter */
ModulePortId child_out_port_id = module_manager.find_module_port(child_module, out_port_name);
mem_out_pin_start_index += module_manager.module_port(child_module, child_out_port_id).get_width();
ModulePortId child_outb_port_id = module_manager.find_module_port(child_module, outb_port_name);
mem_outb_pin_start_index += module_manager.module_port(child_module, child_outb_port_id).get_width();
}
/* Check pin counter */
VTR_ASSERT(mem_out_pin_start_index == num_mems && mem_outb_pin_start_index == num_mems);
/* 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);
/* Count GPIO ports from the sub-modules under this Verilog module
* This is a much easier job after adding sub modules (instances),
* we just need to find all the I/O ports from the child modules and build a
* list of it
*/
add_module_gpio_ports_from_child_modules(module_manager, mem_module);
/* Count shared SRAM ports from the sub-modules under this Verilog module
* This is a much easier job after adding sub modules (instances),
* we just need to find all the I/O ports from the child modules and build a
* list of it
*/
size_t module_num_shared_config_bits =
find_module_num_shared_config_bits_from_child_modules(module_manager,
mem_module);
if (0 < module_num_shared_config_bits) {
add_reserved_sram_ports_to_module_manager(module_manager, mem_module,
module_num_shared_config_bits);
}
/* Count SRAM ports from the sub-modules under this Verilog module
* This is a much easier job after adding sub modules (instances),
* we just need to find all the I/O ports from the child modules and build a
* list of it
*/
size_t module_num_config_bits =
find_module_num_config_bits_from_child_modules(
module_manager, mem_module, circuit_lib, sram_model, sram_orgz_type);
if (0 < module_num_config_bits) {
add_sram_ports_to_module_manager(module_manager, mem_module, circuit_lib,
sram_model, sram_orgz_type,
module_num_config_bits);
}
/* Add module nets to connect memory cells inside
* This is a one-shot addition that covers all the memory modules in this pb
* module!
*/
if (0 < module_manager.configurable_children(mem_module).size()) {
add_module_nets_memory_config_bus(module_manager, decoder_lib, mem_module,
sram_orgz_type,
circuit_lib.design_tech_type(sram_model));
}
return CMD_EXEC_SUCCESS;
}
} /* end namespace openfpga */

View File

@ -29,6 +29,14 @@ void build_memory_modules(ModuleManager& module_manager,
const e_config_protocol_type& sram_orgz_type,
const bool& require_feedthrough_memory);
int build_memory_group_module(ModuleManager& module_manager,
DecoderLibrary& decoder_lib,
const CircuitLibrary& circuit_lib,
const e_config_protocol_type& sram_orgz_type,
const std::string& module_name,
const CircuitModelId& sram_model,
const std::vector<ModuleId>& child_modules);
} /* end namespace openfpga */
#endif

View File

@ -496,4 +496,20 @@ size_t estimate_num_configurable_children_to_skip_by_config_protocol(
return num_child_to_skip;
}
int rec_find_physical_memory_children(const ModuleManager& module_manager, const ModuleId& curr_module, std::vector<ModuleId>& physical_memory_children) {
if (module_manager.logical_configurable_children(curr_module).empty()) {
return CMD_EXEC_SUCCESS;
}
for (size_t ichild = 0; ichild < module_manager.logical_configurable_children(curr_module).size(); ++ichild) {
ModuleId logical_child = module_manager.logical_configurable_children(curr_module)[ichild];
if (module_manager.logical_configurable_children(logical_child).empty()) {
/* This is a leaf node, get the physical memory module */
physical_memory_children.push_back(module_manager.physical_configurable_children(curr_module)[ichild]);
} else {
rec_find_physical_memory_children(module_manager, logical_child, physical_memory_children);
}
}
return CMD_EXEC_SUCCESS;
}
} /* end namespace openfpga */

View File

@ -53,6 +53,13 @@ size_t generate_pb_sram_port_size(const e_config_protocol_type sram_orgz_type,
size_t estimate_num_configurable_children_to_skip_by_config_protocol(
const ConfigProtocol& config_protocol, size_t curr_region_num_config_child);
/**
* @brief Find the physical memory child modules with a given root module
* This function will walk through the module tree in a recursive way until reaching the leaf node (which require configurable memories)
* Return a list of modules
*/
int rec_find_physical_memory_children(const ModuleManager& module_manager, const ModuleId& curr_module, std::vector<ModuleId>& physical_memory_children);
} /* end namespace openfpga */
#endif

View File

@ -2370,6 +2370,7 @@ size_t find_module_num_config_bits_from_child_modules(
size_t num_config_bits = 0;
switch (sram_orgz_type) {
case CONFIG_MEM_FEEDTHROUGH:
case CONFIG_MEM_STANDALONE:
case CONFIG_MEM_SCAN_CHAIN:
case CONFIG_MEM_QL_MEMORY_BANK: