2015-02-13 14:34:51 +01:00
/*
* yosys - - Yosys Open SYnthesis Suite
*
2021-06-08 00:39:36 +02:00
* Copyright ( C ) 2012 Claire Xenia Wolf < claire @ yosyshq . com >
2015-07-02 11:14:30 +02:00
*
2015-02-13 14:34:51 +01:00
* 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 .
2015-07-02 11:14:30 +02:00
*
2015-02-13 14:34:51 +01:00
* THE SOFTWARE IS PROVIDED " AS IS " AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS . IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL , DIRECT , INDIRECT , OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE , DATA OR PROFITS , WHETHER IN AN
* ACTION OF CONTRACT , NEGLIGENCE OR OTHER TORTIOUS ACTION , ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE .
*
*/
# include "kernel/yosys.h"
# include "kernel/sigtools.h"
2024-02-05 14:44:55 +01:00
# include "kernel/celledges.h"
2025-11-26 00:50:41 +01:00
# include "kernel/newcelltypes.h"
2015-02-13 14:34:51 +01:00
# include "kernel/utils.h"
2025-08-06 03:52:12 +02:00
# include "kernel/log_help.h"
2026-05-29 08:40:23 +02:00
# include "kernel/mem.h"
2015-02-13 14:34:51 +01:00
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
struct CheckPass : public Pass {
CheckPass ( ) : Pass ( " check " , " check for obvious problems in the design " ) { }
2025-08-06 03:52:12 +02:00
bool formatted_help ( ) override {
auto * help = PrettyHelp : : get_current ( ) ;
help - > set_group ( " passes/status " ) ;
return false ;
}
2020-06-19 01:34:52 +02:00
void help ( ) override
2015-02-13 14:34:51 +01:00
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log ( " \n " ) ;
2015-02-15 12:58:12 +01:00
log ( " check [options] [selection] \n " ) ;
2015-02-13 14:34:51 +01:00
log ( " \n " ) ;
log ( " This pass identifies the following problems in the current design: \n " ) ;
log ( " \n " ) ;
2020-11-02 07:33:03 +01:00
log ( " - combinatorial loops \n " ) ;
log ( " - two or more conflicting drivers for one wire \n " ) ;
log ( " - used wires that do not have a driver \n " ) ;
2015-02-13 14:34:51 +01:00
log ( " \n " ) ;
2019-10-03 11:49:56 +02:00
log ( " Options: \n " ) ;
2015-02-15 12:58:12 +01:00
log ( " \n " ) ;
2020-11-02 07:33:03 +01:00
log ( " -noinit \n " ) ;
log ( " also check for wires which have the 'init' attribute set \n " ) ;
2017-01-04 18:12:41 +01:00
log ( " \n " ) ;
2020-11-02 07:33:03 +01:00
log ( " -initdrv \n " ) ;
log ( " also check for wires that have the 'init' attribute set and are not \n " ) ;
log ( " driven by an FF cell type \n " ) ;
2019-10-02 13:35:03 +02:00
log ( " \n " ) ;
2020-11-02 07:33:03 +01:00
log ( " -mapped \n " ) ;
log ( " also check for internal cells that have not been mapped to cells of the \n " ) ;
log ( " target architecture \n " ) ;
2019-10-03 11:49:56 +02:00
log ( " \n " ) ;
2020-11-02 07:33:03 +01:00
log ( " -allow-tbuf \n " ) ;
log ( " modify the -mapped behavior to still allow $_TBUF_ cells \n " ) ;
2019-10-03 11:49:56 +02:00
log ( " \n " ) ;
2020-11-02 07:33:03 +01:00
log ( " -assert \n " ) ;
log ( " produce a runtime error if any problems are found in the current design \n " ) ;
2015-02-22 13:02:48 +01:00
log ( " \n " ) ;
2024-07-18 13:04:27 +02:00
log ( " -force-detailed-loop-check \n " ) ;
log ( " for the detection of combinatorial loops, use a detailed connectivity \n " ) ;
log ( " model for all internal cells for which it is available. This disables \n " ) ;
log ( " falling back to a simpler overapproximating model for those cells for \n " ) ;
log ( " which the detailed model is expected costly. \n " ) ;
log ( " \n " ) ;
2015-02-13 14:34:51 +01:00
}
2020-06-19 01:34:52 +02:00
void execute ( std : : vector < std : : string > args , RTLIL : : Design * design ) override
2015-02-13 14:34:51 +01:00
{
int counter = 0 ;
2015-02-15 12:58:12 +01:00
bool noinit = false ;
2017-01-04 18:12:41 +01:00
bool initdrv = false ;
2019-10-02 13:35:03 +02:00
bool mapped = false ;
2019-10-03 11:49:56 +02:00
bool allow_tbuf = false ;
2015-02-22 13:00:41 +01:00
bool assert_mode = false ;
2024-07-18 13:04:27 +02:00
bool force_detailed_loop_check = false ;
bool suggest_detail = false ;
2015-02-13 14:34:51 +01:00
2015-02-15 12:58:12 +01:00
size_t argidx ;
for ( argidx = 1 ; argidx < args . size ( ) ; argidx + + ) {
if ( args [ argidx ] = = " -noinit " ) {
noinit = true ;
continue ;
}
2017-01-04 18:12:41 +01:00
if ( args [ argidx ] = = " -initdrv " ) {
initdrv = true ;
continue ;
}
2019-10-02 13:35:03 +02:00
if ( args [ argidx ] = = " -mapped " ) {
mapped = true ;
continue ;
}
2019-10-03 11:49:56 +02:00
if ( args [ argidx ] = = " -allow-tbuf " ) {
allow_tbuf = true ;
continue ;
}
2015-02-22 13:00:41 +01:00
if ( args [ argidx ] = = " -assert " ) {
assert_mode = true ;
continue ;
}
2024-07-18 13:04:27 +02:00
if ( args [ argidx ] = = " -force-detailed-loop-check " ) {
force_detailed_loop_check = true ;
continue ;
}
2015-02-15 12:58:12 +01:00
break ;
}
extra_args ( args , argidx , design ) ;
2015-02-13 14:34:51 +01:00
2016-04-21 23:28:37 +02:00
log_header ( design , " Executing CHECK pass (checking for obvious problems). \n " ) ;
2015-02-13 14:34:51 +01:00
for ( auto module : design - > selected_whole_modules_warn ( ) )
{
2026-05-08 09:01:43 +02:00
log ( " Checking module %s... \n " , module ) ;
2015-02-13 14:34:51 +01:00
SigMap sigmap ( module ) ;
dict < SigBit , vector < string > > wire_drivers ;
2024-02-05 14:44:55 +01:00
dict < SigBit , Cell * > driver_cells ;
2015-07-27 09:54:58 +02:00
dict < SigBit , int > wire_drivers_count ;
2015-02-13 14:34:51 +01:00
pool < SigBit > used_wires ;
2024-02-01 10:40:45 +01:00
TopoSort < std : : pair < RTLIL : : IdString , int > > topo ;
2020-11-03 16:36:27 +01:00
for ( auto & proc_it : module - > processes )
{
std : : vector < RTLIL : : CaseRule * > all_cases = { & proc_it . second - > root_case } ;
for ( size_t i = 0 ; i < all_cases . size ( ) ; i + + ) {
for ( auto action : all_cases [ i ] - > actions ) {
for ( auto bit : sigmap ( action . first ) )
2023-06-23 18:07:28 +02:00
wire_drivers [ bit ] . push_back (
stringf ( " action %s <= %s (case rule) in process %s " ,
2026-05-08 09:01:43 +02:00
log_signal ( action . first ) , log_signal ( action . second ) , proc_it . first . unescape ( ) ) ) ;
2023-06-23 18:07:28 +02:00
2020-11-03 16:36:27 +01:00
for ( auto bit : sigmap ( action . second ) )
if ( bit . wire ) used_wires . insert ( bit ) ;
}
for ( auto switch_ : all_cases [ i ] - > switches ) {
for ( auto case_ : switch_ - > cases ) {
all_cases . push_back ( case_ ) ;
for ( auto compare : case_ - > compare )
for ( auto bit : sigmap ( compare ) )
if ( bit . wire ) used_wires . insert ( bit ) ;
}
}
}
for ( auto & sync : proc_it . second - > syncs ) {
for ( auto bit : sigmap ( sync - > signal ) )
if ( bit . wire ) used_wires . insert ( bit ) ;
for ( auto action : sync - > actions ) {
for ( auto bit : sigmap ( action . first ) )
2023-06-23 18:07:28 +02:00
wire_drivers [ bit ] . push_back (
stringf ( " action %s <= %s (sync rule) in process %s " ,
2026-05-08 09:01:43 +02:00
log_signal ( action . first ) , log_signal ( action . second ) , proc_it . first . unescape ( ) ) ) ;
2020-11-03 16:36:27 +01:00
for ( auto bit : sigmap ( action . second ) )
if ( bit . wire ) used_wires . insert ( bit ) ;
}
2021-02-23 00:21:46 +01:00
for ( auto memwr : sync - > mem_write_actions ) {
for ( auto bit : sigmap ( memwr . address ) )
if ( bit . wire ) used_wires . insert ( bit ) ;
for ( auto bit : sigmap ( memwr . data ) )
if ( bit . wire ) used_wires . insert ( bit ) ;
for ( auto bit : sigmap ( memwr . enable ) )
if ( bit . wire ) used_wires . insert ( bit ) ;
}
2020-11-03 16:36:27 +01:00
}
}
2024-02-05 14:44:55 +01:00
struct CircuitEdgesDatabase : AbstractCellEdgesDatabase {
TopoSort < std : : pair < RTLIL : : IdString , int > > & topo ;
SigMap sigmap ;
2024-07-18 13:04:27 +02:00
bool force_detail ;
2024-02-05 14:44:55 +01:00
2024-07-18 13:04:27 +02:00
CircuitEdgesDatabase ( TopoSort < std : : pair < RTLIL : : IdString , int > > & topo , SigMap & sigmap , bool force_detail )
: topo ( topo ) , sigmap ( sigmap ) , force_detail ( force_detail ) { }
2024-02-05 14:44:55 +01:00
void add_edge ( RTLIL : : Cell * cell , RTLIL : : IdString from_port , int from_bit ,
RTLIL : : IdString to_port , int to_bit , int ) override {
2024-02-12 12:32:50 +01:00
SigSpec from_portsig = cell - > getPort ( from_port ) ;
SigSpec to_portsig = cell - > getPort ( to_port ) ;
log_assert ( from_bit > = 0 & & from_bit < from_portsig . size ( ) ) ;
log_assert ( to_bit > = 0 & & to_bit < to_portsig . size ( ) ) ;
SigBit from = sigmap ( from_portsig [ from_bit ] ) ;
SigBit to = sigmap ( to_portsig [ to_bit ] ) ;
2024-02-05 14:44:55 +01:00
if ( from . wire & & to . wire )
topo . edge ( std : : make_pair ( from . wire - > name , from . offset ) , std : : make_pair ( to . wire - > name , to . offset ) ) ;
}
2024-07-18 13:04:27 +02:00
bool detail_costly ( Cell * cell ) {
// Only those cell types for which the edge data can expode quadratically
// in port widths are those for us to check.
if ( ! cell - > type . in (
ID ( $ add ) , ID ( $ sub ) ,
2025-08-12 14:38:20 +02:00
ID ( $ shl ) , ID ( $ shr ) , ID ( $ sshl ) , ID ( $ sshr ) , ID ( $ shift ) , ID ( $ shiftx ) ,
ID ( $ pmux ) , ID ( $ bmux ) ) )
2024-07-18 13:04:27 +02:00
return false ;
int in_widths = 0 , out_widths = 0 ;
2025-08-12 14:38:20 +02:00
if ( cell - > type . in ( ID ( $ pmux ) , ID ( $ bmux ) ) ) {
// We're skipping inputs A and B, since each of their bits contributes only one edge
in_widths = GetSize ( cell - > getPort ( ID : : S ) ) ;
out_widths = GetSize ( cell - > getPort ( ID : : Y ) ) ;
} else {
for ( auto & conn : cell - > connections ( ) ) {
if ( cell - > input ( conn . first ) )
in_widths + = conn . second . size ( ) ;
if ( cell - > output ( conn . first ) )
out_widths + = conn . second . size ( ) ;
}
2024-07-18 13:04:27 +02:00
}
const int threshold = 1024 ;
// if the multiplication may overflow we will catch it here
if ( in_widths + out_widths > = threshold )
2024-02-05 14:44:55 +01:00
return true ;
2024-07-18 13:04:27 +02:00
if ( in_widths * out_widths > = threshold )
return true ;
return false ;
}
bool add_edges_from_cell ( Cell * cell ) {
if ( force_detail | | ! detail_costly ( cell ) ) {
if ( AbstractCellEdgesDatabase : : add_edges_from_cell ( cell ) )
return true ;
}
2024-02-05 14:44:55 +01:00
// We don't have accurate cell edges, do the fallback of all input-output pairs
for ( auto & conn : cell - > connections ( ) ) {
if ( cell - > input ( conn . first ) )
for ( auto bit : sigmap ( conn . second ) )
if ( bit . wire )
topo . edge ( std : : make_pair ( bit . wire - > name , bit . offset ) ,
std : : make_pair ( cell - > name , - 1 ) ) ;
if ( cell - > output ( conn . first ) )
for ( auto bit : sigmap ( conn . second ) )
if ( bit . wire )
topo . edge ( std : : make_pair ( cell - > name , - 1 ) ,
std : : make_pair ( bit . wire - > name , bit . offset ) ) ;
}
2024-07-18 13:04:27 +02:00
// Return false to signify the fallback
return false ;
2024-02-05 14:44:55 +01:00
}
} ;
2024-07-18 13:04:27 +02:00
CircuitEdgesDatabase edges_db ( topo , sigmap , force_detailed_loop_check ) ;
2024-02-05 14:44:55 +01:00
2024-07-18 13:04:27 +02:00
pool < Cell * > coarsened_cells ;
2015-02-13 14:34:51 +01:00
for ( auto cell : module - > cells ( ) )
2019-10-02 13:35:03 +02:00
{
if ( mapped & & cell - > type . begins_with ( " $ " ) & & design - > module ( cell - > type ) = = nullptr ) {
2019-10-03 11:49:56 +02:00
if ( allow_tbuf & & cell - > type = = ID ( $ _TBUF_ ) ) goto cell_allowed ;
2026-05-08 09:01:43 +02:00
log_warning ( " Cell %s.%s is an unmapped internal cell of type %s. \n " , module , cell , cell - > type . unescape ( ) ) ;
2019-10-02 13:35:03 +02:00
counter + + ;
2019-10-03 11:49:56 +02:00
cell_allowed : ;
2019-10-02 13:35:03 +02:00
}
2024-02-05 14:44:55 +01:00
2019-10-02 13:35:03 +02:00
for ( auto & conn : cell - > connections ( ) ) {
2024-02-05 14:44:55 +01:00
bool input = cell - > input ( conn . first ) ;
bool output = cell - > output ( conn . first ) ;
2019-10-02 13:35:03 +02:00
SigSpec sig = sigmap ( conn . second ) ;
2024-02-05 14:44:55 +01:00
for ( int i = 0 ; i < sig . size ( ) ; i + + ) {
SigBit bit = sig [ i ] ;
if ( input & & bit . wire )
used_wires . insert ( bit ) ;
if ( output & & ! input & & bit . wire )
2026-05-08 09:01:43 +02:00
wire_drivers_count [ bit ] + + ;
2024-02-05 14:44:55 +01:00
if ( output & & ( bit . wire | | ! input ) )
2026-05-08 09:01:43 +02:00
wire_drivers [ bit ] . push_back ( stringf ( " port %s[%d] of cell %s (%s) " , conn . first . unescape ( ) , i ,
cell , cell - > type . unescape ( ) ) ) ;
2024-02-05 14:44:55 +01:00
if ( output )
driver_cells [ bit ] = cell ;
}
2019-10-02 13:35:03 +02:00
}
2024-02-05 14:44:55 +01:00
2024-02-23 11:41:03 +01:00
if ( yosys_celltypes . cell_evaluable ( cell - > type ) | | cell - > type . in ( ID ( $ mem_v2 ) , ID ( $ memrd ) , ID ( $ memrd_v2 ) ) \
2025-09-17 05:23:52 +02:00
| | cell - > is_builtin_ff ( ) ) {
2024-07-18 13:04:27 +02:00
if ( ! edges_db . add_edges_from_cell ( cell ) )
coarsened_cells . insert ( cell ) ;
}
2015-02-13 14:34:51 +01:00
}
2017-01-04 18:12:41 +01:00
pool < SigBit > init_bits ;
2015-02-13 14:34:51 +01:00
for ( auto wire : module - > wires ( ) ) {
if ( wire - > port_input ) {
SigSpec sig = sigmap ( wire ) ;
for ( int i = 0 ; i < GetSize ( sig ) ; i + + )
2023-06-23 18:07:28 +02:00
if ( sig [ i ] . wire | | ! wire - > port_output )
2026-05-08 09:01:43 +02:00
wire_drivers [ sig [ i ] ] . push_back ( stringf ( " module input %s[%d] " , wire , i ) ) ;
2015-02-13 14:34:51 +01:00
}
if ( wire - > port_output )
2015-02-13 14:40:49 +01:00
for ( auto bit : sigmap ( wire ) )
if ( bit . wire ) used_wires . insert ( bit ) ;
2015-07-27 09:54:58 +02:00
if ( wire - > port_input & & ! wire - > port_output )
for ( auto bit : sigmap ( wire ) )
if ( bit . wire ) wire_drivers_count [ bit ] + + ;
2020-04-02 18:51:32 +02:00
if ( wire - > attributes . count ( ID : : init ) ) {
Const initval = wire - > attributes . at ( ID : : init ) ;
2017-01-04 18:12:41 +01:00
for ( int i = 0 ; i < GetSize ( initval ) & & i < GetSize ( wire ) ; i + + )
if ( initval [ i ] = = State : : S0 | | initval [ i ] = = State : : S1 )
init_bits . insert ( sigmap ( SigBit ( wire , i ) ) ) ;
if ( noinit ) {
2026-05-08 09:01:43 +02:00
log_warning ( " Wire %s.%s has an unprocessed 'init' attribute. \n " , module , wire ) ;
2017-01-04 18:12:41 +01:00
counter + + ;
}
2015-02-15 12:58:12 +01:00
}
2015-02-13 14:34:51 +01:00
}
2023-06-23 18:07:28 +02:00
for ( auto state : { State : : S0 , State : : S1 , State : : Sx } )
if ( wire_drivers . count ( state ) ) {
string message = stringf ( " Drivers conflicting with a constant %s driver: \n " , log_signal ( state ) ) ;
for ( auto str : wire_drivers [ state ] )
2025-08-20 00:45:26 +02:00
message + = stringf ( " %s \n " , str ) ;
2025-09-17 01:02:16 +02:00
log_warning ( " %s " , message ) ;
2023-06-23 18:07:28 +02:00
counter + + ;
}
2015-02-13 14:34:51 +01:00
for ( auto it : wire_drivers )
2015-07-27 09:54:58 +02:00
if ( wire_drivers_count [ it . first ] > 1 ) {
2026-05-08 09:01:43 +02:00
string message = stringf ( " multiple conflicting drivers for %s.%s: \n " , module , log_signal ( it . first ) ) ;
2015-02-13 14:34:51 +01:00
for ( auto str : it . second )
2025-08-20 00:45:26 +02:00
message + = stringf ( " %s \n " , str ) ;
2025-09-17 01:02:16 +02:00
log_warning ( " %s " , message ) ;
2015-02-13 14:34:51 +01:00
counter + + ;
}
for ( auto bit : used_wires )
if ( ! wire_drivers . count ( bit ) ) {
2026-05-08 09:01:43 +02:00
log_warning ( " Wire %s.%s is used but has no driver. \n " , module , log_signal ( bit ) ) ;
2015-02-13 14:34:51 +01:00
counter + + ;
}
topo . sort ( ) ;
for ( auto & loop : topo . loops ) {
2026-05-08 09:01:43 +02:00
string message = stringf ( " found logic loop in module %s: \n " , module ) ;
2024-02-05 14:44:55 +01:00
2024-07-18 13:01:22 +02:00
// `loop` only contains wire bits, or an occasional special helper node for cells for
// which we have done the edges fallback. The cell and its ports that led to an edge are
// a piece of information we need to recover now. For that we need to have the previous
// wire bit of the loop at hand.
2024-02-05 14:44:55 +01:00
SigBit prev ;
for ( auto it = loop . rbegin ( ) ; it ! = loop . rend ( ) ; it + + )
if ( it - > second ! = - 1 ) { // skip the fallback helper nodes
prev = SigBit ( module - > wire ( it - > first ) , it - > second ) ;
break ;
}
log_assert ( prev ! = SigBit ( ) ) ;
2024-02-01 10:40:45 +01:00
for ( auto & pair : loop ) {
if ( pair . second = = - 1 )
2024-02-05 14:44:55 +01:00
continue ; // helper node for edges fallback, we can ignore it
struct MatchingEdgePrinter : AbstractCellEdgesDatabase {
std : : string & message ;
SigMap & sigmap ;
SigBit from , to ;
int nhits ;
const int HITS_LIMIT = 3 ;
MatchingEdgePrinter ( std : : string & message , SigMap & sigmap , SigBit from , SigBit to )
: message ( message ) , sigmap ( sigmap ) , from ( from ) , to ( to ) , nhits ( 0 ) { }
void add_edge ( RTLIL : : Cell * cell , RTLIL : : IdString from_port , int from_bit ,
RTLIL : : IdString to_port , int to_bit , int ) override {
SigBit edge_from = sigmap ( cell - > getPort ( from_port ) ) [ from_bit ] ;
SigBit edge_to = sigmap ( cell - > getPort ( to_port ) ) [ to_bit ] ;
if ( edge_from = = from & & edge_to = = to & & nhits + + < HITS_LIMIT )
2026-05-08 09:01:43 +02:00
message + = stringf ( " %s[%d] --> %s[%d] \n " , from_port . unescape ( ) , from_bit ,
to_port . unescape ( ) , to_bit ) ;
2024-02-05 14:44:55 +01:00
if ( nhits = = HITS_LIMIT )
2024-07-18 13:01:37 +02:00
message + = " ... \n " ;
2024-02-05 14:44:55 +01:00
}
} ;
Wire * wire = module - > wire ( pair . first ) ;
log_assert ( wire ) ;
SigBit bit ( module - > wire ( pair . first ) , pair . second ) ;
log_assert ( driver_cells . count ( bit ) ) ;
Cell * driver = driver_cells . at ( bit ) ;
std : : string driver_src ;
if ( driver - > has_attribute ( ID : : src ) ) {
std : : string src_attr = driver - > get_src_attribute ( ) ;
2025-08-20 00:45:26 +02:00
driver_src = stringf ( " source: %s " , src_attr ) ;
2024-02-01 10:40:45 +01:00
}
2024-07-18 13:04:27 +02:00
2026-05-08 09:01:43 +02:00
message + = stringf ( " cell %s (%s)%s \n " , driver , driver - > type . unescape ( ) , driver_src ) ;
2024-07-18 13:04:27 +02:00
if ( ! coarsened_cells . count ( driver ) ) {
MatchingEdgePrinter printer ( message , sigmap , prev , bit ) ;
printer . add_edges_from_cell ( driver ) ;
} else {
message + = " (cell's internal connectivity overapproximated; loop may be a false positive) \n " ;
suggest_detail = true ;
}
2024-02-05 14:44:55 +01:00
2024-03-04 11:47:01 +01:00
if ( wire - > name . isPublic ( ) ) {
std : : string wire_src ;
if ( wire - > has_attribute ( ID : : src ) ) {
std : : string src_attr = wire - > get_src_attribute ( ) ;
2025-08-20 00:45:26 +02:00
wire_src = stringf ( " source: %s " , src_attr ) ;
2024-03-04 11:47:01 +01:00
}
2025-08-20 00:45:26 +02:00
message + = stringf ( " wire %s%s \n " , log_signal ( SigBit ( wire , pair . second ) ) , wire_src ) ;
2024-02-01 10:40:45 +01:00
}
2024-03-04 11:47:01 +01:00
2024-02-05 14:44:55 +01:00
prev = bit ;
2024-02-01 10:40:45 +01:00
}
2025-09-17 01:02:16 +02:00
log_warning ( " %s " , message ) ;
2015-02-13 14:34:51 +01:00
counter + + ;
}
2017-01-04 18:12:41 +01:00
if ( initdrv )
{
for ( auto cell : module - > cells ( ) )
{
2025-09-17 05:23:52 +02:00
if ( cell - > is_builtin_ff ( ) = = 0 )
2017-01-04 18:12:41 +01:00
continue ;
2020-04-02 18:51:32 +02:00
for ( auto bit : sigmap ( cell - > getPort ( ID : : Q ) ) )
2017-01-04 18:12:41 +01:00
init_bits . erase ( bit ) ;
}
SigSpec init_sig ( init_bits ) ;
init_sig . sort_and_unify ( ) ;
for ( auto chunk : init_sig . chunks ( ) ) {
2026-05-08 09:01:43 +02:00
log_warning ( " Wire %s.%s has 'init' attribute and is not driven by an FF cell. \n " , module , log_signal ( chunk ) ) ;
2017-01-04 18:12:41 +01:00
counter + + ;
}
}
2015-02-13 14:34:51 +01:00
}
2020-11-02 07:33:03 +01:00
log ( " Found and reported %d problems. \n " , counter ) ;
2015-02-22 13:00:41 +01:00
2024-07-18 13:04:27 +02:00
if ( suggest_detail )
log ( " Consider re-running with '-force-detailed-loop-check' to rule out false positives. \n " ) ;
2015-02-22 16:29:44 +01:00
if ( assert_mode & & counter > 0 )
2015-02-22 13:00:41 +01:00
log_error ( " Found %d problems in 'check -assert'. \n " , counter ) ;
2015-02-13 14:34:51 +01:00
}
} CheckPass ;
2015-07-02 11:14:30 +02:00
2026-05-29 08:40:23 +02:00
struct CheckMemPass : public Pass {
CheckMemPass ( ) : Pass ( " check_mem " , " check for obvious memory problems in the design " ) { }
bool formatted_help ( ) override {
auto * help = PrettyHelp : : get_current ( ) ;
help - > set_group ( " passes/status " ) ;
auto content_root = help - > get_root ( ) ;
content_root - > usage ( " check_mem [selection] " ) ;
content_root - > paragraph (
" This pass identifies the following problems in the current design: "
" addressing invalid memory. "
) ;
2026-05-29 08:40:24 +02:00
content_root - > option ( " -non-const " , " also check non-const address signals (may produce false-positives) " ) ;
2026-05-29 08:40:23 +02:00
content_root - > option ( " -assert " , " produce a runtime error if any problems are found in the current design " ) ;
return true ;
}
void execute ( std : : vector < std : : string > args , RTLIL : : Design * design ) override
{
int counter = 0 ;
bool assert_mode = false ;
2026-05-29 08:40:24 +02:00
bool nonconst_mode = false ;
2026-05-29 08:40:23 +02:00
size_t argidx ;
for ( argidx = 1 ; argidx < args . size ( ) ; argidx + + ) {
if ( args [ argidx ] = = " -assert " ) {
assert_mode = true ;
continue ;
}
2026-05-29 08:40:24 +02:00
if ( args [ argidx ] = = " -non-const " ) {
nonconst_mode = true ;
continue ;
}
2026-05-29 08:40:23 +02:00
break ;
}
extra_args ( args , argidx , design ) ;
log_header ( design , " Executing CHECK_MEM pass. \n " ) ;
for ( auto * module : design - > selected_unboxed_modules_warn ( ) ) {
for ( auto mem : Mem : : get_selected_memories ( module ) ) {
int min_addr = mem . mem - > start_offset ;
int max_addr = mem . mem - > size + min_addr - 1 ;
for ( auto & init : mem . inits ) {
int start = init . addr . as_int ( ) ;
if ( start < min_addr ) {
log_warning ( " Mem %s.%s starts at %d but initializes address %d. \n " , log_id ( module ) , log_id ( mem . mem ) , min_addr , start ) ;
counter + + ;
}
int end = start + ( GetSize ( init . data ) / mem . width ) - 1 ;
if ( end > max_addr ) {
log_warning ( " Mem %s.%s ends at %d but initializes address %d. \n " , log_id ( module ) , log_id ( mem . mem ) , max_addr , end ) ;
counter + + ;
}
}
2026-05-29 08:40:24 +02:00
auto check_addr = [ min_addr , max_addr , & counter , module , & mem , & nonconst_mode ] ( SigSpec & addr_sig , const char * access ) {
2026-05-29 08:40:23 +02:00
if ( addr_sig . is_fully_const ( ) ) {
auto addr = addr_sig . as_int ( ) ;
if ( addr < min_addr | | addr > max_addr ) {
log_warning ( " Mem %s.%s contains entries for addresses %d..%d but %s address %d. \n " , log_id ( module ) , log_id ( mem . mem ) , min_addr , max_addr , access , addr ) ;
counter + + ;
}
2026-05-29 08:40:24 +02:00
} else if ( nonconst_mode ) {
// TODO check addr_sig.has_const() for constant MSb/LSb that may change effective min/max
// TODO consider sat solver for variable addresses
int addr_sig_min = 0 ;
int addr_sig_max = ( 1 < < addr_sig . size ( ) ) - 1 ;
if ( min_addr > addr_sig_min | | max_addr < addr_sig_max ) {
log_warning ( " Mem %s.%s contains entries for addresses %d..%d but has a potentially dangerous non-const input %s \n " , log_id ( module ) , log_id ( mem . mem ) , min_addr , max_addr , log_signal ( addr_sig ) ) ;
counter + + ;
}
2026-05-29 08:40:23 +02:00
}
} ;
// TODO test ABITS and WIDTH?
2026-05-29 08:40:24 +02:00
// TODO can we limit ports via selection?
2026-05-29 08:40:23 +02:00
for ( auto & rd_port : mem . rd_ports )
check_addr ( rd_port . addr , " reads " ) ;
for ( auto & wr_port : mem . wr_ports )
check_addr ( wr_port . addr , " writes " ) ;
}
}
if ( assert_mode & & counter > 0 )
log_error ( " Found %d problems in 'check_mem -assert'. \n " , counter ) ;
}
} CheckMemPass ;
2015-02-13 14:34:51 +01:00
PRIVATE_NAMESPACE_END