refactored rram mux generation
This commit is contained in:
parent
94538b5062
commit
395bf4fbdf
|
@ -178,7 +178,7 @@ void generate_verilog_cmos_mux_branch_body_behavioral(std::fstream& fp,
|
|||
|
||||
/*********************************************************************
|
||||
* Generate Verilog codes modeling an branch circuit
|
||||
* for a multiplexer with the given size
|
||||
* for a CMOS multiplexer with the given size
|
||||
* Support structural and behavioral Verilog codes
|
||||
*********************************************************************/
|
||||
static
|
||||
|
@ -268,6 +268,384 @@ void generate_verilog_cmos_mux_branch_module(ModuleManager& module_manager,
|
|||
print_verilog_module_end(fp, module_name);
|
||||
}
|
||||
|
||||
/*********************************************************************
|
||||
* Dump a structural verilog for RRAM MUX basis module
|
||||
* This is only called when structural verilog dumping option is enabled for this spice model
|
||||
* IMPORTANT: the structural verilog can NOT be used for functionality verification!!!
|
||||
* TODO: This part is quite restricted to the way we implemented our RRAM FPGA
|
||||
* Should be reworked to be more generic !!!
|
||||
*
|
||||
* By structural the schematic is splitted into two parts: left part and right part
|
||||
* The left part includes BLB[0..N-1] and WL[0..N-1] signals as well as RRAMs
|
||||
* The right part includes BLB[N] and WL[N]
|
||||
* Corresponding Schematic is as follows:
|
||||
*
|
||||
* LEFT PART | RIGHT PART
|
||||
*
|
||||
* BLB[0] BLB[N]
|
||||
* | |
|
||||
* \|/ \|/
|
||||
* in[0] ---->RRAM[0]-----+
|
||||
* |
|
||||
* BLB[1] |
|
||||
* | |
|
||||
* \|/ |
|
||||
* in[1] ---->RRAM[1]-----+
|
||||
* |-----> out[0]
|
||||
* ...
|
||||
* |
|
||||
* in[N-1] ---->RRAM[N-1]---+
|
||||
* /|\ /|\
|
||||
* | |
|
||||
* BLB[N-1] WL[N]
|
||||
*
|
||||
* Working principle:
|
||||
* 1. Set a RRAM[i]: enable BLB[i] and WL[N]
|
||||
* 2. Reset a RRAM[i]: enable BLB[N] and WL[i]
|
||||
* 3. Operation: disable all BLBs and WLs
|
||||
*
|
||||
* The structure is done in the way we implement the physical layout of RRAM MUX
|
||||
* It is NOT the only road to the goal!!!
|
||||
*********************************************************************/
|
||||
static
|
||||
void generate_verilog_rram_mux_branch_body_structural(ModuleManager& module_manager,
|
||||
const CircuitLibrary& circuit_lib,
|
||||
std::fstream& fp,
|
||||
const ModuleId& module_id,
|
||||
const CircuitModelId& circuit_model,
|
||||
const BasicPort& input_port,
|
||||
const BasicPort& output_port,
|
||||
const BasicPort& blb_port,
|
||||
const BasicPort& wl_port,
|
||||
const MuxGraph& mux_graph) {
|
||||
std::string progTE_module_name("PROG_TE");
|
||||
std::string progBE_module_name("PROG_BE");
|
||||
|
||||
/* Make sure we have a valid file handler*/
|
||||
check_file_handler(fp);
|
||||
|
||||
/* Verilog Behavior description for a MUX */
|
||||
print_verilog_comment(fp, std::string("---- Structure-level description of RRAM MUX -----"));
|
||||
|
||||
/* Print internal structure of 4T1R programming structures
|
||||
* Written in structural Verilog
|
||||
* The whole structure-level description is divided into two parts:
|
||||
* 1. Left part consists of N PROG_TE modules, each of which
|
||||
* includes a PMOS, a NMOS and a RRAM, which is actually the left
|
||||
* part of a 4T1R programming structure
|
||||
* 2. Right part includes only a PROG_BE module, which consists
|
||||
* of a PMOS and a NMOS, which is actually the right part of a
|
||||
* 4T1R programming sturcture
|
||||
*/
|
||||
/* Create a module for the progTE and register it in the module manager
|
||||
* Structure of progTE
|
||||
*
|
||||
* +----------+
|
||||
* in--->| |
|
||||
* BLB-->| progTE |--> out
|
||||
* WL--->| |
|
||||
* +----------+
|
||||
*/
|
||||
ModuleId progTE_module_id = module_manager.add_module(progTE_module_name);
|
||||
/* If there is already such as module inside, we just ned to find the module id */
|
||||
if (ModuleId::INVALID() == progTE_module_id) {
|
||||
progTE_module_id = module_manager.find_module(progTE_module_name);
|
||||
/* We should have a valid id! */
|
||||
VTR_ASSERT(ModuleId::INVALID() != progTE_module_id);
|
||||
}
|
||||
/* Add ports to the module */
|
||||
/* input port */
|
||||
BasicPort progTE_in_port("A", 1);
|
||||
module_manager.add_port(progTE_module_id, progTE_in_port, ModuleManager::MODULE_INPUT_PORT);
|
||||
/* WL port */
|
||||
BasicPort progTE_wl_port("WL", 1);
|
||||
module_manager.add_port(progTE_module_id, progTE_wl_port, ModuleManager::MODULE_INPUT_PORT);
|
||||
/* BLB port */
|
||||
BasicPort progTE_blb_port("BLB", 1);
|
||||
module_manager.add_port(progTE_module_id, progTE_blb_port, ModuleManager::MODULE_INPUT_PORT);
|
||||
/* output port */
|
||||
BasicPort progTE_out_port("Z", 1);
|
||||
module_manager.add_port(progTE_module_id, progTE_out_port, ModuleManager::MODULE_INPUT_PORT);
|
||||
|
||||
/* LEFT part: Verilog code generation */
|
||||
/* Iterate over the inputs */
|
||||
for (const auto& mux_input : mux_graph.inputs()) {
|
||||
BasicPort cur_input_port(input_port.get_name(), size_t(mux_graph.input_id(mux_input)), size_t(mux_graph.input_id(mux_input)));
|
||||
/* Iterate over the outputs */
|
||||
for (const auto& mux_output : mux_graph.outputs()) {
|
||||
BasicPort cur_output_port(output_port.get_name(), size_t(mux_graph.output_id(mux_output)), size_t(mux_graph.output_id(mux_output)));
|
||||
/* if there is a connection between the input and output, a tgate will be outputted */
|
||||
std::vector<MuxEdgeId> edges = mux_graph.find_edges(mux_input, mux_output);
|
||||
/* There should be only one edge or no edge*/
|
||||
VTR_ASSERT((1 == edges.size()) || (0 == edges.size()));
|
||||
/* No need to output tgates if there are no edges between two nodes */
|
||||
if (0 == edges.size()) {
|
||||
continue;
|
||||
}
|
||||
/* Create a port-to-port name map */
|
||||
std::map<std::string, BasicPort> port2port_name_map;
|
||||
/* input port */
|
||||
port2port_name_map[progTE_in_port.get_name()] = cur_input_port;
|
||||
/* output port */
|
||||
port2port_name_map[progTE_out_port.get_name()] = cur_output_port;
|
||||
/* Find the mem_id controlling the edge */
|
||||
MuxMemId mux_mem = mux_graph.find_edge_mem(edges[0]);
|
||||
BasicPort cur_blb_port(blb_port.get_name(), size_t(mux_mem), size_t(mux_mem));
|
||||
BasicPort cur_wl_port(wl_port.get_name(), size_t(mux_mem), size_t(mux_mem));
|
||||
/* RRAM configuration port: there should not be any inverted edge in RRAM MUX! */
|
||||
VTR_ASSERT( false == mux_graph.is_edge_use_inv_mem(edges[0]) );
|
||||
/* wire mem to mem of module, and wire mem_inv to mem_inv of module */
|
||||
port2port_name_map[progTE_blb_port.get_name()] = cur_blb_port;
|
||||
port2port_name_map[progTE_wl_port.get_name()] = cur_wl_port;
|
||||
/* Output an instance of the module */
|
||||
print_verilog_module_instance(fp, module_manager, module_id, progTE_module_id, port2port_name_map, circuit_lib.dump_explicit_port_map(circuit_model));
|
||||
/* IMPORTANT: this update MUST be called after the instance outputting!!!!
|
||||
* update the module manager with the relationship between the parent and child modules
|
||||
*/
|
||||
module_manager.add_child_module(module_id, progTE_module_id);
|
||||
}
|
||||
}
|
||||
|
||||
/* Create a module for the progBE and register it in the module manager
|
||||
* Structure of progBE
|
||||
*
|
||||
* +----------+
|
||||
* | |
|
||||
* BLB-->| progBE |<-> out
|
||||
* WL--->| |
|
||||
* +----------+
|
||||
*/
|
||||
ModuleId progBE_module_id = module_manager.add_module(progBE_module_name);
|
||||
/* If there is already such as module inside, we just ned to find the module id */
|
||||
if (ModuleId::INVALID() == progBE_module_id) {
|
||||
progBE_module_id = module_manager.find_module(progBE_module_name);
|
||||
/* We should have a valid id! */
|
||||
VTR_ASSERT(ModuleId::INVALID() != progBE_module_id);
|
||||
}
|
||||
/* Add ports to the module */
|
||||
/* inout port */
|
||||
BasicPort progBE_inout_port("INOUT", 1);
|
||||
module_manager.add_port(progBE_module_id, progBE_inout_port, ModuleManager::MODULE_INOUT_PORT);
|
||||
/* WL port */
|
||||
BasicPort progBE_wl_port("WL", 1);
|
||||
module_manager.add_port(progBE_module_id, progBE_wl_port, ModuleManager::MODULE_INPUT_PORT);
|
||||
/* BLB port */
|
||||
BasicPort progBE_blb_port("BLB", 1);
|
||||
module_manager.add_port(progBE_module_id, progBE_blb_port, ModuleManager::MODULE_INPUT_PORT);
|
||||
|
||||
/* RIGHT part: Verilog code generation */
|
||||
/* Iterate over the outputs */
|
||||
for (const auto& mux_output : mux_graph.outputs()) {
|
||||
BasicPort cur_output_port(output_port.get_name(), size_t(mux_graph.output_id(mux_output)), size_t(mux_graph.output_id(mux_output)));
|
||||
/* Create a port-to-port name map */
|
||||
std::map<std::string, BasicPort> port2port_name_map;
|
||||
/* Wire the output port to the INOUT port */
|
||||
port2port_name_map[progBE_inout_port.get_name()] = cur_output_port;
|
||||
/* Find the mem_id controlling the edge */
|
||||
BasicPort cur_blb_port(blb_port.get_name(), mux_graph.num_memory_bits(), mux_graph.num_memory_bits());
|
||||
BasicPort cur_wl_port(wl_port.get_name(), mux_graph.num_memory_bits(), mux_graph.num_memory_bits());
|
||||
port2port_name_map[progBE_blb_port.get_name()] = cur_blb_port;
|
||||
port2port_name_map[progBE_wl_port.get_name()] = cur_wl_port;
|
||||
/* Output an instance of the module */
|
||||
print_verilog_module_instance(fp, module_manager, module_id, progBE_module_id, port2port_name_map, circuit_lib.dump_explicit_port_map(circuit_model));
|
||||
/* IMPORTANT: this update MUST be called after the instance outputting!!!!
|
||||
* update the module manager with the relationship between the parent and child modules
|
||||
*/
|
||||
module_manager.add_child_module(module_id, progBE_module_id);
|
||||
}
|
||||
}
|
||||
|
||||
/*********************************************************************
|
||||
* Generate behavior-level Verilog codes modeling an branch circuit
|
||||
* for a RRAM-based multiplexer with the given size
|
||||
* Corresponding Schematic is as follows:
|
||||
*
|
||||
* BLB[0] BLB[N]
|
||||
* | |
|
||||
* \|/ \|/
|
||||
* in[0] ---->RRAM[0]-----+
|
||||
* |
|
||||
* BLB[1] |
|
||||
* | |
|
||||
* \|/ |
|
||||
* in[1] ---->RRAM[1]-----+
|
||||
* |-----> out[0]
|
||||
* ...
|
||||
* |
|
||||
* in[N-1] ---->RRAM[N-1]---+
|
||||
* /|\ /|\
|
||||
* | |
|
||||
* BLB[N-1] WL[N]
|
||||
*
|
||||
* Working principle:
|
||||
* 1. Set a RRAM[i]: enable BLB[i] and WL[N]
|
||||
* 2. Reset a RRAM[i]: enable BLB[N] and WL[i]
|
||||
* 3. Operation: disable all BLBs and WLs
|
||||
*
|
||||
* TODO: Elaborate the codes to output the circuit logic
|
||||
* following the mux_graph!
|
||||
*********************************************************************/
|
||||
static
|
||||
void generate_verilog_rram_mux_branch_body_behavioral(std::fstream& fp,
|
||||
const CircuitLibrary& circuit_lib,
|
||||
const CircuitModelId& circuit_model,
|
||||
const BasicPort& input_port,
|
||||
const BasicPort& output_port,
|
||||
const BasicPort& blb_port,
|
||||
const BasicPort& wl_port,
|
||||
const MuxGraph& mux_graph) {
|
||||
/* Make sure we have a valid file handler*/
|
||||
check_file_handler(fp);
|
||||
|
||||
/* Verilog Behavior description for a MUX */
|
||||
print_verilog_comment(fp, std::string("---- Behavioral-level description of RRAM MUX -----"));
|
||||
|
||||
/* Add an internal register for the output */
|
||||
BasicPort outreg_port("out_reg", mux_graph.num_inputs());
|
||||
/* Print the port */
|
||||
fp << "\t" << generate_verilog_port(VERILOG_PORT_REG, outreg_port) << ";" << std::endl;
|
||||
|
||||
/* Print the internal logics */
|
||||
fp << "\t" << "always @(";
|
||||
fp << generate_verilog_port(VERILOG_PORT_CONKT, blb_port) << ";" << std::endl;
|
||||
fp << ", ";
|
||||
fp << generate_verilog_port(VERILOG_PORT_CONKT, wl_port) << ";" << std::endl;
|
||||
fp << ")" << std::endl;
|
||||
fp << "\t" << "begin" << std::endl;
|
||||
|
||||
/* Only when the last bit of wl is enabled,
|
||||
* the propagating path can be changed
|
||||
* (RRAM value can be changed) */
|
||||
fp << "\t\t" << "if (";
|
||||
BasicPort set_enable_port(wl_port.get_name(), wl_port.get_width() - 1, wl_port.get_width() - 1);
|
||||
fp << generate_verilog_port(VERILOG_PORT_CONKT, set_enable_port);
|
||||
/* We need two config-enable ports: prog_EN and prog_ENb */
|
||||
bool find_prog_EN = false;
|
||||
bool find_prog_ENb = false;
|
||||
for (const auto& port : circuit_lib.model_global_ports(circuit_model, true)) {
|
||||
/* Bypass non-config-enable ports */
|
||||
if (false == circuit_lib.port_is_config_enable(port)) {
|
||||
continue;
|
||||
}
|
||||
/* Reach here, the port should be is_config_enable */
|
||||
/* Create a port object */
|
||||
fp << " && ";
|
||||
BasicPort prog_en_port(circuit_lib.port_prefix(port), circuit_lib.port_size(port));
|
||||
if ( 1 == circuit_lib.port_default_value(port)) {
|
||||
/* Default value = 0 means that this is a prog_EN port */
|
||||
fp << generate_verilog_port(VERILOG_PORT_CONKT, prog_en_port);
|
||||
find_prog_EN = true;
|
||||
} else {
|
||||
/* Default value = 1 means that this is a prog_ENb port, add inversion in the if condition */
|
||||
fp << "(~" << generate_verilog_port(VERILOG_PORT_CONKT, prog_en_port) << ")";
|
||||
find_prog_ENb = true;
|
||||
}
|
||||
}
|
||||
/* Check if we find any config_enable signals */
|
||||
if (false == find_prog_EN) {
|
||||
vpr_printf(TIO_MESSAGE_ERROR,
|
||||
"(File:%s,[LINE%d])Unable to find a config_enable signal with default value 0 for a RRAM MUX (%s)!\n",
|
||||
__FILE__, __LINE__, circuit_lib.model_name(circuit_model).c_str());
|
||||
exit(1);
|
||||
}
|
||||
if (false == find_prog_ENb) {
|
||||
vpr_printf(TIO_MESSAGE_ERROR,
|
||||
"(File:%s,[LINE%d])Unable to find a config_enable signal with default value 1 for a RRAM MUX (%s)!\n",
|
||||
__FILE__, __LINE__, circuit_lib.model_name(circuit_model).c_str());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Finish the if clause */
|
||||
fp << ") begin" << std::endl;
|
||||
|
||||
for (const auto& mux_input : mux_graph.inputs()) {
|
||||
fp << "\t\t" << "if (1 == ";
|
||||
/* Create a temp port of a BLB bit */
|
||||
BasicPort cur_blb_port(blb_port.get_name(), size_t(mux_graph.input_id(mux_input)), size_t(mux_graph.input_id(mux_input)));
|
||||
fp << generate_verilog_port(VERILOG_PORT_CONKT, cur_blb_port);
|
||||
fp << ") begin" << std::endl;
|
||||
fp << "\t\t\t" << "assign ";
|
||||
fp << generate_verilog_port(VERILOG_PORT_CONKT, outreg_port);
|
||||
fp << " = " << size_t(mux_graph.input_id(mux_input)) << ";" << std::endl;
|
||||
fp << "\t\t" << "end else " << std::endl;
|
||||
}
|
||||
fp << "\t\t\t" << "begin" << std::endl;
|
||||
fp << "\t\t\t\t" << "assign ";
|
||||
fp << generate_verilog_port(VERILOG_PORT_CONKT, outreg_port);
|
||||
fp << " = 0;" << std::endl;
|
||||
fp << "\t\t\t" << "end" << std::endl;
|
||||
fp << "\t\t" << "end" << std::endl;
|
||||
fp << "\t" << "end" << std::endl;
|
||||
|
||||
fp << "\t" << "assign ";
|
||||
fp << generate_verilog_port(VERILOG_PORT_CONKT, output_port);
|
||||
fp << " = ";
|
||||
fp << input_port.get_name() << "[";
|
||||
fp << generate_verilog_port(VERILOG_PORT_CONKT, outreg_port);
|
||||
fp << "];" << std::endl;
|
||||
}
|
||||
|
||||
/*********************************************************************
|
||||
* Generate Verilog codes modeling an branch circuit
|
||||
* for a RRAM-based multiplexer with the given size
|
||||
* Support structural and behavioral Verilog codes
|
||||
*********************************************************************/
|
||||
static
|
||||
void generate_verilog_rram_mux_branch_module(ModuleManager& module_manager,
|
||||
const CircuitLibrary& circuit_lib,
|
||||
std::fstream& fp,
|
||||
const CircuitModelId& circuit_model,
|
||||
const std::string& module_name,
|
||||
const MuxGraph& mux_graph,
|
||||
const bool& use_structural_verilog) {
|
||||
/* Make sure we have a valid file handler*/
|
||||
check_file_handler(fp);
|
||||
|
||||
/* Generate the Verilog netlist according to the mux_graph */
|
||||
/* Find out the number of inputs */
|
||||
size_t num_inputs = mux_graph.num_inputs();
|
||||
/* Find out the number of outputs */
|
||||
size_t num_outputs = mux_graph.num_outputs();
|
||||
/* Find out the number of memory bits */
|
||||
size_t num_mems = mux_graph.num_memory_bits();
|
||||
|
||||
/* Check codes to ensure the port of Verilog netlists will match */
|
||||
/* MUX graph must have only 1 output */
|
||||
VTR_ASSERT(1 == num_outputs);
|
||||
/* MUX graph must have only 1 level*/
|
||||
VTR_ASSERT(1 == mux_graph.num_levels());
|
||||
|
||||
/* Create a Verilog Module based on the circuit model, and add to module manager */
|
||||
ModuleId module_id = module_manager.add_module(module_name);
|
||||
VTR_ASSERT(ModuleId::INVALID() != module_id);
|
||||
/* Add module ports */
|
||||
/* Add programming enable/disable ports */
|
||||
/* Add each input port */
|
||||
BasicPort input_port("in", num_inputs);
|
||||
module_manager.add_port(module_id, input_port, ModuleManager::MODULE_INPUT_PORT);
|
||||
/* Add each output port */
|
||||
BasicPort output_port("out", num_outputs);
|
||||
module_manager.add_port(module_id, output_port, ModuleManager::MODULE_OUTPUT_PORT);
|
||||
/* Add RRAM programming ports */
|
||||
BasicPort blb_port("bl", num_mems);
|
||||
module_manager.add_port(module_id, blb_port, ModuleManager::MODULE_INPUT_PORT);
|
||||
BasicPort wl_port("wl", num_mems);
|
||||
module_manager.add_port(module_id, wl_port, ModuleManager::MODULE_INPUT_PORT);
|
||||
|
||||
/* dump module definition + ports */
|
||||
print_verilog_module_declaration(fp, module_manager, module_id);
|
||||
|
||||
/* Print the internal logic in either structural or behavioral Verilog codes */
|
||||
if (true == use_structural_verilog) {
|
||||
generate_verilog_rram_mux_branch_body_structural(module_manager, circuit_lib, fp, module_id, circuit_model, input_port, output_port, blb_port, wl_port, mux_graph);
|
||||
} else {
|
||||
generate_verilog_rram_mux_branch_body_behavioral(fp, circuit_lib, circuit_model, input_port, output_port, blb_port, wl_port, mux_graph);
|
||||
}
|
||||
|
||||
/* Put an end to the Verilog module */
|
||||
print_verilog_module_end(fp, module_name);
|
||||
}
|
||||
|
||||
/***********************************************
|
||||
* Generate Verilog codes modeling an branch circuit
|
||||
* for a multiplexer with the given size
|
||||
|
@ -288,18 +666,8 @@ void generate_verilog_mux_branch_module(ModuleManager& module_manager,
|
|||
circuit_lib.dump_structural_verilog(circuit_model));
|
||||
break;
|
||||
case SPICE_MODEL_DESIGN_RRAM:
|
||||
/* If requested, we can dump structural verilog for basis module */
|
||||
/*
|
||||
if (true == circuit_lib.dump_structural_verilog(circuit_model)) {
|
||||
dump_verilog_rram_mux_one_basis_module_structural(fp, mux_basis_subckt_name,
|
||||
num_input_basis_subckt,
|
||||
cur_spice_model);
|
||||
} else {
|
||||
dump_verilog_rram_mux_one_basis_module(fp, mux_basis_subckt_name,
|
||||
num_input_basis_subckt,
|
||||
cur_spice_model);
|
||||
}
|
||||
*/
|
||||
generate_verilog_rram_mux_branch_module(module_manager, circuit_lib, fp, circuit_model, module_name, mux_graph,
|
||||
circuit_lib.dump_structural_verilog(circuit_model));
|
||||
break;
|
||||
default:
|
||||
vpr_printf(TIO_MESSAGE_ERROR,
|
||||
|
@ -307,8 +675,6 @@ void generate_verilog_mux_branch_module(ModuleManager& module_manager,
|
|||
__FILE__, __LINE__, circuit_lib.model_name(circuit_model).c_str());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/***********************************************
|
||||
|
|
Loading…
Reference in New Issue