2019-09-10 16:16:29 -05:00
/***************************************************************************************
* This file includes functions to generate Verilog modules of decoders
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* TODO: merge verilog_decoder.c to this source file and rename to verilog_decoder.cpp */
# include <string>
# include "util.h"
# include "vtr_assert.h"
/* Device-level header files */
# include "decoder_library_utils.h"
# include "module_manager.h"
/* FPGA-X2P context header files */
# include "spice_types.h"
# include "fpga_x2p_naming.h"
# include "fpga_x2p_utils.h"
/* FPGA-Verilog context header files */
# include "verilog_global.h"
# include "verilog_writer_utils.h"
# include "verilog_decoders.h"
/***************************************************************************************
* Create a Verilog module for a decoder with a given output size
*
* Inputs
* | | . . . |
* v v v
* + - - - - - - - - - - - +
* / \
* / Decoder \
* + - - - - - - - - - - - - - - - - - +
* | | | . . . | | |
* v v v v v v
* Outputs
*
* The outputs are assumes to be one - hot codes ( at most only one ' 1 ' exist )
* Considering this fact , there are only num_of_outputs conditions to be encoded .
* Therefore , the number of inputs is ceil ( log ( num_of_outputs ) / log ( 2 ) )
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static
void print_verilog_mux_local_decoder_module ( std : : fstream & fp ,
ModuleManager & module_manager ,
const DecoderLibrary & decoder_lib ,
const DecoderId & decoder ) {
/* Get the number of inputs */
size_t addr_size = decoder_lib . addr_size ( decoder ) ;
size_t data_size = decoder_lib . data_size ( decoder ) ;
/* Validate the FILE handler */
check_file_handler ( fp ) ;
/* TODO: create a name for the local encoder */
std : : string module_name = generate_mux_local_decoder_subckt_name ( addr_size , data_size ) ;
/* Create a Verilog Module based on the circuit model, and add to module manager */
2019-10-18 22:59:22 -05:00
ModuleId module_id = module_manager . find_module ( module_name ) ;
VTR_ASSERT ( true = = module_manager . valid_module_id ( module_id ) ) ;
2019-09-10 16:16:29 -05:00
/* Add module ports */
/* Add each input port */
2019-09-28 00:10:43 -05:00
BasicPort addr_port ( generate_mux_local_decoder_addr_port_name ( ) , addr_size ) ;
2019-09-10 16:16:29 -05:00
/* Add each output port */
2019-09-28 00:10:43 -05:00
BasicPort data_port ( generate_mux_local_decoder_data_port_name ( ) , data_size ) ;
2019-09-10 16:16:29 -05:00
/* Data port is registered. It should be outputted as
* output reg [ lsb : msb ] data
*/
/* Add data_in port */
2019-09-28 00:10:43 -05:00
BasicPort data_inv_port ( generate_mux_local_decoder_data_inv_port_name ( ) , data_size ) ;
2019-09-10 16:16:29 -05:00
VTR_ASSERT ( true = = decoder_lib . use_data_inv_port ( decoder ) ) ;
/* dump module definition + ports */
print_verilog_module_declaration ( fp , module_manager , module_id ) ;
/* Finish dumping ports */
print_verilog_comment ( fp , std : : string ( " ----- BEGIN Verilog codes for Decoder convert " + std : : to_string ( addr_size ) + " -bit addr to " + std : : to_string ( data_size ) + " -bit data ----- " ) ) ;
/* Print the truth table of this decoder */
/* Internal logics */
2019-09-28 00:10:43 -05:00
/* Early exit: Corner case for data size = 1 the logic is very simple:
* data = addr ;
* data_inv = ~ data_inv
*/
if ( 1 = = data_size ) {
print_verilog_wire_connection ( fp , addr_port , data_port , false ) ;
print_verilog_wire_connection ( fp , data_inv_port , data_port , true ) ;
print_verilog_comment ( fp , std : : string ( " ----- END Verilog codes for Decoder convert " + std : : to_string ( addr_size ) + " -bit addr to " + std : : to_string ( data_size ) + " -bit data ----- " ) ) ;
/* Put an end to the Verilog module */
print_verilog_module_end ( fp , module_name ) ;
return ;
}
2019-09-10 16:16:29 -05:00
/* We use a magic number -1 as the addr=1 should be mapped to ...1
* Otherwise addr will map addr = 1 to . .10
* Note that there should be a range for the shift operators
* We should narrow the encoding to be applied to a given set of data
* This will lead to that any addr which falls out of the op code of data
* will give a all - zero code
* For example :
* data is 5 - bit while addr is 3 - bit
* data = 8 ' b0_0000 will be encoded to addr = 3 ' b001 ;
* data = 8 ' b0_0001 will be encoded to addr = 3 ' b010 ;
* data = 8 ' b0_0010 will be encoded to addr = 3 ' b011 ;
* data = 8 ' b0_0100 will be encoded to addr = 3 ' b100 ;
* data = 8 ' b0_1000 will be encoded to addr = 3 ' b101 ;
* data = 8 ' b1_0000 will be encoded to addr = 3 ' b110 ;
* The rest of addr codes 3 ' b110 , 3 ' b111 will be decoded to data = 8 ' b0_0000 ;
*/
fp < < " \t " < < " always@( " < < generate_verilog_port ( VERILOG_PORT_CONKT , addr_port ) < < " ) " < < std : : endl ;
fp < < " \t " < < " case ( " < < generate_verilog_port ( VERILOG_PORT_CONKT , addr_port ) < < " ) " < < std : : endl ;
/* Create a string for addr and data */
for ( size_t i = 0 ; i < data_size ; + + i ) {
/* TODO: give a namespace to the itobin function */
fp < < " \t \t " < < generate_verilog_constant_values ( my_itobin_vec ( i , addr_size ) ) ;
fp < < " : " ;
fp < < generate_verilog_port_constant_values ( data_port , my_ito1hot_vec ( i , data_size ) ) ;
fp < < " ; " < < std : : endl ;
}
fp < < " \t \t " < < " default : " ;
fp < < generate_verilog_port_constant_values ( data_port , my_ito1hot_vec ( data_size - 1 , data_size ) ) ;
fp < < " ; " < < std : : endl ;
fp < < " \t " < < " endcase " < < std : : endl ;
print_verilog_wire_connection ( fp , data_inv_port , data_port , true ) ;
print_verilog_comment ( fp , std : : string ( " ----- END Verilog codes for Decoder convert " + std : : to_string ( addr_size ) + " -bit addr to " + std : : to_string ( data_size ) + " -bit data ----- " ) ) ;
/* Put an end to the Verilog module */
print_verilog_module_end ( fp , module_name ) ;
return ;
}
/***************************************************************************************
* This function will generate all the unique Verilog modules of local decoders for
* the multiplexers used in a FPGA fabric
* It will reach the goal in two steps :
* 1. Find the unique local decoders w . r . t . the number of inputs / outputs
* We will generate the subgraphs from the multiplexing graph of each multiplexers
* The number of memory bits is the number of outputs .
* From that we can infer the number of inputs of each local decoders .
* Here is an illustrative example of how local decoders are interfaced with multi - level MUXes
*
* + - - - - - - - - - + + - - - - - - - - - +
* | Local | | Local |
* | Decoder | | Decoder |
* | A | | B |
* + - - - - - - - - - + + - - - - - - - - - +
* | . . . | | . . . |
* v v v v
* + - - - - - - - - - - - - - - + + - - - - - - - - - - - - - - +
* | MUX Level 0 | - - - > | MUX Level 1 |
* + - - - - - - - - - - - - - - + + - - - - - - - - - - - - - - +
* 2. Generate local decoder Verilog modules using behavioral description .
* Note that the implementation of local decoders can be dependent on the technology
* and standard cell libraries .
* Therefore , behavioral Verilog is used and the local decoders should be synthesized
* before running the back - end flow for FPGA fabric
* See more details in the function print_verilog_mux_local_decoder ( ) for more details
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void print_verilog_submodule_mux_local_decoders ( ModuleManager & module_manager ,
const MuxLibrary & mux_lib ,
const CircuitLibrary & circuit_lib ,
const std : : string & verilog_dir ,
const std : : string & submodule_dir ) {
std : : string verilog_fname ( submodule_dir + local_encoder_verilog_file_name ) ;
/* Create the file stream */
std : : fstream fp ;
fp . open ( verilog_fname , std : : fstream : : out | std : : fstream : : trunc ) ;
check_file_handler ( fp ) ;
/* Print out debugging information for if the file is not opened/created properly */
vpr_printf ( TIO_MESSAGE_INFO ,
" Creating Verilog netlist for local decoders for multiplexers (%s)... \n " ,
verilog_fname . c_str ( ) ) ;
print_verilog_file_header ( fp , " Local Decoders for Multiplexers " ) ;
print_verilog_include_defines_preproc_file ( fp , verilog_dir ) ;
/* Create a library for local encoders with different sizes */
DecoderLibrary decoder_lib ;
/* Find unique local decoders for unique branches shared by the multiplexers */
for ( auto mux : mux_lib . muxes ( ) ) {
/* Local decoders are need only when users specify them */
CircuitModelId mux_circuit_model = mux_lib . mux_circuit_model ( mux ) ;
/* If this MUX does not need local decoder, we skip it */
if ( false = = circuit_lib . mux_use_local_encoder ( mux_circuit_model ) ) {
continue ;
}
const MuxGraph & mux_graph = mux_lib . mux_graph ( mux ) ;
/* Create a mux graph for the branch circuit */
std : : vector < MuxGraph > branch_mux_graphs = mux_graph . build_mux_branch_graphs ( ) ;
/* Add the decoder to the decoder library */
for ( auto branch_mux_graph : branch_mux_graphs ) {
2019-09-10 17:56:14 -05:00
/* The decoder size depends on the number of memories of a branch MUX.
* Note that only when there are > = 2 memories , a decoder is needed
*/
size_t decoder_data_size = branch_mux_graph . num_memory_bits ( ) ;
2019-09-28 00:10:43 -05:00
if ( 0 = = decoder_data_size ) {
2019-09-10 17:56:14 -05:00
continue ;
}
2019-09-10 16:16:29 -05:00
/* Try to find if the decoder already exists in the library,
* If there is no such decoder , add it to the library
*/
2019-09-10 17:56:14 -05:00
add_mux_local_decoder_to_library ( decoder_lib , decoder_data_size ) ;
2019-09-10 16:16:29 -05:00
}
}
/* Generate Verilog modules for the found unique local encoders */
for ( const auto & decoder : decoder_lib . decoders ( ) ) {
print_verilog_mux_local_decoder_module ( fp , module_manager , decoder_lib , decoder ) ;
}
2019-09-12 21:49:02 -05:00
/* Close the file stream */
2019-09-10 16:16:29 -05:00
fp . close ( ) ;
2019-09-10 16:34:20 -05:00
/* Add fname to the linked list when debugging is finished */
2019-09-10 16:26:47 -05:00
submodule_verilog_subckt_file_path_head = add_one_subckt_file_name_to_llist ( submodule_verilog_subckt_file_path_head , verilog_fname . c_str ( ) ) ;
2019-09-10 16:16:29 -05:00
}
2019-09-13 21:58:55 -05:00
/***************************************************************************************
* For scan - chain configuration organization :
* Generate the Verilog module of configuration module
2019-09-26 12:32:57 -05:00
* which connect configuration ports to SRAMs / CCFFs in a chain :
2019-09-13 21:58:55 -05:00
*
* + - - - - - - + + - - - - - - + + - - - - - - +
2019-09-26 15:04:40 -05:00
* cc_in - - - > | CCFF | - - - > | CCFF | - - - > . . . - - - > | CCFF | - - - - > sc_out
2019-09-13 21:58:55 -05:00
* + - - - - - - + + - - - - - - + + - - - - - - +
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static
void print_verilog_scan_chain_config_module ( ModuleManager & module_manager ,
std : : fstream & fp ,
t_sram_orgz_info * cur_sram_orgz_info ) {
/* Validate the FILE handler */
check_file_handler ( fp ) ;
/* Get the total memory bits */
int num_mem_bits = get_sram_orgz_info_num_mem_bit ( cur_sram_orgz_info ) ;
/* Create a module definition for the configuration chain */
print_verilog_comment ( fp , std : : string ( " ----- BEGIN Configuration Peripheral for Scan-chain FFs ----- " ) ) ;
/* Create a Verilog Module based on the circuit model, and add to module manager */
ModuleId module_id = module_manager . add_module ( std : : string ( verilog_config_peripheral_prefix ) ) ;
VTR_ASSERT ( ModuleId : : INVALID ( ) ! = module_id ) ;
/* Add module ports */
/* Add the head of scan-chain: a 1-bit input port */
BasicPort sc_head_port ( std : : string ( top_netlist_scan_chain_head_prefix ) , 1 ) ;
module_manager . add_port ( module_id , sc_head_port , ModuleManager : : MODULE_INPUT_PORT ) ;
/* Add the inputs of scan-chain FFs, which are the outputs of the module */
2019-09-26 15:04:40 -05:00
BasicPort cc_input_port ( std : : string ( " chain_input " ) , num_mem_bits ) ;
module_manager . add_port ( module_id , cc_input_port , ModuleManager : : MODULE_OUTPUT_PORT ) ;
2019-09-13 21:58:55 -05:00
/* Add the outputs of scan-chain FFs, which are inputs of the module */
BasicPort sc_output_port ( std : : string ( " chain_output " ) , num_mem_bits ) ;
module_manager . add_port ( module_id , sc_output_port , ModuleManager : : MODULE_INPUT_PORT ) ;
/* dump module definition + ports */
print_verilog_module_declaration ( fp , module_manager , module_id ) ;
/* Finish dumping ports */
/* Declare the sc_output_port is a wire */
fp < < generate_verilog_port ( VERILOG_PORT_WIRE , sc_output_port ) < < " ; " < < std : : endl ;
fp < < std : : endl ;
/* Connect scan-chain input to the first scan-chain input */
2019-09-26 15:04:40 -05:00
BasicPort sc_first_input_port ( cc_input_port . get_name ( ) , 1 ) ;
2019-09-13 21:58:55 -05:00
print_verilog_wire_connection ( fp , sc_first_input_port , sc_head_port , false ) ;
2019-09-26 12:32:57 -05:00
/* Connect the head of current ccff to the tail of previous ccff*/
2019-09-26 15:04:40 -05:00
BasicPort chain_output_port ( cc_input_port . get_name ( ) , 1 , num_mem_bits - 1 ) ;
2019-09-13 21:58:55 -05:00
BasicPort chain_input_port ( sc_output_port . get_name ( ) , 0 , num_mem_bits - 2 ) ;
print_verilog_wire_connection ( fp , chain_output_port , chain_input_port , false ) ;
print_verilog_comment ( fp , std : : string ( " ----- END Configuration Peripheral for Scan-chain FFs ----- " ) ) ;
/* Put an end to the Verilog module */
print_verilog_module_end ( fp , module_manager . module_name ( module_id ) ) ;
return ;
}
/***************************************************************************************
* Generate the configuration peripheral circuits for the top - level Verilog netlist
* This function will create Verilog modules depending on the configuration scheme :
* 1. Scan - chain :
2019-09-26 12:32:57 -05:00
* It will create a module which connects the Scan - Chain Flip - Flops ( CCFFs )
2019-09-13 21:58:55 -05:00
* as a chain :
*
* + - - - - - - + + - - - - - - + + - - - - - - +
2019-09-26 15:04:40 -05:00
* cc_in - - - > | CCFF | - - - > | CCFF | - - - > . . . - - - > | CCFF | - - - - > sc_out
2019-09-13 21:58:55 -05:00
* + - - - - - - + + - - - - - - + + - - - - - - +
*
* 2. Memory bank :
* It will create a BL decoder and a WL decoder which will configure the SRAMs
* as a memory bank
*
* + - - - - - - - - - - - - - - - - - - - - - - - - +
* | WL Decoder |
* + - - - - - - - - - - - - - - - - - - - - - - - - +
* | | | . . . | |
* v v v v v
* + - - - - - - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - +
* | | - - - > | |
* | | | |
* | BL | - - - > | |
* | Decoder | . . | FPGA Core logic |
* | | . . | |
* | | - - - > | |
* + - - - - - - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - +
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void print_verilog_config_peripherals ( ModuleManager & module_manager ,
t_sram_orgz_info * cur_sram_orgz_info ,
const std : : string & verilog_dir ,
const std : : string & submodule_dir ) {
std : : string verilog_fname ( submodule_dir + config_peripheral_verilog_file_name ) ;
verilog_fname + = " .bak " ;
/* Create the file stream */
std : : fstream fp ;
fp . open ( verilog_fname , std : : fstream : : out | std : : fstream : : trunc ) ;
check_file_handler ( fp ) ;
/* Print out debugging information for if the file is not opened/created properly */
vpr_printf ( TIO_MESSAGE_INFO ,
" Creating Verilog netlist for configuration peripherals (%s)... \n " ,
verilog_fname . c_str ( ) ) ;
print_verilog_file_header ( fp , " Configuration Peripheral Circuits " ) ;
print_verilog_include_defines_preproc_file ( fp , verilog_dir ) ;
/* Create a library for decoders */
DecoderLibrary decoder_lib ;
switch ( cur_sram_orgz_info - > type ) {
case SPICE_SRAM_STANDALONE :
break ;
case SPICE_SRAM_SCAN_CHAIN :
print_verilog_scan_chain_config_module ( module_manager , fp , cur_sram_orgz_info ) ;
break ;
case SPICE_SRAM_MEMORY_BANK :
/* TODO: Finish refactoring this part after the sram_orgz_info ! */
/*
dump_verilog_decoder ( fp , cur_sram_orgz_info ) ;
dump_verilog_membank_config_module ( fp , cur_sram_orgz_info ) ;
*/
break ;
default :
vpr_printf ( TIO_MESSAGE_ERROR ,
" (File:%s,[LINE%d])Invalid type of SRAM organization in Verilog Generator! \n " ,
__FILE__ , __LINE__ ) ;
exit ( 1 ) ;
}
/* Close the file stream */
fp . close ( ) ;
/* Add fname to the linked list when debugging is finished */
/* TODO: uncomment this when it is ready to be plugged-in
submodule_verilog_subckt_file_path_head = add_one_subckt_file_name_to_llist ( submodule_verilog_subckt_file_path_head , verilog_fname . c_str ( ) ) ;
*/
return ;
}