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-18 17:38:36 -05:00
// Shamelessly copied from ilang_backend.cc. Something better is surely possible here.
void dump_const ( std : : ostream & f , const RTLIL : : Const & data , int width = - 1 , int offset = 0 , bool autoint = true )
{
if ( width < 0 )
width = data . bits . size ( ) - offset ;
if ( ( data . flags & RTLIL : : CONST_FLAG_STRING ) = = 0 | | width ! = ( int ) data . bits . size ( ) ) {
if ( width = = 32 & & autoint ) {
int32_t val = 0 ;
for ( int i = 0 ; i < width ; i + + ) {
log_assert ( offset + i < ( int ) data . bits . size ( ) ) ;
switch ( data . bits [ offset + i ] ) {
case RTLIL : : S0 : break ;
case RTLIL : : S1 : val | = 1 < < i ; break ;
default : val = - 1 ; break ;
}
}
if ( val > = 0 ) {
f < < stringf ( " %d " , val ) ;
return ;
}
}
f < < stringf ( " %d' " , width ) ;
for ( int i = offset + width - 1 ; i > = offset ; i - - ) {
log_assert ( i < ( int ) data . bits . size ( ) ) ;
switch ( data . bits [ i ] ) {
case RTLIL : : S0 : f < < stringf ( " 0 " ) ; break ;
case RTLIL : : S1 : f < < stringf ( " 1 " ) ; break ;
case RTLIL : : Sx : f < < stringf ( " x " ) ; break ;
case RTLIL : : Sz : f < < stringf ( " z " ) ; break ;
case RTLIL : : Sa : f < < stringf ( " - " ) ; break ;
case RTLIL : : Sm : f < < stringf ( " m " ) ; break ;
}
}
} else {
f < < stringf ( " \" " ) ;
std : : string str = data . decode_string ( ) ;
for ( size_t i = 0 ; i < str . size ( ) ; i + + ) {
if ( str [ i ] = = ' \n ' )
f < < stringf ( " \\ n " ) ;
else if ( str [ i ] = = ' \t ' )
f < < stringf ( " \\ t " ) ;
else if ( str [ i ] < 32 )
f < < stringf ( " \\ %03o " , str [ i ] ) ;
else if ( str [ i ] = = ' " ' )
f < < stringf ( " \\ \" " ) ;
else if ( str [ i ] = = ' \\ ' )
f < < stringf ( " \\ \\ " ) ;
else
f < < str [ i ] ;
}
f < < stringf ( " \" " ) ;
}
}
2020-03-19 08:59:05 -05:00
std : : string getFileinfo ( dict < RTLIL : : IdString , RTLIL : : Const > attributes )
{
std : : ostringstream fileinfo ;
for ( auto & it : attributes ) {
if ( it . first = = " \\ src " ) {
2020-03-20 12:31:12 -05:00
fileinfo < < " @[ " ;
2020-03-19 08:59:05 -05:00
dump_const ( fileinfo , it . second ) ;
2020-03-20 12:31:12 -05:00
fileinfo < < " ] " ;
2020-03-19 08:59:05 -05:00
}
}
return fileinfo . str ( ) ;
}
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 ( ) ) ;
}
}
// We need a default constructor for the dict insert.
memory ( ) : pCell ( 0 ) , read_latency ( 0 ) , write_latency ( 1 ) , init_file ( " " ) , init_file_srcFileSpec ( " " ) { }
const char * atLine ( ) {
if ( srcLine = = " " ) {
if ( pCell ) {
auto p = pCell - > attributes . find ( " \\ src " ) ;
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-19 10:24:18 -05:00
auto cellFileinfo = getFileinfo ( cell - > attributes ) ;
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-19 08:59:05 -05:00
auto moduleFileinfo = getFileinfo ( module - > attributes ) ;
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-19 08:59:05 -05:00
auto wireFileinfo = getFileinfo ( wire - > attributes ) ;
2019-02-15 13:14:17 -06:00
// If a wire has initial data, issue a warning since FIRRTL doesn't currently support it.
if ( wire - > attributes . count ( " \\ init " ) ) {
log_warning ( " Initial value (%s) for (%s.%s) not supported \n " ,
wire - > attributes . at ( " \\ init " ) . as_string ( ) . c_str ( ) ,
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 ) ;
2019-02-15 13:14:17 -06: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.
int a_width = cell - > parameters . at ( " \\ A_WIDTH " , ndef ) . as_int ( ) ; // The width of "A"
int b_width = cell - > parameters . at ( " \\ B_WIDTH " , ndef ) . as_int ( ) ; // The width of "A"
const int y_width = cell - > parameters . at ( " \\ Y_WIDTH " , ndef ) . as_int ( ) ; // The width of the result
const bool a_signed = cell - > parameters . at ( " \\ A_SIGNED " , ndef ) . as_bool ( ) ;
const bool b_signed = cell - > parameters . at ( " \\ B_SIGNED " , ndef ) . as_bool ( ) ;
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 ) ;
2016-11-21 19:28:17 -06:00
if ( cell - > type . in ( " $not " , " $logic_not " , " $neg " , " $reduce_and " , " $reduce_or " , " $reduce_xor " , " $reduce_bool " , " $reduce_xnor " ) )
{
string a_expr = make_expr ( cell - > getPort ( " \\ A " ) ) ;
wire_decls . push_back ( stringf ( " wire %s: UInt<%d> \n " , y_id . c_str ( ) , y_width ) ) ;
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
if ( ! ( cell - > type . in ( " $eq " , " $eqx " , " $gt " , " $ge " , " $lt " , " $le " , " $ne " , " $nex " , " $reduce_bool " , " $logic_not " ) & & y_width = = 1 ) ) {
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 ;
2016-11-21 19:28:17 -06:00
if ( cell - > type = = " $not " ) primop = " not " ;
2019-05-21 15:04:56 -05:00
else if ( cell - > type = = " $neg " ) {
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 ;
2019-05-21 15:04:56 -05:00
} else if ( cell - > type = = " $logic_not " ) {
2016-11-21 19:28:17 -06:00
primop = " eq " ;
a_expr = stringf ( " %s, UInt(0) " , a_expr . c_str ( ) ) ;
}
2019-02-15 13:14:17 -06:00
else if ( cell - > type = = " $reduce_and " ) primop = " andr " ;
else if ( cell - > type = = " $reduce_or " ) primop = " orr " ;
else if ( cell - > type = = " $reduce_xor " ) primop = " xorr " ;
else if ( cell - > type = = " $reduce_xnor " ) {
2016-11-21 19:28:17 -06:00
primop = " not " ;
a_expr = stringf ( " xorr(%s) " , a_expr . c_str ( ) ) ;
}
2019-02-15 13:14:17 -06:00
else if ( cell - > type = = " $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 ( ) ) ;
cell_exprs . push_back ( stringf ( " %s <= %s \n " , y_id . c_str ( ) , expr . c_str ( ) ) ) ;
register_reverse_wire_map ( y_id , cell - > getPort ( " \\ Y " ) ) ;
continue ;
}
2019-07-31 11:27:38 -05:00
if ( cell - > type . in ( " $add " , " $sub " , " $mul " , " $div " , " $mod " , " $xor " , " $xnor " , " $and " , " $or " , " $eq " , " $eqx " ,
2019-02-15 13:14:17 -06:00
" $gt " , " $ge " , " $lt " , " $le " , " $ne " , " $nex " , " $shr " , " $sshr " , " $sshl " , " $shl " ,
2019-07-31 11:27:38 -05:00
" $logic_and " , " $logic_or " , " $pow " ) )
2016-11-17 16:36:47 -06:00
{
string a_expr = make_expr ( cell - > getPort ( " \\ A " ) ) ;
string b_expr = make_expr ( cell - > getPort ( " \\ B " ) ) ;
wire_decls . push_back ( stringf ( " wire %s: UInt<%d> \n " , y_id . c_str ( ) , y_width ) ) ;
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
if ( b_signed & & ! cell - > type . in ( " $shr " , " $sshr " , " $shl " , " $sshl " , " $pow " ) ) {
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.
if ( cell - > type . in ( " $add " , " $sub " , " $mul " , " $div " , " $mod " , " $xor " , " $xnor " , " $and " , " $or " ) )
{
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 ;
2019-02-15 13:14:17 -06:00
auto a_sig = cell - > getPort ( " \\ A " ) ;
2016-11-21 19:28:17 -06:00
2019-07-31 11:27:38 -05:00
if ( cell - > type = = " $add " ) {
primop = " add " ;
firrtl_is_signed = a_signed | b_signed ;
firrtl_width = max ( a_width , b_width ) ;
} else if ( cell - > type = = " $sub " ) {
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 ) ;
} else if ( cell - > type = = " $mul " ) {
primop = " mul " ;
firrtl_is_signed = a_signed | b_signed ;
firrtl_width = a_width + b_width ;
} else if ( cell - > type = = " $div " ) {
primop = " div " ;
firrtl_is_signed = a_signed | b_signed ;
firrtl_width = a_width ;
} else if ( cell - > type = = " $mod " ) {
primop = " rem " ;
firrtl_width = min ( a_width , b_width ) ;
} else if ( cell - > type = = " $and " ) {
primop = " and " ;
always_uint = true ;
firrtl_width = max ( a_width , b_width ) ;
2016-11-21 19:28:17 -06:00
}
2019-02-15 13:14:17 -06:00
else if ( cell - > type = = " $or " ) {
2019-07-31 11:27:38 -05:00
primop = " or " ;
always_uint = true ;
firrtl_width = max ( a_width , b_width ) ;
}
2019-02-15 13:14:17 -06:00
else if ( cell - > type = = " $xor " ) {
2019-07-31 11:27:38 -05:00
primop = " xor " ;
always_uint = true ;
firrtl_width = max ( a_width , b_width ) ;
}
else if ( cell - > type = = " $xnor " ) {
primop = " xnor " ;
always_uint = true ;
firrtl_width = max ( a_width , b_width ) ;
}
2019-02-15 13:14:17 -06:00
else if ( ( cell - > type = = " $eq " ) | ( cell - > type = = " $eqx " ) ) {
2019-07-31 11:27:38 -05:00
primop = " eq " ;
always_uint = true ;
firrtl_width = 1 ;
}
2019-02-15 13:14:17 -06:00
else if ( ( cell - > type = = " $ne " ) | ( cell - > type = = " $nex " ) ) {
2019-07-31 11:27:38 -05:00
primop = " neq " ;
always_uint = true ;
firrtl_width = 1 ;
}
2019-02-15 13:14:17 -06:00
else if ( cell - > type = = " $gt " ) {
2019-07-31 11:27:38 -05:00
primop = " gt " ;
always_uint = true ;
firrtl_width = 1 ;
}
2019-02-15 13:14:17 -06:00
else if ( cell - > type = = " $ge " ) {
2019-07-31 11:27:38 -05:00
primop = " geq " ;
always_uint = true ;
firrtl_width = 1 ;
}
2019-02-15 13:14:17 -06:00
else if ( cell - > type = = " $lt " ) {
2019-07-31 11:27:38 -05:00
primop = " lt " ;
always_uint = true ;
firrtl_width = 1 ;
}
2019-02-15 13:14:17 -06:00
else if ( cell - > type = = " $le " ) {
2019-07-31 11:27:38 -05:00
primop = " leq " ;
always_uint = true ;
firrtl_width = 1 ;
}
2019-02-15 13:14:17 -06:00
else if ( ( cell - > type = = " $shl " ) | ( cell - > type = = " $sshl " ) ) {
// 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?
auto b_sig = cell - > getPort ( " \\ B " ) ;
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
}
}
else if ( ( cell - > type = = " $shr " ) | ( cell - > type = = " $sshr " ) ) {
// We don't need to extract a specific range of bits.
extract_y_bits = false ;
// Is the shift amount constant?
auto b_sig = cell - > getPort ( " \\ B " ) ;
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.
if ( cell - > type = = " $shr " ) {
a_expr = " asUInt( " + a_expr + " ) " ;
}
2019-02-15 13:14:17 -06:00
}
}
else if ( ( cell - > type = = " $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 ;
}
2019-02-15 13:14:17 -06:00
else if ( ( cell - > type = = " $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 ;
}
else if ( ( cell - > type = = " $pow " ) ) {
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?
auto b_sig = cell - > getPort ( " \\ B " ) ;
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
if ( ! cell - > parameters . at ( " \\ B_SIGNED " ) . as_bool ( ) ) {
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
cell_exprs . push_back ( stringf ( " %s <= %s \n " , y_id . c_str ( ) , expr . c_str ( ) ) ) ;
register_reverse_wire_map ( y_id , cell - > getPort ( " \\ Y " ) ) ;
continue ;
}
2016-11-17 19:41:29 -06:00
if ( cell - > type . in ( " $mux " ) )
{
int width = cell - > parameters . at ( " \\ WIDTH " ) . as_int ( ) ;
string a_expr = make_expr ( cell - > getPort ( " \\ A " ) ) ;
string b_expr = make_expr ( cell - > getPort ( " \\ B " ) ) ;
string s_expr = make_expr ( cell - > getPort ( " \\ S " ) ) ;
wire_decls . push_back ( stringf ( " wire %s: UInt<%d> \n " , y_id . c_str ( ) , width ) ) ;
string expr = stringf ( " mux(%s, %s, %s) " , s_expr . c_str ( ) , b_expr . c_str ( ) , a_expr . c_str ( ) ) ;
cell_exprs . push_back ( stringf ( " %s <= %s \n " , y_id . c_str ( ) , expr . c_str ( ) ) ) ;
register_reverse_wire_map ( y_id , cell - > getPort ( " \\ Y " ) ) ;
continue ;
}
if ( cell - > type . in ( " $mem " ) )
{
string mem_id = make_id ( cell - > name ) ;
int abits = cell - > parameters . at ( " \\ ABITS " ) . as_int ( ) ;
int width = cell - > parameters . at ( " \\ WIDTH " ) . as_int ( ) ;
int size = cell - > parameters . at ( " \\ SIZE " ) . as_int ( ) ;
2019-05-01 15:16:01 -05:00
memory m ( cell , mem_id , abits , size , width ) ;
2016-11-17 19:41:29 -06:00
int rd_ports = cell - > parameters . at ( " \\ RD_PORTS " ) . as_int ( ) ;
int wr_ports = cell - > parameters . at ( " \\ WR_PORTS " ) . as_int ( ) ;
Const initdata = cell - > parameters . at ( " \\ INIT " ) ;
for ( State bit : initdata . bits )
if ( bit ! = State : : Sx )
log_error ( " Memory with initialization data: %s.%s \n " , log_id ( module ) , log_id ( cell ) ) ;
Const rd_clk_enable = cell - > parameters . at ( " \\ RD_CLK_ENABLE " ) ;
Const wr_clk_enable = cell - > parameters . at ( " \\ WR_CLK_ENABLE " ) ;
Const wr_clk_polarity = cell - > parameters . at ( " \\ WR_CLK_POLARITY " ) ;
int offset = cell - > parameters . at ( " \\ OFFSET " ) . as_int ( ) ;
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 ) ) ;
2019-04-01 17:02:12 -05:00
SigSpec addr_sig = cell - > getPort ( " \\ RD_ADDR " ) . extract ( i * abits , abits ) ;
2016-11-17 19:41:29 -06:00
SigSpec data_sig = cell - > getPort ( " \\ 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 ;
SigSpec addr_sig = cell - > getPort ( " \\ WR_ADDR " ) . extract ( i * abits , abits ) ;
string addr_expr = make_expr ( addr_sig ) ;
SigSpec data_sig = cell - > getPort ( " \\ WR_DATA " ) . extract ( i * width , width ) ;
string data_expr = make_expr ( data_sig ) ;
SigSpec clk_sig = cell - > getPort ( " \\ WR_CLK " ) . extract ( i ) ;
string clk_expr = make_expr ( clk_sig ) ;
2016-11-17 19:41:29 -06:00
SigSpec wen_sig = cell - > getPort ( " \\ WR_EN " ) . extract ( i * width , width ) ;
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
2019-04-01 17:02:12 -05:00
if ( cell - > type . in ( " $memwr " , " $memrd " , " $meminit " ) )
{
std : : string cell_type = fid ( cell - > type ) ;
std : : string mem_id = make_id ( cell - > parameters [ " \\ MEMID " ] . decode_string ( ) ) ;
2019-05-01 15:16:01 -05:00
int abits = cell - > parameters . at ( " \\ ABITS " ) . as_int ( ) ;
int width = cell - > parameters . at ( " \\ WIDTH " ) . as_int ( ) ;
2019-04-01 17:02:12 -05:00
memory * mp = nullptr ;
if ( cell - > type = = " $meminit " ) {
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.
auto addrSig = cell - > getPort ( " \\ ADDR " ) ;
auto dataSig = cell - > getPort ( " \\ DATA " ) ;
auto enableSig = cell - > getPort ( " \\ EN " ) ;
auto clockSig = cell - > getPort ( " \\ CLK " ) ;
Const clk_enable = cell - > parameters . at ( " \\ CLK_ENABLE " ) ;
Const clk_polarity = cell - > parameters . at ( " \\ CLK_POLARITY " ) ;
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 ) ;
if ( cell - > type . in ( " $memwr " ) ) {
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 ( ) ) ) ;
} else if ( cell - > type . in ( " $memrd " ) ) {
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 ;
}
2016-11-17 17:32:35 -06:00
if ( cell - > type . in ( " $dff " ) )
{
bool clkpol = cell - > parameters . at ( " \\ CLK_POLARITY " ) . as_bool ( ) ;
if ( clkpol = = false )
log_error ( " Negative edge clock on FF %s.%s. \n " , log_id ( module ) , log_id ( cell ) ) ;
int width = cell - > parameters . at ( " \\ WIDTH " ) . as_int ( ) ;
string expr = make_expr ( cell - > getPort ( " \\ D " ) ) ;
string clk_expr = " asClock( " + make_expr ( cell - > getPort ( " \\ CLK " ) ) + " ) " ;
2019-07-31 11:27:38 -05:00
wire_decls . push_back ( stringf ( " reg %s: UInt<%d>, %s \n " , y_id . c_str ( ) , width , clk_expr . c_str ( ) ) ) ;
2016-11-17 17:32:35 -06:00
2019-07-31 11:27:38 -05:00
cell_exprs . push_back ( stringf ( " %s <= %s \n " , y_id . c_str ( ) , expr . c_str ( ) ) ) ;
register_reverse_wire_map ( y_id , cell - > getPort ( " \\ 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 ;
}
if ( cell - > type = = " $shiftx " ) {
// assign y = a[b +: y_width];
// We'll extract the correct bits as part of the primop.
string a_expr = make_expr ( cell - > getPort ( " \\ A " ) ) ;
// Get the initial bit selector
string b_expr = make_expr ( cell - > getPort ( " \\ B " ) ) ;
wire_decls . push_back ( stringf ( " wire %s: UInt<%d> \n " , y_id . c_str ( ) , y_width ) ) ;
if ( cell - > getParam ( " \\ B_SIGNED " ) . as_bool ( ) ) {
// Use validif to constrain the selection (test the sign bit)
auto b_string = b_expr . c_str ( ) ;
int b_sign = cell - > parameters . at ( " \\ B_WIDTH " ) . as_int ( ) - 1 ;
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 ( ) ) ) ;
register_reverse_wire_map ( y_id , cell - > getPort ( " \\ Y " ) ) ;
continue ;
}
if ( cell - > type = = " $shift " ) {
// assign y = a >> b;
// where b may be negative
string a_expr = make_expr ( cell - > getPort ( " \\ A " ) ) ;
string b_expr = make_expr ( cell - > getPort ( " \\ B " ) ) ;
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 ) ) ;
if ( cell - > getParam ( " \\ B_SIGNED " ) . as_bool ( ) ) {
// 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 ( ) ) ) ;
register_reverse_wire_map ( y_id , cell - > getPort ( " \\ Y " ) ) ;
2019-07-31 11:27:38 -05:00
continue ;
}
if ( cell - > type = = " $pos " ) {
// assign y = a;
// printCell(cell);
string a_expr = make_expr ( cell - > getPort ( " \\ A " ) ) ;
// 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 ( ) ) ) ;
register_reverse_wire_map ( y_id , cell - > getPort ( " \\ 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 ;
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 ) {
wire_decls . push_back ( stringf ( " wire %s: UInt<1> \n " , unconn_id . c_str ( ) ) ) ;
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
}
wire_exprs . push_back ( stringf ( " %s <= %s \n " , make_id ( wire - > name ) , expr . c_str ( ) ) ) ;
} else {
if ( make_unconn_id ) {
unconn_id . clear ( ) ;
}
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 ;
if ( top = = nullptr & & module - > get_bool_attribute ( " \\ top " ) ) {
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-19 08:59:05 -05:00
auto circuitFileinfo = getFileinfo ( top - > attributes ) ;
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