/***********************************/ /* Synthesizable Verilog Dumping */ /* Xifan TANG, EPFL/LSI */ /***********************************/ #include #include #include #include #include #include #include #include /* Include vpr structs*/ #include "util.h" #include "physical_types.h" #include "vpr_types.h" #include "globals.h" #include "rr_graph.h" #include "vpr_utils.h" #include "path_delay.h" #include "stats.h" /* Include FPGA-SPICE utils */ #include "linkedlist.h" #include "fpga_spice_utils.h" #include "spice_mux.h" #include "fpga_spice_globals.h" /* Include verilog utils */ #include "verilog_global.h" #include "verilog_utils.h" #include "verilog_pbtypes.h" void dump_verilog_submodule_timing(FILE* fp, t_spice_model* cur_spice_model) { int iport, ipin, iedge; int num_input_port; t_spice_model_port** input_port= NULL; input_port = find_spice_model_ports(cur_spice_model, SPICE_MODEL_PORT_INPUT, &num_input_port, TRUE); /* return if there is no delay info */ if ( 0 == cur_spice_model->num_delay_info) { return; } /* Return if there is no input ports */ if (0 == num_input_port) { return; } /* Ensure a valid file handler*/ if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR,"(FILE:%s,LINE[%d])Invalid File handler.\n", __FILE__, __LINE__); exit(1); } fprintf(fp, "\n`ifdef %s\n", verilog_timing_preproc_flag); fprintf(fp, " //------ BEGIN Pin-to-pin Timing constraints -----\n"); fprintf(fp, " specify\n"); /* Give pin-to-pin delays */ /* Enumerate timing edges of each input ports */ for (iport = 0; iport < num_input_port; iport++) { for (ipin = 0; ipin < input_port[iport]->size; ipin++) { for (iedge = 0; iedge < input_port[iport]->num_tedges[ipin]; iedge++) { fprintf(fp, " (%s[%d] => %s[%d]) = (%.2g, %.2g);\n", input_port[iport]->prefix, ipin, input_port[iport]->tedge[ipin][iedge]->to_port->prefix, input_port[iport]->tedge[ipin][iedge]->to_port_pin_number, input_port[iport]->tedge[ipin][iedge]->trise / verilog_sim_timescale, input_port[iport]->tedge[ipin][iedge]->tfall / verilog_sim_timescale); } } } fprintf(fp, " endspecify\n"); fprintf(fp, " //------ END Pin-to-pin Timing constraints -----\n"); fprintf(fp, "`endif\n"); return; } void dump_verilog_submodule_init_sim(FILE* fp, t_spice_model* cur_spice_model) { int iport, ipin; int num_input_port; t_spice_model_port** input_port= NULL; input_port = find_spice_model_ports(cur_spice_model, SPICE_MODEL_PORT_INPUT, &num_input_port, TRUE); /* Ensure a valid file handler*/ if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR,"(FILE:%s,LINE[%d])Invalid File handler.\n", __FILE__, __LINE__); exit(1); } fprintf(fp, "\n`ifdef %s\n", verilog_init_sim_preproc_flag); fprintf(fp, " //------ BEGIN driver initialization -----\n"); fprintf(fp, "initial begin\n"); for (iport = 0; iport < num_input_port; iport++) { fprintf(fp, " $signal_force(\"%s\", %d, 0, 1, , 1);\n", input_port[iport]->prefix, input_port[iport]->default_val); } fprintf(fp, "end\n"); fprintf(fp, " //------ END driver initialization -----\n"); fprintf(fp, "`endif\n"); return; } /***** Subroutines *****/ /* Dump a module of inverter or buffer or tapered buffer */ void dump_verilog_invbuf_module(FILE* fp, t_spice_model* invbuf_spice_model, boolean include_timing, boolean init_sim) { int ipin, iport, port_cnt; int num_input_port = 0; int num_output_port = 0; int num_powergate_port = 0; t_spice_model_port** input_port = NULL; t_spice_model_port** output_port = NULL; t_spice_model_port** powergate_port = NULL; /* Ensure a valid file handler*/ if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR,"(FILE:%s,LINE[%d])Invalid File handler.\n", __FILE__, __LINE__); exit(1); } fprintf(fp, "//----- Verilog module for %s -----\n", invbuf_spice_model->name); /* Find the input port, output port*/ input_port = find_spice_model_ports(invbuf_spice_model, SPICE_MODEL_PORT_INPUT, &num_input_port, TRUE); output_port = find_spice_model_ports(invbuf_spice_model, SPICE_MODEL_PORT_OUTPUT, &num_output_port, TRUE); powergate_port = find_spice_model_config_done_ports(invbuf_spice_model, SPICE_MODEL_PORT_INPUT, &num_powergate_port, FALSE); /* Make sure: * There is only 1 input port and 1 output port, * each size of which is 1 */ assert(1 == num_input_port); assert(1 == input_port[0]->size); assert(1 == num_output_port); assert(1 == output_port[0]->size); /* If power-gated, we need to find enable signals */ if (TRUE == invbuf_spice_model->design_tech_info.power_gated) { if (0 == num_powergate_port) { vpr_printf(TIO_MESSAGE_ERROR,"(FILE:%s,LINE[%d])Inverter, buffer SPICE model is power-gated, but cannot find any power-gate port!\n", __FILE__, __LINE__); exit(1); } assert ( 0 < num_powergate_port); } /* dump module body */ fprintf(fp, "module %s (\n", invbuf_spice_model->name); /* Dump global ports */ if (0 < rec_dump_verilog_spice_model_global_ports(fp, invbuf_spice_model, TRUE, FALSE)) { fprintf(fp, ",\n"); } /* Dump ports */ fprintf(fp, " input [0:0] %s,\n", input_port[0]->prefix); fprintf(fp, " output [0:0] %s\n", output_port[0]->prefix); fprintf(fp, " );\n"); /* Finish dumping ports */ /* Assign logics : depending on topology */ switch (invbuf_spice_model->design_tech_info.buffer_info->type) { case SPICE_MODEL_BUF_INV: if (TRUE == invbuf_spice_model->design_tech_info.power_gated) { /* Create a sensitive list */ fprintf(fp, "reg %s_reg;\n", output_port[0]->prefix); fprintf(fp, "always @("); /* Power-gate port first*/ for (iport = 0; iport < num_powergate_port; iport++) { fprintf(fp, "%s,", powergate_port[iport]->prefix); } fprintf(fp, "%s) begin\n", input_port[0]->prefix); /* Dump the case of power-gated */ fprintf(fp, " if ("); port_cnt = 0; /* Initialize the counter: decide if we need to put down '&&' */ for (iport = 0; iport < num_powergate_port; iport++) { if (0 == powergate_port[iport]->default_val) { for (ipin = 0; ipin < powergate_port[iport]->size; ipin++) { if ( 0 < port_cnt ) { fprintf(fp, "\n\t&&"); } /* Power-gated signal are disable during operating, enabled during configuration, * Therefore, we need to reverse them here */ fprintf(fp, "(~%s[%d])", powergate_port[iport]->prefix, ipin); port_cnt++; /* Update port counter*/ } } else { assert (1 == powergate_port[iport]->default_val); for (ipin = 0; ipin < powergate_port[iport]->size; ipin++) { if ( 0 < port_cnt ) { fprintf(fp, "\n\t&&"); } /* Power-gated signal are disable during operating, enabled during configuration, * Therefore, we need to reverse them here */ fprintf(fp, "(%s[%d])", powergate_port[iport]->prefix, ipin); port_cnt++; /* Update port counter*/ } } } fprintf(fp, ") begin\n"); fprintf(fp, "\t\tassign %s_reg = ~%s;\n", output_port[0]->prefix, input_port[0]->prefix); fprintf(fp, "\tend else begin\n"); fprintf(fp, "\t\tassign %s_reg = 1'bz;\n", output_port[0]->prefix); fprintf(fp, "\tend\n"); fprintf(fp, "end\n"); fprintf(fp, "assign %s = %s_reg;\n", output_port[0]->prefix, output_port[0]->prefix); } else { fprintf(fp, "assign %s = ~%s;\n", output_port[0]->prefix, input_port[0]->prefix); } break; case SPICE_MODEL_BUF_BUF: if (TRUE == invbuf_spice_model->design_tech_info.power_gated) { /* Create a sensitive list */ fprintf(fp, "reg %s_reg;\n", output_port[0]->prefix); fprintf(fp, "always @("); /* Power-gate port first*/ for (iport = 0; iport < num_powergate_port; iport++) { fprintf(fp, "%s,", powergate_port[iport]->prefix); } fprintf(fp, "%s) begin\n", input_port[0]->prefix); /* Dump the case of power-gated */ fprintf(fp, " if ("); port_cnt = 0; /* Initialize the counter: decide if we need to put down '&&' */ for (iport = 0; iport < num_powergate_port; iport++) { if (0 == powergate_port[iport]->default_val) { for (ipin = 0; ipin < powergate_port[iport]->size; ipin++) { if ( 0 < port_cnt ) { fprintf(fp, "\n\t&&"); } /* Power-gated signal are disable during operating, enabled during configuration, * Therefore, we need to reverse them here */ fprintf(fp, "(~%s[%d])", powergate_port[iport]->prefix, ipin); port_cnt++; /* Update port counter*/ } } else { assert (1 == powergate_port[iport]->default_val); for (ipin = 0; ipin < powergate_port[iport]->size; ipin++) { if ( 0 < port_cnt ) { fprintf(fp, "\n\t&&"); } /* Power-gated signal are disable during operating, enabled during configuration, * Therefore, we need to reverse them here */ fprintf(fp, "(%s[%d])", powergate_port[iport]->prefix, ipin); port_cnt++; /* Update port counter*/ } } } fprintf(fp, ") begin\n"); fprintf(fp, "\t\tassign %s_reg = %s;\n", output_port[0]->prefix, input_port[0]->prefix); fprintf(fp, "\tend else begin\n"); fprintf(fp, "\t\tassign %s_reg = 1'bz;\n", output_port[0]->prefix); fprintf(fp, "\tend\n"); fprintf(fp, "end\n"); fprintf(fp, "assign %s = %s_reg;\n", output_port[0]->prefix, output_port[0]->prefix); } else if (FALSE == invbuf_spice_model->design_tech_info.buffer_info->tapered_buf) { fprintf(fp, "assign %s = %s;\n", output_port[0]->prefix, input_port[0]->prefix); } else { assert (TRUE == invbuf_spice_model->design_tech_info.buffer_info->tapered_buf); fprintf(fp, "assign %s = ", output_port[0]->prefix); /* depend on the stage, we may invert the output */ if (1 == invbuf_spice_model->design_tech_info.buffer_info->tap_buf_level % 2) { fprintf(fp, "~"); } fprintf(fp, "%s;\n", input_port[0]->prefix); } break; default: vpr_printf(TIO_MESSAGE_ERROR,"(File:%s,[LINE%d])Invalid topology for spice model (%s)!\n", __FILE__, __LINE__, invbuf_spice_model->name); exit(1); } /* Print timing info */ if (TRUE == include_timing) { dump_verilog_submodule_timing(fp, invbuf_spice_model); } /* Print simulation initialization info */ if (TRUE == init_sim) { dump_verilog_submodule_init_sim(fp, invbuf_spice_model); } fprintf(fp, "endmodule\n"); fprintf(fp, "\n"); /* Free */ my_free(input_port); my_free(output_port); return; } /* Dump a module of pass-gate logic */ void dump_verilog_passgate_module(FILE* fp, t_spice_model* passgate_spice_model, boolean include_timing) { int iport; int num_input_port = 0; int num_output_port = 0; t_spice_model_port** input_port = NULL; t_spice_model_port** output_port = NULL; /* Ensure a valid file handler*/ if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR,"(FILE:%s,LINE[%d])Invalid File handler.\n", __FILE__, __LINE__); exit(1); } /* Find the input port, output port*/ input_port = find_spice_model_ports(passgate_spice_model, SPICE_MODEL_PORT_INPUT, &num_input_port, TRUE); output_port = find_spice_model_ports(passgate_spice_model, SPICE_MODEL_PORT_OUTPUT, &num_output_port, TRUE); /* Make sure: * There is only 1 output port, * each size of which is 1 */ assert(1 == num_output_port); assert(1 == output_port[0]->size); fprintf(fp, "//----- Verilog module for %s -----\n", passgate_spice_model->name); /* dump module body */ fprintf(fp, "module %s (\n", passgate_spice_model->name); /* Assign ports : depending on topology */ switch (passgate_spice_model->design_tech_info.pass_gate_info->type) { case SPICE_MODEL_PASS_GATE_TRANSMISSION: /* Make sure: * There is only 3 input port (in, sel, selb), * each size of which is 1 */ assert(3 == num_input_port); for (iport = 0; iport < num_input_port; iport++) { assert(1 == input_port[iport]->size); } /* Dump ports */ fprintf(fp, " input [0:0] in,\n"); fprintf(fp, " input [0:0] sel,\n"); fprintf(fp, " input [0:0] selb,\n"); fprintf(fp, " output [0:0] %s\n", output_port[0]->prefix); fprintf(fp, " );\n"); /* Finish dumping ports */ break; case SPICE_MODEL_PASS_GATE_TRANSISTOR: /* Make sure: * There is only 2 input port (in, sel), * each size of which is 1 */ assert(2 == num_input_port); for (iport = 0; iport < num_input_port; iport++) { assert(1 == input_port[iport]->size); } /* Dump ports */ fprintf(fp, " input [0:0] in,\n"); fprintf(fp, " input [0:0] sel,\n"); fprintf(fp, " output [0:0] %s\n", output_port[0]->prefix); fprintf(fp, " );\n"); /* Finish dumping ports */ break; default: vpr_printf(TIO_MESSAGE_ERROR,"(File:%s,[LINE%d])Invalid topology for spice model (%s)!\n", __FILE__, __LINE__, passgate_spice_model->name); exit(1); } /* Dump logics */ fprintf(fp, "assign %s = sel? in : 1'bz;\n", output_port[0]->prefix); /* Print timing info */ if (TRUE == include_timing) { dump_verilog_submodule_timing(fp, passgate_spice_model); } fprintf(fp, "endmodule\n"); fprintf(fp, "\n"); /* Free */ my_free(input_port); my_free(output_port); return; } /* Dump Essential modules: * 1. inverters * 2. buffers * 3. pass-gate logics */ void dump_verilog_submodule_essentials(char* submodule_dir, int num_spice_model, t_spice_model* spice_models, boolean include_timing, boolean init_sim) { int imodel; char* verilog_name = my_strcat(submodule_dir, essentials_verilog_file_name); FILE* fp = NULL; /* Create file */ fp = fopen(verilog_name, "w"); if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR,"(FILE:%s,LINE[%d])Failure in create Verilog netlist %s", __FILE__, __LINE__, essentials_verilog_file_name); exit(1); } dump_verilog_file_header(fp,"Essential gates"); dump_verilog_preproc(fp, include_timing); /* Output essential models*/ for (imodel = 0; imodel < num_spice_model; imodel++) { /* By pass user-defined modules */ if (NULL != spice_models[imodel].verilog_netlist) { continue; } if (SPICE_MODEL_INVBUF == spice_models[imodel].type) { dump_verilog_invbuf_module(fp, &(spice_models[imodel]), include_timing, init_sim); } if (SPICE_MODEL_PASSGATE == spice_models[imodel].type) { dump_verilog_passgate_module(fp, &(spice_models[imodel]), include_timing); } } /* Close file handler*/ fclose(fp); /* Add fname to the linked list */ submodule_verilog_subckt_file_path_head = add_one_subckt_file_name_to_llist(submodule_verilog_subckt_file_path_head, verilog_name); /* Free */ return; } /* Dump a CMOS MUX basis module */ void dump_verilog_cmos_mux_one_basis_module(FILE* fp, char* mux_basis_subckt_name, int mux_size, int num_input_basis_subckt, t_spice_model* cur_spice_model, boolean special_basis) { int cur_mem, i; int num_mem = num_input_basis_subckt; /* Make sure we have a valid file handler*/ if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR,"(FILE:%s,LINE[%d])Invalid file handler!\n",__FILE__, __LINE__); exit(1); } /* Determine the number of memory bit * The function considers a special case : * 2-input basis in tree-like MUX only requires 1 memory bit */ num_mem = determine_num_sram_bits_mux_basis_subckt(cur_spice_model, mux_size, num_input_basis_subckt, special_basis); /* Comment lines */ fprintf(fp, "//---- CMOS MUX basis module: %s -----\n", mux_basis_subckt_name); /* Print the port list and definition */ fprintf(fp, "module %s (\n", mux_basis_subckt_name); /* Dump global ports */ if (0 < rec_dump_verilog_spice_model_global_ports(fp, cur_spice_model, TRUE, FALSE)) { fprintf(fp, ",\n"); } /* Port list */ fprintf(fp, " input [0:%d] in,\n", num_input_basis_subckt - 1); fprintf(fp, " output out,\n"); fprintf(fp, " input [0:%d] mem,\n", num_mem - 1); fprintf(fp, " input [0:%d] mem_inv);\n", num_mem - 1); /* Verilog Behavior description for a MUX */ fprintf(fp, "//---- Behavior-level description -----\n"); /* Special case: only one memory, switch case is simpler * When mem = 1, propagate input 0; * when mem = 0, propagate input 1; */ if (1 == num_mem) { fprintf(fp, " reg out_reg;\n"); fprintf(fp, " always @(in, mem)\n"); fprintf(fp, " case (mem)\n"); fprintf(fp, " 1'b1: out_reg = in[0];\n"); fprintf(fp, " 1'b0: out_reg = in[1];\n"); fprintf(fp, " default: out_reg <= 1'bz;\n"); fprintf(fp, " endcase\n"); fprintf(fp, " assign out = out_reg;\n"); } else { /* Other cases, we need to follow the rules: * When mem[k] is enabled, switch on input[k] * Only one memory bit is enabled! */ fprintf(fp, " reg out_reg;\n"); fprintf(fp, " always @(in, mem)\n"); fprintf(fp, " case (mem)\n"); fprintf(fp, "//---- Note that MSB is mem[0] while LSB is mem[%d] -----\n", num_mem-1); fprintf(fp, "//---- Due to the delcare convention of port [MSB:LSB] -----\n"); for (cur_mem = 0; cur_mem < num_mem; cur_mem++) { fprintf(fp, " %d'b", num_mem); for (i = 0; i < cur_mem; i++) { fprintf(fp, "0"); } fprintf(fp, "1"); for (i = cur_mem + 1; i < num_mem; i++) { fprintf(fp, "0"); } fprintf(fp, ":"); fprintf(fp, " out_reg <= in[%d];\n", cur_mem); } fprintf(fp, " default: out_reg <= 1'bz;\n"); fprintf(fp, " endcase\n"); fprintf(fp, " assign out = out_reg;\n"); } /* Put an end to this module */ fprintf(fp, "endmodule\n"); /* Comment lines */ fprintf(fp, "//---- END CMOS MUX basis module: %s -----\n\n", mux_basis_subckt_name); return; } /* Dump a structural verilog for SRAM-based MUX basis module * This is only called when structural verilog dumping option is enabled for this spice model * Note that the structural verilog may be used for functionality verification!!! */ static void dump_verilog_cmos_mux_one_basis_module_structural(FILE* fp, char* mux_basis_subckt_name, int mux_size, int num_input_basis_subckt, t_spice_model* cur_spice_model, boolean special_basis) { int i; int num_mem = num_input_basis_subckt; /* Get the tgate module name */ char* tgate_module_name = cur_spice_model->pass_gate_logic->spice_model_name; assert(TRUE == cur_spice_model->dump_structural_verilog); /* Make sure we have a valid file handler*/ if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR,"(FILE:%s,LINE[%d])Invalid file handler!\n",__FILE__, __LINE__); exit(1); } /* Determine the number of memory bit * The function considers a special case : * 2-input basis in tree-like MUX only requires 1 memory bit */ num_mem = determine_num_sram_bits_mux_basis_subckt(cur_spice_model, mux_size, num_input_basis_subckt, special_basis); /* Comment lines */ fprintf(fp, "//---- Structural Verilog for CMOS MUX basis module: %s -----\n", mux_basis_subckt_name); /* Print the port list and definition */ fprintf(fp, "module %s (\n", mux_basis_subckt_name); /* Dump global ports */ if (0 < rec_dump_verilog_spice_model_global_ports(fp, cur_spice_model, TRUE, FALSE)) { fprintf(fp, ",\n"); } /* Port list */ fprintf(fp, " input [0:%d] in,\n", num_input_basis_subckt - 1); fprintf(fp, " output out,\n"); fprintf(fp, " input [0:%d] mem,\n", num_mem - 1); fprintf(fp, " input [0:%d] mem_inv);\n", num_mem - 1); /* Verilog Behavior description for a MUX */ fprintf(fp, "//---- Structure-level description -----\n"); /* Special case: only one memory, switch case is simpler * When mem = 1, propagate input 0; * when mem = 0, propagate input 1; */ if (1 == num_mem) { /* Transmission gates are connected to each input and also the output*/ fprintf(fp, " %s %s_0 (in[0], mem[0], mem_inv[0], out);\n", tgate_module_name, tgate_module_name); fprintf(fp, " %s %s_1 (in[1], mem_inv[0], mem[0], out);\n", tgate_module_name, tgate_module_name); } else { /* Other cases, we need to follow the rules: * When mem[k] is enabled, switch on input[k] * Only one memory bit is enabled! */ for (i = 0; i < num_mem; i++) { fprintf(fp, " %s %s_%d (in[%d], mem[%d], mem_inv[%d], out);\n", tgate_module_name, tgate_module_name, i, i, i, i); } } /* Put an end to this module */ fprintf(fp, "endmodule\n"); /* Comment lines */ fprintf(fp, "//---- END Structural Verilog CMOS MUX basis module: %s -----\n\n", mux_basis_subckt_name); return; } /* Dump a structural verilog for RRAM MUX basis module * This is only called when structural verilog dumping option is enabled for this spice model * Note that the structural verilog cannot be used for functionality verification!!! */ static void dump_verilog_rram_mux_one_basis_module_structural(FILE* fp, char* mux_basis_subckt_name, int num_input_basis_subckt, t_spice_model* cur_spice_model) { /* RRAM MUX needs 2*(input_size + 1) memory bits for configuration purpose */ int num_mem = num_input_basis_subckt + 1; int i; char* progTE_module_name = "PROG_TE"; char* progBE_module_name = "PROG_BE"; /* Make sure we have a valid file handler*/ if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR,"(FILE:%s,LINE[%d])Invalid file handler!\n",__FILE__, __LINE__); exit(1); } assert(TRUE == cur_spice_model->dump_structural_verilog); /* Comment lines */ fprintf(fp, "//---- Structural Verilog for RRAM MUX basis module: %s -----\n", mux_basis_subckt_name); /* Print the port list and definition */ fprintf(fp, "module %s (\n", mux_basis_subckt_name); /* Dump global ports */ if (0 < rec_dump_verilog_spice_model_global_ports(fp, cur_spice_model, TRUE, FALSE)) { fprintf(fp, ",\n"); } /* Port list */ fprintf(fp, " input wire [0:%d] in,\n", num_input_basis_subckt - 1); fprintf(fp, " output wire out,\n"); fprintf(fp, " input wire [0:%d] bl,\n", num_mem - 1); fprintf(fp, " input wire [0:%d] wl);\n", num_mem - 1); /* 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 */ /* LEFT part */ for (i = 0; i < num_input_basis_subckt - 1; i++) { fprintf(fp, "%s %s_%d (in[%d], wl[%d], bl[%d], out);\n", progTE_module_name, progTE_module_name, i, i, i, i); } /* RIGHT part */ fprintf(fp, "%s %s_%d (out, wl[%d], bl[%d]);\n", progBE_module_name, progBE_module_name, i, i, i); /* Put an end to this module */ fprintf(fp, "endmodule\n"); /* Comment lines */ fprintf(fp, "//---- END Structural Verilog for RRAM MUX basis module: %s -----\n\n", mux_basis_subckt_name); return; } /* Dump a RRAM MUX basis module */ void dump_verilog_rram_mux_one_basis_module(FILE* fp, char* mux_basis_subckt_name, int num_input_basis_subckt, t_spice_model* cur_spice_model) { /* RRAM MUX needs 2*(input_size + 1) memory bits for configuration purpose */ int num_mem = num_input_basis_subckt + 1; int i, iport, ipin; int find_prog_EN = 0; int find_prog_ENb = 0; /* Make sure we have a valid file handler*/ if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR,"(FILE:%s,LINE[%d])Invalid file handler!\n",__FILE__, __LINE__); exit(1); } /* Comment lines */ fprintf(fp, "//---- RRAM MUX basis module: %s -----\n", mux_basis_subckt_name); /* Print the port list and definition */ fprintf(fp, "module %s (\n", mux_basis_subckt_name); /* Dump global ports */ if (0 < rec_dump_verilog_spice_model_global_ports(fp, cur_spice_model, TRUE, FALSE)) { fprintf(fp, ",\n"); } /* Port list */ fprintf(fp, " input wire [0:%d] in,\n", num_input_basis_subckt - 1); fprintf(fp, " output wire out,\n"); fprintf(fp, " input wire [0:%d] bl,\n", num_mem - 1); fprintf(fp, " input wire [0:%d] wl);\n", num_mem - 1); /* Print the internal logics: * ONLY 4T1R programming structure is supported up to now */ fprintf(fp, "reg [0:%d] reg_out;\n", num_input_basis_subckt - 1); fprintf(fp, "always @("); for (i = 0; i < num_mem; i++) { if (0 < i) { fprintf(fp, ","); } fprintf(fp, "wl[%d], bl[%d] ", i, i); } fprintf(fp, ")\n"); fprintf(fp, "begin \n"); /* Only when the last bit of wl is enabled, * the propagating path can be changed * (RRAM value can be changed) */ fprintf(fp, "\tif ((wl[%d])", num_mem - 1); /* Find the config_enable ports (prog_EN and prog_ENb) * in global ports*/ for (iport = 0; iport < cur_spice_model->num_port; iport++) { if (FALSE == cur_spice_model->ports[iport].is_config_enable) { continue; } /* Reach here, the port should be is_config_enable */ if (0 == cur_spice_model->ports[iport].default_val) { for (ipin = 0; ipin < cur_spice_model->ports[iport].size; ipin++) { fprintf(fp, "\n\t&&(%s[%d])", cur_spice_model->ports[iport].prefix, ipin); } /* Update counter */ find_prog_EN++; } else { assert (1 == cur_spice_model->ports[iport].default_val); for (ipin = 0; ipin < cur_spice_model->ports[iport].size; ipin++) { fprintf(fp, "\n\t&&(~%s[%d])", cur_spice_model->ports[iport].prefix, ipin); } /* Update counter */ find_prog_ENb++; } } /* Check if we find any config_enable signals */ if (0 == 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__, cur_spice_model->name); exit(1); } if (0 == 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__, cur_spice_model->name); exit(1); } /* Finish the if clause */ fprintf(fp, ") begin\n"); for (i = 0; i < num_input_basis_subckt; i++) { fprintf(fp, "\tif (1 == bl[%d]) begin\n", i); fprintf(fp, "\t\tassign reg_out = %d;\n",i); fprintf(fp, "\tend else "); } fprintf(fp, "\tbegin\n"); fprintf(fp, "\t\t\tassign reg_out = 0;\n"); fprintf(fp, "\t\tend\n"); fprintf(fp, "\tend\n"); fprintf(fp, "end\n"); fprintf(fp, "assign out = in[reg_out];\n"); /* Put an end to this module */ fprintf(fp, "endmodule\n"); /* Comment lines */ fprintf(fp, "//---- END RRAM MUX basis module: %s -----\n\n", mux_basis_subckt_name); return; } /* Print a basis submodule */ void dump_verilog_mux_one_basis_module(FILE* fp, char* mux_basis_subckt_name, int mux_size, int num_input_basis_subckt, t_spice_model* cur_spice_model, boolean special_basis) { /* Make sure we have a valid file handler*/ if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR,"(FILE:%s,LINE[%d])Invalid file handler!\n",__FILE__, __LINE__); exit(1); } /* Depend on the technology */ switch (cur_spice_model->design_tech) { case SPICE_MODEL_DESIGN_CMOS: if (TRUE == cur_spice_model->dump_structural_verilog) { dump_verilog_cmos_mux_one_basis_module_structural(fp, mux_basis_subckt_name, mux_size, num_input_basis_subckt, cur_spice_model, special_basis); } else { dump_verilog_cmos_mux_one_basis_module(fp, mux_basis_subckt_name, mux_size, num_input_basis_subckt, cur_spice_model, special_basis); } break; case SPICE_MODEL_DESIGN_RRAM: /* If requested, we can dump structural verilog for basis module */ if (TRUE == cur_spice_model->dump_structural_verilog) { 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); } break; default: vpr_printf(TIO_MESSAGE_ERROR,"(FILE:%s,LINE[%d])Invalid design_technology of MUX(name: %s)\n", __FILE__, __LINE__, cur_spice_model->name); exit(1); } return; } /** * Dump a verilog module for the basis circuit of a MUX */ void dump_verilog_mux_basis_module(FILE* fp, t_spice_mux_model* spice_mux_model) { /** Act depends on the structure of MUX * 1. tree-like/one-level: we generate a basis module * 2. two/multi-level: we generate a basis and a special module (if required) */ int num_input_basis_subckt = 0; int num_input_special_basis_subckt = 0; char* mux_basis_subckt_name = NULL; char* special_basis_subckt_name = NULL; /* Make sure we have a valid file handler*/ if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR,"(FILE:%s,LINE[%d])Invalid file handler!\n",__FILE__, __LINE__); exit(1); } /* Try to find a mux in cmos technology, * if we have, then build CMOS 2:1 MUX, and given cmos_mux2to1_subckt_name */ /* Exception: LUT require an auto-generation of netlist can run as well*/ assert((SPICE_MODEL_MUX == spice_mux_model->spice_model->type) ||(SPICE_MODEL_LUT == spice_mux_model->spice_model->type)); /* Generate the spice_mux_arch */ spice_mux_model->spice_mux_arch = (t_spice_mux_arch*)my_malloc(sizeof(t_spice_mux_arch)); init_spice_mux_arch(spice_mux_model->spice_model, spice_mux_model->spice_mux_arch, spice_mux_model->size); /* Corner case: Error out MUX_SIZE = 2, automatcially give a one-level structure */ /* if ((2 == spice_mux_model->size)&&(SPICE_MODEL_STRUCTURE_ONELEVEL != spice_mux_model->spice_model->design_tech_info.structure)) { vpr_printf(TIO_MESSAGE_ERROR, "(File:%s,[LINE%d])Structure of SPICE model (%s) should be one-level because it is linked to a 2:1 MUX!\n", __FILE__, __LINE__, spice_mux_model->spice_model->name); exit(1); } */ /* Prepare the basis subckt name: */ mux_basis_subckt_name = (char*)my_malloc(sizeof(char)*(strlen(spice_mux_model->spice_model->name) + 5 + strlen(my_itoa(spice_mux_model->size)) + strlen(verilog_mux_basis_posfix) + 1)); sprintf(mux_basis_subckt_name, "%s_size%d%s", spice_mux_model->spice_model->name, spice_mux_model->size, verilog_mux_basis_posfix); special_basis_subckt_name = (char*)my_malloc(sizeof(char)*(strlen(spice_mux_model->spice_model->name) + 5 + strlen(my_itoa(spice_mux_model->size)) + strlen(verilog_mux_special_basis_posfix) + 1)); sprintf(special_basis_subckt_name, "%s_size%d%s", spice_mux_model->spice_model->name, spice_mux_model->size, verilog_mux_special_basis_posfix); /* deteremine the number of inputs of basis subckt */ num_input_basis_subckt = spice_mux_model->spice_mux_arch->num_input_basis; /* Print the basis subckt*/ dump_verilog_mux_one_basis_module(fp, mux_basis_subckt_name, spice_mux_model->size, num_input_basis_subckt, spice_mux_model->spice_model, FALSE); /* See if we need a special basis */ switch (spice_mux_model->spice_model->design_tech_info.structure) { case SPICE_MODEL_STRUCTURE_TREE: case SPICE_MODEL_STRUCTURE_ONELEVEL: break; case SPICE_MODEL_STRUCTURE_MULTILEVEL: num_input_special_basis_subckt = find_spice_mux_arch_special_basis_size(*(spice_mux_model->spice_mux_arch)); if (0 < num_input_special_basis_subckt) { dump_verilog_mux_one_basis_module(fp, special_basis_subckt_name, spice_mux_model->size, num_input_special_basis_subckt, spice_mux_model->spice_model, FALSE); } break; default: vpr_printf(TIO_MESSAGE_ERROR,"(File:%s,[LINE%d])Invalid structure for spice model (%s)!\n", __FILE__, __LINE__, spice_mux_model->spice_model->name); exit(1); } /* Free */ my_free(mux_basis_subckt_name); my_free(special_basis_subckt_name); return; } void dump_verilog_cmos_mux_tree_structure(FILE* fp, char* mux_basis_subckt_name, t_spice_model spice_model, t_spice_mux_arch spice_mux_arch, int num_sram_port, t_spice_model_port** sram_port) { int i, j, level, nextlevel; int nextj, out_idx; int mux_basis_cnt = 0; /* Make sure we have a valid file handler*/ if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR,"(FILE:%s,LINE[%d])Invalid file handler!\n",__FILE__, __LINE__); exit(1); } mux_basis_cnt = 0; for (i = 0; i < spice_mux_arch.num_level; i++) { level = spice_mux_arch.num_level - i; nextlevel = spice_mux_arch.num_level - i - 1; /* Check */ assert(nextlevel > -1); fprintf(fp, "wire [%d:%d] mux2_l%d_in; \n", 0, spice_mux_arch.num_input_per_level[nextlevel] -1, /* input0 input1 */ level); } fprintf(fp, "wire [%d:%d] mux2_l%d_in; \n", 0, 0, 0); for (i = 0; i < spice_mux_arch.num_level; i++) { level = spice_mux_arch.num_level - i; nextlevel = spice_mux_arch.num_level - i - 1; /* Check */ assert(nextlevel > -1); /* Print basis mux2to1 for each level*/ for (j = 0; j < spice_mux_arch.num_input_per_level[nextlevel]; j++) { nextj = j + 1; out_idx = j/2; /* Each basis mux2to1: svdd sgnd */ fprintf(fp, "%s mux_basis_no%d (", mux_basis_subckt_name, mux_basis_cnt); /* given_name */ /* Dump global ports */ if (0 < rec_dump_verilog_spice_model_global_ports(fp, &spice_model, FALSE, FALSE)) { fprintf(fp, ",\n"); } fprintf(fp, "mux2_l%d_in[%d:%d], ", level, j, nextj); /* input0 input1 */ fprintf(fp, "mux2_l%d_in[%d], ", nextlevel, out_idx); /* output */ fprintf(fp, "%s[%d], %s_inv[%d]);\n", sram_port[0]->prefix, i, sram_port[0]->prefix, i); /* sram sram_inv */ /* Update the counter */ j = nextj; mux_basis_cnt++; } } /* Assert */ assert(0 == nextlevel); assert(0 == out_idx); assert(mux_basis_cnt == spice_mux_arch.num_input - 1); return; } void dump_verilog_cmos_mux_multilevel_structure(FILE* fp, char* mux_basis_subckt_name, char* mux_special_basis_subckt_name, t_spice_model spice_model, t_spice_mux_arch spice_mux_arch, int num_sram_port, t_spice_model_port** sram_port) { int i, j, level, nextlevel, sram_idx; int out_idx; int mux_basis_cnt = 0; int special_basis_cnt = 0; int cur_num_input_basis = 0; /* Make sure we have a valid file handler*/ if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR,"(FILE:%s,LINE[%d])Invalid file handler!\n",__FILE__, __LINE__); exit(1); } mux_basis_cnt = 0; assert((2 == spice_mux_arch.num_input_basis)||(2 < spice_mux_arch.num_input_basis)); for (i = 0; i < spice_mux_arch.num_level; i++) { level = spice_mux_arch.num_level - i; nextlevel = spice_mux_arch.num_level - i - 1; sram_idx = nextlevel * spice_mux_arch.num_input_basis; /* Check */ assert(nextlevel > -1); fprintf(fp, "wire [%d:%d] mux2_l%d_in; \n", 0, spice_mux_arch.num_input_per_level[nextlevel] -1, /* input0 input1 */ level); } fprintf(fp, "wire [%d:%d] mux2_l%d_in; \n", 0, 0, 0); for (i = 0; i < spice_mux_arch.num_level; i++) { level = spice_mux_arch.num_level - i; nextlevel = spice_mux_arch.num_level - i - 1; sram_idx = nextlevel * spice_mux_arch.num_input_basis; /* Check */ assert(nextlevel > -1); /* Print basis muxQto1 for each level*/ for (j = 0; j < spice_mux_arch.num_input_per_level[nextlevel]; j = j+cur_num_input_basis) { /* output index */ out_idx = j/spice_mux_arch.num_input_basis; /* Determine the number of input of this basis */ cur_num_input_basis = spice_mux_arch.num_input_basis; if ((j + cur_num_input_basis) > spice_mux_arch.num_input_per_level[nextlevel]) { cur_num_input_basis = find_spice_mux_arch_special_basis_size(spice_mux_arch); if (0 < cur_num_input_basis) { /* Print the special basis */ fprintf(fp, "%s special_basis(", mux_special_basis_subckt_name); /* Dump global ports */ if (0 < rec_dump_verilog_spice_model_global_ports(fp, &spice_model, FALSE, FALSE)) { fprintf(fp, ",\n"); } fprintf(fp, "mux2_l%d_in[%d:%d], ", level, j, j + cur_num_input_basis - 1); /* input0 input1 */ fprintf(fp, "mux2_l%d_in[%d], ", nextlevel, out_idx); /* output */ fprintf(fp, "%s[%d:%d], %s_inv[%d:%d] ", sram_port[0]->prefix, sram_idx, sram_idx + cur_num_input_basis -1, sram_port[0]->prefix, sram_idx, sram_idx + cur_num_input_basis -1); fprintf(fp, ");\n"); special_basis_cnt++; } continue; } /* Each basis muxQto1: svdd sgnd */ fprintf(fp, "%s ", mux_basis_subckt_name); /* subckt_name */ fprintf(fp, "mux_basis_no%d (", mux_basis_cnt); /* given_name */ /* Dump global ports */ if (0 < rec_dump_verilog_spice_model_global_ports(fp, &spice_model, FALSE, FALSE)) { fprintf(fp, ",\n"); } fprintf(fp, "mux2_l%d_in[%d:%d], ", level, j, j + cur_num_input_basis - 1); /* input0 input1 */ fprintf(fp, "mux2_l%d_in[%d], ", nextlevel, out_idx); /* output */ /* Print number of sram bits for this basis */ fprintf(fp, "%s[%d:%d], %s_inv[%d:%d] ", sram_port[0]->prefix, sram_idx, sram_idx + cur_num_input_basis -1, sram_port[0]->prefix, sram_idx, sram_idx + cur_num_input_basis -1); fprintf(fp, ");\n"); fprintf(fp, "\n"); /* Update the counter */ mux_basis_cnt++; } } /* Assert */ assert(0 == nextlevel); assert(0 == out_idx); assert((1 == special_basis_cnt)||(0 == special_basis_cnt)); /* assert((mux_basis_cnt + special_basis_cnt) == (int)((spice_mux_arch.num_input - 1)/(spice_mux_arch.num_input_basis - 1)) + 1); */ return; } void dump_verilog_cmos_mux_onelevel_structure(FILE* fp, char* mux_basis_subckt_name, t_spice_model spice_model, t_spice_mux_arch spice_mux_arch, int num_sram_port, t_spice_model_port** sram_port) { /* Make sure we have a valid file handler*/ if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR,"(FILE:%s,LINE[%d])Invalid file handler!\n",__FILE__, __LINE__); exit(1); } assert(SPICE_MODEL_DESIGN_CMOS == spice_model.design_tech); fprintf(fp, "wire [0:%d] mux2_l%d_in; \n", spice_mux_arch.num_input - 1, 1); /* input0 */ fprintf(fp, "wire [0:%d] mux2_l%d_in; \n", 0, 0); /* output */ fprintf(fp, "%s mux_basis (\n", mux_basis_subckt_name); /* given_name */ /* Dump global ports */ if (0 < rec_dump_verilog_spice_model_global_ports(fp, &spice_model, FALSE, FALSE)) { fprintf(fp, ",\n"); } fprintf(fp, "//----- MUX inputs -----\n"); fprintf(fp, "mux2_l%d_in[0:%d], ", 1, spice_mux_arch.num_input - 1); /* input0 */ fprintf(fp, "mux2_l%d_in[%d], ", 0, 0); /* output */ fprintf(fp, "\n"); fprintf(fp, "//----- SRAM ports -----\n"); /* Special basis for 2-input MUX, there is only one configuration bit */ if (2 == spice_mux_arch.num_input) { fprintf(fp, "%s[0:%d], %s_inv[0:%d] ", sram_port[0]->prefix, 0, sram_port[0]->prefix, 0); /* sram sram_inv */ } else { fprintf(fp, "%s[0:%d], %s_inv[0:%d] ", sram_port[0]->prefix, spice_mux_arch.num_input - 1, sram_port[0]->prefix, spice_mux_arch.num_input - 1); /* sram sram_inv */ } fprintf(fp, "\n"); fprintf(fp, ");\n"); return; } void dump_verilog_cmos_mux_submodule(FILE* fp, int mux_size, t_spice_model spice_model, t_spice_mux_arch spice_mux_arch) { int i, num_conf_bits; int num_input_port = 0; int num_output_port = 0; int num_sram_port = 0; t_spice_model_port** input_port = NULL; t_spice_model_port** output_port = NULL; t_spice_model_port** sram_port = NULL; /* Find the basis subckt*/ char* mux_basis_subckt_name = NULL; char* mux_special_basis_subckt_name = NULL; mux_basis_subckt_name = (char*)my_malloc(sizeof(char)*(strlen(spice_model.name) + 5 + strlen(my_itoa(mux_size)) + strlen(verilog_mux_basis_posfix) + 1)); sprintf(mux_basis_subckt_name, "%s_size%d%s", spice_model.name, mux_size, verilog_mux_basis_posfix); mux_special_basis_subckt_name = (char*)my_malloc(sizeof(char)*(strlen(spice_model.name) + 5 + strlen(my_itoa(spice_mux_arch.num_input)) + strlen(verilog_mux_special_basis_posfix) + 1)); sprintf(mux_special_basis_subckt_name, "%s_size%d%s", spice_model.name, spice_mux_arch.num_input, verilog_mux_special_basis_posfix); /* Make sure we have a valid file handler*/ if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR,"(FILE:%s,LINE[%d])Invalid file handler!\n",__FILE__, __LINE__); exit(1); } /* Ensure we have a CMOS MUX, * ATTENTION: support LUT as well */ assert((SPICE_MODEL_MUX == spice_model.type)||(SPICE_MODEL_LUT == spice_model.type)); assert(SPICE_MODEL_DESIGN_CMOS == spice_model.design_tech); /* Find the input port, output port, and sram port*/ input_port = find_spice_model_ports(&spice_model, SPICE_MODEL_PORT_INPUT, &num_input_port, TRUE); output_port = find_spice_model_ports(&spice_model, SPICE_MODEL_PORT_OUTPUT, &num_output_port, TRUE); sram_port = find_spice_model_ports(&spice_model, SPICE_MODEL_PORT_SRAM, &num_sram_port, TRUE); /* Asserts*/ assert(1 == num_input_port); assert(1 == num_output_port); assert(1 == num_sram_port); assert(1 == output_port[0]->size); /* We have two types of naming rules in terms of the usage of MUXes: * 1. MUXes, the naming rule is __size * 2. LUTs, the naming rule is _mux_size */ num_conf_bits = count_num_sram_bits_one_spice_model(&spice_model, /* sram_verilog_orgz_info->type, */ mux_size); if (SPICE_MODEL_LUT == spice_model.type) { /* Special for LUT MUX */ fprintf(fp, "//------ CMOS MUX info: spice_model_name= %s_MUX, size=%d -----\n", spice_model.name, mux_size); fprintf(fp, "module %s_mux(\n", spice_model.name); /* Dump global ports */ if (0 < rec_dump_verilog_spice_model_global_ports(fp, &spice_model, TRUE, FALSE)) { fprintf(fp, ",\n"); } /* Print input ports*/ assert(mux_size == num_conf_bits); fprintf(fp, "input wire [0:%d] %s,\n", num_conf_bits - 1, input_port[0]->prefix); /* Print output ports*/ fprintf(fp, "output wire %s,\n", output_port[0]->prefix); /* Print configuration ports*/ /* The configuration port in MUX context is the input port in LUT context ! */ fprintf(fp, "input wire [0:%d] %s,\n", input_port[0]->size - 1, sram_port[0]->prefix); fprintf(fp, "input wire [0:%d] %s_inv\n", input_port[0]->size - 1, sram_port[0]->prefix); } else { fprintf(fp, "//----- CMOS MUX info: spice_model_name=%s, size=%d, structure: %s -----\n", spice_model.name, mux_size, gen_str_spice_model_structure(spice_model.design_tech_info.structure)); fprintf(fp, "module %s_size%d (", spice_model.name, mux_size); /* Print input ports*/ fprintf(fp, "input wire [0:%d] %s,\n", mux_size - 1, input_port[0]->prefix); /* Print output ports*/ fprintf(fp, "output wire %s,\n", output_port[0]->prefix); /* Print configuration ports*/ fprintf(fp, "input wire [0:%d] %s,\n", num_conf_bits - 1, sram_port[0]->prefix); fprintf(fp, "input wire [0:%d] %s_inv\n", num_conf_bits - 1, sram_port[0]->prefix); } /* Print local vdd and gnd*/ fprintf(fp, ");"); fprintf(fp, "\n"); /* Print internal architecture*/ switch (spice_model.design_tech_info.structure) { case SPICE_MODEL_STRUCTURE_TREE: dump_verilog_cmos_mux_tree_structure(fp, mux_basis_subckt_name, spice_model, spice_mux_arch, num_sram_port, sram_port); break; case SPICE_MODEL_STRUCTURE_ONELEVEL: dump_verilog_cmos_mux_onelevel_structure(fp, mux_basis_subckt_name, spice_model, spice_mux_arch, num_sram_port, sram_port); break; case SPICE_MODEL_STRUCTURE_MULTILEVEL: dump_verilog_cmos_mux_multilevel_structure(fp, mux_basis_subckt_name, mux_special_basis_subckt_name, spice_model, spice_mux_arch, num_sram_port, sram_port); break; default: vpr_printf(TIO_MESSAGE_ERROR,"(File:%s,[LINE%d])Invalid structure for spice model (%s)!\n", __FILE__, __LINE__, spice_model.name); exit(1); } /* To connect the input ports*/ for (i = 0; i < mux_size; i++) { if (1 == spice_model.input_buffer->exist) { switch (spice_model.input_buffer->type) { case SPICE_MODEL_BUF_INV: /* Each inv: svdd sgnd size=param*/ fprintf(fp, "%s inv%d (", spice_model.input_buffer->spice_model_name, i); /* Given name*/ /* Dump global ports */ if (0 < rec_dump_verilog_spice_model_global_ports(fp, spice_model.input_buffer->spice_model, FALSE, FALSE)) { fprintf(fp, ",\n"); } fprintf(fp, "%s[%d], ", input_port[0]->prefix, i); /* input port */ fprintf(fp, "mux2_l%d_in[%d]); ", spice_mux_arch.input_level[i], spice_mux_arch.input_offset[i]); /* output port*/ fprintf(fp, "\n"); break; case SPICE_MODEL_BUF_BUF: /* TODO: what about tapered buffer, can we support? */ /* Each buf: svdd sgnd size=param*/ fprintf(fp, "%s buf%d (", spice_model.input_buffer->spice_model_name, i); /* Given name*/ /* Dump global ports */ if (0 < rec_dump_verilog_spice_model_global_ports(fp, spice_model.input_buffer->spice_model, FALSE, FALSE)) { fprintf(fp, ",\n"); } fprintf(fp, "%s[%d], ", input_port[0]->prefix, i); /* input port */ fprintf(fp, "mux2_l%d_in[%d]); ", spice_mux_arch.input_level[i], spice_mux_arch.input_offset[i]); /* output port*/ fprintf(fp, "\n"); break; default: vpr_printf(TIO_MESSAGE_ERROR,"(File:%s,[LINE%d])Invalid type for spice_model_buffer.\n", __FILE__, __LINE__); exit(1); } } else { /* There is no buffer, I create a zero resisitance between*/ /* Resistance R 0*/ fprintf(fp, "assign %s[%d] = mux2_l%d_in[%d];\n", input_port[0]->prefix, i, spice_mux_arch.input_level[i], spice_mux_arch.input_offset[i]); } } /* Output buffer*/ if (1 == spice_model.output_buffer->exist) { switch (spice_model.output_buffer->type) { case SPICE_MODEL_BUF_INV: if (TRUE == spice_model.output_buffer->tapered_buf) { break; } /* Each inv: svdd sgnd size=param*/ fprintf(fp, "%s inv_out (", spice_model.output_buffer->spice_model_name); /* Given name*/ /* Dump global ports */ if (0 < rec_dump_verilog_spice_model_global_ports(fp, spice_model.output_buffer->spice_model, FALSE, FALSE)) { fprintf(fp, ",\n"); } fprintf(fp, "mux2_l%d_in[%d], ", 0, 0); /* input port */ fprintf(fp, "%s );", output_port[0]->prefix); /* Output port*/ fprintf(fp, "\n"); break; case SPICE_MODEL_BUF_BUF: if (TRUE == spice_model.output_buffer->tapered_buf) { break; } /* Each buf: svdd sgnd size=param*/ fprintf(fp, "%s buf_out (", spice_model.output_buffer->spice_model_name); /* Given name*/ /* Dump global ports */ if (0 < rec_dump_verilog_spice_model_global_ports(fp, spice_model.output_buffer->spice_model, FALSE, FALSE)) { fprintf(fp, ",\n"); } fprintf(fp, "mux2_l%d_in[%d], ", 0, 0); /* input port */ fprintf(fp, "%s );", output_port[0]->prefix); /* Output port*/ fprintf(fp, "\n"); break; default: vpr_printf(TIO_MESSAGE_ERROR,"(File:%s,[LINE%d])Invalid type for spice_model_buffer.\n", __FILE__, __LINE__); exit(1); } /* Tapered buffer support */ if (TRUE == spice_model.output_buffer->tapered_buf) { /* Each buf: svdd sgnd size=param*/ fprintf(fp, "%s buf_out (", spice_model.output_buffer->spice_model_name); /* subckt name */ /* Dump global ports */ if (0 < rec_dump_verilog_spice_model_global_ports(fp, spice_model.output_buffer->spice_model, FALSE, FALSE)) { fprintf(fp, ",\n"); } fprintf(fp, "mux2_l%d_in[%d], ", 0, 0); /* input port */ fprintf(fp, "%s );", output_port[0]->prefix); /* Output port*/ fprintf(fp, "\n"); } } else { /* There is no buffer, I create a zero resisitance between*/ /* Resistance R 0*/ fprintf(fp, "assign mux2_l0_in[0] = %s;\n",output_port[0]->prefix); } fprintf(fp, "endmodule\n"); fprintf(fp, "//----- END CMOS MUX info: spice_model_name=%s, size=%d -----\n\n", spice_model.name, mux_size); fprintf(fp, "\n"); /* Free */ my_free(mux_basis_subckt_name); my_free(mux_special_basis_subckt_name); my_free(input_port); my_free(output_port); my_free(sram_port); return; } /* Print the RRAM MUX SPICE model. * The internal structures of CMOS and RRAM MUXes are similar. * This one can be merged to CMOS function. * However I use another function, because in future the internal structure may change. * We will suffer less software problems. */ void dump_verilog_rram_mux_tree_structure(FILE* fp, char* mux_basis_subckt_name, t_spice_model spice_model, t_spice_mux_arch spice_mux_arch, int num_sram_port, t_spice_model_port** sram_port) { int i, j, level, nextlevel; int nextj, out_idx; int mux_basis_cnt = 0; int cur_mem_lsb = 0; int cur_mem_msb = 0; assert(SPICE_MODEL_DESIGN_RRAM == spice_model.design_tech); /* Make sure we have a valid file handler*/ if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR,"(FILE:%s,LINE[%d])Invalid file handler!\n",__FILE__, __LINE__); exit(1); } for (i = 0; i < spice_mux_arch.num_level; i++) { level = spice_mux_arch.num_level - i; nextlevel = spice_mux_arch.num_level - i - 1; /* Check */ assert(nextlevel > -1); fprintf(fp, "wire [%d:%d] mux2_l%d_in; \n", 0, spice_mux_arch.num_input_per_level[nextlevel] -1, /* input0 input1 */ level); } fprintf(fp, "wire [%d:%d] mux2_l%d_in; \n", 0, 0, 0); mux_basis_cnt = 0; for (i = 0; i < spice_mux_arch.num_level; i++) { level = spice_mux_arch.num_level - i; nextlevel = spice_mux_arch.num_level - i - 1; /* Check */ assert(nextlevel > -1); /* Print basis mux2to1 for each level*/ for (j = 0; j < spice_mux_arch.num_input_per_level[nextlevel]; j++) { nextj = j + 1; out_idx = j/2; cur_mem_lsb = cur_mem_msb; cur_mem_msb += 6; /* Each basis mux2to1: svdd sgnd */ fprintf(fp, "%s mux_basis_no%d (", mux_basis_subckt_name, mux_basis_cnt); /* given_name */ /* Dump global ports */ if (0 < rec_dump_verilog_spice_model_global_ports(fp, &spice_model, FALSE, FALSE)) { fprintf(fp, ",\n"); } fprintf(fp, "mux2_l%d_in[%d:%d], ", level, j, nextj); /* input0 input1 */ fprintf(fp, "mux2_l%d_in[%d], ", nextlevel, out_idx); /* output */ fprintf(fp, "%s[%d:%d] %s_inv[%d:%d]);\n", sram_port[0]->prefix, cur_mem_lsb, cur_mem_msb - 1, sram_port[0]->prefix, cur_mem_lsb, cur_mem_msb - 1); /* sram sram_inv */ /* Update the counter */ j = nextj; mux_basis_cnt++; } } /* Assert */ assert(0 == nextlevel); assert(0 == out_idx); assert(mux_basis_cnt == spice_mux_arch.num_input - 1); assert(cur_mem_msb == 6 * spice_mux_arch.num_level); return; } void dump_verilog_rram_mux_multilevel_structure(FILE* fp, char* mux_basis_subckt_name, char* mux_special_basis_subckt_name, t_spice_model spice_model, t_spice_mux_arch spice_mux_arch, int num_sram_port, t_spice_model_port** sram_port) { int i, j, level, nextlevel, sram_idx; int out_idx; int mux_basis_cnt = 0; int special_basis_cnt = 0; int cur_num_input_basis = 0; int cur_mem_lsb = 0; int cur_mem_msb = 0; assert(SPICE_MODEL_DESIGN_RRAM == spice_model.design_tech); /* Make sure we have a valid file handler*/ if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR,"(FILE:%s,LINE[%d])Invalid file handler!\n",__FILE__, __LINE__); exit(1); } for (i = 0; i < spice_mux_arch.num_level; i++) { level = spice_mux_arch.num_level - i; nextlevel = spice_mux_arch.num_level - i - 1; sram_idx = nextlevel * spice_mux_arch.num_input_basis; /* Check */ assert(nextlevel > -1); fprintf(fp, "wire [%d:%d] mux2_l%d_in; \n", 0, spice_mux_arch.num_input_per_level[nextlevel] -1, /* input0 input1 */ level); } fprintf(fp, "wire [%d:%d] mux2_l%d_in; \n", 0, 0, 0); mux_basis_cnt = 0; assert((2 == spice_mux_arch.num_input_basis)||(2 < spice_mux_arch.num_input_basis)); for (i = 0; i < spice_mux_arch.num_level; i++) { level = spice_mux_arch.num_level - i; nextlevel = spice_mux_arch.num_level - i - 1; /* Check */ assert(nextlevel > -1); /* Memory port offset update */ cur_mem_lsb = cur_mem_msb; /* Print basis muxQto1 for each level*/ for (j = 0; j < spice_mux_arch.num_input_per_level[nextlevel]; j = j+cur_num_input_basis) { /* output index */ out_idx = j/spice_mux_arch.num_input_basis; /* Determine the number of input of this basis */ cur_num_input_basis = spice_mux_arch.num_input_basis; cur_mem_msb = cur_mem_lsb + (cur_num_input_basis + 1); if ((j + cur_num_input_basis) > spice_mux_arch.num_input_per_level[nextlevel]) { cur_num_input_basis = find_spice_mux_arch_special_basis_size(spice_mux_arch); if (0 < cur_num_input_basis) { /* Print the special basis */ fprintf(fp, "%s special_basis(\n", mux_special_basis_subckt_name); /* Dump global ports */ if (0 < rec_dump_verilog_spice_model_global_ports(fp, &spice_model, FALSE, FALSE)) { fprintf(fp, ",\n"); } fprintf(fp, "mux2_l%d_in[%d:%d], ", level, j, j + cur_num_input_basis - 1); /* inputs */ fprintf(fp, "mux2_l%d_in[%d], ", nextlevel, out_idx); /* output */ cur_mem_msb = cur_mem_lsb + (cur_num_input_basis + 1); fprintf(fp, "%s[%d:%d], %s_inv[%d,%d]", sram_port[0]->prefix, cur_mem_lsb, cur_mem_msb - 1, sram_port[0]->prefix, cur_mem_lsb, cur_mem_msb - 1); /* sram sram_inv */ fprintf(fp, ");\n"); special_basis_cnt++; continue; } } /* Each basis muxQto1: svdd sgnd */ fprintf(fp, "%s ", mux_basis_subckt_name); /* subckt_name */ fprintf(fp, "mux_basis_no%d (", mux_basis_cnt); /* given_name */ /* Dump global ports */ if (0 < rec_dump_verilog_spice_model_global_ports(fp, &spice_model, FALSE, FALSE)) { fprintf(fp, ",\n"); } fprintf(fp, "mux2_l%d_in[%d:%d], ", level, j, j + cur_num_input_basis - 1); /* input0 input1 */ fprintf(fp, "mux2_l%d_in[%d], ", nextlevel, out_idx); /* output */ /* Print number of sram bits for this basis */ fprintf(fp, "%s[%d:%d], %s_inv[%d:%d]", sram_port[0]->prefix, cur_mem_lsb, cur_mem_msb - 1, sram_port[0]->prefix, cur_mem_lsb, cur_mem_msb - 1); /* sram sram_inv */ fprintf(fp, ");\n"); /* Update the counter */ mux_basis_cnt++; } } /* Assert */ assert(0 == nextlevel); assert(0 == out_idx); assert((1 == special_basis_cnt)||(0 == special_basis_cnt)); /* assert((mux_basis_cnt + special_basis_cnt) == (int)((spice_mux_arch.num_input - 1)/(spice_mux_arch.num_input_basis - 1)) + 1); */ /* Free */ return; } void dump_verilog_rram_mux_onelevel_structure(FILE* fp, char* mux_basis_subckt_name, t_spice_model spice_model, t_spice_mux_arch spice_mux_arch, int num_sram_port, t_spice_model_port** sram_port) { int num_conf_bits; /* Make sure we have a valid file handler*/ if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR,"(FILE:%s,LINE[%d])Invalid file handler!\n",__FILE__, __LINE__); exit(1); } assert(SPICE_MODEL_DESIGN_RRAM == spice_model.design_tech); fprintf(fp, "wire [0:%d] mux2_l%d_in; \n", spice_mux_arch.num_input - 1, 1); /* input0 */ fprintf(fp, "wire [0:%d] mux2_l%d_in; \n", 0, 0); /* output */ fprintf(fp, "%s mux_basis (\n", mux_basis_subckt_name); /* given_name */ /* Dump global ports */ if (0 < rec_dump_verilog_spice_model_global_ports(fp, &spice_model, FALSE, FALSE)) { fprintf(fp, ",\n"); } fprintf(fp, "//----- MUX inputs -----\n"); fprintf(fp, "mux2_l%d_in[0:%d],\n ", 1, spice_mux_arch.num_input - 1); /* inputs */ fprintf(fp, "mux2_l%d_in[%d],\n", 0, 0); /* output */ fprintf(fp, "//----- SRAM ports -----\n"); num_conf_bits = count_num_sram_bits_one_spice_model(&spice_model, /* sram_verilog_orgz_info->type,*/ spice_mux_arch.num_input); fprintf(fp, "%s[0:%d], %s_inv[0:%d]", sram_port[0]->prefix, num_conf_bits - 1, sram_port[0]->prefix, num_conf_bits - 1); /* sram sram_inv */ fprintf(fp, "\n"); fprintf(fp, ");\n"); return; } void dump_verilog_rram_mux_submodule(FILE* fp, int mux_size, t_spice_model spice_model, t_spice_mux_arch spice_mux_arch) { int i, num_conf_bits; int num_input_port = 0; int num_output_port = 0; int num_sram_port = 0; t_spice_model_port** input_port = NULL; t_spice_model_port** output_port = NULL; t_spice_model_port** sram_port = NULL; /* Find the basis subckt*/ char* mux_basis_subckt_name = NULL; char* mux_special_basis_subckt_name = NULL; mux_basis_subckt_name = (char*)my_malloc(sizeof(char)*(strlen(spice_model.name) + 5 + strlen(my_itoa(mux_size)) + strlen(verilog_mux_basis_posfix) + 1)); sprintf(mux_basis_subckt_name, "%s_size%d%s", spice_model.name, mux_size, verilog_mux_basis_posfix); mux_special_basis_subckt_name = (char*)my_malloc(sizeof(char)*(strlen(spice_model.name) + 5 + strlen(my_itoa(spice_mux_arch.num_input)) + strlen(verilog_mux_special_basis_posfix) + 1)); sprintf(mux_special_basis_subckt_name, "%s_size%d%s", spice_model.name, spice_mux_arch.num_input, verilog_mux_special_basis_posfix); /* Make sure we have a valid file handler*/ if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR,"(FILE:%s,LINE[%d])Invalid file handler!\n",__FILE__, __LINE__); exit(1); } /* Ensure we have a RRAM MUX*/ assert((SPICE_MODEL_MUX == spice_model.type)||(SPICE_MODEL_LUT == spice_model.type)); assert(SPICE_MODEL_DESIGN_RRAM == spice_model.design_tech); /* Find the input port, output port, and sram port*/ input_port = find_spice_model_ports(&spice_model, SPICE_MODEL_PORT_INPUT, &num_input_port, TRUE); output_port = find_spice_model_ports(&spice_model, SPICE_MODEL_PORT_OUTPUT, &num_output_port, TRUE); sram_port = find_spice_model_ports(&spice_model, SPICE_MODEL_PORT_SRAM, &num_sram_port, TRUE); /* Asserts*/ assert(1 == num_input_port); assert(1 == num_output_port); assert(1 == num_sram_port); assert(1 == output_port[0]->size); /* Print the definition of subckt*/ if (SPICE_MODEL_LUT == spice_model.type) { /* RRAM LUT is not supported now... */ vpr_printf(TIO_MESSAGE_ERROR, "(File:%s,[LINE%d])RRAM LUT is not supported!\n", __FILE__, __LINE__); exit(1); /* Special for LUT MUX*/ /* fprintf(fp, "***** RRAM MUX info: spice_model_name= %s_MUX, size=%d *****\n", spice_model.name, mux_size); fprintf(fp, ".subckt %s_mux_size%d ", spice_model.name, mux_size); */ } else { fprintf(fp, "//----- RRAM MUX info: spice_model_name=%s, size=%d, structure: %s -----\n", spice_model.name, mux_size, gen_str_spice_model_structure(spice_model.design_tech_info.structure)); fprintf(fp, "module %s_size%d( \n", spice_model.name, mux_size); } /* Dump global ports */ if (0 < rec_dump_verilog_spice_model_global_ports(fp, &spice_model, TRUE, FALSE)) { fprintf(fp, ",\n"); } /* Print input ports*/ fprintf(fp, "input wire [0:%d] %s,\n ", mux_size - 1, input_port[0]->prefix); /* Print output ports*/ fprintf(fp, "output wire %s,\n ", output_port[0]->prefix); /* Print configuration ports */ num_conf_bits = count_num_sram_bits_one_spice_model(&spice_model, /* sram_verilog_orgz_info->type,*/ mux_size); fprintf(fp, "input wire [0:%d] %s,\n", num_conf_bits - 1, sram_port[0]->prefix); fprintf(fp, "input wire [0:%d] %s_inv\n", num_conf_bits - 1, sram_port[0]->prefix); /* Print local vdd and gnd*/ fprintf(fp, ");\n"); /* Print internal architecture*/ /* RRAM MUX is optimal in terms of area, delay and power for one-level structure. */ switch (spice_model.design_tech_info.structure) { case SPICE_MODEL_STRUCTURE_TREE: dump_verilog_rram_mux_tree_structure(fp, mux_basis_subckt_name, spice_model, spice_mux_arch, num_sram_port, sram_port); break; case SPICE_MODEL_STRUCTURE_MULTILEVEL: dump_verilog_rram_mux_multilevel_structure(fp, mux_basis_subckt_name, mux_special_basis_subckt_name, spice_model, spice_mux_arch, num_sram_port, sram_port); break; case SPICE_MODEL_STRUCTURE_ONELEVEL: dump_verilog_rram_mux_onelevel_structure(fp, mux_basis_subckt_name, spice_model, spice_mux_arch, num_sram_port, sram_port); break; default: vpr_printf(TIO_MESSAGE_ERROR,"(File:%s,[LINE%d])Invalid structure for spice model (%s)!\n", __FILE__, __LINE__, spice_model.name); exit(1); } /* To connect the input ports*/ for (i = 0; i < mux_size; i++) { if (1 == spice_model.input_buffer->exist) { switch (spice_model.input_buffer->type) { case SPICE_MODEL_BUF_INV: /* Each inv: svdd sgnd size=param*/ fprintf(fp, "%s inv%d (", spice_model.input_buffer->spice_model_name, i); /* Given name*/ /* Dump global ports */ if (0 < rec_dump_verilog_spice_model_global_ports(fp, spice_model.input_buffer->spice_model, FALSE, FALSE)) { fprintf(fp, ",\n"); } fprintf(fp, "%s[%d], ", input_port[0]->prefix, i); /* input port */ fprintf(fp, "mux2_l%d_in[%d]);", spice_mux_arch.input_level[i], spice_mux_arch.input_offset[i]); /* output port*/ fprintf(fp, "\n"); break; case SPICE_MODEL_BUF_BUF: /* TODO: what about tapered buffer, can we support? */ /* Each buf: svdd sgnd size=param*/ fprintf(fp, "%s buf%d (", spice_model.input_buffer->spice_model_name, i); /* Given name*/ /* Dump global ports */ if (0 < rec_dump_verilog_spice_model_global_ports(fp, spice_model.input_buffer->spice_model, FALSE, FALSE)) { fprintf(fp, ",\n"); } fprintf(fp, "%s[%d], ", input_port[0]->prefix, i); /* input port */ fprintf(fp, "mux2_l%d_in[%d)];", spice_mux_arch.input_level[i], spice_mux_arch.input_offset[i]); /* output port*/ fprintf(fp, "\n"); break; default: vpr_printf(TIO_MESSAGE_ERROR,"(File:%s,[LINE%d])Invalid type for spice_model_buffer.\n", __FILE__, __LINE__); exit(1); } } else { /* There is no buffer, I create a zero resisitance between*/ /* Resistance R 0*/ fprintf(fp, "assign %s[%d] = mux2_l%d_in[%d];\n", input_port[0]->prefix, i, spice_mux_arch.input_level[i], spice_mux_arch.input_offset[i]); } } /* Output buffer*/ if (1 == spice_model.output_buffer->exist) { switch (spice_model.output_buffer->type) { case SPICE_MODEL_BUF_INV: if (TRUE == spice_model.output_buffer->tapered_buf) { break; } /* Each inv: svdd sgnd size=param*/ fprintf(fp, "%s inv_out (", spice_model.output_buffer->spice_model_name); /* Given name*/ /* Dump global ports */ if (0 < rec_dump_verilog_spice_model_global_ports(fp, spice_model.output_buffer->spice_model, FALSE, FALSE)) { fprintf(fp, ",\n"); } fprintf(fp, "mux2_l%d_in[%d], ", 0, 0); /* input port */ fprintf(fp, "%s );", output_port[0]->prefix); /* Output port*/ fprintf(fp, "\n"); break; case SPICE_MODEL_BUF_BUF: if (TRUE == spice_model.output_buffer->tapered_buf) { break; } /* Each buf: svdd sgnd size=param*/ fprintf(fp, "%s buf_out (", spice_model.output_buffer->spice_model_name); /* Given name*/ /* Dump global ports */ if (0 < rec_dump_verilog_spice_model_global_ports(fp, spice_model.output_buffer->spice_model, FALSE, FALSE)) { fprintf(fp, ",\n"); } fprintf(fp, "mux2_l%d_in[%d], ", 0, 0); /* input port */ fprintf(fp, "%s );", output_port[0]->prefix); /* Output port*/ fprintf(fp, "\n"); break; default: vpr_printf(TIO_MESSAGE_ERROR,"(File:%s,[LINE%d])Invalid type for spice_model_buffer.\n", __FILE__, __LINE__); exit(1); } /* Tapered buffer support */ if (TRUE == spice_model.output_buffer->tapered_buf) { /* Each buf: svdd sgnd size=param*/ fprintf(fp, "%s buf_out (", spice_model.output_buffer->spice_model_name); /* subckt name */ /* Dump global ports */ if (0 < rec_dump_verilog_spice_model_global_ports(fp, spice_model.output_buffer->spice_model, FALSE, FALSE)) { fprintf(fp, ",\n"); } fprintf(fp, "mux2_l%d_in[%d], ", 0 , 0); /* input port */ fprintf(fp, "%s );", output_port[0]->prefix); /* Output port*/ fprintf(fp, "\n"); } } else { /* There is no buffer, I create a zero resisitance between*/ /* Resistance R 0*/ fprintf(fp, "assign mux2_l0_in[0] %s;\n",output_port[0]->prefix); } fprintf(fp, "endmodule\n"); fprintf(fp, "//------ END RRAM MUX info: spice_model_name=%s, size=%d -----\n\n", spice_model.name, mux_size); fprintf(fp, "\n"); /* Free */ my_free(mux_basis_subckt_name); my_free(mux_special_basis_subckt_name); my_free(input_port); my_free(output_port); my_free(sram_port); return; } /** Dump a verilog module for a MUX * We always dump a basis submodule for a MUX * whatever structure it is: one-level, two-level or multi-level */ void dump_verilog_mux_module(FILE* fp, t_spice_mux_model* spice_mux_model) { /* Make sure we have a valid file handler*/ if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR,"(FILE:%s,LINE[%d])Invalid file handler!\n",__FILE__, __LINE__); exit(1); } /* Make sure we have a valid spice_model*/ if (NULL == spice_mux_model) { vpr_printf(TIO_MESSAGE_ERROR,"(FILE:%s,LINE[%d])Invalid spice_mux_model!\n",__FILE__, __LINE__); exit(1); } /* Make sure we have a valid spice_model*/ if (NULL == spice_mux_model->spice_model) { vpr_printf(TIO_MESSAGE_ERROR,"(FILE:%s,LINE[%d])Invalid spice_model!\n",__FILE__, __LINE__); exit(1); } /* Check the mux size*/ if (spice_mux_model->size < 2) { vpr_printf(TIO_MESSAGE_ERROR,"(FILE:%s,LINE[%d])Invalid MUX size(=%d)! Should be at least 2.\n", __FILE__, __LINE__, spice_mux_model->size); exit(1); } /* Corner case: Error out MUX_SIZE = 2, automatcially give a one-level structure */ /* if ((2 == spice_mux_model->size)&&(SPICE_MODEL_STRUCTURE_ONELEVEL != spice_mux_model->spice_model->design_tech_info.structure)) { vpr_printf(TIO_MESSAGE_ERROR, "(File:%s,[LINE%d])Structure of SPICE model (%s) should be one-level because it is linked to a 2:1 MUX!\n", __FILE__, __LINE__, spice_mux_model->spice_model->name); exit(1); } */ /* Print the definition of subckt*/ /* Check the design technology*/ switch (spice_mux_model->spice_model->design_tech) { case SPICE_MODEL_DESIGN_CMOS: dump_verilog_cmos_mux_submodule(fp, spice_mux_model->size, *(spice_mux_model->spice_model), *(spice_mux_model->spice_mux_arch)); break; case SPICE_MODEL_DESIGN_RRAM: dump_verilog_rram_mux_submodule(fp, spice_mux_model->size, *(spice_mux_model->spice_model), *(spice_mux_model->spice_mux_arch)); break; default: vpr_printf(TIO_MESSAGE_ERROR,"(FILE:%s,LINE[%d])Invalid design_technology of MUX(name: %s)\n", __FILE__, __LINE__, spice_mux_model->spice_model->name); exit(1); } return; } /*** Top-level function *****/ /* We should count how many multiplexers with different sizes are needed */ /**/ void dump_verilog_submodule_muxes(char* submodule_dir, int num_switch, t_switch_inf* switches, t_spice* spice, t_det_routing_arch* routing_arch) { /* Statisitcs for input sizes and structures of MUXes * used in FPGA architecture */ /* We have linked list whichs stores spice model information of multiplexer*/ t_llist* muxes_head = NULL; t_llist* temp = NULL; int mux_cnt = 0; int max_mux_size = -1; int min_mux_size = -1; FILE* fp = NULL; char* verilog_name = my_strcat(submodule_dir,muxes_verilog_file_name); int num_input_ports = 0; t_spice_model_port** input_ports = NULL; int num_sram_ports = 0; t_spice_model_port** sram_ports = NULL; int num_input_basis = 0; t_spice_mux_model* cur_spice_mux_model = NULL; int max_routing_mux_size = -1; /* Alloc the muxes*/ muxes_head = stats_spice_muxes(num_switch, switches, spice, routing_arch); /* Print the muxes netlist*/ fp = fopen(verilog_name, "w"); if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR,"(FILE:%s,LINE[%d])Failure in create subckt SPICE netlist %s",__FILE__, __LINE__, verilog_name); exit(1); } /* Generate the descriptions*/ dump_verilog_file_header(fp,"MUXes used in FPGA"); /* Print mux netlist one by one*/ temp = muxes_head; while(temp) { assert(NULL != temp->dptr); cur_spice_mux_model = (t_spice_mux_model*)(temp->dptr); /* Bypass the spice models who has a user-defined subckt */ if (NULL != cur_spice_mux_model->spice_model->verilog_netlist) { input_ports = find_spice_model_ports(cur_spice_mux_model->spice_model, SPICE_MODEL_PORT_INPUT, &num_input_ports, TRUE); sram_ports = find_spice_model_ports(cur_spice_mux_model->spice_model, SPICE_MODEL_PORT_SRAM, &num_sram_ports, TRUE); assert(0 != num_input_ports); assert(0 != num_sram_ports); /* Check the Input port size */ if (cur_spice_mux_model->size != input_ports[0]->size) { vpr_printf(TIO_MESSAGE_ERROR, "(File:%s,[LINE%d])User-defined MUX SPICE MODEL(%s) size(%d) unmatch with the architecture needs(%d)!\n", __FILE__, __LINE__, cur_spice_mux_model->spice_model->name, input_ports[0]->size,cur_spice_mux_model->size); exit(1); } /* Check the SRAM port size */ num_input_basis = determine_num_input_basis_multilevel_mux(cur_spice_mux_model->size, cur_spice_mux_model->spice_model->design_tech_info.mux_num_level); if ((num_input_basis * cur_spice_mux_model->spice_model->design_tech_info.mux_num_level) != sram_ports[0]->size) { vpr_printf(TIO_MESSAGE_ERROR, "(File:%s,[LINE%d])User-defined MUX SPICE MODEL(%s) SRAM size(%d) unmatch with the num of level(%d)!\n", __FILE__, __LINE__, cur_spice_mux_model->spice_model->name, sram_ports[0]->size, cur_spice_mux_model->spice_model->design_tech_info.mux_num_level*num_input_basis); exit(1); } /* Move on to the next*/ temp = temp->next; continue; } /* Let's have a N:1 MUX as basis*/ dump_verilog_mux_basis_module(fp, cur_spice_mux_model); /* Print the mux subckt */ dump_verilog_mux_module(fp, cur_spice_mux_model); /* Update the statistics*/ mux_cnt++; if ((-1 == max_mux_size)||(max_mux_size < cur_spice_mux_model->size)) { max_mux_size = cur_spice_mux_model->size; } if ((-1 == min_mux_size)||(min_mux_size > cur_spice_mux_model->size)) { min_mux_size = cur_spice_mux_model->size; } /* Exclude LUT MUX from this statistics */ if ((SPICE_MODEL_MUX == cur_spice_mux_model->spice_model->type) &&((-1 == max_routing_mux_size)||(max_routing_mux_size < cur_spice_mux_model->size))) { max_routing_mux_size = cur_spice_mux_model->size; } /* Move on to the next*/ temp = temp->next; } /* TODO: * Scan-chain configuration circuit does not need any BLs/WLs! * SRAM MUX does not need any reserved BL/WLs! */ /* Determine reserved Bit/Word Lines if a memory bank is specified, * At least 1 BL/WL should be reserved! */ try_update_sram_orgz_info_reserved_blwl(sram_verilog_orgz_info, max_routing_mux_size, max_routing_mux_size); vpr_printf(TIO_MESSAGE_INFO,"Generated %d Multiplexer submodules.\n", mux_cnt); vpr_printf(TIO_MESSAGE_INFO,"Max. MUX size = %d.\t", max_mux_size); vpr_printf(TIO_MESSAGE_INFO,"Min. MUX size = %d.\n", min_mux_size); /* Add fname to the linked list */ submodule_verilog_subckt_file_path_head = add_one_subckt_file_name_to_llist(submodule_verilog_subckt_file_path_head, verilog_name); /* remember to free the linked list*/ free_muxes_llist(muxes_head); /* Free strings */ free(verilog_name); /* Close the file*/ fclose(fp); return; } void dump_verilog_wire_module(FILE* fp, char* wire_subckt_name, t_spice_model verilog_model) { int num_input_port = 0; int num_output_port = 0; t_spice_model_port** input_port = NULL; t_spice_model_port** output_port = NULL; /* Ensure a valid file handler*/ if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR,"(FILE:%s,LINE[%d])Invalid File handler.\n", __FILE__, __LINE__); exit(1); } /* Check the wire model*/ assert(NULL != verilog_model.wire_param); assert(0 < verilog_model.wire_param->level); /* Find the input port, output port*/ input_port = find_spice_model_ports(&verilog_model, SPICE_MODEL_PORT_INPUT, &num_input_port, TRUE); output_port = find_spice_model_ports(&verilog_model, SPICE_MODEL_PORT_OUTPUT, &num_output_port, TRUE); /* Asserts*/ assert(1 == num_input_port); assert(1 == num_output_port); assert(1 == input_port[0]->size); assert(1 == output_port[0]->size); /* print the spice model*/ fprintf(fp, "//-----Wire module, verilog_model_name=%s -----\n", verilog_model.name); switch (verilog_model.type) { case SPICE_MODEL_CHAN_WIRE: /* Add an output at middle point for connecting CB inputs */ fprintf(fp, "module %s (\n", wire_subckt_name); /* Dump global ports */ if (0 < rec_dump_verilog_spice_model_global_ports(fp, &verilog_model, TRUE, FALSE)) { fprintf(fp, ",\n"); } fprintf(fp, "input wire %s, output wire %s, output wire mid_out);\n", input_port[0]->prefix, output_port[0]->prefix); fprintf(fp, "\tassign %s = %s;\n", output_port[0]->prefix, input_port[0]->prefix); fprintf(fp, "\tassign mid_out = %s;\n", input_port[0]->prefix); break; case SPICE_MODEL_WIRE: /* Add an output at middle point for connecting CB inputs */ fprintf(fp, "module %s (\n", wire_subckt_name); /* Dump global ports */ if (0 < rec_dump_verilog_spice_model_global_ports(fp, &verilog_model, TRUE, FALSE)) { fprintf(fp, ",\n"); } fprintf(fp, "input wire %s, output wire %s);\n", input_port[0]->prefix, output_port[0]->prefix); /* Direct shortcut */ fprintf(fp, "\t\tassign %s = %s;\n", output_port[0]->prefix, input_port[0]->prefix); break; default: vpr_printf(TIO_MESSAGE_ERROR, "(File:%s,[LINE%d])Invalid type of spice_model! Expect [chan_wire|wire].\n", __FILE__, __LINE__); exit(1); } /* Finish*/ fprintf(fp, "endmodule\n"); fprintf(fp, "//-----END Wire module, verilog_model_name=%s -----\n", verilog_model.name); fprintf(fp, "\n"); return; } /* Dump one module of a LUT */ void dump_verilog_submodule_one_lut(FILE* fp, t_spice_model* verilog_model, boolean include_timing) { int num_input_port = 0; int num_output_port = 0; int num_sram_port = 0; t_spice_model_port** input_port = NULL; t_spice_model_port** output_port = NULL; t_spice_model_port** sram_port = NULL; int iport, ipin, iedge; /* Check */ if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR,"(FILE:%s,LINE[%d])Invalid File handler.\n", __FILE__, __LINE__); exit(1);} assert(SPICE_MODEL_LUT == verilog_model->type); /* Print module name */ fprintf(fp, "//-----LUT module, verilog_model_name=%s -----\n", verilog_model->name); fprintf(fp, "module %s (", verilog_model->name); /* Dump global ports */ if (0 < rec_dump_verilog_spice_model_global_ports(fp, verilog_model, TRUE, FALSE)) { fprintf(fp, ",\n"); } /* Print module port list */ /* Find the input port, output port, and sram port*/ input_port = find_spice_model_ports(verilog_model, SPICE_MODEL_PORT_INPUT, &num_input_port, TRUE); output_port = find_spice_model_ports(verilog_model, SPICE_MODEL_PORT_OUTPUT, &num_output_port, TRUE); sram_port = find_spice_model_ports(verilog_model, SPICE_MODEL_PORT_SRAM, &num_sram_port, TRUE); /* Asserts*/ assert(1 == num_input_port); assert(1 == num_output_port); assert(1 == num_sram_port); assert(1 == output_port[0]->size); /* input port */ fprintf(fp, "input wire [0:%d] %s,\n", input_port[0]->size - 1, input_port[0]->prefix); /* Print output ports*/ fprintf(fp, "output wire [0:%d] %s,\n", output_port[0]->size - 1, output_port[0]->prefix); /* Print configuration ports*/ fprintf(fp, "input wire [0:%d] %s_out,\n", sram_port[0]->size - 1, sram_port[0]->prefix); /* Inverted configuration port is not connected to any internal signal of a LUT */ fprintf(fp, "input wire [0:%d] %s_outb\n", sram_port[0]->size - 1, sram_port[0]->prefix); /* End of port list */ fprintf(fp, ");\n"); /* Create inverted input port */ fprintf(fp, " wire [0:%d] %s_b;\n", input_port[0]->size - 1, input_port[0]->prefix); /* Create inverters between input port and its inversion */ fprintf(fp, " assign %s_b = ~ %s;\n", input_port[0]->prefix, input_port[0]->prefix); /* Internal structure of a LUT */ /* Call the LUT MUX */ fprintf(fp, " %s_mux %s_mux_0_ (", verilog_model->name, verilog_model->name); /* Connect MUX inputs to LUT configuration port */ fprintf(fp, " %s_out,", sram_port[0]->prefix); /* Connect MUX output to LUT output */ fprintf(fp, " %s,", output_port[0]->prefix); /* Connect MUX configuration port to LUT inputs */ fprintf(fp, " %s,", input_port[0]->prefix); /* Connect MUX inverted configuration port to inverted LUT inputs */ fprintf(fp, " %s_b", input_port[0]->prefix); /* End of call LUT MUX */ fprintf(fp, ");\n"); /* Give timing information */ if (TRUE == include_timing) { dump_verilog_submodule_timing(fp, verilog_model); } /* Print end of module */ fprintf(fp, "endmodule\n"); fprintf(fp, "//-----END LUT module, verilog_model_name=%s -----\n", verilog_model->name); fprintf(fp, "\n"); return; } /* Dump verilog top-level module for LUTs */ void dump_verilog_submodule_luts(char* submodule_dir, int num_spice_model, t_spice_model* spice_models, boolean include_timing) { FILE* fp = NULL; char* verilog_name = my_strcat(submodule_dir, luts_verilog_file_name); int imodel; /* Create File Handlers */ fp = fopen(verilog_name, "w"); if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR,"(FILE:%s,LINE[%d])Failure in create Verilog netlist %s",__FILE__, __LINE__, luts_verilog_file_name); exit(1); } dump_verilog_file_header(fp,"Look-Up Tables"); dump_verilog_preproc(fp, include_timing); /* Search for each LUT spice model */ for (imodel = 0; imodel < num_spice_model; imodel++) { /* Bypass user-defined spice models */ if (NULL != spice_models[imodel].verilog_netlist) { continue; } if (SPICE_MODEL_LUT == spice_models[imodel].type) { dump_verilog_submodule_one_lut(fp, &(spice_models[imodel]), include_timing); } } /* Close the file handler */ fclose(fp); /* Add fname to the linked list */ submodule_verilog_subckt_file_path_head = add_one_subckt_file_name_to_llist(submodule_verilog_subckt_file_path_head, verilog_name); return; } /* Dump a submodule which is a constant vdd */ void dump_verilog_hard_wired_vdd(FILE* fp, t_spice_model verilog_model) { int num_output_port = 0; t_spice_model_port** output_port = NULL; /* Find the input port, output port*/ output_port = find_spice_model_ports(&verilog_model, SPICE_MODEL_PORT_OUTPUT, &num_output_port, TRUE); /* Asserts*/ assert(1 == num_output_port); assert(1 == output_port[0]->size); /* Ensure a valid file handler*/ if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR,"(FILE:%s,LINE[%d])Invalid File handler.\n", __FILE__, __LINE__); exit(1); } /* print the spice model*/ fprintf(fp, "//-----Hard-wired VDD module, verilog_model_name=%s -----\n", verilog_model.name); fprintf(fp, "module %s(output wire %s);\n", verilog_model.name, output_port[0]->prefix); /* Constant logic 1*/ fprintf(fp, "assign %s = 1\'b1;\n", output_port[0]->prefix); /* Finish*/ fprintf(fp, "endmodule\n"); fprintf(fp, "//-----END VDD module, verilog_model_name=%s -----\n", verilog_model.name); fprintf(fp, "\n"); return; } /* Dump a submodule which is a constant vdd */ void dump_verilog_hard_wired_gnd(FILE* fp, t_spice_model verilog_model) { int num_output_port = 0; t_spice_model_port** output_port = NULL; /* Find the input port, output port*/ output_port = find_spice_model_ports(&verilog_model, SPICE_MODEL_PORT_OUTPUT, &num_output_port, TRUE); /* Asserts*/ assert(1 == num_output_port); assert(1 == output_port[0]->size); /* Ensure a valid file handler*/ if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR,"(FILE:%s,LINE[%d])Invalid File handler.\n", __FILE__, __LINE__); exit(1); } /* print the spice model*/ fprintf(fp, "//-----Hard-wired GND module, verilog_model_name=%s -----\n", verilog_model.name); fprintf(fp, "module %s(output wire %s);\n", verilog_model.name, output_port[0]->prefix); /* Constant logic 1*/ fprintf(fp, "assign %s = 1\'b0;\n", output_port[0]->prefix); /* Finish*/ fprintf(fp, "endmodule\n"); fprintf(fp, "//-----END GND module, verilog_model_name=%s -----\n", verilog_model.name); fprintf(fp, "\n"); return; } void dump_verilog_submodule_wires(char* subckt_dir, int num_segments, t_segment_inf* segments, int num_spice_model, t_spice_model* spice_models) { FILE* fp = NULL; char* verilog_name = my_strcat(subckt_dir, wires_verilog_file_name); char* seg_wire_subckt_name = NULL; char* seg_index_str = NULL; int iseg, imodel, len_seg_subckt_name; fp = fopen(verilog_name, "w"); if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR,"(FILE:%s,LINE[%d])Failure in create Verilog netlist %s",__FILE__, __LINE__, wires_verilog_file_name); exit(1); } dump_verilog_file_header(fp,"Wires"); /* Output wire models*/ for (imodel = 0; imodel < num_spice_model; imodel++) { /* Bypass user-defined spice models */ if (NULL != spice_models[imodel].verilog_netlist) { continue; } if (SPICE_MODEL_WIRE == spice_models[imodel].type) { assert(NULL != spice_models[imodel].wire_param); dump_verilog_wire_module(fp, spice_models[imodel].name, spice_models[imodel]); } } /* Create wire models for routing segments*/ fprintf(fp,"//----- Wire models for segments in routing -----\n"); for (iseg = 0; iseg < num_segments; iseg++) { assert(NULL != segments[iseg].spice_model); assert(SPICE_MODEL_CHAN_WIRE == segments[iseg].spice_model->type); assert(NULL != segments[iseg].spice_model->wire_param); /* Give a unique name for subckt of wire_model of segment, * spice_model name is unique, and segment name is unique as well */ seg_index_str = my_itoa(iseg); len_seg_subckt_name = strlen(segments[iseg].spice_model->name) + 4 + strlen(seg_index_str) + 1; /* '\0'*/ seg_wire_subckt_name = (char*)my_malloc(sizeof(char)*len_seg_subckt_name); sprintf(seg_wire_subckt_name,"%s_seg%s", segments[iseg].spice_model->name, seg_index_str); /* Bypass user-defined spice models */ if (NULL != segments[iseg].spice_model->verilog_netlist) { continue; } dump_verilog_wire_module(fp, seg_wire_subckt_name, *(segments[iseg].spice_model)); } /* Create module for hard-wired VDD and GND */ for (imodel = 0; imodel < num_spice_model; imodel++) { if (SPICE_MODEL_VDD == spice_models[imodel].type) { dump_verilog_hard_wired_vdd(fp, spice_models[imodel]); } else if (SPICE_MODEL_GND == spice_models[imodel].type) { dump_verilog_hard_wired_gnd(fp, spice_models[imodel]); } } /* Close the file handler */ fclose(fp); /* Add fname to the linked list */ submodule_verilog_subckt_file_path_head = add_one_subckt_file_name_to_llist(submodule_verilog_subckt_file_path_head, verilog_name); /*Free*/ my_free(seg_index_str); my_free(seg_wire_subckt_name); return; } /* Dump verilog files of submodules to be used in FPGA components : * 1. MUXes */ void dump_verilog_submodules(char* submodule_dir, t_arch Arch, t_det_routing_arch* routing_arch, boolean include_timing, boolean init_sim) { /* 0. basic units: inverter, buffers and pass-gate logics, */ vpr_printf(TIO_MESSAGE_INFO, "Generating essential modules...\n"); dump_verilog_submodule_essentials(submodule_dir, Arch.spice->num_spice_model, Arch.spice->spice_models, include_timing, init_sim); /* 1. MUXes */ vpr_printf(TIO_MESSAGE_INFO, "Generating modules of multiplexers...\n"); dump_verilog_submodule_muxes(submodule_dir, routing_arch->num_switch, switch_inf, Arch.spice, routing_arch); /* 2. LUTes */ vpr_printf(TIO_MESSAGE_INFO, "Generating modules of LUTs...\n"); dump_verilog_submodule_luts(submodule_dir, Arch.spice->num_spice_model, Arch.spice->spice_models, include_timing); /* 3. Hardwires */ vpr_printf(TIO_MESSAGE_INFO, "Generating modules of hardwires...\n"); dump_verilog_submodule_wires(submodule_dir, Arch.num_segments, Arch.Segments, Arch.spice->num_spice_model, Arch.spice->spice_models); /*Create a header file to include all the subckts */ vpr_printf(TIO_MESSAGE_INFO,"Generating header file for basic submodules...\n"); dump_verilog_subckt_header_file(submodule_verilog_subckt_file_path_head, submodule_dir, submodule_verilog_file_name); return; }