2016-11-17 16:36:47 -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/rtlil.h"
# include "kernel/register.h"
# include "kernel/sigtools.h"
# include "kernel/celltypes.h"
# include "kernel/cellaigs.h"
# include "kernel/log.h"
2019-02-15 13:14:17 -06:00
# include <algorithm>
2016-11-17 16:36:47 -06:00
# include <string>
2019-02-15 13:14:17 -06:00
# include <vector>
# include <cmath>
2016-11-17 16:36:47 -06:00
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
pool < string > used_names ;
dict < IdString , string > namecache ;
int autoid_counter ;
2018-08-23 16:35:11 -05:00
typedef unsigned FDirection ;
2018-09-21 13:43:49 -05:00
static const FDirection FD_NODIRECTION = 0x0 ;
static const FDirection FD_IN = 0x1 ;
static const FDirection FD_OUT = 0x2 ;
static const FDirection FD_INOUT = 0x3 ;
2019-02-15 13:14:17 -06:00
static const int FIRRTL_MAX_DSH_WIDTH_ERROR = 20 ; // For historic reasons, this is actually one greater than the maximum allowed shift width
2018-08-23 16:35:11 -05:00
2020-03-24 04:36:14 -05:00
std : : string getFileinfo ( const RTLIL : : AttrObject * design_entity )
2020-03-18 17:38:36 -05:00
{
2020-03-24 04:36:14 -05:00
std : : string src ( design_entity - > get_src_attribute ( ) ) ;
2020-03-24 15:07:08 -05:00
std : : string fileinfo_str = src . empty ( ) ? " " : " @[ " + src + " ] " ;
2020-03-23 03:01:17 -05:00
return fileinfo_str ;
2020-03-19 08:59:05 -05:00
}
2018-08-23 16:35:11 -05:00
// Get a port direction with respect to a specific module.
FDirection getPortFDirection ( IdString id , Module * module )
{
Wire * wire = module - > wires_ . at ( id ) ;
2018-09-21 13:43:49 -05:00
FDirection direction = FD_NODIRECTION ;
2018-08-23 16:35:11 -05:00
if ( wire & & wire - > port_id )
{
if ( wire - > port_input )
2018-09-21 13:43:49 -05:00
direction | = FD_IN ;
2018-08-23 16:35:11 -05:00
if ( wire - > port_output )
2018-09-21 13:43:49 -05:00
direction | = FD_OUT ;
2018-08-23 16:35:11 -05:00
}
return direction ;
}
2016-11-17 16:36:47 -06:00
string next_id ( )
{
string new_id ;
while ( 1 ) {
new_id = stringf ( " _%d " , autoid_counter + + ) ;
if ( used_names . count ( new_id ) = = 0 ) break ;
}
used_names . insert ( new_id ) ;
return new_id ;
}
const char * make_id ( IdString id )
{
if ( namecache . count ( id ) ! = 0 )
return namecache . at ( id ) . c_str ( ) ;
string new_id = log_id ( id ) ;
for ( int i = 0 ; i < GetSize ( new_id ) ; i + + )
{
char & ch = new_id [ i ] ;
if ( ' a ' < = ch & & ch < = ' z ' ) continue ;
if ( ' A ' < = ch & & ch < = ' Z ' ) continue ;
if ( ' 0 ' < = ch & & ch < = ' 9 ' & & i ! = 0 ) continue ;
if ( ' _ ' = = ch ) continue ;
ch = ' _ ' ;
}
while ( used_names . count ( new_id ) ! = 0 )
new_id + = ' _ ' ;
namecache [ id ] = new_id ;
used_names . insert ( new_id ) ;
return namecache . at ( id ) . c_str ( ) ;
}
struct FirrtlWorker
{
Module * module ;
std : : ostream & f ;
dict < SigBit , pair < string , int > > reverse_wire_map ;
string unconn_id ;
2018-08-23 16:35:11 -05:00
RTLIL : : Design * design ;
std : : string indent ;
2016-11-17 16:36:47 -06:00
2019-04-01 17:02:12 -05:00
// Define read/write ports and memories.
// We'll collect their definitions and emit the corresponding FIRRTL definitions at the appropriate point in module construction.
// For the moment, we don't handle $readmemh or $readmemb.
// These will be part of a subsequent PR.
struct read_port {
string name ;
bool clk_enable ;
bool clk_parity ;
bool transparent ;
RTLIL : : SigSpec clk ;
RTLIL : : SigSpec ena ;
RTLIL : : SigSpec addr ;
read_port ( string name , bool clk_enable , bool clk_parity , bool transparent , RTLIL : : SigSpec clk , RTLIL : : SigSpec ena , RTLIL : : SigSpec addr ) : name ( name ) , clk_enable ( clk_enable ) , clk_parity ( clk_parity ) , transparent ( transparent ) , clk ( clk ) , ena ( ena ) , addr ( addr ) {
// Current (3/13/2019) conventions:
// generate a constant 0 for clock and a constant 1 for enable if they are undefined.
if ( ! clk . is_fully_def ( ) )
2019-08-06 18:22:47 -05:00
this - > clk = SigSpec ( State : : S0 ) ;
2019-04-01 17:02:12 -05:00
if ( ! ena . is_fully_def ( ) )
2019-08-06 18:22:47 -05:00
this - > ena = SigSpec ( State : : S1 ) ;
2019-04-01 17:02:12 -05:00
}
string gen_read ( const char * indent ) {
string addr_expr = make_expr ( addr ) ;
string ena_expr = make_expr ( ena ) ;
string clk_expr = make_expr ( clk ) ;
string addr_str = stringf ( " %s%s.addr <= %s \n " , indent , name . c_str ( ) , addr_expr . c_str ( ) ) ;
string ena_str = stringf ( " %s%s.en <= %s \n " , indent , name . c_str ( ) , ena_expr . c_str ( ) ) ;
string clk_str = stringf ( " %s%s.clk <= asClock(%s) \n " , indent , name . c_str ( ) , clk_expr . c_str ( ) ) ;
return addr_str + ena_str + clk_str ;
}
} ;
struct write_port : read_port {
RTLIL : : SigSpec mask ;
write_port ( string name , bool clk_enable , bool clk_parity , bool transparent , RTLIL : : SigSpec clk , RTLIL : : SigSpec ena , RTLIL : : SigSpec addr , RTLIL : : SigSpec mask ) : read_port ( name , clk_enable , clk_parity , transparent , clk , ena , addr ) , mask ( mask ) {
if ( ! clk . is_fully_def ( ) )
this - > clk = SigSpec ( RTLIL : : Const ( 0 ) ) ;
if ( ! ena . is_fully_def ( ) )
this - > ena = SigSpec ( RTLIL : : Const ( 0 ) ) ;
if ( ! mask . is_fully_def ( ) )
this - > ena = SigSpec ( RTLIL : : Const ( 1 ) ) ;
}
2019-05-21 15:04:56 -05:00
string gen_read ( const char * /* indent */ ) {
2019-04-01 17:02:12 -05:00
log_error ( " gen_read called on write_port: %s \n " , name . c_str ( ) ) ;
return stringf ( " gen_read called on write_port: %s \n " , name . c_str ( ) ) ;
}
string gen_write ( const char * indent ) {
string addr_expr = make_expr ( addr ) ;
string ena_expr = make_expr ( ena ) ;
string clk_expr = make_expr ( clk ) ;
string mask_expr = make_expr ( mask ) ;
string mask_str = stringf ( " %s%s.mask <= %s \n " , indent , name . c_str ( ) , mask_expr . c_str ( ) ) ;
string addr_str = stringf ( " %s%s.addr <= %s \n " , indent , name . c_str ( ) , addr_expr . c_str ( ) ) ;
string ena_str = stringf ( " %s%s.en <= %s \n " , indent , name . c_str ( ) , ena_expr . c_str ( ) ) ;
string clk_str = stringf ( " %s%s.clk <= asClock(%s) \n " , indent , name . c_str ( ) , clk_expr . c_str ( ) ) ;
return addr_str + ena_str + clk_str + mask_str ;
}
} ;
/* Memories defined within this module. */
2019-05-01 18:21:13 -05:00
struct memory {
Cell * pCell ; // for error reporting
string name ; // memory name
int abits ; // number of address bits
int size ; // size (in units) of the memory
int width ; // size (in bits) of each element
int read_latency ;
int write_latency ;
vector < read_port > read_ports ;
vector < write_port > write_ports ;
std : : string init_file ;
std : : string init_file_srcFileSpec ;
string srcLine ;
memory ( Cell * pCell , string name , int abits , int size , int width ) : pCell ( pCell ) , name ( name ) , abits ( abits ) , size ( size ) , width ( width ) , read_latency ( 0 ) , write_latency ( 1 ) , init_file ( " " ) , init_file_srcFileSpec ( " " ) {
2019-05-01 15:16:01 -05:00
// Provide defaults for abits or size if one (but not the other) is specified.
if ( this - > abits = = 0 & & this - > size ! = 0 ) {
this - > abits = ceil_log2 ( this - > size ) ;
} else if ( this - > abits ! = 0 & & this - > size = = 0 ) {
this - > size = 1 < < this - > abits ;
}
// Sanity-check this construction.
if ( this - > name = = " " ) {
log_error ( " Nameless memory%s \n " , this - > atLine ( ) ) ;
}
if ( this - > abits = = 0 & & this - > size = = 0 ) {
log_error ( " Memory %s has zero address bits and size%s \n " , this - > name . c_str ( ) , this - > atLine ( ) ) ;
}
if ( this - > width = = 0 ) {
log_error ( " Memory %s has zero width%s \n " , this - > name . c_str ( ) , this - > atLine ( ) ) ;
}
2020-03-23 03:01:17 -05:00
}
2019-05-01 15:16:01 -05:00
// We need a default constructor for the dict insert.
2020-03-23 03:01:17 -05:00
memory ( ) : pCell ( 0 ) , read_latency ( 0 ) , write_latency ( 1 ) , init_file ( " " ) , init_file_srcFileSpec ( " " ) { }
2019-05-01 15:16:01 -05:00
const char * atLine ( ) {
if ( srcLine = = " " ) {
if ( pCell ) {
2020-03-12 14:57:01 -05:00
auto p = pCell - > attributes . find ( ID : : src ) ;
2019-05-01 15:16:01 -05:00
srcLine = " at " + p - > second . decode_string ( ) ;
}
}
return srcLine . c_str ( ) ;
}
2019-05-01 18:21:13 -05:00
void add_memory_read_port ( read_port & rp ) {
read_ports . push_back ( rp ) ;
}
void add_memory_write_port ( write_port & wp ) {
write_ports . push_back ( wp ) ;
}
void add_memory_file ( std : : string init_file , std : : string init_file_srcFileSpec ) {
this - > init_file = init_file ;
this - > init_file_srcFileSpec = init_file_srcFileSpec ;
}
2019-04-01 17:02:12 -05:00
2019-05-01 18:21:13 -05:00
} ;
2019-04-01 17:02:12 -05:00
dict < string , memory > memories ;
void register_memory ( memory & m )
{
memories [ m . name ] = m ;
}
2016-11-17 16:36:47 -06:00
void register_reverse_wire_map ( string id , SigSpec sig )
{
for ( int i = 0 ; i < GetSize ( sig ) ; i + + )
reverse_wire_map [ sig [ i ] ] = make_pair ( id , i ) ;
}
2018-08-23 16:35:11 -05:00
FirrtlWorker ( Module * module , std : : ostream & f , RTLIL : : Design * theDesign ) : module ( module ) , f ( f ) , design ( theDesign ) , indent ( " " )
2016-11-17 16:36:47 -06:00
{
}
2019-04-01 17:02:12 -05:00
static string make_expr ( const SigSpec & sig )
2016-11-17 16:36:47 -06:00
{
string expr ;
for ( auto chunk : sig . chunks ( ) )
{
string new_expr ;
if ( chunk . wire = = nullptr )
{
std : : vector < RTLIL : : State > bits = chunk . data ;
new_expr = stringf ( " UInt<%d>( \" h " , GetSize ( bits ) ) ;
while ( GetSize ( bits ) % 4 ! = 0 )
bits . push_back ( State : : S0 ) ;
for ( int i = GetSize ( bits ) - 4 ; i > = 0 ; i - = 4 )
{
int val = 0 ;
if ( bits [ i + 0 ] = = State : : S1 ) val + = 1 ;
if ( bits [ i + 1 ] = = State : : S1 ) val + = 2 ;
if ( bits [ i + 2 ] = = State : : S1 ) val + = 4 ;
if ( bits [ i + 3 ] = = State : : S1 ) val + = 8 ;
new_expr . push_back ( val < 10 ? ' 0 ' + val : ' a ' + val - 10 ) ;
}
new_expr + = " \" ) " ;
}
else if ( chunk . offset = = 0 & & chunk . width = = chunk . wire - > width )
{
new_expr = make_id ( chunk . wire - > name ) ;
}
else
{
string wire_id = make_id ( chunk . wire - > name ) ;
new_expr = stringf ( " bits(%s, %d, %d) " , wire_id . c_str ( ) , chunk . offset + chunk . width - 1 , chunk . offset ) ;
}
if ( expr . empty ( ) )
expr = new_expr ;
else
expr = " cat( " + new_expr + " , " + expr + " ) " ;
}
return expr ;
}
2018-08-23 16:35:11 -05:00
std : : string fid ( RTLIL : : IdString internal_id )
{
2019-03-04 15:23:58 -06:00
return make_id ( internal_id ) ;
2018-08-23 16:35:11 -05:00
}
std : : string cellname ( RTLIL : : Cell * cell )
{
return fid ( cell - > name ) . c_str ( ) ;
}
void process_instance ( RTLIL : : Cell * cell , vector < string > & wire_exprs )
{
std : : string cell_type = fid ( cell - > type ) ;
2019-02-15 13:14:17 -06:00
std : : string instanceOf ;
// If this is a parameterized module, its parent module is encoded in the cell type
2019-08-06 18:42:25 -05:00
if ( cell - > type . begins_with ( " $paramod " ) )
2019-02-15 13:14:17 -06:00
{
std : : string : : iterator it ;
for ( it = cell_type . begin ( ) ; it < cell_type . end ( ) ; it + + )
{
switch ( * it ) {
case ' \\ ' : /* FALL_THROUGH */
case ' = ' : /* FALL_THROUGH */
case ' \' ' : /* FALL_THROUGH */
case ' $ ' : instanceOf . append ( " _ " ) ; break ;
default : instanceOf . append ( 1 , * it ) ; break ;
}
}
}
else
{
instanceOf = cell_type ;
}
2018-08-23 16:35:11 -05:00
std : : string cell_name = cellname ( cell ) ;
std : : string cell_name_comment ;
if ( cell_name ! = fid ( cell - > name ) )
cell_name_comment = " /* " + fid ( cell - > name ) + " */ " ;
else
cell_name_comment = " " ;
// Find the module corresponding to this instance.
auto instModule = design - > module ( cell - > type ) ;
2019-02-15 13:14:17 -06:00
// If there is no instance for this, just return.
if ( instModule = = NULL )
{
log_warning ( " No instance for %s.%s \n " , cell_type . c_str ( ) , cell_name . c_str ( ) ) ;
return ;
}
2020-03-24 15:07:08 -05:00
std : : string cellFileinfo = getFileinfo ( cell ) ;
2020-03-20 12:31:12 -05:00
wire_exprs . push_back ( stringf ( " %s " " inst %s%s of %s %s " , indent . c_str ( ) , cell_name . c_str ( ) , cell_name_comment . c_str ( ) , instanceOf . c_str ( ) , cellFileinfo . c_str ( ) ) ) ;
2018-08-23 16:35:11 -05:00
for ( auto it = cell - > connections ( ) . begin ( ) ; it ! = cell - > connections ( ) . end ( ) ; + + it ) {
if ( it - > second . size ( ) > 0 ) {
const SigSpec & secondSig = it - > second ;
const std : : string firstName = cell_name + " . " + make_id ( it - > first ) ;
2019-02-25 18:18:13 -06:00
const std : : string secondExpr = make_expr ( secondSig ) ;
2018-08-23 16:35:11 -05:00
// Find the direction for this port.
FDirection dir = getPortFDirection ( it - > first , instModule ) ;
2019-02-25 18:18:13 -06:00
std : : string sourceExpr , sinkExpr ;
const SigSpec * sinkSig = nullptr ;
2018-08-23 16:35:11 -05:00
switch ( dir ) {
2018-09-21 13:43:49 -05:00
case FD_INOUT :
2019-02-15 13:14:17 -06:00
log_warning ( " Instance port connection %s.%s is INOUT; treating as OUT \n " , cell_type . c_str ( ) , log_signal ( it - > second ) ) ;
2019-05-05 03:00:27 -05:00
/* FALLTHRU */
2018-09-21 13:43:49 -05:00
case FD_OUT :
2019-02-25 18:18:13 -06:00
sourceExpr = firstName ;
sinkExpr = secondExpr ;
sinkSig = & secondSig ;
2018-08-23 16:35:11 -05:00
break ;
2018-09-21 13:43:49 -05:00
case FD_NODIRECTION :
2019-02-15 13:14:17 -06:00
log_warning ( " Instance port connection %s.%s is NODIRECTION; treating as IN \n " , cell_type . c_str ( ) , log_signal ( it - > second ) ) ;
2019-05-05 03:00:27 -05:00
/* FALLTHRU */
2018-09-21 13:43:49 -05:00
case FD_IN :
2019-02-25 18:18:13 -06:00
sourceExpr = secondExpr ;
sinkExpr = firstName ;
2018-08-23 16:35:11 -05:00
break ;
default :
2019-02-15 13:14:17 -06:00
log_error ( " Instance port %s.%s unrecognized connection direction 0x%x ! \n " , cell_type . c_str ( ) , log_signal ( it - > second ) , dir ) ;
2018-08-23 16:35:11 -05:00
break ;
}
2019-02-25 18:18:13 -06:00
// Check for subfield assignment.
std : : string bitsString = " bits( " ;
2019-08-07 14:20:08 -05:00
if ( sinkExpr . compare ( 0 , bitsString . length ( ) , bitsString ) = = 0 ) {
2019-02-25 18:18:13 -06:00
if ( sinkSig = = nullptr )
log_error ( " Unknown subfield %s.%s \n " , cell_type . c_str ( ) , sinkExpr . c_str ( ) ) ;
// Don't generate the assignment here.
// Add the source and sink to the "reverse_wire_map" and we'll output the assignment
// as part of the coalesced subfield assignments for this wire.
register_reverse_wire_map ( sourceExpr , * sinkSig ) ;
} else {
2020-03-20 12:31:12 -05:00
wire_exprs . push_back ( stringf ( " \n %s%s <= %s %s " , indent . c_str ( ) , sinkExpr . c_str ( ) , sourceExpr . c_str ( ) , cellFileinfo . c_str ( ) ) ) ;
2019-02-25 18:18:13 -06:00
}
2018-08-23 16:35:11 -05:00
}
}
wire_exprs . push_back ( stringf ( " \n " ) ) ;
}
2019-02-15 13:14:17 -06:00
// Given an expression for a shift amount, and a maximum width,
// generate the FIRRTL expression for equivalent dynamic shift taking into account FIRRTL shift semantics.
2019-07-31 11:27:38 -05:00
std : : string gen_dshl ( const string b_expr , const int b_width )
2019-02-15 13:14:17 -06:00
{
string result = b_expr ;
2019-07-31 11:27:38 -05:00
if ( b_width > = FIRRTL_MAX_DSH_WIDTH_ERROR ) {
2019-02-15 13:14:17 -06:00
int max_shift_width_bits = FIRRTL_MAX_DSH_WIDTH_ERROR - 1 ;
string max_shift_string = stringf ( " UInt<%d>(%d) " , max_shift_width_bits , ( 1 < < max_shift_width_bits ) - 1 ) ;
// Deal with the difference in semantics between FIRRTL and verilog
result = stringf ( " mux(gt(%s, %s), %s, bits(%s, %d, 0)) " , b_expr . c_str ( ) , max_shift_string . c_str ( ) , max_shift_string . c_str ( ) , b_expr . c_str ( ) , max_shift_width_bits - 1 ) ;
}
return result ;
}
2016-11-17 16:36:47 -06:00
void run ( )
{
2020-03-24 15:07:08 -05:00
std : : string moduleFileinfo = getFileinfo ( module ) ;
2020-03-20 12:31:12 -05:00
f < < stringf ( " module %s: %s \n " , make_id ( module - > name ) , moduleFileinfo . c_str ( ) ) ;
2016-11-17 16:36:47 -06:00
vector < string > port_decls , wire_decls , cell_exprs , wire_exprs ;
for ( auto wire : module - > wires ( ) )
{
2018-08-23 16:35:11 -05:00
const auto wireName = make_id ( wire - > name ) ;
2020-03-24 15:07:08 -05:00
std : : string wireFileinfo = getFileinfo ( wire ) ;
2020-03-19 08:59:05 -05:00
2019-02-15 13:14:17 -06:00
// If a wire has initial data, issue a warning since FIRRTL doesn't currently support it.
2020-04-02 11:51:32 -05:00
if ( wire - > attributes . count ( ID : : init ) ) {
2019-02-15 13:14:17 -06:00
log_warning ( " Initial value (%s) for (%s.%s) not supported \n " ,
2020-04-02 11:51:32 -05:00
wire - > attributes . at ( ID : : init ) . as_string ( ) . c_str ( ) ,
2019-02-15 13:14:17 -06:00
log_id ( module ) , log_id ( wire ) ) ;
}
2016-11-17 16:36:47 -06:00
if ( wire - > port_id )
{
if ( wire - > port_input & & wire - > port_output )
log_error ( " Module port %s.%s is inout! \n " , log_id ( module ) , log_id ( wire ) ) ;
2020-03-20 12:31:12 -05:00
port_decls . push_back ( stringf ( " %s %s: UInt<%d> %s \n " , wire - > port_input ? " input " : " output " ,
2020-03-19 08:59:05 -05:00
wireName , wire - > width , wireFileinfo . c_str ( ) ) ) ;
2016-11-17 16:36:47 -06:00
}
else
{
2020-03-20 12:31:12 -05:00
wire_decls . push_back ( stringf ( " wire %s: UInt<%d> %s \n " , wireName , wire - > width , wireFileinfo . c_str ( ) ) ) ;
2016-11-17 16:36:47 -06:00
}
}
for ( auto cell : module - > cells ( ) )
{
2019-07-31 11:27:38 -05:00
static Const ndef ( 0 , 0 ) ;
2020-03-21 06:54:23 -05:00
// Is this cell is a module instance?
2018-08-23 16:35:11 -05:00
if ( cell - > type [ 0 ] ! = ' $ ' )
{
process_instance ( cell , wire_exprs ) ;
continue ;
}
2019-07-31 11:27:38 -05:00
// Not a module instance. Set up cell properties
bool extract_y_bits = false ; // Assume no extraction of final bits will be required.
2020-04-02 11:51:32 -05:00
int a_width = cell - > parameters . at ( ID : : A_WIDTH , ndef ) . as_int ( ) ; // The width of "A"
int b_width = cell - > parameters . at ( ID : : B_WIDTH , ndef ) . as_int ( ) ; // The width of "A"
const int y_width = cell - > parameters . at ( ID : : Y_WIDTH , ndef ) . as_int ( ) ; // The width of the result
const bool a_signed = cell - > parameters . at ( ID : : A_SIGNED , ndef ) . as_bool ( ) ;
const bool b_signed = cell - > parameters . at ( ID : : B_SIGNED , ndef ) . as_bool ( ) ;
2019-07-31 11:27:38 -05:00
bool firrtl_is_signed = a_signed ; // The result is signed (subsequent code may change this).
int firrtl_width = 0 ;
string primop ;
bool always_uint = false ;
string y_id = make_id ( cell - > name ) ;
2020-03-24 04:36:14 -05:00
std : : string cellFileinfo = getFileinfo ( cell ) ;
2019-07-31 11:27:38 -05:00
2020-04-02 11:51:32 -05:00
if ( cell - > type . in ( ID ( $ not ) , ID ( $ logic_not ) , ID ( $ neg ) , ID ( $ reduce_and ) , ID ( $ reduce_or ) , ID ( $ reduce_xor ) , ID ( $ reduce_bool ) , ID ( $ reduce_xnor ) ) )
2016-11-21 19:28:17 -06:00
{
2020-03-12 14:57:01 -05:00
string a_expr = make_expr ( cell - > getPort ( ID : : A ) ) ;
2020-03-21 06:54:23 -05:00
wire_decls . push_back ( stringf ( " wire %s: UInt<%d> %s \n " , y_id . c_str ( ) , y_width , cellFileinfo . c_str ( ) ) ) ;
2016-11-21 19:28:17 -06:00
2019-07-31 11:27:38 -05:00
if ( a_signed ) {
2016-11-21 19:28:17 -06:00
a_expr = " asSInt( " + a_expr + " ) " ;
}
2018-08-23 16:35:11 -05:00
// Don't use the results of logical operations (a single bit) to control padding
2020-04-02 11:51:32 -05:00
if ( ! ( cell - > type . in ( ID ( $ eq ) , ID ( $ eqx ) , ID ( $ gt ) , ID ( $ ge ) , ID ( $ lt ) , ID ( $ le ) , ID ( $ ne ) , ID ( $ nex ) , ID ( $ reduce_bool ) , ID ( $ logic_not ) ) & & y_width = = 1 ) ) {
2018-08-23 16:35:11 -05:00
a_expr = stringf ( " pad(%s, %d) " , a_expr . c_str ( ) , y_width ) ;
}
2016-11-21 19:28:17 -06:00
2019-07-31 11:27:38 -05:00
// Assume the FIRRTL width is a single bit.
firrtl_width = 1 ;
2020-04-02 11:51:32 -05:00
if ( cell - > type = = ID ( $ not ) ) primop = " not " ;
else if ( cell - > type = = ID ( $ neg ) ) {
2019-05-21 15:04:56 -05:00
primop = " neg " ;
2019-07-31 11:27:38 -05:00
firrtl_is_signed = true ; // Result of "neg" is signed (an SInt).
firrtl_width = a_width ;
2020-04-02 11:51:32 -05:00
} else if ( cell - > type = = ID ( $ logic_not ) ) {
2020-03-21 06:54:23 -05:00
primop = " eq " ;
a_expr = stringf ( " %s, UInt(0) " , a_expr . c_str ( ) ) ;
}
2020-04-02 11:51:32 -05:00
else if ( cell - > type = = ID ( $ reduce_and ) ) primop = " andr " ;
else if ( cell - > type = = ID ( $ reduce_or ) ) primop = " orr " ;
else if ( cell - > type = = ID ( $ reduce_xor ) ) primop = " xorr " ;
else if ( cell - > type = = ID ( $ reduce_xnor ) ) {
2020-03-21 06:54:23 -05:00
primop = " not " ;
a_expr = stringf ( " xorr(%s) " , a_expr . c_str ( ) ) ;
}
2020-04-02 11:51:32 -05:00
else if ( cell - > type = = ID ( $ reduce_bool ) ) {
2018-08-23 16:35:11 -05:00
primop = " neq " ;
// Use the sign of the a_expr and its width as the type (UInt/SInt) and width of the comparand.
a_expr = stringf ( " %s, %cInt<%d>(0) " , a_expr . c_str ( ) , a_signed ? ' S ' : ' U ' , a_width ) ;
}
2016-11-21 19:28:17 -06:00
string expr = stringf ( " %s(%s) " , primop . c_str ( ) , a_expr . c_str ( ) ) ;
2019-07-31 11:27:38 -05:00
if ( ( firrtl_is_signed & & ! always_uint ) )
2016-11-21 19:28:17 -06:00
expr = stringf ( " asUInt(%s) " , expr . c_str ( ) ) ;
2020-03-21 06:54:23 -05:00
cell_exprs . push_back ( stringf ( " %s <= %s %s \n " , y_id . c_str ( ) , expr . c_str ( ) , cellFileinfo . c_str ( ) ) ) ;
2020-03-12 14:57:01 -05:00
register_reverse_wire_map ( y_id , cell - > getPort ( ID : : Y ) ) ;
2016-11-21 19:28:17 -06:00
continue ;
}
2020-04-02 11:51:32 -05:00
if ( cell - > type . in ( ID ( $ add ) , ID ( $ sub ) , ID ( $ mul ) , ID ( $ div ) , ID ( $ mod ) , ID ( $ xor ) , ID ( $ xnor ) , ID ( $ and ) , ID ( $ or ) , ID ( $ eq ) , ID ( $ eqx ) ,
2020-04-08 16:50:37 -05:00
ID ( $ gt ) , ID ( $ ge ) , ID ( $ lt ) , ID ( $ le ) , ID ( $ ne ) , ID ( $ nex ) , ID ( $ shr ) , ID ( $ sshr ) , ID ( $ sshl ) , ID ( $ shl ) ,
ID ( $ logic_and ) , ID ( $ logic_or ) , ID ( $ pow ) ) )
2016-11-17 16:36:47 -06:00
{
2020-03-12 14:57:01 -05:00
string a_expr = make_expr ( cell - > getPort ( ID : : A ) ) ;
string b_expr = make_expr ( cell - > getPort ( ID : : B ) ) ;
2020-03-24 04:36:14 -05:00
std : : string cellFileinfo = getFileinfo ( cell ) ;
2020-03-21 06:54:23 -05:00
wire_decls . push_back ( stringf ( " wire %s: UInt<%d> %s \n " , y_id . c_str ( ) , y_width , cellFileinfo . c_str ( ) ) ) ;
2016-11-17 16:36:47 -06:00
2019-07-31 11:27:38 -05:00
if ( a_signed ) {
2016-11-17 16:36:47 -06:00
a_expr = " asSInt( " + a_expr + " ) " ;
2019-07-31 11:27:38 -05:00
// Expand the "A" operand to the result width
if ( a_width < y_width ) {
a_expr = stringf ( " pad(%s, %d) " , a_expr . c_str ( ) , y_width ) ;
a_width = y_width ;
2018-08-23 16:35:11 -05:00
}
2019-07-31 11:27:38 -05:00
}
// Shift amount is always unsigned, and needn't be padded to result width,
// otherwise, we need to cast the b_expr appropriately
2020-04-02 11:51:32 -05:00
if ( b_signed & & ! cell - > type . in ( ID ( $ shr ) , ID ( $ sshr ) , ID ( $ shl ) , ID ( $ sshl ) , ID ( $ pow ) ) ) {
2019-07-31 11:27:38 -05:00
b_expr = " asSInt( " + b_expr + " ) " ;
// Expand the "B" operand to the result width
if ( b_width < y_width ) {
b_expr = stringf ( " pad(%s, %d) " , b_expr . c_str ( ) , y_width ) ;
b_width = y_width ;
2019-02-15 13:14:17 -06:00
}
2016-11-17 16:36:47 -06:00
}
2019-07-31 11:27:38 -05:00
// For the arithmetic ops, expand operand widths to result widths befor performing the operation.
// This corresponds (according to iverilog) to what verilog compilers implement.
2020-04-02 11:51:32 -05:00
if ( cell - > type . in ( ID ( $ add ) , ID ( $ sub ) , ID ( $ mul ) , ID ( $ div ) , ID ( $ mod ) , ID ( $ xor ) , ID ( $ xnor ) , ID ( $ and ) , ID ( $ or ) ) )
2019-07-31 11:27:38 -05:00
{
if ( a_width < y_width ) {
a_expr = stringf ( " pad(%s, %d) " , a_expr . c_str ( ) , y_width ) ;
a_width = y_width ;
}
if ( b_width < y_width ) {
b_expr = stringf ( " pad(%s, %d) " , b_expr . c_str ( ) , y_width ) ;
b_width = y_width ;
}
}
// Assume the FIRRTL width is the width of "A"
firrtl_width = a_width ;
2020-03-12 14:57:01 -05:00
auto a_sig = cell - > getPort ( ID : : A ) ;
2016-11-21 19:28:17 -06:00
2020-04-02 11:51:32 -05:00
if ( cell - > type = = ID ( $ add ) ) {
2019-07-31 11:27:38 -05:00
primop = " add " ;
firrtl_is_signed = a_signed | b_signed ;
firrtl_width = max ( a_width , b_width ) ;
2020-04-02 11:51:32 -05:00
} else if ( cell - > type = = ID ( $ sub ) ) {
2019-07-31 11:27:38 -05:00
primop = " sub " ;
firrtl_is_signed = true ;
int a_widthInc = ( ! a_signed & & b_signed ) ? 2 : ( a_signed & & ! b_signed ) ? 1 : 0 ;
int b_widthInc = ( a_signed & & ! b_signed ) ? 2 : ( ! a_signed & & b_signed ) ? 1 : 0 ;
firrtl_width = max ( a_width + a_widthInc , b_width + b_widthInc ) ;
2020-04-02 11:51:32 -05:00
} else if ( cell - > type = = ID ( $ mul ) ) {
2019-07-31 11:27:38 -05:00
primop = " mul " ;
firrtl_is_signed = a_signed | b_signed ;
firrtl_width = a_width + b_width ;
2020-04-02 11:51:32 -05:00
} else if ( cell - > type = = ID ( $ div ) ) {
2019-07-31 11:27:38 -05:00
primop = " div " ;
firrtl_is_signed = a_signed | b_signed ;
firrtl_width = a_width ;
2020-04-02 11:51:32 -05:00
} else if ( cell - > type = = ID ( $ mod ) ) {
2019-07-31 11:27:38 -05:00
primop = " rem " ;
firrtl_width = min ( a_width , b_width ) ;
2020-04-02 11:51:32 -05:00
} else if ( cell - > type = = ID ( $ and ) ) {
2019-07-31 11:27:38 -05:00
primop = " and " ;
always_uint = true ;
firrtl_width = max ( a_width , b_width ) ;
2016-11-21 19:28:17 -06:00
}
2020-04-02 11:51:32 -05:00
else if ( cell - > type = = ID ( $ or ) ) {
2019-07-31 11:27:38 -05:00
primop = " or " ;
always_uint = true ;
firrtl_width = max ( a_width , b_width ) ;
}
2020-04-02 11:51:32 -05:00
else if ( cell - > type = = ID ( $ xor ) ) {
2019-07-31 11:27:38 -05:00
primop = " xor " ;
always_uint = true ;
firrtl_width = max ( a_width , b_width ) ;
}
2020-04-02 11:51:32 -05:00
else if ( cell - > type = = ID ( $ xnor ) ) {
2019-07-31 11:27:38 -05:00
primop = " xnor " ;
always_uint = true ;
firrtl_width = max ( a_width , b_width ) ;
}
2020-04-02 11:51:32 -05:00
else if ( ( cell - > type = = ID ( $ eq ) ) | ( cell - > type = = ID ( $ eqx ) ) ) {
2019-07-31 11:27:38 -05:00
primop = " eq " ;
always_uint = true ;
firrtl_width = 1 ;
}
2020-04-02 11:51:32 -05:00
else if ( ( cell - > type = = ID ( $ ne ) ) | ( cell - > type = = ID ( $ nex ) ) ) {
2019-07-31 11:27:38 -05:00
primop = " neq " ;
always_uint = true ;
firrtl_width = 1 ;
}
2020-04-02 11:51:32 -05:00
else if ( cell - > type = = ID ( $ gt ) ) {
2019-07-31 11:27:38 -05:00
primop = " gt " ;
always_uint = true ;
firrtl_width = 1 ;
}
2020-04-02 11:51:32 -05:00
else if ( cell - > type = = ID ( $ ge ) ) {
2019-07-31 11:27:38 -05:00
primop = " geq " ;
always_uint = true ;
firrtl_width = 1 ;
}
2020-04-02 11:51:32 -05:00
else if ( cell - > type = = ID ( $ lt ) ) {
2019-07-31 11:27:38 -05:00
primop = " lt " ;
always_uint = true ;
firrtl_width = 1 ;
}
2020-04-02 11:51:32 -05:00
else if ( cell - > type = = ID ( $ le ) ) {
2019-07-31 11:27:38 -05:00
primop = " leq " ;
always_uint = true ;
firrtl_width = 1 ;
}
2020-04-02 11:51:32 -05:00
else if ( ( cell - > type = = ID ( $ shl ) ) | ( cell - > type = = ID ( $ sshl ) ) ) {
2019-02-15 13:14:17 -06:00
// FIRRTL will widen the result (y) by the amount of the shift.
// We'll need to offset this by extracting the un-widened portion as Verilog would do.
extract_y_bits = true ;
// Is the shift amount constant?
2020-03-12 14:57:01 -05:00
auto b_sig = cell - > getPort ( ID : : B ) ;
2019-02-15 13:14:17 -06:00
if ( b_sig . is_fully_const ( ) ) {
primop = " shl " ;
2019-07-31 11:27:38 -05:00
int shift_amount = b_sig . as_int ( ) ;
b_expr = std : : to_string ( shift_amount ) ;
firrtl_width = a_width + shift_amount ;
2019-02-15 13:14:17 -06:00
} else {
primop = " dshl " ;
// Convert from FIRRTL left shift semantics.
2019-07-31 11:27:38 -05:00
b_expr = gen_dshl ( b_expr , b_width ) ;
firrtl_width = a_width + ( 1 < < b_width ) - 1 ;
2019-02-15 13:14:17 -06:00
}
}
2020-04-02 11:51:32 -05:00
else if ( ( cell - > type = = ID ( $ shr ) ) | ( cell - > type = = ID ( $ sshr ) ) ) {
2019-02-15 13:14:17 -06:00
// We don't need to extract a specific range of bits.
extract_y_bits = false ;
// Is the shift amount constant?
2020-03-12 14:57:01 -05:00
auto b_sig = cell - > getPort ( ID : : B ) ;
2019-02-15 13:14:17 -06:00
if ( b_sig . is_fully_const ( ) ) {
primop = " shr " ;
2019-07-31 11:27:38 -05:00
int shift_amount = b_sig . as_int ( ) ;
b_expr = std : : to_string ( shift_amount ) ;
firrtl_width = max ( 1 , a_width - shift_amount ) ;
2019-02-15 13:14:17 -06:00
} else {
primop = " dshr " ;
2019-07-31 11:27:38 -05:00
firrtl_width = a_width ;
}
// We'll need to do some special fixups if the source (and thus result) is signed.
if ( firrtl_is_signed ) {
// If this is a "logical" shift right, pretend the source is unsigned.
2020-04-02 11:51:32 -05:00
if ( cell - > type = = ID ( $ shr ) ) {
2019-07-31 11:27:38 -05:00
a_expr = " asUInt( " + a_expr + " ) " ;
}
2019-02-15 13:14:17 -06:00
}
}
2020-04-02 11:51:32 -05:00
else if ( ( cell - > type = = ID ( $ logic_and ) ) ) {
2019-07-31 11:27:38 -05:00
primop = " and " ;
a_expr = " neq( " + a_expr + " , UInt(0)) " ;
b_expr = " neq( " + b_expr + " , UInt(0)) " ;
always_uint = true ;
firrtl_width = 1 ;
}
2020-04-02 11:51:32 -05:00
else if ( ( cell - > type = = ID ( $ logic_or ) ) ) {
2019-07-31 11:27:38 -05:00
primop = " or " ;
a_expr = " neq( " + a_expr + " , UInt(0)) " ;
b_expr = " neq( " + b_expr + " , UInt(0)) " ;
always_uint = true ;
firrtl_width = 1 ;
}
2020-04-02 11:51:32 -05:00
else if ( ( cell - > type = = ID ( $ pow ) ) ) {
2019-07-31 11:27:38 -05:00
if ( a_sig . is_fully_const ( ) & & a_sig . as_int ( ) = = 2 ) {
// We'll convert this to a shift. To simplify things, change the a_expr to "1"
// so we can use b_expr directly as a shift amount.
// Only support 2 ** N (i.e., shift left)
// FIRRTL will widen the result (y) by the amount of the shift.
// We'll need to offset this by extracting the un-widened portion as Verilog would do.
a_expr = firrtl_is_signed ? " SInt(1) " : " UInt(1) " ;
extract_y_bits = true ;
// Is the shift amount constant?
2020-03-12 14:57:01 -05:00
auto b_sig = cell - > getPort ( ID : : B ) ;
2019-07-31 11:27:38 -05:00
if ( b_sig . is_fully_const ( ) ) {
primop = " shl " ;
int shiftAmount = b_sig . as_int ( ) ;
if ( shiftAmount < 0 ) {
log_error ( " Negative power exponent - %d: %s.%s \n " , shiftAmount , log_id ( module ) , log_id ( cell ) ) ;
}
b_expr = std : : to_string ( shiftAmount ) ;
firrtl_width = a_width + shiftAmount ;
} else {
primop = " dshl " ;
// Convert from FIRRTL left shift semantics.
b_expr = gen_dshl ( b_expr , b_width ) ;
firrtl_width = a_width + ( 1 < < b_width ) - 1 ;
}
} else {
log_error ( " Non power 2: %s.%s \n " , log_id ( module ) , log_id ( cell ) ) ;
}
}
2016-11-21 19:28:17 -06:00
2020-04-02 11:51:32 -05:00
if ( ! cell - > parameters . at ( ID : : B_SIGNED ) . as_bool ( ) ) {
2016-11-21 19:28:17 -06:00
b_expr = " asUInt( " + b_expr + " ) " ;
}
2016-11-17 16:36:47 -06:00
2019-07-31 11:27:38 -05:00
string expr ;
// Deal with $xnor == ~^ (not xor)
if ( primop = = " xnor " ) {
expr = stringf ( " not(xor(%s, %s)) " , a_expr . c_str ( ) , b_expr . c_str ( ) ) ;
} else {
expr = stringf ( " %s(%s, %s) " , primop . c_str ( ) , a_expr . c_str ( ) , b_expr . c_str ( ) ) ;
}
2016-11-17 16:36:47 -06:00
2019-07-31 11:27:38 -05:00
// Deal with FIRRTL's "shift widens" semantics, or the need to widen the FIRRTL result.
// If the operation is signed, the FIRRTL width will be 1 one bit larger.
2019-02-15 13:14:17 -06:00
if ( extract_y_bits ) {
expr = stringf ( " bits(%s, %d, 0) " , expr . c_str ( ) , y_width - 1 ) ;
2019-07-31 11:27:38 -05:00
} else if ( firrtl_is_signed & & ( firrtl_width + 1 ) < y_width ) {
expr = stringf ( " pad(%s, %d) " , expr . c_str ( ) , y_width ) ;
2019-02-15 13:14:17 -06:00
}
2019-07-31 11:27:38 -05:00
if ( ( firrtl_is_signed & & ! always_uint ) )
2016-11-17 17:32:35 -06:00
expr = stringf ( " asUInt(%s) " , expr . c_str ( ) ) ;
2016-11-17 16:36:47 -06:00
2020-03-21 06:54:23 -05:00
cell_exprs . push_back ( stringf ( " %s <= %s %s \n " , y_id . c_str ( ) , expr . c_str ( ) , cellFileinfo . c_str ( ) ) ) ;
2020-03-12 14:57:01 -05:00
register_reverse_wire_map ( y_id , cell - > getPort ( ID : : Y ) ) ;
2016-11-17 16:36:47 -06:00
continue ;
}
2020-04-02 11:51:32 -05:00
if ( cell - > type . in ( ID ( $ mux ) ) )
2016-11-17 19:41:29 -06:00
{
2020-04-02 11:51:32 -05:00
int width = cell - > parameters . at ( ID : : WIDTH ) . as_int ( ) ;
2020-03-12 14:57:01 -05:00
string a_expr = make_expr ( cell - > getPort ( ID : : A ) ) ;
string b_expr = make_expr ( cell - > getPort ( ID : : B ) ) ;
string s_expr = make_expr ( cell - > getPort ( ID : : S ) ) ;
2020-03-21 06:54:23 -05:00
wire_decls . push_back ( stringf ( " wire %s: UInt<%d> %s \n " , y_id . c_str ( ) , width , cellFileinfo . c_str ( ) ) ) ;
2016-11-17 19:41:29 -06:00
string expr = stringf ( " mux(%s, %s, %s) " , s_expr . c_str ( ) , b_expr . c_str ( ) , a_expr . c_str ( ) ) ;
2020-03-21 06:54:23 -05:00
cell_exprs . push_back ( stringf ( " %s <= %s %s \n " , y_id . c_str ( ) , expr . c_str ( ) , cellFileinfo . c_str ( ) ) ) ;
2020-03-12 14:57:01 -05:00
register_reverse_wire_map ( y_id , cell - > getPort ( ID : : Y ) ) ;
2016-11-17 19:41:29 -06:00
continue ;
}
2020-04-02 11:51:32 -05:00
if ( cell - > type . in ( ID ( $ mem ) ) )
2016-11-17 19:41:29 -06:00
{
string mem_id = make_id ( cell - > name ) ;
2020-04-02 11:51:32 -05:00
int abits = cell - > parameters . at ( ID : : ABITS ) . as_int ( ) ;
int width = cell - > parameters . at ( ID : : WIDTH ) . as_int ( ) ;
int size = cell - > parameters . at ( ID : : SIZE ) . as_int ( ) ;
2019-05-01 15:16:01 -05:00
memory m ( cell , mem_id , abits , size , width ) ;
2020-04-02 11:51:32 -05:00
int rd_ports = cell - > parameters . at ( ID : : RD_PORTS ) . as_int ( ) ;
int wr_ports = cell - > parameters . at ( ID : : WR_PORTS ) . as_int ( ) ;
2016-11-17 19:41:29 -06:00
2020-04-02 11:51:32 -05:00
Const initdata = cell - > parameters . at ( ID : : INIT ) ;
2016-11-17 19:41:29 -06:00
for ( State bit : initdata . bits )
if ( bit ! = State : : Sx )
log_error ( " Memory with initialization data: %s.%s \n " , log_id ( module ) , log_id ( cell ) ) ;
2020-04-02 11:51:32 -05:00
Const rd_clk_enable = cell - > parameters . at ( ID : : RD_CLK_ENABLE ) ;
Const wr_clk_enable = cell - > parameters . at ( ID : : WR_CLK_ENABLE ) ;
Const wr_clk_polarity = cell - > parameters . at ( ID : : WR_CLK_POLARITY ) ;
2016-11-17 19:41:29 -06:00
2020-04-02 11:51:32 -05:00
int offset = cell - > parameters . at ( ID : : OFFSET ) . as_int ( ) ;
2016-11-17 19:41:29 -06:00
if ( offset ! = 0 )
log_error ( " Memory with nonzero offset: %s.%s \n " , log_id ( module ) , log_id ( cell ) ) ;
for ( int i = 0 ; i < rd_ports ; i + + )
{
if ( rd_clk_enable [ i ] ! = State : : S0 )
log_error ( " Clocked read port %d on memory %s.%s. \n " , i , log_id ( module ) , log_id ( cell ) ) ;
2020-04-02 11:51:32 -05:00
SigSpec addr_sig = cell - > getPort ( ID : : RD_ADDR ) . extract ( i * abits , abits ) ;
SigSpec data_sig = cell - > getPort ( ID : : RD_DATA ) . extract ( i * width , width ) ;
2019-04-01 17:02:12 -05:00
string addr_expr = make_expr ( addr_sig ) ;
string name ( stringf ( " %s.r%d " , m . name . c_str ( ) , i ) ) ;
bool clk_enable = false ;
bool clk_parity = true ;
bool transparency = false ;
SigSpec ena_sig = RTLIL : : SigSpec ( RTLIL : : State : : S1 , 1 ) ;
SigSpec clk_sig = RTLIL : : SigSpec ( RTLIL : : State : : S0 , 1 ) ;
read_port rp ( name , clk_enable , clk_parity , transparency , clk_sig , ena_sig , addr_sig ) ;
m . add_memory_read_port ( rp ) ;
cell_exprs . push_back ( rp . gen_read ( indent . c_str ( ) ) ) ;
register_reverse_wire_map ( stringf ( " %s.data " , name . c_str ( ) ) , data_sig ) ;
2016-11-17 19:41:29 -06:00
}
for ( int i = 0 ; i < wr_ports ; i + + )
{
if ( wr_clk_enable [ i ] ! = State : : S1 )
log_error ( " Unclocked write port %d on memory %s.%s. \n " , i , log_id ( module ) , log_id ( cell ) ) ;
if ( wr_clk_polarity [ i ] ! = State : : S1 )
log_error ( " Negedge write port %d on memory %s.%s. \n " , i , log_id ( module ) , log_id ( cell ) ) ;
2019-04-01 17:02:12 -05:00
string name ( stringf ( " %s.w%d " , m . name . c_str ( ) , i ) ) ;
bool clk_enable = true ;
bool clk_parity = true ;
bool transparency = false ;
2020-04-02 11:51:32 -05:00
SigSpec addr_sig = cell - > getPort ( ID : : WR_ADDR ) . extract ( i * abits , abits ) ;
2019-04-01 17:02:12 -05:00
string addr_expr = make_expr ( addr_sig ) ;
2020-04-02 11:51:32 -05:00
SigSpec data_sig = cell - > getPort ( ID : : WR_DATA ) . extract ( i * width , width ) ;
2019-04-01 17:02:12 -05:00
string data_expr = make_expr ( data_sig ) ;
2020-04-02 11:51:32 -05:00
SigSpec clk_sig = cell - > getPort ( ID : : WR_CLK ) . extract ( i ) ;
2019-04-01 17:02:12 -05:00
string clk_expr = make_expr ( clk_sig ) ;
2016-11-17 19:41:29 -06:00
2020-04-02 11:51:32 -05:00
SigSpec wen_sig = cell - > getPort ( ID : : WR_EN ) . extract ( i * width , width ) ;
2016-11-17 19:41:29 -06:00
string wen_expr = make_expr ( wen_sig [ 0 ] ) ;
for ( int i = 1 ; i < GetSize ( wen_sig ) ; i + + )
if ( wen_sig [ 0 ] ! = wen_sig [ i ] )
log_error ( " Complex write enable on port %d on memory %s.%s. \n " , i , log_id ( module ) , log_id ( cell ) ) ;
2019-04-01 17:02:12 -05:00
SigSpec mask_sig = RTLIL : : SigSpec ( RTLIL : : State : : S1 , 1 ) ;
write_port wp ( name , clk_enable , clk_parity , transparency , clk_sig , wen_sig [ 0 ] , addr_sig , mask_sig ) ;
m . add_memory_write_port ( wp ) ;
cell_exprs . push_back ( stringf ( " %s%s.data <= %s \n " , indent . c_str ( ) , name . c_str ( ) , data_expr . c_str ( ) ) ) ;
cell_exprs . push_back ( wp . gen_write ( indent . c_str ( ) ) ) ;
2016-11-17 19:41:29 -06:00
}
2019-04-01 17:02:12 -05:00
register_memory ( m ) ;
continue ;
}
2016-11-17 19:41:29 -06:00
2020-04-02 11:51:32 -05:00
if ( cell - > type . in ( ID ( $ memwr ) , ID ( $ memrd ) , ID ( $ meminit ) ) )
2019-04-01 17:02:12 -05:00
{
std : : string cell_type = fid ( cell - > type ) ;
2020-04-02 11:51:32 -05:00
std : : string mem_id = make_id ( cell - > parameters [ ID : : MEMID ] . decode_string ( ) ) ;
int abits = cell - > parameters . at ( ID : : ABITS ) . as_int ( ) ;
int width = cell - > parameters . at ( ID : : WIDTH ) . as_int ( ) ;
2019-04-01 17:02:12 -05:00
memory * mp = nullptr ;
2020-04-02 11:51:32 -05:00
if ( cell - > type = = ID ( $ meminit ) ) {
2019-04-01 17:02:12 -05:00
log_error ( " $meminit (%s.%s.%s) currently unsupported \n " , log_id ( module ) , log_id ( cell ) , mem_id . c_str ( ) ) ;
} else {
// It's a $memwr or $memrd. Remember the read/write port parameters for the eventual FIRRTL memory definition.
2020-04-02 11:51:32 -05:00
auto addrSig = cell - > getPort ( ID : : ADDR ) ;
auto dataSig = cell - > getPort ( ID : : DATA ) ;
auto enableSig = cell - > getPort ( ID : : EN ) ;
auto clockSig = cell - > getPort ( ID : : CLK ) ;
Const clk_enable = cell - > parameters . at ( ID : : CLK_ENABLE ) ;
Const clk_polarity = cell - > parameters . at ( ID : : CLK_POLARITY ) ;
2019-04-01 17:02:12 -05:00
2019-05-01 15:16:01 -05:00
// Do we already have an entry for this memory?
if ( memories . count ( mem_id ) = = 0 ) {
memory m ( cell , mem_id , abits , 0 , width ) ;
register_memory ( m ) ;
}
2019-04-01 17:02:12 -05:00
mp = & memories . at ( mem_id ) ;
int portNum = 0 ;
bool transparency = false ;
string data_expr = make_expr ( dataSig ) ;
2020-04-02 11:51:32 -05:00
if ( cell - > type . in ( ID ( $ memwr ) ) ) {
2019-04-01 17:02:12 -05:00
portNum = ( int ) mp - > write_ports . size ( ) ;
write_port wp ( stringf ( " %s.w%d " , mem_id . c_str ( ) , portNum ) , clk_enable . as_bool ( ) , clk_polarity . as_bool ( ) , transparency , clockSig , enableSig , addrSig , dataSig ) ;
mp - > add_memory_write_port ( wp ) ;
cell_exprs . push_back ( stringf ( " %s%s.data <= %s \n " , indent . c_str ( ) , wp . name . c_str ( ) , data_expr . c_str ( ) ) ) ;
cell_exprs . push_back ( wp . gen_write ( indent . c_str ( ) ) ) ;
2020-04-02 11:51:32 -05:00
} else if ( cell - > type . in ( ID ( $ memrd ) ) ) {
2019-04-01 17:02:12 -05:00
portNum = ( int ) mp - > read_ports . size ( ) ;
read_port rp ( stringf ( " %s.r%d " , mem_id . c_str ( ) , portNum ) , clk_enable . as_bool ( ) , clk_polarity . as_bool ( ) , transparency , clockSig , enableSig , addrSig ) ;
mp - > add_memory_read_port ( rp ) ;
cell_exprs . push_back ( rp . gen_read ( indent . c_str ( ) ) ) ;
register_reverse_wire_map ( stringf ( " %s.data " , rp . name . c_str ( ) ) , dataSig ) ;
}
}
2016-11-17 19:41:29 -06:00
continue ;
}
2020-04-02 11:51:32 -05:00
if ( cell - > type . in ( ID ( $ dff ) ) )
2016-11-17 17:32:35 -06:00
{
2020-04-02 11:51:32 -05:00
bool clkpol = cell - > parameters . at ( ID : : CLK_POLARITY ) . as_bool ( ) ;
2016-11-17 17:32:35 -06:00
if ( clkpol = = false )
log_error ( " Negative edge clock on FF %s.%s. \n " , log_id ( module ) , log_id ( cell ) ) ;
2020-04-02 11:51:32 -05:00
int width = cell - > parameters . at ( ID : : WIDTH ) . as_int ( ) ;
string expr = make_expr ( cell - > getPort ( ID : : D ) ) ;
string clk_expr = " asClock( " + make_expr ( cell - > getPort ( ID : : CLK ) ) + " ) " ;
2016-11-17 17:32:35 -06:00
2020-03-21 06:54:23 -05:00
wire_decls . push_back ( stringf ( " reg %s: UInt<%d>, %s %s \n " , y_id . c_str ( ) , width , clk_expr . c_str ( ) , cellFileinfo . c_str ( ) ) ) ;
2016-11-17 17:32:35 -06:00
2020-03-21 06:54:23 -05:00
cell_exprs . push_back ( stringf ( " %s <= %s %s \n " , y_id . c_str ( ) , expr . c_str ( ) , cellFileinfo . c_str ( ) ) ) ;
2020-04-02 11:51:32 -05:00
register_reverse_wire_map ( y_id , cell - > getPort ( ID : : Q ) ) ;
2016-11-17 17:32:35 -06:00
continue ;
}
2019-02-15 13:14:17 -06:00
// This may be a parameterized module - paramod.
2019-08-06 18:42:25 -05:00
if ( cell - > type . begins_with ( " $paramod " ) )
2019-02-15 13:14:17 -06:00
{
process_instance ( cell , wire_exprs ) ;
continue ;
}
2020-04-02 11:51:32 -05:00
if ( cell - > type = = ID ( $ shiftx ) ) {
2019-02-15 13:14:17 -06:00
// assign y = a[b +: y_width];
// We'll extract the correct bits as part of the primop.
2020-03-12 14:57:01 -05:00
string a_expr = make_expr ( cell - > getPort ( ID : : A ) ) ;
2019-02-15 13:14:17 -06:00
// Get the initial bit selector
2020-03-12 14:57:01 -05:00
string b_expr = make_expr ( cell - > getPort ( ID : : B ) ) ;
2019-02-15 13:14:17 -06:00
wire_decls . push_back ( stringf ( " wire %s: UInt<%d> \n " , y_id . c_str ( ) , y_width ) ) ;
2020-04-02 11:51:32 -05:00
if ( cell - > getParam ( ID : : B_SIGNED ) . as_bool ( ) ) {
2019-02-15 13:14:17 -06:00
// Use validif to constrain the selection (test the sign bit)
auto b_string = b_expr . c_str ( ) ;
2020-04-02 11:51:32 -05:00
int b_sign = cell - > parameters . at ( ID : : B_WIDTH ) . as_int ( ) - 1 ;
2019-02-15 13:14:17 -06:00
b_expr = stringf ( " validif(not(bits(%s, %d, %d)), %s) " , b_string , b_sign , b_sign , b_string ) ;
}
string expr = stringf ( " dshr(%s, %s) " , a_expr . c_str ( ) , b_expr . c_str ( ) ) ;
cell_exprs . push_back ( stringf ( " %s <= %s \n " , y_id . c_str ( ) , expr . c_str ( ) ) ) ;
2020-03-12 14:57:01 -05:00
register_reverse_wire_map ( y_id , cell - > getPort ( ID : : Y ) ) ;
2019-02-15 13:14:17 -06:00
continue ;
}
2020-04-02 11:51:32 -05:00
if ( cell - > type = = ID ( $ shift ) ) {
2019-02-15 13:14:17 -06:00
// assign y = a >> b;
// where b may be negative
2020-03-12 14:57:01 -05:00
string a_expr = make_expr ( cell - > getPort ( ID : : A ) ) ;
string b_expr = make_expr ( cell - > getPort ( ID : : B ) ) ;
2019-02-15 13:14:17 -06:00
auto b_string = b_expr . c_str ( ) ;
string expr ;
wire_decls . push_back ( stringf ( " wire %s: UInt<%d> \n " , y_id . c_str ( ) , y_width ) ) ;
2020-04-02 11:51:32 -05:00
if ( cell - > getParam ( ID : : B_SIGNED ) . as_bool ( ) ) {
2019-02-15 13:14:17 -06:00
// We generate a left or right shift based on the sign of b.
2019-07-31 11:27:38 -05:00
std : : string dshl = stringf ( " bits(dshl(%s, %s), 0, %d) " , a_expr . c_str ( ) , gen_dshl ( b_expr , b_width ) . c_str ( ) , y_width ) ;
2019-02-15 13:14:17 -06:00
std : : string dshr = stringf ( " dshr(%s, %s) " , a_expr . c_str ( ) , b_string ) ;
expr = stringf ( " mux(%s < 0, %s, %s) " ,
b_string ,
dshl . c_str ( ) ,
dshr . c_str ( )
) ;
} else {
expr = stringf ( " dshr(%s, %s) " , a_expr . c_str ( ) , b_string ) ;
}
cell_exprs . push_back ( stringf ( " %s <= %s \n " , y_id . c_str ( ) , expr . c_str ( ) ) ) ;
2020-03-12 14:57:01 -05:00
register_reverse_wire_map ( y_id , cell - > getPort ( ID : : Y ) ) ;
2019-07-31 11:27:38 -05:00
continue ;
}
2020-04-02 11:51:32 -05:00
if ( cell - > type = = ID ( $ pos ) ) {
2019-07-31 11:27:38 -05:00
// assign y = a;
// printCell(cell);
2020-03-12 14:57:01 -05:00
string a_expr = make_expr ( cell - > getPort ( ID : : A ) ) ;
2019-07-31 11:27:38 -05:00
// Verilog appears to treat the result as signed, so if the result is wider than "A",
// we need to pad.
if ( a_width < y_width ) {
a_expr = stringf ( " pad(%s, %d) " , a_expr . c_str ( ) , y_width ) ;
}
wire_decls . push_back ( stringf ( " wire %s: UInt<%d> \n " , y_id . c_str ( ) , y_width ) ) ;
cell_exprs . push_back ( stringf ( " %s <= %s \n " , y_id . c_str ( ) , a_expr . c_str ( ) ) ) ;
2020-03-12 14:57:01 -05:00
register_reverse_wire_map ( y_id , cell - > getPort ( ID : : Y ) ) ;
2019-02-15 13:14:17 -06:00
continue ;
}
2019-07-24 15:33:16 -05:00
log_error ( " Cell type not supported: %s (%s.%s) \n " , log_id ( cell - > type ) , log_id ( module ) , log_id ( cell ) ) ;
2016-11-17 16:36:47 -06:00
}
for ( auto conn : module - > connections ( ) )
{
string y_id = next_id ( ) ;
int y_width = GetSize ( conn . first ) ;
string expr = make_expr ( conn . second ) ;
wire_decls . push_back ( stringf ( " wire %s: UInt<%d> \n " , y_id . c_str ( ) , y_width ) ) ;
cell_exprs . push_back ( stringf ( " %s <= %s \n " , y_id . c_str ( ) , expr . c_str ( ) ) ) ;
register_reverse_wire_map ( y_id , conn . first ) ;
}
for ( auto wire : module - > wires ( ) )
{
string expr ;
2020-03-24 04:36:14 -05:00
std : : string wireFileinfo = getFileinfo ( wire ) ;
2016-11-17 16:36:47 -06:00
if ( wire - > port_input )
continue ;
int cursor = 0 ;
bool is_valid = false ;
bool make_unconn_id = false ;
while ( cursor < wire - > width )
{
int chunk_width = 1 ;
string new_expr ;
SigBit start_bit ( wire , cursor ) ;
if ( reverse_wire_map . count ( start_bit ) )
{
pair < string , int > start_map = reverse_wire_map . at ( start_bit ) ;
while ( cursor + chunk_width < wire - > width )
{
SigBit stop_bit ( wire , cursor + chunk_width ) ;
if ( reverse_wire_map . count ( stop_bit ) = = 0 )
break ;
pair < string , int > stop_map = reverse_wire_map . at ( stop_bit ) ;
stop_map . second - = chunk_width ;
if ( start_map ! = stop_map )
break ;
chunk_width + + ;
}
new_expr = stringf ( " bits(%s, %d, %d) " , start_map . first . c_str ( ) ,
start_map . second + chunk_width - 1 , start_map . second ) ;
is_valid = true ;
}
else
{
if ( unconn_id . empty ( ) ) {
unconn_id = next_id ( ) ;
make_unconn_id = true ;
}
new_expr = unconn_id ;
}
if ( expr . empty ( ) )
expr = new_expr ;
else
expr = " cat( " + new_expr + " , " + expr + " ) " ;
cursor + = chunk_width ;
}
if ( is_valid ) {
if ( make_unconn_id ) {
2020-03-21 06:54:23 -05:00
wire_decls . push_back ( stringf ( " wire %s: UInt<1> %s \n " , unconn_id . c_str ( ) , wireFileinfo . c_str ( ) ) ) ;
// `invalid` is a firrtl construction for simulation so we will not
// tag it with a @[fileinfo] tag as it doesn't directly correspond to
// a specific line of verilog code.
2018-08-23 16:35:11 -05:00
wire_decls . push_back ( stringf ( " %s is invalid \n " , unconn_id . c_str ( ) ) ) ;
2016-11-17 16:36:47 -06:00
}
2020-03-21 06:54:23 -05:00
wire_exprs . push_back ( stringf ( " %s <= %s %s \n " , make_id ( wire - > name ) , expr . c_str ( ) , wireFileinfo . c_str ( ) ) ) ;
2016-11-17 16:36:47 -06:00
} else {
if ( make_unconn_id ) {
unconn_id . clear ( ) ;
}
2020-03-21 06:54:23 -05:00
// `invalid` is a firrtl construction for simulation so we will not
// tag it with a @[fileinfo] tag as it doesn't directly correspond to
// a specific line of verilog code.
2018-08-23 16:35:11 -05:00
wire_decls . push_back ( stringf ( " %s is invalid \n " , make_id ( wire - > name ) ) ) ;
2016-11-17 16:36:47 -06:00
}
}
for ( auto str : port_decls )
f < < str ;
f < < stringf ( " \n " ) ;
for ( auto str : wire_decls )
f < < str ;
f < < stringf ( " \n " ) ;
2019-04-01 17:02:12 -05:00
// If we have any memory definitions, output them.
for ( auto kv : memories ) {
2019-05-01 15:16:01 -05:00
memory & m = kv . second ;
2019-04-01 17:02:12 -05:00
f < < stringf ( " mem %s: \n " , m . name . c_str ( ) ) ;
f < < stringf ( " data-type => UInt<%d> \n " , m . width ) ;
f < < stringf ( " depth => %d \n " , m . size ) ;
for ( int i = 0 ; i < ( int ) m . read_ports . size ( ) ; i + = 1 ) {
f < < stringf ( " reader => r%d \n " , i ) ;
}
for ( int i = 0 ; i < ( int ) m . write_ports . size ( ) ; i + = 1 ) {
f < < stringf ( " writer => w%d \n " , i ) ;
}
f < < stringf ( " read-latency => %d \n " , m . read_latency ) ;
f < < stringf ( " write-latency => %d \n " , m . write_latency ) ;
f < < stringf ( " read-under-write => undefined \n " ) ;
}
f < < stringf ( " \n " ) ;
2016-11-17 16:36:47 -06:00
for ( auto str : cell_exprs )
f < < str ;
f < < stringf ( " \n " ) ;
for ( auto str : wire_exprs )
f < < str ;
}
} ;
struct FirrtlBackend : public Backend {
FirrtlBackend ( ) : Backend ( " firrtl " , " write design to a FIRRTL file " ) { }
2018-07-21 01:41:18 -05:00
void help ( ) YS_OVERRIDE
2016-11-17 16:36:47 -06:00
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log ( " \n " ) ;
log ( " write_firrtl [options] [filename] \n " ) ;
log ( " \n " ) ;
log ( " Write a FIRRTL netlist of the current design. \n " ) ;
2019-02-15 13:14:17 -06:00
log ( " The following commands are executed by this command: \n " ) ;
log ( " pmuxtree \n " ) ;
2016-11-17 16:36:47 -06:00
log ( " \n " ) ;
}
2018-07-21 01:41:18 -05:00
void execute ( std : : ostream * & f , std : : string filename , std : : vector < std : : string > args , RTLIL : : Design * design ) YS_OVERRIDE
2016-11-17 16:36:47 -06:00
{
2019-02-15 13:14:17 -06:00
size_t argidx = args . size ( ) ; // We aren't expecting any arguments.
// If we weren't explicitly passed a filename, use the last argument (if it isn't a flag).
if ( filename = = " " ) {
if ( argidx > 0 & & args [ argidx - 1 ] [ 0 ] ! = ' - ' ) {
// extra_args and friends need to see this argument.
argidx - = 1 ;
filename = args [ argidx ] ;
}
2016-11-17 16:36:47 -06:00
}
extra_args ( f , filename , args , argidx ) ;
2019-02-15 13:14:17 -06:00
if ( ! design - > full_selection ( ) )
log_cmd_error ( " This command only operates on fully selected designs! \n " ) ;
2016-11-17 16:36:47 -06:00
2019-02-15 13:14:17 -06:00
log_header ( design , " Executing FIRRTL backend. \n " ) ;
log_push ( ) ;
2016-11-17 16:36:47 -06:00
2019-02-15 13:14:17 -06:00
Pass : : call ( design , stringf ( " pmuxtree " ) ) ;
2016-11-17 16:36:47 -06:00
namecache . clear ( ) ;
autoid_counter = 0 ;
2019-02-15 13:14:17 -06:00
// Get the top module, or a reasonable facsimile - we need something for the circuit name.
Module * top = design - > top_module ( ) ;
Module * last = nullptr ;
// Generate module and wire names.
2016-11-17 16:36:47 -06:00
for ( auto module : design - > modules ( ) ) {
make_id ( module - > name ) ;
2019-02-15 13:14:17 -06:00
last = module ;
2020-03-12 14:57:01 -05:00
if ( top = = nullptr & & module - > get_bool_attribute ( ID : : top ) ) {
2019-02-15 13:14:17 -06:00
top = module ;
}
2016-11-17 16:36:47 -06:00
for ( auto wire : module - > wires ( ) )
if ( wire - > port_id )
make_id ( wire - > name ) ;
}
2019-02-15 13:14:17 -06:00
if ( top = = nullptr )
top = last ;
2020-03-24 15:07:08 -05:00
std : : string circuitFileinfo = getFileinfo ( top ) ;
2020-03-20 12:31:12 -05:00
* f < < stringf ( " circuit %s: %s \n " , make_id ( top - > name ) , circuitFileinfo . c_str ( ) ) ;
2016-11-17 16:36:47 -06:00
for ( auto module : design - > modules ( ) )
{
2018-08-23 16:35:11 -05:00
FirrtlWorker worker ( module , * f , design ) ;
2016-11-17 16:36:47 -06:00
worker . run ( ) ;
}
namecache . clear ( ) ;
autoid_counter = 0 ;
}
} FirrtlBackend ;
PRIVATE_NAMESPACE_END