2014-12-31 09:53:53 -06:00
/*
* yosys - - Yosys Open SYnthesis Suite
*
* Copyright ( C ) 2012 Clifford Wolf < clifford @ clifford . at >
*
* Permission to use , copy , modify , and / or distribute this software for any
* purpose with or without fee is hereby granted , provided that the above
* copyright notice and this permission notice appear in all copies .
*
* THE SOFTWARE IS PROVIDED " AS IS " AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS . IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL , DIRECT , INDIRECT , OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE , DATA OR PROFITS , WHETHER IN AN
* ACTION OF CONTRACT , NEGLIGENCE OR OTHER TORTIOUS ACTION , ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE .
*
*/
# include "kernel/yosys.h"
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
struct rules_t
{
2014-12-31 15:50:08 -06:00
struct portinfo_t {
2015-01-01 08:32:37 -06:00
int group , index , dupidx ;
2014-12-31 15:50:08 -06:00
int wrmode , enable , transp , clocks , clkpol ;
2015-01-01 08:32:37 -06:00
SigBit sig_clock ;
SigSpec sig_addr , sig_data , sig_en ;
2015-01-01 17:07:44 -06:00
bool effective_clkpol ;
2015-01-18 12:05:29 -06:00
bool make_transp ;
2015-04-09 09:08:54 -05:00
bool make_outreg ;
2015-01-01 08:32:37 -06:00
int mapped_port ;
2014-12-31 15:50:08 -06:00
} ;
2014-12-31 09:53:53 -06:00
struct bram_t {
IdString name ;
2015-01-18 12:05:29 -06:00
int variant ;
2014-12-31 09:53:53 -06:00
int groups , abits , dbits , init ;
2014-12-31 15:50:08 -06:00
vector < int > ports , wrmode , enable , transp , clocks , clkpol ;
2015-01-18 12:05:29 -06:00
void dump_config ( ) const
{
log ( " bram %s # variant %d \n " , log_id ( name ) , variant ) ;
log ( " init %d \n " , init ) ;
log ( " abits %d \n " , abits ) ;
log ( " dbits %d \n " , dbits ) ;
log ( " groups %d \n " , groups ) ;
log ( " ports " ) ; for ( int v : ports ) log ( " %4d " , v ) ; log ( " \n " ) ;
log ( " wrmode " ) ; for ( int v : wrmode ) log ( " %4d " , v ) ; log ( " \n " ) ;
log ( " enable " ) ; for ( int v : enable ) log ( " %4d " , v ) ; log ( " \n " ) ;
log ( " transp " ) ; for ( int v : transp ) log ( " %4d " , v ) ; log ( " \n " ) ;
log ( " clocks " ) ; for ( int v : clocks ) log ( " %4d " , v ) ; log ( " \n " ) ;
log ( " clkpol " ) ; for ( int v : clkpol ) log ( " %4d " , v ) ; log ( " \n " ) ;
log ( " endbram \n " ) ;
}
void check_vectors ( ) const
{
if ( groups ! = GetSize ( ports ) ) log_error ( " Bram %s variant %d has %d groups but only %d entries in 'ports'. \n " , log_id ( name ) , variant , groups , GetSize ( ports ) ) ;
if ( groups ! = GetSize ( wrmode ) ) log_error ( " Bram %s variant %d has %d groups but only %d entries in 'wrmode'. \n " , log_id ( name ) , variant , groups , GetSize ( wrmode ) ) ;
if ( groups ! = GetSize ( enable ) ) log_error ( " Bram %s variant %d has %d groups but only %d entries in 'enable'. \n " , log_id ( name ) , variant , groups , GetSize ( enable ) ) ;
if ( groups ! = GetSize ( transp ) ) log_error ( " Bram %s variant %d has %d groups but only %d entries in 'transp'. \n " , log_id ( name ) , variant , groups , GetSize ( transp ) ) ;
if ( groups ! = GetSize ( clocks ) ) log_error ( " Bram %s variant %d has %d groups but only %d entries in 'clocks'. \n " , log_id ( name ) , variant , groups , GetSize ( clocks ) ) ;
if ( groups ! = GetSize ( clkpol ) ) log_error ( " Bram %s variant %d has %d groups but only %d entries in 'clkpol'. \n " , log_id ( name ) , variant , groups , GetSize ( clkpol ) ) ;
}
2014-12-31 15:50:08 -06:00
vector < portinfo_t > make_portinfos ( ) const
{
vector < portinfo_t > portinfos ;
2015-01-18 12:05:29 -06:00
for ( int i = 0 ; i < groups ; i + + )
2014-12-31 15:50:08 -06:00
for ( int j = 0 ; j < ports [ i ] ; j + + ) {
portinfo_t pi ;
pi . group = i ;
pi . index = j ;
2015-01-01 08:32:37 -06:00
pi . dupidx = 0 ;
2015-01-18 12:05:29 -06:00
pi . wrmode = wrmode [ i ] ;
pi . enable = enable [ i ] ;
pi . transp = transp [ i ] ;
pi . clocks = clocks [ i ] ;
pi . clkpol = clkpol [ i ] ;
2015-01-01 08:32:37 -06:00
pi . mapped_port = - 1 ;
2015-01-18 12:05:29 -06:00
pi . make_transp = false ;
2015-04-09 09:08:54 -05:00
pi . make_outreg = false ;
2015-01-18 12:05:29 -06:00
pi . effective_clkpol = false ;
2014-12-31 15:50:08 -06:00
portinfos . push_back ( pi ) ;
}
return portinfos ;
}
2015-01-18 12:05:29 -06:00
void find_variant_params ( dict < IdString , Const > & variant_params , const bram_t & other ) const
{
log_assert ( name = = other . name ) ;
if ( groups ! = other . groups )
log_error ( " Bram %s variants %d and %d have different values for 'groups'. \n " , log_id ( name ) , variant , other . variant ) ;
if ( abits ! = other . abits )
variant_params [ " \\ CFG_ABITS " ] = abits ;
if ( dbits ! = other . dbits )
variant_params [ " \\ CFG_DBITS " ] = dbits ;
if ( init ! = other . init )
variant_params [ " \\ CFG_INIT " ] = init ;
for ( int i = 0 ; i < groups ; i + + )
{
if ( ports [ i ] ! = other . ports [ i ] )
log_error ( " Bram %s variants %d and %d have different number of %c-ports. \n " , log_id ( name ) , variant , other . variant , ' A ' + i ) ;
if ( wrmode [ i ] ! = other . wrmode [ i ] )
variant_params [ stringf ( " \\ CFG_WRMODE_%c " , ' A ' + i ) ] = wrmode [ 1 ] ;
if ( enable [ i ] ! = other . enable [ i ] )
variant_params [ stringf ( " \\ CFG_ENABLE_%c " , ' A ' + i ) ] = enable [ 1 ] ;
if ( transp [ i ] ! = other . transp [ i ] )
variant_params [ stringf ( " \\ CFG_TRANSP_%c " , ' A ' + i ) ] = transp [ 1 ] ;
if ( clocks [ i ] ! = other . clocks [ i ] )
variant_params [ stringf ( " \\ CFG_CLOCKS_%c " , ' A ' + i ) ] = clocks [ 1 ] ;
if ( clkpol [ i ] ! = other . clkpol [ i ] )
variant_params [ stringf ( " \\ CFG_CLKPOL_%c " , ' A ' + i ) ] = clkpol [ 1 ] ;
}
}
2014-12-31 09:53:53 -06:00
} ;
struct match_t {
IdString name ;
dict < string , int > min_limits , max_limits ;
2015-04-09 09:08:54 -05:00
bool or_next_if_better , make_transp , make_outreg ;
2015-01-18 12:05:29 -06:00
char shuffle_enable ;
2014-12-31 09:53:53 -06:00
} ;
2015-01-18 12:05:29 -06:00
dict < IdString , vector < bram_t > > brams ;
2014-12-31 09:53:53 -06:00
vector < match_t > matches ;
std : : ifstream infile ;
vector < string > tokens ;
2015-01-18 12:05:29 -06:00
vector < string > labels ;
2014-12-31 09:53:53 -06:00
int linecount ;
void syntax_error ( )
{
2015-01-04 07:23:30 -06:00
if ( tokens . empty ( ) )
2014-12-31 09:53:53 -06:00
log_error ( " Unexpected end of rules file in line %d. \n " , linecount ) ;
2015-01-04 07:23:30 -06:00
log_error ( " Syntax error in rules file line %d. \n " , linecount ) ;
2014-12-31 09:53:53 -06:00
}
bool next_line ( )
{
2015-01-04 07:23:30 -06:00
string line ;
2014-12-31 09:53:53 -06:00
while ( std : : getline ( infile , line ) ) {
2015-01-18 12:05:29 -06:00
tokens . clear ( ) ;
labels . clear ( ) ;
linecount + + ;
2014-12-31 09:53:53 -06:00
for ( string tok = next_token ( line ) ; ! tok . empty ( ) ; tok = next_token ( line ) ) {
2015-01-18 12:05:29 -06:00
if ( tok [ 0 ] = = ' @ ' ) {
labels . push_back ( tok . substr ( 1 ) ) ;
continue ;
}
2014-12-31 09:53:53 -06:00
if ( tok [ 0 ] = = ' # ' )
break ;
tokens . push_back ( tok ) ;
}
if ( ! tokens . empty ( ) )
return true ;
}
return false ;
}
bool parse_single_int ( const char * stmt , int & value )
{
if ( GetSize ( tokens ) = = 2 & & tokens [ 0 ] = = stmt ) {
value = atoi ( tokens [ 1 ] . c_str ( ) ) ;
return true ;
}
return false ;
}
bool parse_int_vect ( const char * stmt , vector < int > & value )
{
if ( GetSize ( tokens ) > = 2 & & tokens [ 0 ] = = stmt ) {
value . resize ( GetSize ( tokens ) - 1 ) ;
for ( int i = 1 ; i < GetSize ( tokens ) ; i + + )
2014-12-31 15:50:08 -06:00
value [ i - 1 ] = atoi ( tokens [ i ] . c_str ( ) ) ;
2014-12-31 09:53:53 -06:00
return true ;
}
return false ;
}
void parse_bram ( )
{
2015-01-18 12:05:29 -06:00
IdString bram_name = RTLIL : : escape_id ( tokens [ 1 ] ) ;
2014-12-31 09:53:53 -06:00
if ( GetSize ( tokens ) ! = 2 )
syntax_error ( ) ;
2015-01-18 12:05:29 -06:00
vector < vector < string > > lines_nolabels ;
std : : map < string , vector < vector < string > > > lines_labels ;
2014-12-31 09:53:53 -06:00
while ( next_line ( ) )
{
2015-01-18 12:05:29 -06:00
if ( GetSize ( tokens ) = = 1 & & tokens [ 0 ] = = " endbram " )
break ;
if ( labels . empty ( ) )
lines_nolabels . push_back ( tokens ) ;
for ( auto lab : labels )
lines_labels [ lab ] . push_back ( tokens ) ;
}
2014-12-31 09:53:53 -06:00
2015-01-18 12:05:29 -06:00
std : : map < string , vector < vector < string > > > variant_lines ;
2014-12-31 09:53:53 -06:00
2015-01-18 12:05:29 -06:00
if ( lines_labels . empty ( ) )
variant_lines [ " " ] = lines_nolabels ;
for ( auto & it : lines_labels ) {
variant_lines [ it . first ] = lines_nolabels ;
variant_lines [ it . first ] . insert ( variant_lines [ it . first ] . end ( ) , it . second . begin ( ) , it . second . end ( ) ) ;
}
2014-12-31 09:53:53 -06:00
2015-01-18 12:05:29 -06:00
for ( auto & it : variant_lines )
{
bram_t data ;
data . name = bram_name ;
data . variant = GetSize ( brams [ data . name ] ) + 1 ;
data . groups = 0 ;
data . abits = 0 ;
data . dbits = 0 ;
data . init = 0 ;
for ( auto & line_tokens : it . second )
{
tokens = line_tokens ;
2014-12-31 09:53:53 -06:00
2015-01-18 12:05:29 -06:00
if ( parse_single_int ( " groups " , data . groups ) )
continue ;
2014-12-31 09:53:53 -06:00
2015-01-18 12:05:29 -06:00
if ( parse_single_int ( " abits " , data . abits ) )
continue ;
2014-12-31 09:53:53 -06:00
2015-01-18 12:05:29 -06:00
if ( parse_single_int ( " dbits " , data . dbits ) )
continue ;
2014-12-31 09:53:53 -06:00
2015-01-18 12:05:29 -06:00
if ( parse_single_int ( " init " , data . init ) )
continue ;
2014-12-31 09:53:53 -06:00
2015-01-18 12:05:29 -06:00
if ( parse_int_vect ( " ports " , data . ports ) )
continue ;
2014-12-31 09:53:53 -06:00
2015-01-18 12:05:29 -06:00
if ( parse_int_vect ( " wrmode " , data . wrmode ) )
continue ;
2014-12-31 09:53:53 -06:00
2015-01-18 12:05:29 -06:00
if ( parse_int_vect ( " enable " , data . enable ) )
continue ;
2014-12-31 09:53:53 -06:00
2015-01-18 12:05:29 -06:00
if ( parse_int_vect ( " transp " , data . transp ) )
continue ;
if ( parse_int_vect ( " clocks " , data . clocks ) )
continue ;
if ( parse_int_vect ( " clkpol " , data . clkpol ) )
continue ;
syntax_error ( ) ;
}
2014-12-31 09:53:53 -06:00
2015-01-18 12:05:29 -06:00
data . check_vectors ( ) ;
brams [ data . name ] . push_back ( data ) ;
}
2014-12-31 09:53:53 -06:00
}
void parse_match ( )
{
if ( GetSize ( tokens ) ! = 2 )
syntax_error ( ) ;
match_t data ;
data . name = RTLIL : : escape_id ( tokens [ 1 ] ) ;
2015-01-03 10:34:05 -06:00
data . or_next_if_better = false ;
2015-01-18 12:05:29 -06:00
data . make_transp = false ;
2015-04-09 09:08:54 -05:00
data . make_outreg = false ;
2015-01-04 06:14:30 -06:00
data . shuffle_enable = 0 ;
2014-12-31 09:53:53 -06:00
while ( next_line ( ) )
{
2015-01-18 12:05:29 -06:00
if ( ! labels . empty ( ) )
syntax_error ( ) ;
2014-12-31 09:53:53 -06:00
if ( GetSize ( tokens ) = = 1 & & tokens [ 0 ] = = " endmatch " ) {
matches . push_back ( data ) ;
2015-01-18 12:05:29 -06:00
break ;
2014-12-31 09:53:53 -06:00
}
if ( GetSize ( tokens ) = = 3 & & tokens [ 0 ] = = " min " ) {
data . min_limits [ tokens [ 1 ] ] = atoi ( tokens [ 2 ] . c_str ( ) ) ;
continue ;
}
if ( GetSize ( tokens ) = = 3 & & tokens [ 0 ] = = " max " ) {
data . max_limits [ tokens [ 1 ] ] = atoi ( tokens [ 2 ] . c_str ( ) ) ;
continue ;
}
2015-01-18 12:05:29 -06:00
if ( GetSize ( tokens ) = = 2 & & tokens [ 0 ] = = " shuffle_enable " & & GetSize ( tokens [ 1 ] ) = = 1 & & ' A ' < = tokens [ 1 ] [ 0 ] & & tokens [ 1 ] [ 0 ] < = ' Z ' ) {
data . shuffle_enable = tokens [ 1 ] [ 0 ] ;
continue ;
}
if ( GetSize ( tokens ) = = 1 & & tokens [ 0 ] = = " make_transp " ) {
data . make_transp = true ;
2015-01-04 06:14:30 -06:00
continue ;
}
2015-04-09 09:08:54 -05:00
if ( GetSize ( tokens ) = = 1 & & tokens [ 0 ] = = " make_outreg " ) {
data . make_transp = true ;
data . make_outreg = true ;
continue ;
}
2015-01-03 10:34:05 -06:00
if ( GetSize ( tokens ) = = 1 & & tokens [ 0 ] = = " or_next_if_better " ) {
data . or_next_if_better = true ;
continue ;
}
2015-01-18 12:05:29 -06:00
syntax_error ( ) ;
2014-12-31 09:53:53 -06:00
}
}
2015-01-01 08:32:37 -06:00
void parse ( string filename )
2014-12-31 09:53:53 -06:00
{
2015-04-08 05:14:34 -05:00
rewrite_filename ( filename ) ;
2014-12-31 09:53:53 -06:00
infile . open ( filename ) ;
linecount = 0 ;
if ( infile . fail ( ) )
log_error ( " Can't open rules file `%s'. \n " , filename . c_str ( ) ) ;
while ( next_line ( ) )
{
2015-01-18 12:05:29 -06:00
if ( ! labels . empty ( ) )
syntax_error ( ) ;
if ( tokens [ 0 ] = = " bram " ) {
parse_bram ( ) ;
continue ;
}
if ( tokens [ 0 ] = = " match " ) {
parse_match ( ) ;
continue ;
}
syntax_error ( ) ;
2014-12-31 09:53:53 -06:00
}
infile . close ( ) ;
}
} ;
2015-01-18 12:05:29 -06:00
bool replace_cell ( Cell * cell , const rules_t & rules , const rules_t : : bram_t & bram , const rules_t : : match_t & match , dict < string , int > & match_properties , int mode )
2014-12-31 09:53:53 -06:00
{
2015-01-01 08:32:37 -06:00
Module * module = cell - > module ;
2014-12-31 15:50:08 -06:00
auto portinfos = bram . make_portinfos ( ) ;
2015-01-01 08:32:37 -06:00
int dup_count = 1 ;
2015-01-18 12:05:29 -06:00
pair < SigBit , bool > make_transp_clk ;
bool enable_make_transp = false ;
int make_transp_enbits = 0 ;
2014-12-31 15:50:08 -06:00
dict < int , pair < SigBit , bool > > clock_domains ;
2015-01-02 15:45:26 -06:00
dict < int , bool > clock_polarities ;
2015-01-03 05:41:46 -06:00
dict < int , bool > read_transp ;
2015-01-01 08:32:37 -06:00
pool < int > clocks_wr_ports ;
2015-01-02 15:45:26 -06:00
pool < int > clkpol_wr_ports ;
2015-01-01 08:32:37 -06:00
int clocks_max = 0 ;
2015-01-02 15:45:26 -06:00
int clkpol_max = 0 ;
2015-01-03 05:41:46 -06:00
int transp_max = 0 ;
2015-01-02 15:45:26 -06:00
clock_polarities [ 0 ] = false ;
clock_polarities [ 1 ] = true ;
2015-01-01 08:32:37 -06:00
for ( auto & pi : portinfos ) {
2015-01-02 15:45:26 -06:00
if ( pi . wrmode ) {
2015-01-01 08:32:37 -06:00
clocks_wr_ports . insert ( pi . clocks ) ;
2015-01-02 15:45:26 -06:00
if ( pi . clkpol > 1 )
clkpol_wr_ports . insert ( pi . clkpol ) ;
}
2015-01-01 08:32:37 -06:00
clocks_max = std : : max ( clocks_max , pi . clocks ) ;
2015-01-02 15:45:26 -06:00
clkpol_max = std : : max ( clkpol_max , pi . clkpol ) ;
2015-01-03 05:41:46 -06:00
transp_max = std : : max ( transp_max , pi . transp ) ;
2015-01-01 08:32:37 -06:00
}
2014-12-31 15:50:08 -06:00
2015-01-18 12:05:29 -06:00
log ( " Mapping to bram type %s (variant %d): \n " , log_id ( bram . name ) , bram . variant ) ;
// bram.dump_config();
2014-12-31 15:50:08 -06:00
2015-01-01 05:17:19 -06:00
int mem_size = cell - > getParam ( " \\ SIZE " ) . as_int ( ) ;
int mem_abits = cell - > getParam ( " \\ ABITS " ) . as_int ( ) ;
int mem_width = cell - > getParam ( " \\ WIDTH " ) . as_int ( ) ;
2015-01-01 08:32:37 -06:00
// int mem_offset = cell->getParam("\\OFFSET").as_int();
2015-01-01 05:17:19 -06:00
2015-04-06 10:06:15 -05:00
bool cell_init = ! SigSpec ( cell - > getParam ( " \\ INIT " ) ) . is_fully_undef ( ) ;
vector < Const > initdata ;
if ( cell_init ) {
Const initparam = cell - > getParam ( " \\ INIT " ) ;
initdata . reserve ( mem_size ) ;
for ( int i = 0 ; i < mem_size ; i + + )
initdata . push_back ( initparam . extract ( mem_width * i , mem_width , State : : Sx ) ) ;
}
2015-01-01 05:17:19 -06:00
int wr_ports = cell - > getParam ( " \\ WR_PORTS " ) . as_int ( ) ;
2014-12-31 15:50:08 -06:00
auto wr_clken = SigSpec ( cell - > getParam ( " \\ WR_CLK_ENABLE " ) ) ;
auto wr_clkpol = SigSpec ( cell - > getParam ( " \\ WR_CLK_POLARITY " ) ) ;
2015-01-01 05:17:19 -06:00
wr_clken . extend_u0 ( wr_ports ) ;
wr_clkpol . extend_u0 ( wr_ports ) ;
2014-12-31 15:50:08 -06:00
SigSpec wr_en = cell - > getPort ( " \\ WR_EN " ) ;
2015-01-01 05:17:19 -06:00
SigSpec wr_clk = cell - > getPort ( " \\ WR_CLK " ) ;
SigSpec wr_data = cell - > getPort ( " \\ WR_DATA " ) ;
SigSpec wr_addr = cell - > getPort ( " \\ WR_ADDR " ) ;
int rd_ports = cell - > getParam ( " \\ RD_PORTS " ) . as_int ( ) ;
auto rd_clken = SigSpec ( cell - > getParam ( " \\ RD_CLK_ENABLE " ) ) ;
auto rd_clkpol = SigSpec ( cell - > getParam ( " \\ RD_CLK_POLARITY " ) ) ;
auto rd_transp = SigSpec ( cell - > getParam ( " \\ RD_TRANSPARENT " ) ) ;
rd_clken . extend_u0 ( rd_ports ) ;
rd_clkpol . extend_u0 ( rd_ports ) ;
rd_transp . extend_u0 ( rd_ports ) ;
SigSpec rd_clk = cell - > getPort ( " \\ RD_CLK " ) ;
SigSpec rd_data = cell - > getPort ( " \\ RD_DATA " ) ;
SigSpec rd_addr = cell - > getPort ( " \\ RD_ADDR " ) ;
2015-01-18 12:05:29 -06:00
if ( match . shuffle_enable & & bram . dbits > = portinfos . at ( match . shuffle_enable - ' A ' ) . enable * 2 & & portinfos . at ( match . shuffle_enable - ' A ' ) . enable > 0 )
2015-01-04 06:14:30 -06:00
{
2015-01-18 12:05:29 -06:00
int bucket_size = bram . dbits / portinfos . at ( match . shuffle_enable - ' A ' ) . enable ;
2015-01-06 16:59:53 -06:00
log ( " Shuffle bit order to accommodate enable buckets of size %d.. \n " , bucket_size ) ;
2015-01-04 06:14:30 -06:00
// extract unshuffled data/enable bits
std : : vector < SigSpec > old_wr_en ;
std : : vector < SigSpec > old_wr_data ;
std : : vector < SigSpec > old_rd_data ;
for ( int i = 0 ; i < wr_ports ; i + + ) {
old_wr_en . push_back ( wr_en . extract ( i * mem_width , mem_width ) ) ;
old_wr_data . push_back ( wr_data . extract ( i * mem_width , mem_width ) ) ;
}
for ( int i = 0 ; i < rd_ports ; i + + )
old_rd_data . push_back ( rd_data . extract ( i * mem_width , mem_width ) ) ;
// analyze enable structure
std : : vector < SigSpec > en_order ;
dict < SigSpec , vector < int > > bits_wr_en ;
for ( int i = 0 ; i < mem_width ; i + + ) {
SigSpec sig ;
for ( int j = 0 ; j < wr_ports ; j + + )
sig . append ( old_wr_en [ j ] [ i ] ) ;
if ( bits_wr_en . count ( sig ) = = 0 )
en_order . push_back ( sig ) ;
bits_wr_en [ sig ] . push_back ( i ) ;
}
// re-create memory ports
std : : vector < SigSpec > new_wr_en ( GetSize ( old_wr_en ) ) ;
std : : vector < SigSpec > new_wr_data ( GetSize ( old_wr_data ) ) ;
std : : vector < SigSpec > new_rd_data ( GetSize ( old_rd_data ) ) ;
std : : vector < int > shuffle_map ;
for ( auto & it : en_order )
{
auto & bits = bits_wr_en . at ( it ) ;
int buckets = ( GetSize ( bits ) + bucket_size - 1 ) / bucket_size ;
int fillbits = buckets * bucket_size - GetSize ( bits ) ;
SigBit fillbit ;
for ( int i = 0 ; i < GetSize ( bits ) ; i + + ) {
for ( int j = 0 ; j < wr_ports ; j + + ) {
new_wr_en [ j ] . append ( old_wr_en [ j ] [ bits [ i ] ] ) ;
new_wr_data [ j ] . append ( old_wr_data [ j ] [ bits [ i ] ] ) ;
fillbit = old_wr_en [ j ] [ bits [ i ] ] ;
}
for ( int j = 0 ; j < rd_ports ; j + + )
new_rd_data [ j ] . append ( old_rd_data [ j ] [ bits [ i ] ] ) ;
shuffle_map . push_back ( bits [ i ] ) ;
}
for ( int i = 0 ; i < fillbits ; i + + ) {
for ( int j = 0 ; j < wr_ports ; j + + ) {
new_wr_en [ j ] . append ( fillbit ) ;
2015-01-06 07:26:51 -06:00
new_wr_data [ j ] . append ( State : : S0 ) ;
2015-01-04 06:14:30 -06:00
}
for ( int j = 0 ; j < rd_ports ; j + + )
new_rd_data [ j ] . append ( State : : Sx ) ;
shuffle_map . push_back ( - 1 ) ;
}
}
2015-01-06 16:59:53 -06:00
log ( " Results of bit order shuffling: " ) ;
2015-01-04 06:14:30 -06:00
for ( int v : shuffle_map )
log ( " %d " , v ) ;
log ( " \n " ) ;
// update mem_*, wr_*, and rd_* variables
mem_width = GetSize ( new_wr_en . front ( ) ) ;
wr_en = SigSpec ( 0 , wr_ports * mem_width ) ;
wr_data = SigSpec ( 0 , wr_ports * mem_width ) ;
rd_data = SigSpec ( 0 , rd_ports * mem_width ) ;
for ( int i = 0 ; i < wr_ports ; i + + ) {
wr_en . replace ( i * mem_width , new_wr_en [ i ] ) ;
wr_data . replace ( i * mem_width , new_wr_data [ i ] ) ;
}
for ( int i = 0 ; i < rd_ports ; i + + )
rd_data . replace ( i * mem_width , new_rd_data [ i ] ) ;
}
// assign write ports
2015-01-01 05:17:19 -06:00
for ( int cell_port_i = 0 , bram_port_i = 0 ; cell_port_i < wr_ports ; cell_port_i + + )
2014-12-31 15:50:08 -06:00
{
bool clken = wr_clken [ cell_port_i ] = = State : : S1 ;
2015-01-03 05:41:46 -06:00
bool clkpol = wr_clkpol [ cell_port_i ] = = State : : S1 ;
SigBit clksig = wr_clk [ cell_port_i ] ;
2014-12-31 15:50:08 -06:00
pair < SigBit , bool > clkdom ( clksig , clkpol ) ;
if ( ! clken )
clkdom = pair < SigBit , bool > ( State : : S1 , false ) ;
2015-01-03 10:34:05 -06:00
log ( " Write port #%d is in clock domain %s%s. \n " ,
2014-12-31 15:50:08 -06:00
cell_port_i , clkdom . second ? " " : " ! " ,
clken ? log_signal ( clkdom . first ) : " ~async~ " ) ;
for ( ; bram_port_i < GetSize ( portinfos ) ; bram_port_i + + )
{
auto & pi = portinfos [ bram_port_i ] ;
2015-01-18 12:05:29 -06:00
make_transp_enbits = pi . enable ;
make_transp_clk = clkdom ;
2014-12-31 15:50:08 -06:00
if ( pi . wrmode ! = 1 )
skip_bram_wport :
continue ;
if ( clken ) {
if ( pi . clocks = = 0 ) {
2015-01-03 10:34:05 -06:00
log ( " Bram port %c%d has incompatible clock type. \n " , pi . group + ' A ' , pi . index + 1 ) ;
2014-12-31 15:50:08 -06:00
goto skip_bram_wport ;
}
if ( clock_domains . count ( pi . clocks ) & & clock_domains . at ( pi . clocks ) ! = clkdom ) {
2015-01-03 10:34:05 -06:00
log ( " Bram port %c%d is in a different clock domain. \n " , pi . group + ' A ' , pi . index + 1 ) ;
2014-12-31 15:50:08 -06:00
goto skip_bram_wport ;
}
2015-01-02 15:45:26 -06:00
if ( clock_polarities . count ( pi . clkpol ) & & clock_polarities . at ( pi . clkpol ) ! = clkpol ) {
2015-01-03 10:34:05 -06:00
log ( " Bram port %c%d has incompatible clock polarity. \n " , pi . group + ' A ' , pi . index + 1 ) ;
2015-01-02 15:45:26 -06:00
goto skip_bram_wport ;
}
2014-12-31 15:50:08 -06:00
} else {
if ( pi . clocks ! = 0 ) {
2015-01-03 10:34:05 -06:00
log ( " Bram port %c%d has incompatible clock type. \n " , pi . group + ' A ' , pi . index + 1 ) ;
2014-12-31 15:50:08 -06:00
goto skip_bram_wport ;
}
}
2015-01-01 08:32:37 -06:00
SigSpec sig_en ;
2014-12-31 15:50:08 -06:00
SigBit last_en_bit = State : : S1 ;
2015-01-01 08:32:37 -06:00
for ( int i = 0 ; i < mem_width ; i + + ) {
if ( pi . enable & & i % ( bram . dbits / pi . enable ) = = 0 ) {
2015-01-02 06:59:47 -06:00
last_en_bit = wr_en [ i + cell_port_i * mem_width ] ;
2015-01-01 08:32:37 -06:00
sig_en . append ( last_en_bit ) ;
}
2015-01-02 06:59:47 -06:00
if ( last_en_bit ! = wr_en [ i + cell_port_i * mem_width ] ) {
2015-01-03 10:34:05 -06:00
log ( " Bram port %c%d has incompatible enable structure. \n " , pi . group + ' A ' , pi . index + 1 ) ;
2014-12-31 15:50:08 -06:00
goto skip_bram_wport ;
}
}
2015-01-03 10:34:05 -06:00
log ( " Mapped to bram port %c%d. \n " , pi . group + ' A ' , pi . index + 1 ) ;
2015-01-01 08:32:37 -06:00
pi . mapped_port = cell_port_i ;
if ( clken ) {
2014-12-31 15:50:08 -06:00
clock_domains [ pi . clocks ] = clkdom ;
2015-01-02 15:45:26 -06:00
clock_polarities [ pi . clkpol ] = clkdom . second ;
2015-01-01 08:32:37 -06:00
pi . sig_clock = clkdom . first ;
2015-01-01 17:07:44 -06:00
pi . effective_clkpol = clkdom . second ;
2015-01-01 08:32:37 -06:00
}
pi . sig_en = sig_en ;
pi . sig_addr = wr_addr . extract ( cell_port_i * mem_abits , mem_abits ) ;
pi . sig_data = wr_data . extract ( cell_port_i * mem_width , mem_width ) ;
2015-01-02 06:59:47 -06:00
bram_port_i + + ;
2015-01-01 05:17:19 -06:00
goto mapped_wr_port ;
2014-12-31 15:50:08 -06:00
}
2015-01-03 10:34:05 -06:00
log ( " Failed to map write port #%d. \n " , cell_port_i ) ;
2014-12-31 15:50:08 -06:00
return false ;
2015-01-01 05:17:19 -06:00
mapped_wr_port : ;
}
2015-01-04 06:14:30 -06:00
// houskeeping stuff for growing more read ports and restarting read port assignments
2015-01-01 05:17:19 -06:00
int grow_read_ports_cursor = - 1 ;
bool try_growing_more_read_ports = false ;
2015-01-02 06:59:47 -06:00
auto backup_clock_domains = clock_domains ;
2015-01-02 15:45:26 -06:00
auto backup_clock_polarities = clock_polarities ;
2015-01-01 05:17:19 -06:00
if ( 0 ) {
grow_read_ports : ;
2015-01-01 08:32:37 -06:00
vector < rules_t : : portinfo_t > new_portinfos ;
for ( auto & pi : portinfos ) {
if ( pi . wrmode = = 0 ) {
pi . mapped_port = - 1 ;
pi . sig_clock = SigBit ( ) ;
pi . sig_addr = SigSpec ( ) ;
pi . sig_data = SigSpec ( ) ;
pi . sig_en = SigSpec ( ) ;
}
new_portinfos . push_back ( pi ) ;
if ( pi . dupidx = = dup_count - 1 ) {
if ( pi . clocks & & ! clocks_wr_ports [ pi . clocks ] )
pi . clocks + = clocks_max ;
2015-01-02 15:45:26 -06:00
if ( pi . clkpol > 1 & & ! clkpol_wr_ports [ pi . clkpol ] )
pi . clkpol + = clkpol_max ;
2015-01-03 05:41:46 -06:00
if ( pi . transp > 1 )
pi . transp + = transp_max ;
2015-01-01 08:32:37 -06:00
pi . dupidx + + ;
new_portinfos . push_back ( pi ) ;
}
}
try_growing_more_read_ports = false ;
portinfos . swap ( new_portinfos ) ;
2015-01-02 06:59:47 -06:00
clock_domains = backup_clock_domains ;
2015-01-02 15:45:26 -06:00
clock_polarities = backup_clock_polarities ;
2015-01-01 08:32:37 -06:00
dup_count + + ;
2014-12-31 15:50:08 -06:00
}
2015-01-03 05:41:46 -06:00
read_transp . clear ( ) ;
read_transp [ 0 ] = false ;
read_transp [ 1 ] = true ;
2015-01-04 06:14:30 -06:00
// assign read ports
2015-01-01 05:17:19 -06:00
for ( int cell_port_i = 0 ; cell_port_i < rd_ports ; cell_port_i + + )
{
bool clken = rd_clken [ cell_port_i ] = = State : : S1 ;
2015-01-03 05:41:46 -06:00
bool clkpol = rd_clkpol [ cell_port_i ] = = State : : S1 ;
bool transp = rd_transp [ cell_port_i ] = = State : : S1 ;
SigBit clksig = rd_clk [ cell_port_i ] ;
2015-01-01 05:17:19 -06:00
pair < SigBit , bool > clkdom ( clksig , clkpol ) ;
if ( ! clken )
clkdom = pair < SigBit , bool > ( State : : S1 , false ) ;
2015-01-03 10:34:05 -06:00
log ( " Read port #%d is in clock domain %s%s. \n " ,
2015-01-01 05:17:19 -06:00
cell_port_i , clkdom . second ? " " : " ! " ,
clken ? log_signal ( clkdom . first ) : " ~async~ " ) ;
for ( int bram_port_i = 0 ; bram_port_i < GetSize ( portinfos ) ; bram_port_i + + )
{
auto & pi = portinfos [ bram_port_i ] ;
2015-01-01 08:32:37 -06:00
if ( pi . wrmode ! = 0 | | pi . mapped_port > = 0 )
2015-01-01 05:17:19 -06:00
skip_bram_rport :
continue ;
if ( clken ) {
if ( pi . clocks = = 0 ) {
2015-04-09 09:08:54 -05:00
if ( match . make_outreg ) {
pi . make_outreg = true ;
goto skip_bram_rport_clkcheck ;
}
2015-01-03 10:34:05 -06:00
log ( " Bram port %c%d.%d has incompatible clock type. \n " , pi . group + ' A ' , pi . index + 1 , pi . dupidx + 1 ) ;
2015-01-01 05:17:19 -06:00
goto skip_bram_rport ;
}
if ( clock_domains . count ( pi . clocks ) & & clock_domains . at ( pi . clocks ) ! = clkdom ) {
2015-01-03 10:34:05 -06:00
log ( " Bram port %c%d.%d is in a different clock domain. \n " , pi . group + ' A ' , pi . index + 1 , pi . dupidx + 1 ) ;
2015-01-02 15:45:26 -06:00
goto skip_bram_rport ;
}
if ( clock_polarities . count ( pi . clkpol ) & & clock_polarities . at ( pi . clkpol ) ! = clkpol ) {
2015-01-03 10:34:05 -06:00
log ( " Bram port %c%d.%d has incompatible clock polarity. \n " , pi . group + ' A ' , pi . index + 1 , pi . dupidx + 1 ) ;
2015-01-01 05:17:19 -06:00
goto skip_bram_rport ;
}
2015-04-09 09:08:54 -05:00
skip_bram_rport_clkcheck :
2015-01-03 05:41:46 -06:00
if ( read_transp . count ( pi . transp ) & & read_transp . at ( pi . transp ) ! = transp ) {
2015-01-18 12:05:29 -06:00
if ( match . make_transp & & wr_ports < = 1 ) {
pi . make_transp = true ;
enable_make_transp = true ;
} else {
log ( " Bram port %c%d.%d has incompatible read transparancy. \n " , pi . group + ' A ' , pi . index + 1 , pi . dupidx + 1 ) ;
goto skip_bram_rport ;
}
2015-01-03 05:41:46 -06:00
}
2015-01-01 05:17:19 -06:00
} else {
if ( pi . clocks ! = 0 ) {
2015-01-03 10:34:05 -06:00
log ( " Bram port %c%d.%d has incompatible clock type. \n " , pi . group + ' A ' , pi . index + 1 , pi . dupidx + 1 ) ;
2015-01-01 05:17:19 -06:00
goto skip_bram_rport ;
}
}
2015-01-03 10:34:05 -06:00
log ( " Mapped to bram port %c%d.%d. \n " , pi . group + ' A ' , pi . index + 1 , pi . dupidx + 1 ) ;
2015-01-01 08:32:37 -06:00
pi . mapped_port = cell_port_i ;
if ( clken ) {
2015-01-01 05:17:19 -06:00
clock_domains [ pi . clocks ] = clkdom ;
2015-01-02 15:45:26 -06:00
clock_polarities [ pi . clkpol ] = clkdom . second ;
2015-01-03 05:41:46 -06:00
read_transp [ pi . transp ] = transp ;
2015-01-01 08:32:37 -06:00
pi . sig_clock = clkdom . first ;
2015-01-01 17:07:44 -06:00
pi . effective_clkpol = clkdom . second ;
2015-01-01 08:32:37 -06:00
}
pi . sig_addr = rd_addr . extract ( cell_port_i * mem_abits , mem_abits ) ;
pi . sig_data = rd_data . extract ( cell_port_i * mem_width , mem_width ) ;
if ( grow_read_ports_cursor < cell_port_i ) {
grow_read_ports_cursor = cell_port_i ;
2015-01-01 05:17:19 -06:00
try_growing_more_read_ports = true ;
}
2015-01-03 05:41:46 -06:00
2015-01-01 05:17:19 -06:00
goto mapped_rd_port ;
}
2015-01-03 10:34:05 -06:00
log ( " Failed to map read port #%d. \n " , cell_port_i ) ;
2015-01-01 05:17:19 -06:00
if ( try_growing_more_read_ports ) {
2015-01-03 10:34:05 -06:00
log ( " Growing more read ports by duplicating bram cells. \n " ) ;
2015-01-01 05:17:19 -06:00
goto grow_read_ports ;
}
return false ;
mapped_rd_port : ;
}
2015-01-04 06:14:30 -06:00
// update properties and re-check conditions
2015-01-03 10:34:05 -06:00
if ( mode < = 1 )
{
match_properties [ " dups " ] = dup_count ;
match_properties [ " waste " ] = match_properties [ " dups " ] * match_properties [ " bwaste " ] ;
2015-01-04 06:14:30 -06:00
int cells = ( ( mem_width + bram . dbits - 1 ) / bram . dbits ) * ( ( mem_size + ( 1 < < bram . abits ) - 1 ) / ( 1 < < bram . abits ) ) ;
2015-01-03 10:34:05 -06:00
match_properties [ " efficiency " ] = ( 100 * match_properties [ " bits " ] ) / ( dup_count * cells * bram . dbits * ( 1 < < bram . abits ) ) ;
2015-01-18 12:05:29 -06:00
match_properties [ " dcells " ] = ( ( mem_width + bram . dbits - 1 ) / bram . dbits ) ;
match_properties [ " acells " ] = ( ( mem_size + ( 1 < < bram . abits ) - 1 ) / ( 1 < < bram . abits ) ) ;
match_properties [ " cells " ] = match_properties [ " dcells " ] * match_properties [ " acells " ] * match_properties [ " dups " ] ;
2015-01-03 10:34:05 -06:00
log ( " Updated properties: dups=%d waste=%d efficiency=%d \n " ,
match_properties [ " dups " ] , match_properties [ " waste " ] , match_properties [ " efficiency " ] ) ;
for ( auto it : match . min_limits ) {
if ( ! match_properties . count ( it . first ) )
log_error ( " Unknown property '%s' in match rule for bram type %s. \n " ,
it . first . c_str ( ) , log_id ( match . name ) ) ;
if ( match_properties [ it . first ] > = it . second )
continue ;
log ( " Rule for bram type %s rejected: requirement 'min %s %d' not met. \n " ,
log_id ( match . name ) , it . first . c_str ( ) , it . second ) ;
return false ;
}
for ( auto it : match . max_limits ) {
if ( ! match_properties . count ( it . first ) )
log_error ( " Unknown property '%s' in match rule for bram type %s. \n " ,
it . first . c_str ( ) , log_id ( match . name ) ) ;
if ( match_properties [ it . first ] < = it . second )
continue ;
log ( " Rule for bram type %s rejected: requirement 'max %s %d' not met. \n " ,
log_id ( match . name ) , it . first . c_str ( ) , it . second ) ;
return false ;
}
if ( mode = = 1 )
return true ;
}
2015-01-18 12:05:29 -06:00
// prepare variant parameters
dict < IdString , Const > variant_params ;
for ( auto & other_bram : rules . brams . at ( bram . name ) )
bram . find_variant_params ( variant_params , other_bram ) ;
2015-01-04 06:14:30 -06:00
// actually replace that memory cell
2015-01-01 08:32:37 -06:00
dict < SigSpec , pair < SigSpec , SigSpec > > dout_cache ;
for ( int grid_d = 0 ; grid_d * bram . dbits < mem_width ; grid_d + + )
{
2015-01-18 12:05:29 -06:00
SigSpec mktr_wraddr , mktr_wrdata , mktr_wrdata_q ;
vector < SigSpec > mktr_wren ;
if ( enable_make_transp ) {
mktr_wraddr = module - > addWire ( NEW_ID , bram . abits ) ;
mktr_wrdata = module - > addWire ( NEW_ID , bram . dbits ) ;
mktr_wrdata_q = module - > addWire ( NEW_ID , bram . dbits ) ;
module - > addDff ( NEW_ID , make_transp_clk . first , mktr_wrdata , mktr_wrdata_q , make_transp_clk . second ) ;
for ( int grid_a = 0 ; grid_a * ( 1 < < bram . abits ) < mem_size ; grid_a + + )
mktr_wren . push_back ( module - > addWire ( NEW_ID , make_transp_enbits ) ) ;
}
2015-01-01 08:32:37 -06:00
2015-01-18 12:05:29 -06:00
for ( int grid_a = 0 ; grid_a * ( 1 < < bram . abits ) < mem_size ; grid_a + + )
for ( int dupidx = 0 ; dupidx < dup_count ; dupidx + + )
2015-01-01 08:32:37 -06:00
{
2015-01-18 12:05:29 -06:00
Cell * c = module - > addCell ( module - > uniquify ( stringf ( " %s.%d.%d.%d " , cell - > name . c_str ( ) , grid_d , grid_a , dupidx ) ) , bram . name ) ;
log ( " Creating %s cell at grid position <%d %d %d>: %s \n " , log_id ( bram . name ) , grid_d , grid_a , dupidx , log_id ( c ) ) ;
2015-01-01 08:32:37 -06:00
2015-01-18 12:05:29 -06:00
for ( auto & vp : variant_params )
c - > setParam ( vp . first , vp . second ) ;
2015-01-01 08:32:37 -06:00
2015-04-06 10:06:15 -05:00
if ( cell_init ) {
int init_offset = grid_a * ( 1 < < bram . abits ) ;
int init_shift = grid_d * bram . dbits ;
int init_size = ( 1 < < bram . abits ) ;
Const initparam ( State : : Sx , init_size * bram . dbits ) ;
for ( int i = 0 ; i < init_size ; i + + ) {
State padding = State : : Sx ;
for ( int j = 0 ; j < bram . dbits ; j + + )
if ( init_offset + i < GetSize ( initdata ) & & init_shift + j < GetSize ( initdata [ init_offset + i ] ) )
padding = initparam [ i * bram . dbits + j ] = initdata [ init_offset + i ] [ init_shift + j ] ;
else
initparam [ i * bram . dbits + j ] = padding ;
}
c - > setParam ( " \\ INIT " , initparam ) ;
}
2015-01-18 12:05:29 -06:00
for ( auto & pi : portinfos )
{
if ( pi . dupidx ! = dupidx )
continue ;
string prefix = stringf ( " %c%d " , pi . group + ' A ' , pi . index + 1 ) ;
const char * pf = prefix . c_str ( ) ;
if ( pi . clocks & & ( ! c - > hasPort ( stringf ( " \\ CLK%d " , ( pi . clocks - 1 ) % clocks_max + 1 ) ) | | pi . sig_clock . wire ) ) {
c - > setPort ( stringf ( " \\ CLK%d " , ( pi . clocks - 1 ) % clocks_max + 1 ) , pi . sig_clock ) ;
if ( pi . clkpol > 1 & & pi . sig_clock . wire )
c - > setParam ( stringf ( " \\ CLKPOL%d " , ( pi . clkpol - 1 ) % clkpol_max + 1 ) , clock_polarities . at ( pi . clkpol ) ) ;
if ( pi . transp > 1 & & pi . sig_clock . wire )
c - > setParam ( stringf ( " \\ TRANSP%d " , ( pi . transp - 1 ) % transp_max + 1 ) , read_transp . at ( pi . transp ) ) ;
}
2015-01-01 08:32:37 -06:00
2015-01-18 12:05:29 -06:00
SigSpec addr_ok ;
if ( GetSize ( pi . sig_addr ) > bram . abits ) {
SigSpec extra_addr = pi . sig_addr . extract ( bram . abits , GetSize ( pi . sig_addr ) - bram . abits ) ;
SigSpec extra_addr_sel = SigSpec ( grid_a , GetSize ( extra_addr ) ) ;
addr_ok = module - > Eq ( NEW_ID , extra_addr , extra_addr_sel ) ;
}
2015-01-01 08:32:37 -06:00
2015-01-18 12:05:29 -06:00
if ( pi . enable )
{
SigSpec sig_en = pi . sig_en ;
sig_en . extend_u0 ( ( grid_d + 1 ) * pi . enable ) ;
sig_en = sig_en . extract ( grid_d * pi . enable , pi . enable ) ;
2015-01-01 08:32:37 -06:00
2015-01-18 12:05:29 -06:00
if ( ! addr_ok . empty ( ) )
sig_en = module - > Mux ( NEW_ID , SigSpec ( 0 , GetSize ( sig_en ) ) , sig_en , addr_ok ) ;
2015-01-01 08:32:37 -06:00
2015-01-18 12:05:29 -06:00
c - > setPort ( stringf ( " \\ %sEN " , pf ) , sig_en ) ;
2015-01-01 08:32:37 -06:00
2015-01-18 12:05:29 -06:00
if ( pi . wrmode = = 1 & & enable_make_transp )
module - > connect ( mktr_wren [ grid_a ] , sig_en ) ;
}
2015-01-01 08:32:37 -06:00
2015-01-18 12:05:29 -06:00
SigSpec sig_addr = pi . sig_addr ;
sig_addr . extend_u0 ( bram . abits ) ;
c - > setPort ( stringf ( " \\ %sADDR " , pf ) , sig_addr ) ;
if ( pi . wrmode = = 1 & & enable_make_transp & & grid_a = = 0 )
module - > connect ( mktr_wraddr , sig_addr ) ;
SigSpec sig_data = pi . sig_data ;
sig_data . extend_u0 ( ( grid_d + 1 ) * bram . dbits ) ;
sig_data = sig_data . extract ( grid_d * bram . dbits , bram . dbits ) ;
if ( pi . wrmode = = 1 ) {
c - > setPort ( stringf ( " \\ %sDATA " , pf ) , sig_data ) ;
if ( enable_make_transp & & grid_a = = 0 )
module - > connect ( mktr_wrdata , sig_data ) ;
} else {
SigSpec bram_dout = module - > addWire ( NEW_ID , bram . dbits ) ;
c - > setPort ( stringf ( " \\ %sDATA " , pf ) , bram_dout ) ;
2015-04-09 09:08:54 -05:00
if ( pi . make_outreg ) {
SigSpec bram_dout_q = module - > addWire ( NEW_ID , bram . dbits ) ;
module - > addDff ( NEW_ID , pi . sig_clock , bram_dout , bram_dout_q , pi . effective_clkpol ) ;
bram_dout = bram_dout_q ;
}
2015-01-18 12:05:29 -06:00
if ( pi . make_transp )
{
log ( " Adding extra logic for transparent port %c%d.%d. \n " , pi . group + ' A ' , pi . index + 1 , pi . dupidx + 1 ) ;
SigSpec transp_en_d = module - > Mux ( NEW_ID , SigSpec ( 0 , make_transp_enbits ) ,
mktr_wren [ grid_a ] , module - > Eq ( NEW_ID , mktr_wraddr , sig_addr ) ) ;
SigSpec transp_en_q = module - > addWire ( NEW_ID , make_transp_enbits ) ;
module - > addDff ( NEW_ID , make_transp_clk . first , transp_en_d , transp_en_q , make_transp_clk . second ) ;
for ( int i = 0 ; i < make_transp_enbits ; i + + ) {
int en_width = bram . dbits / make_transp_enbits ;
SigSpec orig_bram_dout = bram_dout . extract ( i * en_width , en_width ) ;
SigSpec bypass_dout = mktr_wrdata_q . extract ( i * en_width , en_width ) ;
bram_dout . replace ( i * en_width , module - > Mux ( NEW_ID , orig_bram_dout , bypass_dout , transp_en_q [ i ] ) ) ;
}
}
2015-01-01 08:32:37 -06:00
2015-01-18 12:05:29 -06:00
for ( int i = bram . dbits - 1 ; i > = 0 ; i - - )
if ( sig_data [ i ] . wire = = nullptr ) {
sig_data . remove ( i ) ;
bram_dout . remove ( i ) ;
}
SigSpec addr_ok_q = addr_ok ;
2015-04-09 09:08:54 -05:00
if ( ( pi . clocks | | pi . make_outreg ) & & ! addr_ok . empty ( ) ) {
2015-01-18 12:05:29 -06:00
addr_ok_q = module - > addWire ( NEW_ID ) ;
module - > addDff ( NEW_ID , pi . sig_clock , addr_ok , addr_ok_q , pi . effective_clkpol ) ;
2015-01-01 08:32:37 -06:00
}
2015-01-18 12:05:29 -06:00
dout_cache [ sig_data ] . first . append ( addr_ok_q ) ;
dout_cache [ sig_data ] . second . append ( bram_dout ) ;
2015-01-01 17:07:44 -06:00
}
2015-01-01 08:32:37 -06:00
}
}
}
for ( auto & it : dout_cache )
{
if ( it . second . first . empty ( ) )
{
log_assert ( GetSize ( it . first ) = = GetSize ( it . second . second ) ) ;
module - > connect ( it . first , it . second . second ) ;
}
else
{
log_assert ( GetSize ( it . first ) * GetSize ( it . second . first ) = = GetSize ( it . second . second ) ) ;
module - > addPmux ( NEW_ID , SigSpec ( State : : Sx , GetSize ( it . first ) ) , it . second . second , it . second . first , it . first ) ;
}
}
module - > remove ( cell ) ;
return true ;
2014-12-31 09:53:53 -06:00
}
void handle_cell ( Cell * cell , const rules_t & rules )
{
log ( " Processing %s.%s: \n " , log_id ( cell - > module ) , log_id ( cell ) ) ;
2015-04-06 10:06:15 -05:00
bool cell_init = ! SigSpec ( cell - > getParam ( " \\ INIT " ) ) . is_fully_undef ( ) ;
2015-02-14 05:55:03 -06:00
2015-01-01 05:17:19 -06:00
dict < string , int > match_properties ;
match_properties [ " words " ] = cell - > getParam ( " \\ SIZE " ) . as_int ( ) ;
match_properties [ " abits " ] = cell - > getParam ( " \\ ABITS " ) . as_int ( ) ;
match_properties [ " dbits " ] = cell - > getParam ( " \\ WIDTH " ) . as_int ( ) ;
match_properties [ " wports " ] = cell - > getParam ( " \\ WR_PORTS " ) . as_int ( ) ;
match_properties [ " rports " ] = cell - > getParam ( " \\ RD_PORTS " ) . as_int ( ) ;
match_properties [ " bits " ] = match_properties [ " words " ] * match_properties [ " dbits " ] ;
match_properties [ " ports " ] = match_properties [ " wports " ] + match_properties [ " rports " ] ;
2014-12-31 09:53:53 -06:00
log ( " Properties: " ) ;
2015-01-01 05:17:19 -06:00
for ( auto & it : match_properties )
2014-12-31 09:53:53 -06:00
log ( " %s=%d " , it . first . c_str ( ) , it . second ) ;
log ( " \n " ) ;
2015-01-18 12:05:29 -06:00
pool < pair < IdString , int > > failed_brams ;
dict < pair < int , int > , std : : tuple < int , int , int > > best_rule_cache ;
2014-12-31 15:50:08 -06:00
2014-12-31 09:53:53 -06:00
for ( int i = 0 ; i < GetSize ( rules . matches ) ; i + + )
{
2015-01-18 12:05:29 -06:00
auto & match = rules . matches . at ( i ) ;
2015-01-01 05:17:19 -06:00
if ( ! rules . brams . count ( rules . matches [ i ] . name ) )
log_error ( " No bram description for resource %s found! \n " , log_id ( rules . matches [ i ] . name ) ) ;
2015-01-18 12:05:29 -06:00
for ( int vi = 0 ; vi < GetSize ( rules . brams . at ( match . name ) ) ; vi + + )
{
auto & bram = rules . brams . at ( match . name ) . at ( vi ) ;
bool or_next_if_better = match . or_next_if_better | | vi + 1 < GetSize ( rules . brams . at ( match . name ) ) ;
2015-01-03 10:34:05 -06:00
2015-01-18 12:05:29 -06:00
if ( failed_brams . count ( pair < IdString , int > ( bram . name , bram . variant ) ) )
continue ;
2015-01-03 10:34:05 -06:00
2015-01-18 12:05:29 -06:00
int avail_rd_ports = 0 ;
int avail_wr_ports = 0 ;
for ( int j = 0 ; j < bram . groups ; j + + ) {
if ( GetSize ( bram . wrmode ) < j | | bram . wrmode . at ( j ) = = 0 )
avail_rd_ports + = GetSize ( bram . ports ) < j ? bram . ports . at ( j ) : 0 ;
if ( GetSize ( bram . wrmode ) < j | | bram . wrmode . at ( j ) ! = 0 )
avail_wr_ports + = GetSize ( bram . ports ) < j ? bram . ports . at ( j ) : 0 ;
}
2015-01-03 10:34:05 -06:00
2015-01-18 12:05:29 -06:00
log ( " Checking rule #%d for bram type %s (variant %d): \n " , i + 1 , log_id ( bram . name ) , bram . variant ) ;
log ( " Bram geometry: abits=%d dbits=%d wports=%d rports=%d \n " , bram . abits , bram . dbits , avail_wr_ports , avail_rd_ports ) ;
2015-01-03 10:34:05 -06:00
2015-01-18 12:05:29 -06:00
int dups = avail_rd_ports ? ( match_properties [ " rports " ] + avail_rd_ports - 1 ) / avail_rd_ports : 1 ;
match_properties [ " dups " ] = dups ;
2015-01-01 05:17:19 -06:00
2015-01-18 12:05:29 -06:00
log ( " Estimated number of duplicates for more read ports: dups=%d \n " , match_properties [ " dups " ] ) ;
2015-01-01 05:17:19 -06:00
2015-01-18 12:05:29 -06:00
int aover = match_properties [ " words " ] % ( 1 < < bram . abits ) ;
int awaste = aover ? ( 1 < < bram . abits ) - aover : 0 ;
match_properties [ " awaste " ] = awaste ;
2015-01-03 10:34:05 -06:00
2015-01-18 12:05:29 -06:00
int dover = match_properties [ " dbits " ] % bram . dbits ;
int dwaste = dover ? bram . dbits - dover : 0 ;
match_properties [ " dwaste " ] = dwaste ;
2015-01-01 05:17:19 -06:00
2015-01-18 12:05:29 -06:00
int bwaste = awaste * bram . dbits + dwaste * ( 1 < < bram . abits ) - awaste * dwaste ;
match_properties [ " bwaste " ] = bwaste ;
2015-01-03 10:34:05 -06:00
2015-01-18 12:05:29 -06:00
int waste = match_properties [ " dups " ] * bwaste ;
match_properties [ " waste " ] = waste ;
2015-01-01 05:17:19 -06:00
2015-01-18 12:05:29 -06:00
int cells = ( ( match_properties [ " dbits " ] + bram . dbits - 1 ) / bram . dbits ) * ( ( match_properties [ " words " ] + ( 1 < < bram . abits ) - 1 ) / ( 1 < < bram . abits ) ) ;
int efficiency = ( 100 * match_properties [ " bits " ] ) / ( dups * cells * bram . dbits * ( 1 < < bram . abits ) ) ;
match_properties [ " efficiency " ] = efficiency ;
2015-01-03 10:34:05 -06:00
2015-01-18 12:05:29 -06:00
log ( " Metrics for %s: awaste=%d dwaste=%d bwaste=%d waste=%d efficiency=%d \n " ,
log_id ( match . name ) , awaste , dwaste , bwaste , waste , efficiency ) ;
2015-01-03 10:34:05 -06:00
2015-04-06 10:06:15 -05:00
if ( cell_init & & bram . init = = 0 ) {
log ( " Rule #%d for bram type %s (variant %d) rejected: cannot be initialized. \n " ,
i + 1 , log_id ( bram . name ) , bram . variant ) ;
goto next_match_rule ;
}
2015-01-18 12:05:29 -06:00
for ( auto it : match . min_limits ) {
if ( it . first = = " waste " | | it . first = = " dups " | | it . first = = " acells " | | it . first = = " dcells " | | it . first = = " cells " )
continue ;
if ( ! match_properties . count ( it . first ) )
log_error ( " Unknown property '%s' in match rule for bram type %s. \n " ,
it . first . c_str ( ) , log_id ( match . name ) ) ;
if ( match_properties [ it . first ] > = it . second )
continue ;
log ( " Rule #%d for bram type %s (variant %d) rejected: requirement 'min %s %d' not met. \n " ,
i + 1 , log_id ( bram . name ) , bram . variant , it . first . c_str ( ) , it . second ) ;
goto next_match_rule ;
}
2015-04-06 10:06:15 -05:00
2015-01-18 12:05:29 -06:00
for ( auto it : match . max_limits ) {
if ( it . first = = " acells " | | it . first = = " dcells " | | it . first = = " cells " )
continue ;
if ( ! match_properties . count ( it . first ) )
log_error ( " Unknown property '%s' in match rule for bram type %s. \n " ,
it . first . c_str ( ) , log_id ( match . name ) ) ;
if ( match_properties [ it . first ] < = it . second )
continue ;
log ( " Rule #%d for bram type %s (variant %d) rejected: requirement 'max %s %d' not met. \n " ,
i + 1 , log_id ( bram . name ) , bram . variant , it . first . c_str ( ) , it . second ) ;
2015-01-03 10:34:05 -06:00
goto next_match_rule ;
}
2015-01-18 12:05:29 -06:00
log ( " Rule #%d for bram type %s (variant %d) accepted. \n " , i + 1 , log_id ( bram . name ) , bram . variant ) ;
2015-01-03 10:34:05 -06:00
2015-01-18 12:05:29 -06:00
if ( or_next_if_better | | ! best_rule_cache . empty ( ) )
{
if ( or_next_if_better & & i + 1 = = GetSize ( rules . matches ) & & vi + 1 = = GetSize ( rules . brams . at ( match . name ) ) )
log_error ( " Found 'or_next_if_better' in last match rule. \n " ) ;
2015-01-03 10:34:05 -06:00
2015-01-18 12:05:29 -06:00
if ( ! replace_cell ( cell , rules , bram , match , match_properties , 1 ) ) {
log ( " Mapping to bram type %s failed. \n " , log_id ( match . name ) ) ;
failed_brams . insert ( pair < IdString , int > ( bram . name , bram . variant ) ) ;
goto next_match_rule ;
}
2015-01-03 10:34:05 -06:00
2015-01-18 12:05:29 -06:00
log ( " Storing for later selection. \n " ) ;
best_rule_cache [ pair < int , int > ( i , vi ) ] = std : : tuple < int , int , int > ( match_properties [ " efficiency " ] , - match_properties [ " cells " ] , - match_properties [ " acells " ] ) ;
2015-01-03 10:34:05 -06:00
2015-01-18 12:05:29 -06:00
next_match_rule :
if ( or_next_if_better | | best_rule_cache . empty ( ) )
continue ;
2015-01-03 10:34:05 -06:00
2015-01-18 12:05:29 -06:00
log ( " Selecting best of %d rules: \n " , GetSize ( best_rule_cache ) ) ;
pair < int , int > best_rule = best_rule_cache . begin ( ) - > first ;
for ( auto & it : best_rule_cache ) {
if ( it . second > best_rule_cache [ best_rule ] )
best_rule = it . first ;
log ( " Efficiency for rule %d.%d: efficiency=%d, cells=%d, acells=%d \n " , it . first . first + 1 , it . first . second + 1 ,
std : : get < 0 > ( it . second ) , - std : : get < 1 > ( it . second ) , - std : : get < 2 > ( it . second ) ) ;
}
log ( " Selected rule %d.%d with efficiency %d. \n " , best_rule . first + 1 , best_rule . second + 1 , std : : get < 0 > ( best_rule_cache [ best_rule ] ) ) ;
best_rule_cache . clear ( ) ;
2014-12-31 09:53:53 -06:00
2015-01-18 12:05:29 -06:00
auto & best_bram = rules . brams . at ( rules . matches . at ( best_rule . first ) . name ) . at ( best_rule . second ) ;
if ( ! replace_cell ( cell , rules , best_bram , rules . matches . at ( best_rule . first ) , match_properties , 2 ) )
log_error ( " Mapping to bram type %s (variant %d) after pre-selection failed. \n " , log_id ( best_bram . name ) , best_bram . variant ) ;
return ;
}
if ( ! replace_cell ( cell , rules , bram , match , match_properties , 0 ) ) {
log ( " Mapping to bram type %s failed. \n " , log_id ( match . name ) ) ;
failed_brams . insert ( pair < IdString , int > ( bram . name , bram . variant ) ) ;
goto next_match_rule ;
}
return ;
2014-12-31 15:50:08 -06:00
}
2014-12-31 09:53:53 -06:00
}
log ( " No acceptable bram resources found. \n " ) ;
}
struct MemoryBramPass : public Pass {
MemoryBramPass ( ) : Pass ( " memory_bram " , " map memories to block rams " ) { }
virtual void help ( )
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log ( " \n " ) ;
log ( " memory_bram -rules <rule_file> [selection] \n " ) ;
log ( " \n " ) ;
log ( " This pass converts the multi-port $mem memory cells into block ram instances. \n " ) ;
log ( " The given rules file describes the available resources and how they should be \n " ) ;
log ( " used. \n " ) ;
log ( " \n " ) ;
2015-01-01 08:32:37 -06:00
log ( " The rules file contains a set of block ram description and a sequence of match \n " ) ;
log ( " rules. A block ram description looks like this: \n " ) ;
log ( " \n " ) ;
log ( " bram RAMB1024X32 # name of BRAM cell \n " ) ;
2015-04-06 10:06:15 -05:00
log ( " init 1 # set to '1' if BRAM can be initialized \n " ) ;
2015-01-01 08:32:37 -06:00
log ( " abits 10 # number of address bits \n " ) ;
log ( " dbits 32 # number of data bits \n " ) ;
log ( " groups 2 # number of port groups \n " ) ;
log ( " ports 1 1 # number of ports in each group \n " ) ;
log ( " wrmode 1 0 # set to '1' if this groups is write ports \n " ) ;
log ( " enable 4 0 # number of enable bits (for write ports) \n " ) ;
log ( " transp 0 2 # transparatent (for read ports) \n " ) ;
log ( " clocks 1 2 # clock configuration \n " ) ;
log ( " clkpol 2 2 # clock polarity configuration \n " ) ;
log ( " endbram \n " ) ;
log ( " \n " ) ;
log ( " For the option 'transp' the value 0 means non-transparent, 1 means transparent \n " ) ;
log ( " and a value greater than 1 means configurable. All groups with the same \n " ) ;
log ( " value greater than 1 share the same configuration bit. \n " ) ;
log ( " \n " ) ;
log ( " For the option 'clocks' the value 0 means non-clocked, and a value greater \n " ) ;
log ( " than 0 means clocked. All groups with the same value share the same clock \n " ) ;
log ( " signal. \n " ) ;
log ( " \n " ) ;
log ( " For the option 'clkpol' the value 0 means negative edge, 1 means positive edge \n " ) ;
log ( " and a value greater than 1 means configurable. All groups with the same value \n " ) ;
log ( " greater than 1 share the same configuration bit. \n " ) ;
log ( " \n " ) ;
2015-01-18 12:05:29 -06:00
log ( " Using the same bram name in different bram blocks will create different variants \n " ) ;
log ( " of the bram. Verilog configration parameters for the bram are created as needed. \n " ) ;
log ( " \n " ) ;
log ( " It is also possible to create variants by repeating statements in the bram block \n " ) ;
log ( " and appending '@<label>' to the individual statements. \n " ) ;
log ( " \n " ) ;
2015-01-01 08:32:37 -06:00
log ( " A match rule looks like this: \n " ) ;
log ( " \n " ) ;
log ( " match RAMB1024X32 \n " ) ;
2015-01-03 10:34:05 -06:00
log ( " max waste 16384 # only use this bram if <= 16k ram bits are unused \n " ) ;
log ( " min efficiency 80 # only use this bram if efficiency is at least 80%% \n " ) ;
2015-01-01 08:32:37 -06:00
log ( " endmatch \n " ) ;
log ( " \n " ) ;
log ( " It is possible to match against the following values with min/max rules: \n " ) ;
log ( " \n " ) ;
2015-01-03 10:34:05 -06:00
log ( " words ........ number of words in memory in design \n " ) ;
2015-02-01 14:49:55 -06:00
log ( " abits ........ number of address bits on memory in design \n " ) ;
2015-01-03 10:34:05 -06:00
log ( " dbits ........ number of data bits on memory in design \n " ) ;
log ( " wports ....... number of write ports on memory in design \n " ) ;
log ( " rports ....... number of read ports on memory in design \n " ) ;
log ( " ports ........ number of ports on memory in design \n " ) ;
log ( " bits ......... number of bits in memory in design \n " ) ;
log ( " dups .......... number of duplications for more read ports \n " ) ;
2015-01-01 08:32:37 -06:00
log ( " \n " ) ;
2015-01-03 10:34:05 -06:00
log ( " awaste ....... number of unused address slots for this match \n " ) ;
log ( " dwaste ....... number of unused data bits for this match \n " ) ;
log ( " bwaste ....... number of unused bram bits for this match \n " ) ;
log ( " waste ........ total number of unused bram bits (bwaste*dups) \n " ) ;
log ( " efficiency ... total percentage of used and non-duplicated bits \n " ) ;
2015-01-01 08:32:37 -06:00
log ( " \n " ) ;
2015-01-18 12:05:29 -06:00
log ( " acells ....... number of cells in 'address-direction' \n " ) ;
log ( " dcells ....... number of cells in 'data-direction' \n " ) ;
log ( " cells ........ total number of cells (acells*dcells*dups) \n " ) ;
log ( " \n " ) ;
2015-01-01 08:32:37 -06:00
log ( " The interface for the created bram instances is dervived from the bram \n " ) ;
log ( " description. Use 'techmap' to convert the created bram instances into \n " ) ;
log ( " instances of the actual bram cells of your target architecture. \n " ) ;
log ( " \n " ) ;
2015-01-03 10:34:05 -06:00
log ( " A match containing the command 'or_next_if_better' is only used if it \n " ) ;
log ( " has a higher efficiency than the next match (and the one after that if \n " ) ;
log ( " the next also has 'or_next_if_better' set, and so forth). \n " ) ;
log ( " \n " ) ;
2015-01-18 12:05:29 -06:00
log ( " A match containing the command 'make_transp' will add external circuitry \n " ) ;
2015-02-01 14:49:55 -06:00
log ( " to simulate 'transparent read', if necessary. \n " ) ;
2015-01-18 12:05:29 -06:00
log ( " \n " ) ;
2015-04-09 09:08:54 -05:00
log ( " A match containing the command 'make_outreg' will add external flip-flops \n " ) ;
log ( " to implement synchronous read ports, if necessary. \n " ) ;
log ( " \n " ) ;
2015-01-18 12:05:29 -06:00
log ( " A match containing the command 'shuffle_enable A' will re-organize \n " ) ;
log ( " the data bits to accommodate the enable pattern of port A. \n " ) ;
2015-01-04 06:14:30 -06:00
log ( " \n " ) ;
2014-12-31 09:53:53 -06:00
}
virtual void execute ( vector < string > args , Design * design )
{
rules_t rules ;
log_header ( " Executing MEMORY_BRAM pass (mapping $mem cells to block memories). \n " ) ;
size_t argidx ;
for ( argidx = 1 ; argidx < args . size ( ) ; argidx + + ) {
if ( args [ argidx ] = = " -rules " & & argidx + 1 < args . size ( ) ) {
rules . parse ( args [ + + argidx ] ) ;
continue ;
}
break ;
}
extra_args ( args , argidx , design ) ;
for ( auto mod : design - > selected_modules ( ) )
for ( auto cell : mod - > selected_cells ( ) )
if ( cell - > type = = " $mem " )
handle_cell ( cell , rules ) ;
}
} MemoryBramPass ;
PRIVATE_NAMESPACE_END