| /* |
| Permission is hereby granted, free of charge, to any person |
| obtaining a copy of this software and associated documentation |
| files (the "Software"), to deal in the Software without |
| restriction, including without limitation the rights to use, |
| copy, modify, merge, publish, distribute, sublicense, and/or sell |
| copies of the Software, and to permit persons to whom the |
| Software is furnished to do so, subject to the following |
| conditions: |
| |
| The above copyright notice and this permission notice shall be |
| included in all copies or substantial portions of the Software. |
| |
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
| EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES |
| OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
| NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT |
| HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, |
| WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
| FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR |
| OTHER DEALINGS IN THE SOFTWARE. |
| */ |
| #include <string.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <stdarg.h> |
| #include <algorithm> |
| #include "odin_types.h" |
| #include "odin_globals.h" |
| |
| #include "netlist_utils.h" |
| #include "node_creation_library.h" |
| #include "odin_util.h" |
| |
| #include "partial_map.h" |
| #include "multipliers.h" |
| #include "hard_blocks.h" |
| #include "math.h" |
| #include "memories.h" |
| #include "adders.h" |
| #include "subtractions.h" |
| #include "vtr_memory.h" |
| #include "vtr_util.h" |
| |
| void depth_first_traversal_to_partial_map(short marker_value, netlist_t *netlist); |
| void depth_first_traverse_partial_map(nnode_t *node, int traverse_mark_number, netlist_t *netlist); |
| |
| void partial_map_node(nnode_t *node, short traverse_number, netlist_t *netlist); |
| |
| void instantiate_not_logic(nnode_t *node, short mark, netlist_t *netlist); |
| void instantiate_buffer(nnode_t *node, short mark, netlist_t *netlist); |
| void instantiate_bitwise_logic(nnode_t *node, operation_list op, short mark, netlist_t *netlist); |
| void instantiate_bitwise_reduction(nnode_t *node, operation_list op, short mark, netlist_t *netlist); |
| void instantiate_logical_logic(nnode_t *node, operation_list op, short mark, netlist_t *netlist); |
| void instantiate_EQUAL(nnode_t *node, operation_list type, short mark, netlist_t *netlist); |
| void instantiate_GE(nnode_t *node, operation_list type, short mark, netlist_t *netlist); |
| void instantiate_GT(nnode_t *node, operation_list type, short mark, netlist_t *netlist); |
| void instantiate_shift_left_or_right(nnode_t *node, operation_list type, short mark, netlist_t *netlist); |
| void instantiate_arithmetic_shift_right(nnode_t *node, short mark, netlist_t *netlist); |
| void instantiate_unary_sub(nnode_t *node, short mark, netlist_t *netlist); |
| void instantiate_sub_w_carry(nnode_t *node, short mark, netlist_t *netlist); |
| |
| void instantiate_soft_logic_ram(nnode_t *node, short mark, netlist_t *netlist); |
| |
| /*------------------------------------------------------------------------- |
| * (function: partial_map_top) |
| *-----------------------------------------------------------------------*/ |
| void partial_map_top(netlist_t *netlist) |
| { |
| /* depending on the output target choose how to do partial mapping */ |
| if (strcmp(configuration.output_type.c_str(), "blif") == 0) |
| { |
| /* do the partial map without any larger structures identified */ |
| depth_first_traversal_to_partial_map(PARTIAL_MAP_TRAVERSE_VALUE, netlist); |
| } |
| } |
| |
| /*--------------------------------------------------------------------------------------------- |
| * (function: depth_first_traversal_to_parital_map() |
| *-------------------------------------------------------------------------------------------*/ |
| void depth_first_traversal_to_partial_map(short marker_value, netlist_t *netlist) |
| { |
| int i; |
| |
| /* start with the primary input list */ |
| for (i = 0; i < netlist->num_top_input_nodes; i++) |
| { |
| if (netlist->top_input_nodes[i] != NULL) |
| { |
| depth_first_traverse_partial_map(netlist->top_input_nodes[i], marker_value, netlist); |
| } |
| } |
| /* now traverse the ground and vcc pins */ |
| depth_first_traverse_partial_map(netlist->gnd_node, marker_value, netlist); |
| depth_first_traverse_partial_map(netlist->vcc_node, marker_value, netlist); |
| depth_first_traverse_partial_map(netlist->pad_node, marker_value, netlist); |
| } |
| |
| /*--------------------------------------------------------------------------------------------- |
| * (function: depth_first_traverse) |
| *-------------------------------------------------------------------------------------------*/ |
| void depth_first_traverse_partial_map(nnode_t *node, int traverse_mark_number, netlist_t *netlist) |
| { |
| int i, j; |
| |
| if (node->traverse_visited != traverse_mark_number) |
| { |
| /*this is a new node so depth visit it */ |
| |
| /* mark that we have visitied this node now */ |
| node->traverse_visited = traverse_mark_number; |
| |
| for (i = 0; i < node->num_output_pins; i++) |
| { |
| if (node->output_pins[i]->net) |
| { |
| nnet_t *next_net = node->output_pins[i]->net; |
| if(next_net->fanout_pins) |
| { |
| for (j = 0; j < next_net->num_fanout_pins; j++) |
| { |
| if (next_net->fanout_pins[j]) |
| { |
| if (next_net->fanout_pins[j]->node) |
| { |
| /* recursive call point */ |
| depth_first_traverse_partial_map(next_net->fanout_pins[j]->node, traverse_mark_number, netlist); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| /* POST traverse map the node since you might delete */ |
| partial_map_node(node, traverse_mark_number, netlist); |
| } |
| } |
| |
| /*---------------------------------------------------------------------- |
| * (function: partial_map_node) |
| *--------------------------------------------------------------------*/ |
| void partial_map_node(nnode_t *node, short traverse_number, netlist_t *netlist) |
| { |
| switch (node->type) |
| { |
| case BITWISE_NOT: |
| instantiate_not_logic(node, traverse_number, netlist); |
| break; |
| case BUF_NODE: |
| instantiate_buffer(node, traverse_number, netlist); |
| break; |
| |
| case BITWISE_AND: |
| case BITWISE_OR: |
| case BITWISE_NAND: |
| case BITWISE_NOR: |
| case BITWISE_XNOR: |
| case BITWISE_XOR: |
| if (node->num_input_port_sizes >= 2) |
| { |
| instantiate_bitwise_logic(node, node->type, traverse_number, netlist); |
| } |
| else if (node->num_input_port_sizes == 1) |
| { |
| instantiate_bitwise_reduction(node, node->type, traverse_number, netlist); |
| } |
| else |
| oassert(false); |
| break; |
| |
| case LOGICAL_OR: |
| case LOGICAL_AND: |
| case LOGICAL_NOR: |
| case LOGICAL_NAND: |
| case LOGICAL_XOR: |
| case LOGICAL_XNOR: |
| if (node->num_input_port_sizes == 2) |
| { |
| instantiate_logical_logic(node, node->type, traverse_number, netlist); |
| } |
| break; |
| |
| case LOGICAL_NOT: |
| /* don't need to do anything since this is a reduction */ |
| break; |
| |
| case ADD: |
| if (hard_adders && node->bit_width >= min_threshold_adder){ |
| // Check if the size of this adder is greater than the hard vs soft logic threshold |
| instantiate_hard_adder(node, traverse_number, netlist); |
| }else{ |
| instantiate_add_w_carry(node, traverse_number, netlist); |
| } |
| break; |
| case MINUS: |
| if (hard_adders) |
| { |
| if(node->num_input_port_sizes == 3) |
| { |
| int max_num = (node->input_port_sizes[0] >= node->input_port_sizes[1])? node->input_port_sizes[0] : node->input_port_sizes[1]; |
| if (max_num >= min_add) |
| instantiate_hard_adder_subtraction(node, traverse_number, netlist); |
| else |
| instantiate_add_w_carry(node, traverse_number, netlist); |
| } |
| else if (node->num_input_port_sizes == 2) |
| { |
| instantiate_sub_w_carry(node, traverse_number, netlist); |
| } |
| else if (node->num_input_port_sizes == 1) |
| { |
| instantiate_unary_sub(node, traverse_number, netlist); |
| } |
| else |
| oassert(false); |
| } |
| else{ |
| if (node->num_input_port_sizes == 2) |
| { |
| instantiate_sub_w_carry(node, traverse_number, netlist); |
| } |
| else if (node->num_input_port_sizes == 1) |
| { |
| instantiate_unary_sub(node, traverse_number, netlist); |
| } |
| else |
| oassert(false); |
| } |
| |
| break; |
| case LOGICAL_EQUAL: |
| case NOT_EQUAL: |
| instantiate_EQUAL(node, node->type, traverse_number, netlist); |
| break; |
| case GTE: |
| case LTE: |
| instantiate_GE(node, node->type, traverse_number, netlist); |
| break; |
| case GT: |
| case LT: |
| instantiate_GT(node, node->type, traverse_number, netlist); |
| break; |
| case SL: |
| case SR: |
| instantiate_shift_left_or_right(node, node->type, traverse_number, netlist); |
| break; |
| case ASR: |
| instantiate_arithmetic_shift_right(node, node->type, netlist); |
| break; |
| case MULTI_PORT_MUX: |
| instantiate_multi_port_mux(node, traverse_number, netlist); |
| break; |
| case MULTIPLY: |
| { |
| int mult_size = std::max<int>(node->input_port_sizes[0], node->input_port_sizes[1]); |
| if (hard_multipliers && mult_size >= min_mult) { |
| instantiate_hard_multiplier(node, traverse_number, netlist); |
| } else if (!hard_adders) { |
| instantiate_simple_soft_multiplier(node, traverse_number, netlist); |
| } |
| break; |
| } |
| case MEMORY: |
| { |
| ast_node_t *ast_node = node->related_ast_node; |
| char *identifier = ast_node->children[0]->types.identifier; |
| if (find_hard_block(identifier)) |
| { |
| long depth = is_sp_ram(node)? get_sp_ram_depth(node) : get_dp_ram_depth(node); |
| long width = is_sp_ram(node)? get_sp_ram_width(node) : get_dp_ram_width(node); |
| |
| // If the memory satisfies the threshold for the use of a hard logic block, use one. |
| if (depth > configuration.soft_logic_memory_depth_threshold || width > configuration.soft_logic_memory_width_threshold) |
| { |
| instantiate_hard_block(node, traverse_number, netlist); |
| } |
| else |
| { |
| printf("\tInferring soft logic ram: %zux%zu\n", width, depth); |
| instantiate_soft_logic_ram(node, traverse_number, netlist); |
| } |
| } |
| else |
| { |
| instantiate_soft_logic_ram(node, traverse_number, netlist); |
| } |
| break; |
| } |
| case HARD_IP: |
| instantiate_hard_block(node, traverse_number, netlist); |
| |
| break; |
| case ADDER_FUNC: |
| case CARRY_FUNC: |
| case MUX_2: |
| case INPUT_NODE: |
| case CLOCK_NODE: |
| case OUTPUT_NODE: |
| case GND_NODE: |
| case VCC_NODE: |
| case FF_NODE: |
| case PAD_NODE: |
| /* some nodes already in the form that is mapable */ |
| break; |
| case CASE_EQUAL: |
| case CASE_NOT_EQUAL: |
| case DIVIDE: |
| case MODULO: |
| default: |
| error_message(NETLIST_ERROR, 0, -1, "%s", "Partial map: node should have been converted to softer version."); |
| break; |
| } |
| } |
| |
| void instantiate_soft_logic_ram(nnode_t *node, short mark, netlist_t *netlist) |
| { |
| if (is_sp_ram(node)) |
| instantiate_soft_single_port_ram(node, mark, netlist); |
| else if (is_dp_ram(node)) |
| instantiate_soft_dual_port_ram(node, mark, netlist); |
| else |
| oassert(false); |
| } |
| |
| |
| /*--------------------------------------------------------------------------------------------- |
| * (function: instantiate_multi_port_mux ) |
| * Makes the multiport into a series of 2-Mux-decoded |
| *-------------------------------------------------------------------------------------------*/ |
| void instantiate_multi_port_mux(nnode_t *node, short mark, netlist_t * /*netlist*/) |
| { |
| int i, j; |
| int width_of_one_hot_logic; |
| int num_ports; |
| int port_offset; |
| nnode_t **muxes; |
| |
| /* setup the calculations for padding and indexing */ |
| width_of_one_hot_logic = node->input_port_sizes[0]; |
| num_ports = node->num_input_port_sizes; |
| port_offset = node->input_port_sizes[1]; |
| |
| muxes = (nnode_t**)vtr::malloc(sizeof(nnode_t*)*(num_ports-1)); |
| for(i = 0; i < num_ports-1; i++) |
| { |
| muxes[i] = make_2port_gate(MUX_2, width_of_one_hot_logic, width_of_one_hot_logic, 1, node, mark); |
| } |
| |
| for(j = 0; j < num_ports - 1; j++) |
| { |
| for(i = 0; i < width_of_one_hot_logic; i++) |
| { |
| /* map the inputs to the muxt */ |
| remap_pin_to_new_node(node->input_pins[i+(j+1)*port_offset], muxes[j], width_of_one_hot_logic+i); |
| |
| /* map the one hot logic control */ |
| if (j == 0) |
| remap_pin_to_new_node(node->input_pins[i], muxes[j], i); |
| else |
| add_input_pin_to_node(muxes[j], copy_input_npin(muxes[0]->input_pins[i]), i); |
| } |
| |
| /* now hookup outputs */ |
| remap_pin_to_new_node(node->output_pins[j], muxes[j], 0); |
| } |
| vtr::free(muxes); |
| free_nnode(node); |
| } |
| |
| /*--------------------------------------------------------------------------------------------- |
| * (function: instantiate_not_logic ) |
| *-------------------------------------------------------------------------------------------*/ |
| void instantiate_not_logic(nnode_t *node, short mark, netlist_t * /*netlist*/) |
| { |
| int width = node->num_input_pins; |
| nnode_t **new_not_cells; |
| int i; |
| |
| new_not_cells = (nnode_t**)vtr::malloc(sizeof(nnode_t*)*width); |
| |
| for (i = 0; i < width; i++) |
| { |
| new_not_cells[i] = make_not_gate(node, mark); |
| } |
| |
| /* connect inputs and outputs */ |
| for(i = 0; i < width; i++) |
| { |
| /* Joining the inputs to the new soft NOT GATES */ |
| remap_pin_to_new_node(node->input_pins[i], new_not_cells[i], 0); |
| remap_pin_to_new_node(node->output_pins[i], new_not_cells[i], 0); |
| } |
| |
| vtr::free(new_not_cells); |
| free_nnode(node); |
| } |
| |
| /*--------------------------------------------------------------------------------------------- |
| * (function: instantiate_buffer ) |
| * Buffers just pass through signals |
| *-------------------------------------------------------------------------------------------*/ |
| void instantiate_buffer(nnode_t *node, short /*mark*/, netlist_t * /*netlist*/) |
| { |
| int width = node->num_input_pins; |
| int i; |
| |
| /* for now we just pass the signals directly through */ |
| for (i = 0; i < width; i++) |
| { |
| int idx_2_buffer = node->input_pins[i]->pin_net_idx; |
| |
| /* join all fanouts of the output net with the input pins net */ |
| join_nets(node->input_pins[i]->net, node->output_pins[i]->net); |
| |
| /* erase the pointer to this buffer */ |
| node->input_pins[i]->net->fanout_pins[idx_2_buffer] = NULL; |
| } |
| } |
| |
| /*--------------------------------------------------------------------------------------------- |
| * (function: instantiate_logical_logic ) |
| *-------------------------------------------------------------------------------------------*/ |
| void instantiate_logical_logic(nnode_t *node, operation_list op, short mark, netlist_t *netlist) |
| { |
| int i; |
| int port_B_offset; |
| int width_a; |
| int width_b; |
| nnode_t *new_logic_cell; |
| nnode_t *reduction1; |
| nnode_t *reduction2; |
| |
| oassert(node->num_input_pins > 0); |
| oassert(node->num_input_port_sizes == 2); |
| oassert(node->num_output_pins == 1); |
| /* setup the calculations for padding and indexing */ |
| width_a = node->input_port_sizes[0]; |
| width_b = node->input_port_sizes[1]; |
| port_B_offset = width_a; |
| |
| /* instantiate the cells */ |
| new_logic_cell = make_1port_logic_gate(op, 2, node, mark); |
| reduction1 = make_1port_logic_gate(BITWISE_OR, width_a, node, mark); |
| reduction2 = make_1port_logic_gate(BITWISE_OR, width_b, node, mark); |
| |
| /* connect inputs. In the case that a signal is smaller than the other then zero pad */ |
| for(i = 0; i < width_a; i++) |
| { |
| /* Joining the inputs to the input 1 of that gate */ |
| if (i < width_a) |
| { |
| remap_pin_to_new_node(node->input_pins[i], reduction1, i); |
| } |
| else |
| { |
| /* ELSE - the B input does not exist, so this answer goes right through */ |
| add_input_pin_to_node(reduction1, get_zero_pin(netlist), i); |
| } |
| } |
| for(i = 0; i < width_b; i++) |
| { |
| /* Joining the inputs to the input 1 of that gate */ |
| if (i < width_b) |
| { |
| remap_pin_to_new_node(node->input_pins[i+port_B_offset], reduction2, i); |
| } |
| else |
| { |
| /* ELSE - the B input does not exist, so this answer goes right through */ |
| add_input_pin_to_node(reduction2, get_zero_pin(netlist), i); |
| } |
| } |
| |
| connect_nodes(reduction1, 0, new_logic_cell, 0); |
| connect_nodes(reduction2, 0, new_logic_cell, 1); |
| |
| instantiate_bitwise_reduction(reduction1, BITWISE_OR, mark, netlist); |
| instantiate_bitwise_reduction(reduction2, BITWISE_OR, mark, netlist); |
| |
| remap_pin_to_new_node(node->output_pins[0], new_logic_cell, 0); |
| free_nnode(node); |
| } |
| /*--------------------------------------------------------------------------------------------- |
| * (function: instantiate_bitwise_reduction ) |
| * Makes 2 input gates to break into bitwise |
| *-------------------------------------------------------------------------------------------*/ |
| void instantiate_bitwise_reduction(nnode_t *node, operation_list op, short mark, netlist_t *netlist) |
| { |
| int i; |
| int width_a; |
| nnode_t *new_logic_cell; |
| operation_list cell_op; |
| |
| oassert(node->num_input_pins > 0); |
| oassert(node->num_input_port_sizes == 1); |
| oassert(node->output_port_sizes[0] == 1); |
| /* setup the calculations for padding and indexing */ |
| width_a = node->input_port_sizes[0]; |
| |
| switch (op) |
| { |
| case BITWISE_AND: |
| case LOGICAL_AND: |
| cell_op = LOGICAL_AND; |
| break; |
| case BITWISE_OR: |
| case LOGICAL_OR: |
| cell_op = LOGICAL_OR; |
| break; |
| case BITWISE_NAND: |
| case LOGICAL_NAND: |
| cell_op = LOGICAL_NAND; |
| break; |
| case BITWISE_NOR: |
| case LOGICAL_NOR: |
| cell_op = LOGICAL_NOR; |
| break; |
| case BITWISE_XNOR: |
| case LOGICAL_XNOR: |
| cell_op = LOGICAL_XNOR; |
| break; |
| case BITWISE_XOR: |
| case LOGICAL_XOR: |
| cell_op = LOGICAL_XOR; |
| break; |
| default: |
| cell_op = NO_OP; |
| oassert(false); |
| break; |
| } |
| /* instantiate the cells */ |
| new_logic_cell = make_1port_logic_gate(cell_op, width_a, node, mark); |
| |
| /* connect inputs. In the case that a signal is smaller than the other then zero pad */ |
| for(i = 0; i < width_a; i++) |
| { |
| /* Joining the inputs to the input 1 of that gate */ |
| if (i < width_a) |
| { |
| remap_pin_to_new_node(node->input_pins[i], new_logic_cell, i); |
| } |
| else |
| { |
| /* ELSE - the B input does not exist, so this answer goes right through */ |
| add_input_pin_to_node(new_logic_cell, get_zero_pin(netlist), i); |
| } |
| } |
| |
| remap_pin_to_new_node(node->output_pins[0], new_logic_cell, 0); |
| free_nnode(node); |
| } |
| |
| /*--------------------------------------------------------------------------------------------- |
| * (function: instantiate_bitwise_logic ) |
| * Makes 2 input gates to break into bitwise |
| *-------------------------------------------------------------------------------------------*/ |
| void instantiate_bitwise_logic(nnode_t *node, operation_list op, short mark, netlist_t *netlist) |
| { |
| int i, j; |
| |
| operation_list cell_op; |
| if (!node) return; |
| oassert(node->num_input_pins > 0); |
| oassert(node->num_input_port_sizes >= 2); |
| |
| switch (op) |
| { |
| case BITWISE_AND: |
| cell_op = LOGICAL_AND; |
| break; |
| case BITWISE_OR: |
| cell_op = LOGICAL_OR; |
| break; |
| case BITWISE_NAND: |
| cell_op = LOGICAL_NAND; |
| break; |
| case BITWISE_NOR: |
| cell_op = LOGICAL_NOR; |
| break; |
| case BITWISE_XNOR: |
| cell_op = LOGICAL_XNOR; |
| break; |
| case BITWISE_XOR: |
| cell_op = LOGICAL_XOR; |
| break; |
| default: |
| cell_op = NO_OP; |
| oassert(false); |
| break; |
| } |
| |
| /* connect inputs. In the case that a signal is smaller than the other then zero pad */ |
| for(i = 0; i < node->output_port_sizes[0]; i++) |
| { |
| nnode_t *new_logic_cells = make_nport_gate(cell_op, node->num_input_port_sizes, 1, 1, node, mark); |
| int current_port_offset = 0; |
| /* Joining the inputs to the input 1 of that gate */ |
| for(j = 0; j < node->num_input_port_sizes; j++) |
| { |
| /* IF - this current input will also have a corresponding other input ports then join it to the gate */ |
| if(i < node->input_port_sizes[j]) |
| remap_pin_to_new_node(node->input_pins[i+current_port_offset], new_logic_cells, j); |
| |
| /* ELSE - the input does not exist, so this answer goes right through */ |
| else |
| add_input_pin_to_node(new_logic_cells, get_zero_pin(netlist), j); |
| |
| current_port_offset += node->input_port_sizes[j]; |
| } |
| |
| remap_pin_to_new_node(node->output_pins[i], new_logic_cells, 0); |
| } |
| |
| free_nnode(node); |
| } |
| |
| /*-------------------------------------------------------------------------- |
| * (function: instantiate_add_w_carry ) |
| * This is for soft addition in output formats that don't handle |
| * multi-output logic functions (BLIF). We use one function for the |
| * add, and one for the carry. |
| *------------------------------------------------------------------------*/ |
| void instantiate_add_w_carry(nnode_t *node, short mark, netlist_t *netlist) |
| { |
| // define locations in array when fetching pins |
| const int out = 0, input_a = 1, input_b = 2, pinout_count = 3; |
| |
| oassert(node->num_input_pins > 0); |
| |
| int *width = (int*)vtr::malloc(pinout_count * sizeof(int)); |
| |
| if(node->num_input_port_sizes == 2) |
| width[out] = node->output_port_sizes[0]; |
| else |
| width[out] = node->num_output_pins; |
| |
| width[input_a] = node->input_port_sizes[0]; |
| width[input_b] = node->input_port_sizes[1]; |
| |
| instantiate_add_w_carry_block(width, node, mark, netlist, 0); |
| |
| vtr::free(width); |
| } |
| |
| /*--------------------------------------------------------------------------------------------- |
| * (function: instantiate_sub_w_carry ) |
| * This subtraction is intended for sof subtraction with output formats that can't handle |
| * multi output logic functions. We split the add and the carry over two logic functions. |
| *-------------------------------------------------------------------------------------------*/ |
| void instantiate_sub_w_carry(nnode_t *node, short mark, netlist_t *netlist) |
| { |
| // define locations in array when fetching pins |
| const int out = 0, input_a = 1, input_b = 2, pinout_count = 3; |
| |
| oassert(node->num_input_pins > 0); |
| |
| int *width = (int*)vtr::malloc(pinout_count * sizeof(int)); |
| width[out] = node->output_port_sizes[0]; |
| |
| if(node->num_input_port_sizes == 1) |
| { |
| width[input_a] = 0; |
| width[input_b] = node->input_port_sizes[0]; |
| } |
| else if(node->num_input_port_sizes == 2) |
| { |
| width[input_a] = node->input_port_sizes[0]; |
| width[input_b] = node->input_port_sizes[1]; |
| } |
| |
| instantiate_add_w_carry_block(width, node, mark, netlist, 1); |
| |
| vtr::free(width); |
| } |
| |
| /*--------------------------------------------------------------------------------------------- |
| * (function: instantiate_unary_sub ) |
| * Does 2's complement which is the equivalent of a unary subtraction as a HW implementation. |
| *-------------------------------------------------------------------------------------------*/ |
| void instantiate_unary_sub(nnode_t *node, short mark, netlist_t *netlist) |
| { |
| instantiate_sub_w_carry(node, mark, netlist); |
| } |
| |
| |
| /*--------------------------------------------------------------------------------------------- |
| * (function: instantiate_EQUAL ) |
| * Builds the hardware for an equal comparison by building EQ for parallel lines and then |
| * taking them all through an AND tree. |
| *-------------------------------------------------------------------------------------------*/ |
| void instantiate_EQUAL(nnode_t *node, operation_list type, short mark, netlist_t *netlist) |
| { |
| int width_a; |
| int width_b; |
| int width_max; |
| int i; |
| int port_B_offset; |
| nnode_t *compare; |
| nnode_t *combine; |
| |
| oassert(node->num_output_pins == 1); |
| oassert(node->num_input_pins > 0); |
| oassert(node->num_input_port_sizes == 2); |
| width_a = node->input_port_sizes[0]; |
| width_b = node->input_port_sizes[1]; |
| width_max = width_a > width_b ? width_a : width_b; |
| port_B_offset = width_a; |
| |
| /* build an xnor bitwise XNOR */ |
| if (type == LOGICAL_EQUAL) |
| { |
| compare = make_2port_gate(LOGICAL_XNOR, width_a, width_b, width_max, node, mark); |
| combine = make_1port_logic_gate(LOGICAL_AND, width_max, node, mark); |
| } |
| else |
| { |
| compare = make_2port_gate(LOGICAL_XOR, width_a, width_b, width_max, node, mark); |
| combine = make_1port_logic_gate(LOGICAL_OR, width_max, node, mark); |
| } |
| /* build an and bitwise AND */ |
| |
| |
| /* connect inputs. In the case that a signal is smaller than the other then zero pad */ |
| for(i = 0; i < width_max; i++) |
| { |
| /* Joining the inputs to the input 1 of that gate */ |
| if (i < width_a) |
| { |
| if (i < width_b) |
| { |
| /* IF - this current input will also have a corresponding b_port input then join it to the gate */ |
| remap_pin_to_new_node(node->input_pins[i], compare, i); |
| } |
| else |
| { |
| /* ELSE - the B input does not exist, so this answer goes right through */ |
| add_input_pin_to_node(compare, get_zero_pin(netlist), i); |
| } |
| } |
| |
| if (i < width_b) |
| { |
| if (i < width_a) |
| { |
| /* IF - this current input will also have a corresponding a_port input then join it to the gate */ |
| /* Joining the inputs to the input 2 of that gate */ |
| remap_pin_to_new_node(node->input_pins[i+port_B_offset], compare, i+port_B_offset); |
| } |
| else |
| { |
| /* ELSE - the A input does not exist, so this answer goes right through */ |
| add_input_pin_to_node(compare, get_zero_pin(netlist), i+port_B_offset); |
| } |
| } |
| |
| /* hook it up to the logcial AND */ |
| connect_nodes(compare, i, combine, i); |
| } |
| |
| /* join that gate to the output */ |
| remap_pin_to_new_node(node->output_pins[0], combine, 0); |
| |
| if (type == LOGICAL_EQUAL) |
| instantiate_bitwise_logic(compare, BITWISE_XNOR, mark, netlist); |
| else |
| instantiate_bitwise_logic(compare, BITWISE_XOR, mark, netlist); |
| /* Don't need to instantiate a Logic and gate since it is a function itself */ |
| |
| oassert(combine->num_output_pins == 1); |
| |
| free_nnode(node); |
| } |
| |
| /*--------------------------------------------------------------------------------------------- |
| * (function: instantiate_GT ) |
| * Defines the HW needed for greter than equal with EQ, GT, AND and OR gates to create |
| * the appropriate logic function. |
| *-------------------------------------------------------------------------------------------*/ |
| void instantiate_GT(nnode_t *node, operation_list type, short mark, netlist_t *netlist) |
| { |
| int width_a; |
| int width_b; |
| int width_max; |
| int i; |
| int port_A_offset; |
| int port_B_offset; |
| int port_A_index; |
| int port_B_index; |
| int index = 0; |
| nnode_t *xor_gate=NULL; |
| nnode_t *logical_or_gate; |
| nnode_t **or_cells; |
| nnode_t **gt_cells; |
| |
| oassert(node->num_output_pins == 1); |
| oassert(node->num_input_pins > 0); |
| oassert(node->num_input_port_sizes == 2); |
| oassert(node->input_port_sizes[0] == node->input_port_sizes[1]); |
| width_a = node->input_port_sizes[0]; |
| width_b = node->input_port_sizes[1]; |
| width_max = width_a > width_b ? width_a : width_b; |
| |
| /* swaps ports A and B */ |
| if (type == GT) |
| { |
| port_A_offset = 0; |
| port_B_offset = width_a; |
| port_A_index = 0; |
| port_B_index = width_a-1; |
| } |
| else if (type == LT) |
| { |
| port_A_offset = width_b; |
| port_B_offset = 0; |
| port_A_index = width_b-1; |
| port_B_index = 0; |
| } |
| else |
| { |
| port_A_offset = 0; |
| port_B_offset = 0; |
| port_A_index = 0; |
| port_B_index = 0; |
| error_message(NETLIST_ERROR, node->related_ast_node->line_number, node->related_ast_node->file_number, "Invalid node type %s in instantiate_GT\n", |
| node_name_based_on_op(node)); |
| } |
| |
| if (width_max>1) |
| { |
| /* xor gate identifies if any bits don't match */ |
| xor_gate = make_2port_gate(LOGICAL_XOR, width_a-1, width_b-1, width_max-1, node, mark); |
| } |
| |
| /* collects all the GT signals and determines if gt */ |
| logical_or_gate = make_1port_logic_gate(LOGICAL_OR, width_max, node, mark); |
| /* collects a chain if any 1 happens than the GT cells output 0 */ |
| or_cells = (nnode_t**)vtr::malloc(sizeof(nnode_t*)*width_max-1); |
| /* each cell checks if A > B and sends out a 1 if history has no 1s (3rd input) */ |
| gt_cells = (nnode_t**)vtr::malloc(sizeof(nnode_t*)*width_max); |
| |
| for (i = 0; i < width_max; i++) |
| { |
| gt_cells[i] = make_3port_gate(GT, 1, 1, 1, 1, node, mark); |
| if (i < width_max-1) |
| { |
| or_cells[i] = make_2port_gate(LOGICAL_OR, 1, 1, 1, node, mark); |
| } |
| } |
| |
| /* connect inputs. In the case that a signal is smaller than the other then zero pad */ |
| for(i = 0; i < width_max; i++) |
| { |
| /* Joining the inputs to the input 1 of that gate */ |
| if (i < width_a) |
| { |
| /* IF - this current input will also have a corresponding b_port input then join it to the gate */ |
| remap_pin_to_new_node(node->input_pins[i+port_A_offset], gt_cells[i], 0); |
| if (i > 0) |
| add_input_pin_to_node(xor_gate, copy_input_npin(gt_cells[i]->input_pins[0]), index+port_A_index); |
| } |
| else |
| { |
| /* ELSE - the B input does not exist, so this answer goes right through */ |
| add_input_pin_to_node(gt_cells[i], get_zero_pin(netlist), 0); |
| if (i > 0) |
| add_input_pin_to_node(xor_gate, get_zero_pin(netlist), index+port_A_index); |
| } |
| |
| if (i < width_b) |
| { |
| /* IF - this current input will also have a corresponding a_port input then join it to the gate */ |
| /* Joining the inputs to the input 2 of that gate */ |
| remap_pin_to_new_node(node->input_pins[i+port_B_offset], gt_cells[i], 1); |
| if (i > 0) |
| add_input_pin_to_node(xor_gate, copy_input_npin(gt_cells[i]->input_pins[1]), index+port_B_index); |
| } |
| else |
| { |
| /* ELSE - the A input does not exist, so this answer goes right through */ |
| add_input_pin_to_node(gt_cells[i], get_zero_pin(netlist), 1); |
| if (i > 0) |
| add_input_pin_to_node(xor_gate, get_zero_pin(netlist), index+port_B_index); |
| } |
| |
| if (i < width_max-1) |
| { |
| /* number of OR gates */ |
| if (i < width_max-2) |
| { |
| /* connect the msb or to the next lower bit */ |
| connect_nodes(or_cells[i+1], 0, or_cells[i], 1); |
| } |
| else |
| { |
| /* deal with the first greater than test which autom gets a zero */ |
| add_input_pin_to_node(or_cells[i], get_zero_pin(netlist), 1); |
| } |
| if (width_max>1) |
| { |
| /* get all the equals with the or gates */ |
| connect_nodes(xor_gate, i, or_cells[i], 0); |
| } |
| |
| connect_nodes(or_cells[i], 0, gt_cells[i], 2); |
| |
| } |
| else |
| { |
| /* deal with the first greater than test which autom gets a zero */ |
| add_input_pin_to_node(gt_cells[i], get_zero_pin(netlist), 2); |
| } |
| |
| /* hook it up to the logcial AND */ |
| connect_nodes(gt_cells[i], 0, logical_or_gate, i); |
| |
| if (i > 0) |
| { |
| index++; |
| } |
| } |
| |
| /* join that gate to the output */ |
| remap_pin_to_new_node(node->output_pins[0], logical_or_gate, 0); |
| oassert(logical_or_gate->num_output_pins == 1); |
| if (xor_gate!= NULL) |
| { |
| instantiate_bitwise_logic(xor_gate, BITWISE_XOR, mark, netlist); |
| } |
| |
| vtr::free(gt_cells); |
| vtr::free(or_cells); |
| free_nnode(node); |
| } |
| |
| /*--------------------------------------------------------------------------------------------- |
| * (function: instantiate_GE ) |
| * Defines the HW needed for greter than equal with EQ, GT, AND and OR gates to create |
| * the appropriate logic function. |
| *-------------------------------------------------------------------------------------------*/ |
| void instantiate_GE(nnode_t *node, operation_list type, short mark, netlist_t *netlist) |
| { |
| int width_a; |
| int width_b; |
| int width_max; |
| int i; |
| int port_B_offset; |
| int port_A_offset; |
| nnode_t *equal; |
| nnode_t *compare; |
| nnode_t *logical_or_final_gate; |
| |
| oassert(node->num_output_pins == 1); |
| oassert(node->num_input_pins > 0); |
| oassert(node->num_input_port_sizes == 2); |
| oassert(node->input_port_sizes[0] == node->input_port_sizes[1]); |
| width_a = node->input_port_sizes[0]; |
| width_b = node->input_port_sizes[1]; |
| oassert(width_a == width_b); |
| width_max = width_a > width_b ? width_a : width_b; |
| |
| port_A_offset = 0; |
| port_B_offset = width_a; |
| |
| /* build an xnor bitwise XNOR */ |
| equal = make_2port_gate(LOGICAL_EQUAL, width_a, width_b, 1, node, mark); |
| |
| if (type == GTE) compare = make_2port_gate(GT, width_a, width_b, 1, node, mark); |
| else compare = make_2port_gate(LT, width_a, width_b, 1, node, mark); |
| |
| logical_or_final_gate = make_1port_logic_gate(LOGICAL_OR, 2, node, mark); |
| |
| /* connect inputs. In the case that a signal is smaller than the other then zero pad */ |
| for(i = 0; i < width_max; i++) |
| { |
| /* Joining the inputs to the input 1 of that gate */ |
| if (i < width_a) |
| { |
| /* IF - this current input will also have a corresponding b_port input then join it to the gate */ |
| remap_pin_to_new_node(node->input_pins[i+port_A_offset], equal, i+port_A_offset); |
| add_input_pin_to_node(compare, copy_input_npin(equal->input_pins[i+port_A_offset]), i+port_A_offset); |
| } |
| else |
| { |
| /* ELSE - the B input does not exist, so this answer goes right through */ |
| add_input_pin_to_node(equal, get_zero_pin(netlist), i+port_A_offset); |
| add_input_pin_to_node(compare, get_zero_pin(netlist), i+port_A_offset); |
| } |
| |
| if (i < width_b) |
| { |
| /* IF - this current input will also have a corresponding a_port input then join it to the gate */ |
| /* Joining the inputs to the input 2 of that gate */ |
| remap_pin_to_new_node(node->input_pins[i+port_B_offset], equal, i+port_B_offset); |
| add_input_pin_to_node(compare, copy_input_npin(equal->input_pins[i+port_B_offset]), i+port_B_offset); |
| } |
| else |
| { |
| /* ELSE - the A input does not exist, so this answer goes right through */ |
| add_input_pin_to_node(equal, get_zero_pin(netlist), i+port_B_offset); |
| add_input_pin_to_node(compare, get_zero_pin(netlist), i+port_B_offset); |
| } |
| } |
| connect_nodes(equal, 0, logical_or_final_gate, 0); |
| connect_nodes(compare, 0, logical_or_final_gate, 1); |
| |
| /* join that gate to the output */ |
| remap_pin_to_new_node(node->output_pins[0], logical_or_final_gate, 0); |
| oassert(logical_or_final_gate->num_output_pins == 1); |
| |
| /* make the two intermediate gates */ |
| instantiate_EQUAL(equal, LOGICAL_EQUAL, mark, netlist); |
| |
| if (type == GTE) instantiate_GT(compare, GT, mark, netlist); |
| else instantiate_GT(compare, LT, mark, netlist); |
| |
| free_nnode(node); |
| } |
| |
| /*--------------------------------------------------------------------------------------------- |
| * (function: instantiate_shift_left_or_right ) |
| * Creates the hardware for a shift left or right operation by a constant size. |
| *-------------------------------------------------------------------------------------------*/ |
| void instantiate_shift_left_or_right(nnode_t *node, operation_list type, short mark, netlist_t *netlist) |
| { |
| /* these variables are used in an attempt so that I don't need if cases. Probably a bad idea, but fun */ |
| int width; |
| int i; |
| int shift_size; |
| nnode_t *buf_node; |
| |
| width = node->input_port_sizes[0]; |
| |
| if (node->related_ast_node->children[1]->type == NUMBERS) |
| { |
| /* record the size of the shift */ |
| shift_size = node->related_ast_node->children[1]->types.vnumber->get_value(); |
| } |
| else |
| { |
| shift_size = 0; |
| error_message(NETLIST_ERROR, node->related_ast_node->line_number, node->related_ast_node->file_number, "%s\n", "Odin only supports constant shifts at present"); |
| } |
| |
| buf_node = make_1port_gate(BUF_NODE, width, width, node, mark); |
| |
| if (type == SL) |
| { |
| /* IF shift left */ |
| |
| /* connect inputs to outputs */ |
| for(i = 0; i < width - shift_size; i++) |
| { |
| // connect higher output pin to lower input pin |
| remap_pin_to_new_node(node->input_pins[i], buf_node, i+shift_size); |
| } |
| |
| /* connect ZERO to outputs that don't have inputs connected */ |
| for(i = 0; i < shift_size; i++) |
| { |
| // connect 0 to lower outputs |
| add_input_pin_to_node(buf_node, get_zero_pin(netlist), i); |
| } |
| |
| for(i = width-1; i >= width-shift_size; i--) |
| { |
| /* demap the node from the net */ |
| int idx_2_buffer = node->input_pins[i]->pin_net_idx; |
| node->input_pins[i]->net->fanout_pins[idx_2_buffer] = NULL; |
| } |
| } |
| else |
| { |
| /* ELSE shift right */ |
| |
| /* connect inputs to outputs */ |
| for(i = width - 1; i >= shift_size; i--) |
| { |
| // connect higher input pin to lower output pin |
| remap_pin_to_new_node(node->input_pins[i], buf_node, i-shift_size); |
| } |
| |
| /* connect ZERO to outputs that don't have inputs connected */ |
| for(i = width - 1; i >= width - shift_size; i--) |
| { |
| // connect 0 to lower outputs |
| add_input_pin_to_node(buf_node, get_zero_pin(netlist), i); |
| } |
| for(i = 0; i < shift_size; i++) |
| { |
| /* demap the node from the net */ |
| int idx_2_buffer = node->input_pins[i]->pin_net_idx; |
| node->input_pins[i]->net->fanout_pins[idx_2_buffer] = NULL; |
| } |
| } |
| |
| for(i = 0; i < width; i++) |
| { |
| remap_pin_to_new_node(node->output_pins[i], buf_node, i); |
| } |
| /* instantiate the buffer */ |
| instantiate_buffer(buf_node, mark, netlist); |
| |
| /* clean up */ |
| for (i = 0; i < buf_node->num_input_pins; i++) { |
| buf_node->output_pins[i]->net = free_nnet(buf_node->output_pins[i]->net); |
| buf_node->input_pins[i] = free_npin(buf_node->input_pins[i]); |
| } |
| free_nnode(buf_node); |
| free_nnode(node); |
| } |
| |
| /*--------------------------------------------------------------------------------------------- |
| * (function: instantiate_arithmatic_shift_right ) |
| * Creates the hardware for an arithmatic shift right operation by a constant size. |
| *-------------------------------------------------------------------------------------------*/ |
| void instantiate_arithmetic_shift_right(nnode_t *node, short mark, netlist_t *netlist) |
| { |
| /* these variables are used in an attempt so that I don't need if cases. Probably a bad idea, but fun */ |
| int width; |
| int i; |
| int shift_size; |
| nnode_t *buf_node; |
| width = node->input_port_sizes[0]; |
| if (node->related_ast_node->children[1]->type == NUMBERS) |
| { |
| /* record the size of the shift */ |
| shift_size = node->related_ast_node->children[1]->types.vnumber->get_value(); |
| } |
| else |
| { |
| shift_size = 0; |
| error_message(NETLIST_ERROR, node->related_ast_node->line_number, node->related_ast_node->file_number, "%s\n", "Odin only supports constant shifts at present"); |
| } |
| buf_node = make_1port_gate(BUF_NODE, width, width, node, mark); |
| /* connect inputs to outputs */ |
| for(i = width - 1; i >= shift_size; i--) |
| { |
| // connect higher input pin to lower output pin |
| remap_pin_to_new_node(node->input_pins[i], buf_node, i-shift_size); |
| } |
| |
| int pad_bit = width - 1 - shift_size; |
| |
| /* connect pad_bit pin to outputs that don't have inputs connected */ |
| for(i = width - 1; i >= width - shift_size; i--) |
| { |
| add_input_pin_to_node(buf_node, copy_input_npin(buf_node->input_pins[pad_bit]), i); |
| } |
| |
| for(i = 0; i < shift_size; i++) |
| { |
| /* demap the node from the net */ |
| int idx_2_buffer = node->input_pins[i]->pin_net_idx; |
| node->input_pins[i]->net->fanout_pins[idx_2_buffer] = NULL; |
| } |
| for(i = 0; i < width; i++) |
| { |
| remap_pin_to_new_node(node->output_pins[i], buf_node, i); |
| } |
| /* instantiate the buffer */ |
| instantiate_buffer(buf_node, mark, netlist); |
| |
| |
| /* clean up */ |
| for (i = 0; i < buf_node->num_input_pins; i++) { |
| buf_node->output_pins[i]->net = free_nnet(buf_node->output_pins[i]->net); |
| buf_node->input_pins[i] = free_npin(buf_node->input_pins[i]); |
| } |
| free_nnode(buf_node); |
| free_nnode(node); |
| } |