2019-02-08 23:53:12 +01:00
/*
* yosys - - Yosys Open SYnthesis Suite
*
2021-06-08 00:39:36 +02:00
* Copyright ( C ) 2012 Claire Xenia Wolf < claire @ yosyshq . com >
2019-06-12 18:40:51 +02:00
* 2019 Eddie Hung < eddie @ fpgeh . com >
2019-02-08 23:53:12 +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 .
*
* 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"
2019-04-17 20:08:42 +02:00
# include "kernel/utils.h"
2020-02-14 21:54:47 +01:00
# include "kernel/timinginfo.h"
2019-02-08 23:53:12 +01:00
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
void aiger_encode ( std : : ostream & f , int x )
{
log_assert ( x > = 0 ) ;
while ( x & ~ 0x7f ) {
f . put ( ( x & 0x7f ) | 0x80 ) ;
x = x > > 7 ;
}
f . put ( x ) ;
}
2019-02-12 00:18:42 +01:00
struct XAigerWriter
2019-02-08 23:53:12 +01:00
{
2020-05-14 03:02:05 +02:00
Design * design ;
2019-02-08 23:53:12 +01:00
Module * module ;
SigMap sigmap ;
2020-04-13 22:10:57 +02:00
dict < SigBit , State > init_map ;
2020-01-03 23:37:58 +01:00
pool < SigBit > input_bits , output_bits ;
2019-10-01 00:24:03 +02:00
dict < SigBit , SigBit > not_map , alias_map ;
2019-02-08 23:53:12 +01:00
dict < SigBit , pair < SigBit , SigBit > > and_map ;
2019-12-28 00:35:19 +01:00
vector < SigBit > ci_bits , co_bits ;
2020-05-25 16:17:48 +02:00
vector < Cell * > ff_list ;
2019-08-19 21:33:24 +02:00
dict < SigBit , float > arrival_times ;
2019-02-08 23:53:12 +01:00
vector < pair < int , int > > aig_gates ;
2025-02-14 00:21:39 +01:00
vector < SigBit > bit2aig_stack ;
int next_loop_check = 1024 ;
2019-10-01 00:24:03 +02:00
vector < int > aig_outputs ;
2019-02-08 23:53:12 +01:00
int aig_m = 0 , aig_i = 0 , aig_l = 0 , aig_o = 0 , aig_a = 0 ;
dict < SigBit , int > aig_map ;
dict < SigBit , int > ordered_outputs ;
2019-04-12 23:13:11 +02:00
vector < Cell * > box_list ;
2019-02-08 23:53:12 +01:00
int mkgate ( int a0 , int a1 )
{
aig_m + + , aig_a + + ;
aig_gates . push_back ( a0 > a1 ? make_pair ( a0 , a1 ) : make_pair ( a1 , a0 ) ) ;
return 2 * aig_m ;
}
int bit2aig ( SigBit bit )
{
2019-06-21 07:09:13 +02:00
auto it = aig_map . find ( bit ) ;
if ( it ! = aig_map . end ( ) ) {
log_assert ( it - > second > = 0 ) ;
return it - > second ;
}
2025-02-14 00:21:39 +01:00
if ( GetSize ( bit2aig_stack ) = = next_loop_check ) {
for ( int i = 0 ; i < next_loop_check ; + + i )
{
SigBit report_bit = bit2aig_stack [ i ] ;
if ( report_bit ! = bit )
continue ;
for ( int j = i ; j < next_loop_check ; + + j ) {
report_bit = bit2aig_stack [ j ] ;
if ( report_bit . is_wire ( ) & & report_bit . wire - > name . isPublic ( ) )
break ;
}
log_error ( " Found combinatorial logic loop while processing signal %s. \n " , log_signal ( report_bit ) ) ;
}
next_loop_check * = 2 ;
}
bit2aig_stack . push_back ( bit ) ;
2019-06-21 21:43:20 +02:00
// NB: Cannot use iterator returned from aig_map.insert()
// since this function is called recursively
2019-06-21 07:09:13 +02:00
int a = - 1 ;
if ( not_map . count ( bit ) ) {
a = bit2aig ( not_map . at ( bit ) ) ^ 1 ;
} else
if ( and_map . count ( bit ) ) {
auto args = and_map . at ( bit ) ;
int a0 = bit2aig ( args . first ) ;
int a1 = bit2aig ( args . second ) ;
a = mkgate ( a0 , a1 ) ;
} else
if ( alias_map . count ( bit ) ) {
a = bit2aig ( alias_map . at ( bit ) ) ;
}
2019-02-08 23:53:12 +01:00
2025-02-14 00:21:39 +01:00
bit2aig_stack . pop_back ( ) ;
2019-06-21 07:09:13 +02:00
if ( bit = = State : : Sx | | bit = = State : : Sz ) {
2019-06-21 21:46:55 +02:00
log_debug ( " Design contains 'x' or 'z' bits. Treating as 1'b0. \n " ) ;
2019-06-21 07:09:13 +02:00
a = aig_map . at ( State : : S0 ) ;
2019-02-08 23:53:12 +01:00
}
2019-06-21 07:09:13 +02:00
log_assert ( a > = 0 ) ;
aig_map [ bit ] = a ;
return a ;
2019-02-08 23:53:12 +01:00
}
2020-05-14 03:02:05 +02:00
XAigerWriter ( Module * module , bool dff_mode ) : design ( module - > design ) , module ( module ) , sigmap ( module )
2019-02-08 23:53:12 +01:00
{
pool < SigBit > undriven_bits ;
pool < SigBit > unused_bits ;
// promote public wires
for ( auto wire : module - > wires ( ) )
2020-09-14 12:43:18 +02:00
if ( wire - > name . isPublic ( ) )
2019-02-08 23:53:12 +01:00
sigmap . add ( wire ) ;
// promote input wires
for ( auto wire : module - > wires ( ) )
if ( wire - > port_input )
sigmap . add ( wire ) ;
2019-12-07 01:19:10 +01:00
// promote keep wires
2019-12-06 02:54:43 +01:00
for ( auto wire : module - > wires ( ) )
2021-03-30 07:01:57 +02:00
if ( wire - > get_bool_attribute ( ID : : keep ) )
2019-12-06 02:54:43 +01:00
sigmap . add ( wire ) ;
2020-04-13 22:10:57 +02:00
for ( auto wire : module - > wires ( ) ) {
auto it = wire - > attributes . find ( ID : : init ) ;
2019-02-08 23:53:12 +01:00
for ( int i = 0 ; i < GetSize ( wire ) ; i + + )
{
SigBit wirebit ( wire , i ) ;
SigBit bit = sigmap ( wirebit ) ;
2019-12-07 01:19:10 +01:00
if ( bit . wire = = nullptr ) {
if ( wire - > port_output ) {
aig_map [ wirebit ] = ( bit = = State : : S1 ) ? 1 : 0 ;
2019-12-31 03:24:29 +01:00
output_bits . insert ( wirebit ) ;
2019-12-07 01:19:10 +01:00
}
2019-12-06 02:54:43 +01:00
continue ;
}
2019-07-03 04:14:30 +02:00
2020-01-03 21:30:22 +01:00
undriven_bits . insert ( bit ) ;
unused_bits . insert ( bit ) ;
2021-03-30 07:01:57 +02:00
if ( wire - > port_input )
2019-12-07 01:19:10 +01:00
input_bits . insert ( bit ) ;
2019-02-08 23:53:12 +01:00
2021-03-30 07:01:57 +02:00
bool keep = wire - > get_bool_attribute ( ID : : keep ) ;
2020-05-15 01:44:35 +02:00
if ( wire - > port_output | | keep ) {
2019-12-07 01:19:10 +01:00
if ( bit ! = wirebit )
alias_map [ wirebit ] = bit ;
2019-12-31 03:24:29 +01:00
output_bits . insert ( wirebit ) ;
2019-02-08 23:53:12 +01:00
}
2020-04-13 22:10:57 +02:00
if ( it ! = wire - > attributes . end ( ) ) {
auto s = it - > second [ i ] ;
if ( s ! = State : : Sx ) {
auto r = init_map . insert ( std : : make_pair ( bit , it - > second [ i ] ) ) ;
if ( ! r . second & & r . first - > second ! = it - > second [ i ] )
log_error ( " Bit '%s' has a conflicting (* init *) value. \n " , log_signal ( bit ) ) ;
}
}
2019-12-31 18:59:17 +01:00
}
2020-04-13 22:10:57 +02:00
}
2019-11-27 21:35:25 +01:00
2020-02-14 21:54:47 +01:00
TimingInfo timing ;
2020-01-04 00:38:18 +01:00
for ( auto cell : module - > cells ( ) ) {
2026-06-12 00:18:53 +02:00
if ( cell - > type . in ( TW ( $ input_port ) , TW ( $ output_port ) , TW ( $ public ) ) )
2026-05-29 18:02:39 +02:00
continue ;
2020-01-14 04:21:11 +01:00
if ( ! cell - > has_keep_attr ( ) ) {
2026-06-12 00:18:53 +02:00
if ( cell - > type = = TW ( $ _NOT_ ) )
2020-01-14 04:21:11 +01:00
{
2026-06-10 19:22:53 +02:00
SigBit A = sigmap ( cell - > getPort ( TW : : A ) . as_bit ( ) ) ;
SigBit Y = sigmap ( cell - > getPort ( TW : : Y ) . as_bit ( ) ) ;
2020-01-14 04:21:11 +01:00
unused_bits . erase ( A ) ;
undriven_bits . erase ( Y ) ;
not_map [ Y ] = A ;
continue ;
}
2019-02-08 23:53:12 +01:00
2026-06-12 00:18:53 +02:00
if ( cell - > type = = TW ( $ _AND_ ) )
2020-01-14 04:21:11 +01:00
{
2026-06-10 19:22:53 +02:00
SigBit A = sigmap ( cell - > getPort ( TW : : A ) . as_bit ( ) ) ;
SigBit B = sigmap ( cell - > getPort ( TW : : B ) . as_bit ( ) ) ;
SigBit Y = sigmap ( cell - > getPort ( TW : : Y ) . as_bit ( ) ) ;
2020-01-14 04:21:11 +01:00
unused_bits . erase ( A ) ;
unused_bits . erase ( B ) ;
undriven_bits . erase ( Y ) ;
and_map [ Y ] = make_pair ( A , B ) ;
continue ;
}
2019-08-21 03:17:14 +02:00
2026-06-12 00:18:53 +02:00
if ( dff_mode & & cell - > type . in ( TW ( $ _DFF_N_ ) , TW ( $ _DFF_P_ ) ) & & ! cell - > get_bool_attribute ( ID : : abc9_keep ) )
2020-01-14 04:21:11 +01:00
{
2026-06-10 19:22:53 +02:00
SigBit D = sigmap ( cell - > getPort ( TW : : D ) . as_bit ( ) ) ;
SigBit Q = sigmap ( cell - > getPort ( TW : : Q ) . as_bit ( ) ) ;
2020-01-14 04:21:11 +01:00
unused_bits . erase ( D ) ;
undriven_bits . erase ( Q ) ;
alias_map [ Q ] = D ;
2020-05-25 16:17:48 +02:00
ff_list . emplace_back ( cell ) ;
2020-01-14 04:21:11 +01:00
continue ;
2020-01-03 23:37:58 +01:00
}
2026-06-12 00:18:53 +02:00
if ( cell - > type . in ( TW ( $ specify2 ) , TW ( $ specify3 ) , TW ( $ specrule ) ) )
2020-02-13 00:25:30 +01:00
continue ;
2020-02-13 20:15:59 +01:00
}
2020-02-13 00:25:30 +01:00
2020-05-14 03:02:05 +02:00
RTLIL : : Module * inst_module = design - > module ( cell - > type ) ;
if ( inst_module & & inst_module - > get_blackbox_attribute ( ) ) {
2020-02-13 20:15:59 +01:00
bool abc9_flop = false ;
2020-05-14 06:56:06 +02:00
auto it = cell - > attributes . find ( ID : : abc9_box_seq ) ;
if ( it ! = cell - > attributes . end ( ) ) {
log_assert ( ! cell - > has_keep_attr ( ) ) ;
2020-05-24 17:17:30 +02:00
log_assert ( cell - > parameters . empty ( ) ) ;
2020-05-14 06:56:06 +02:00
int abc9_box_seq = it - > second . as_int ( ) ;
if ( GetSize ( box_list ) < = abc9_box_seq )
box_list . resize ( abc9_box_seq + 1 ) ;
box_list [ abc9_box_seq ] = cell ;
// Only flop boxes may have arrival times
// (all others are combinatorial)
log_assert ( cell - > parameters . empty ( ) ) ;
abc9_flop = inst_module - > get_bool_attribute ( ID : : abc9_flop ) ;
if ( ! abc9_flop )
continue ;
2020-02-13 20:15:59 +01:00
}
2020-01-14 04:21:11 +01:00
2026-06-12 00:18:53 +02:00
auto inst_name_id = inst_module - > meta_ - > name ;
2026-06-11 13:17:54 +02:00
if ( ! timing . count ( inst_name_id ) )
2020-02-14 21:54:47 +01:00
timing . setup_module ( inst_module ) ;
2021-11-24 22:21:08 +01:00
2026-06-11 13:17:54 +02:00
for ( auto & i : timing . at ( inst_name_id ) . arrival ) {
2026-06-12 00:18:53 +02:00
auto port_name_ref = i . first . name ;
2026-06-11 13:17:54 +02:00
if ( ! cell - > hasPort ( port_name_ref ) )
2020-02-13 20:15:59 +01:00
continue ;
2020-01-14 21:57:56 +01:00
2026-06-11 13:17:54 +02:00
auto port_wire = inst_module - > wire ( port_name_ref ) ;
2021-11-24 22:21:08 +01:00
log_assert ( port_wire - > port_output ) ;
auto d = i . second . first ;
if ( d = = 0 )
continue ;
auto offset = i . first . offset ;
2020-01-14 22:05:39 +01:00
2026-06-11 13:17:54 +02:00
auto rhs = cell - > getPort ( port_name_ref ) ;
2023-04-23 01:24:36 +02:00
if ( offset > = rhs . size ( ) )
continue ;
2020-01-11 02:13:27 +01:00
# ifndef NDEBUG
2021-11-24 22:21:08 +01:00
if ( ys_debug ( 1 ) ) {
static pool < std : : pair < IdString , TimingInfo : : NameBit > > seen ;
2026-06-11 13:17:54 +02:00
if ( seen . emplace ( inst_name_id , i . first ) . second ) log ( " %s.%s[%d] abc9_arrival = %d \n " ,
2026-06-12 00:18:53 +02:00
cell - > type . unescape ( ) , design - > twines . unescaped_str ( i . first . name ) , offset , d ) ;
2019-12-27 20:30:18 +01:00
}
2021-11-24 22:21:08 +01:00
# endif
2023-04-23 01:24:36 +02:00
arrival_times [ rhs [ offset ] ] = d ;
2019-06-14 22:28:47 +02:00
}
2020-02-13 20:15:59 +01:00
if ( abc9_flop )
continue ;
2019-04-20 00:47:36 +02:00
}
2019-07-03 04:14:30 +02:00
2019-09-28 03:41:43 +02:00
bool cell_known = inst_module | | cell - > known ( ) ;
for ( const auto & c : cell - > connections ( ) ) {
if ( c . second . is_fully_const ( ) ) continue ;
auto port_wire = inst_module ? inst_module - > wire ( c . first ) : nullptr ;
auto is_input = ( port_wire & & port_wire - > port_input ) | | ! cell_known | | cell - > input ( c . first ) ;
auto is_output = ( port_wire & & port_wire - > port_output ) | | ! cell_known | | cell - > output ( c . first ) ;
if ( ! is_input & & ! is_output )
2026-06-11 13:17:54 +02:00
log_error ( " Connection '%s' on cell '%s' (type '%s') not recognised! \n " , RTLIL : : IdString ( design - > twines . str ( c . first ) ) . unescape ( ) , cell , cell - > type . unescape ( ) ) ;
2019-09-28 03:41:43 +02:00
2019-12-31 03:24:29 +01:00
if ( is_input )
2019-09-28 03:41:43 +02:00
for ( auto b : c . second ) {
Wire * w = b . wire ;
if ( ! w ) continue ;
2020-01-14 04:21:11 +01:00
// Do not add as PO if bit is already a PI
if ( input_bits . count ( b ) )
continue ;
2019-09-28 03:41:43 +02:00
if ( ! w - > port_output | | ! cell_known ) {
SigBit I = sigmap ( b ) ;
if ( I ! = b )
alias_map [ b ] = I ;
2019-12-31 03:24:29 +01:00
output_bits . insert ( b ) ;
2019-08-19 22:17:31 +02:00
}
2019-09-28 03:41:43 +02:00
}
2019-02-15 20:51:21 +01:00
}
2019-04-12 23:13:11 +02:00
2026-06-12 00:18:53 +02:00
//log_warning("Unsupported cell type: %s (%s)\n", cell->type.unescaped(), cell);
2019-02-08 23:53:12 +01:00
}
2026-06-11 13:17:54 +02:00
dict < IdString , std : : vector < TwineRef > > box_ports ;
2020-01-03 23:37:58 +01:00
for ( auto cell : box_list ) {
log_assert ( cell ) ;
2020-01-01 02:06:03 +01:00
2020-05-14 03:02:05 +02:00
RTLIL : : Module * box_module = design - > module ( cell - > type ) ;
2020-01-03 23:37:58 +01:00
log_assert ( box_module ) ;
2020-05-14 03:02:05 +02:00
log_assert ( box_module - > has_attribute ( ID : : abc9_box_id ) ) ;
2020-01-01 01:12:40 +01:00
2020-01-03 23:37:58 +01:00
auto r = box_ports . insert ( cell - > type ) ;
if ( r . second ) {
// Make carry in the last PI, and carry out the last PO
// since ABC requires it this way
2026-06-11 13:17:54 +02:00
TwineRef carry_in = Twine : : Null , carry_out = Twine : : Null ;
2020-01-03 23:37:58 +01:00
for ( const auto & port_name : box_module - > ports ) {
2020-01-01 01:12:40 +01:00
auto w = box_module - > wire ( port_name ) ;
2019-05-28 08:10:59 +02:00
log_assert ( w ) ;
2020-04-02 18:51:32 +02:00
if ( w - > get_bool_attribute ( ID : : abc9_carry ) ) {
2020-01-03 23:37:58 +01:00
if ( w - > port_input ) {
2026-06-11 13:17:54 +02:00
if ( carry_in ! = Twine : : Null )
2026-05-08 09:01:43 +02:00
log_error ( " Module '%s' contains more than one 'abc9_carry' input port. \n " , box_module ) ;
2020-01-03 23:37:58 +01:00
carry_in = port_name ;
2019-05-28 08:10:59 +02:00
}
2020-01-03 23:37:58 +01:00
if ( w - > port_output ) {
2026-06-11 13:17:54 +02:00
if ( carry_out ! = Twine : : Null )
2026-05-08 09:01:43 +02:00
log_error ( " Module '%s' contains more than one 'abc9_carry' output port. \n " , box_module ) ;
2020-01-03 23:37:58 +01:00
carry_out = port_name ;
2019-05-22 01:19:23 +02:00
}
}
2020-01-03 23:37:58 +01:00
else
r . first - > second . push_back ( port_name ) ;
}
2019-05-28 08:10:59 +02:00
2026-06-11 13:17:54 +02:00
if ( carry_in ! = Twine : : Null & & carry_out = = Twine : : Null )
2026-05-08 09:01:43 +02:00
log_error ( " Module '%s' contains an 'abc9_carry' input port but no output port. \n " , box_module ) ;
2026-06-11 13:17:54 +02:00
if ( carry_in = = Twine : : Null & & carry_out ! = Twine : : Null )
2026-05-08 09:01:43 +02:00
log_error ( " Module '%s' contains an 'abc9_carry' output port but no input port. \n " , box_module ) ;
2026-06-11 13:17:54 +02:00
if ( carry_in ! = Twine : : Null ) {
2020-01-03 23:37:58 +01:00
r . first - > second . push_back ( carry_in ) ;
r . first - > second . push_back ( carry_out ) ;
2019-04-17 20:08:42 +02:00
}
2020-01-03 23:37:58 +01:00
}
2019-09-29 08:48:17 +02:00
2020-01-03 23:37:58 +01:00
for ( auto port_name : r . first - > second ) {
auto w = box_module - > wire ( port_name ) ;
log_assert ( w ) ;
2020-01-14 21:25:45 +01:00
auto rhs = cell - > connections_ . at ( port_name , SigSpec ( ) ) ;
rhs . append ( Const ( State : : Sx , GetSize ( w ) - GetSize ( rhs ) ) ) ;
2020-01-06 22:34:45 +01:00
if ( w - > port_input )
2019-10-01 01:33:40 +02:00
for ( auto b : rhs ) {
2019-09-29 08:48:17 +02:00
SigBit I = sigmap ( b ) ;
if ( b = = RTLIL : : Sx )
b = State : : S0 ;
else if ( I ! = b ) {
if ( I = = RTLIL : : Sx )
alias_map [ b ] = State : : S0 ;
else
alias_map [ b ] = I ;
}
2019-12-30 23:31:42 +01:00
co_bits . emplace_back ( b ) ;
2019-12-06 02:54:43 +01:00
unused_bits . erase ( I ) ;
2019-09-29 08:48:17 +02:00
}
2020-01-06 22:34:45 +01:00
if ( w - > port_output )
2020-01-14 23:27:29 +01:00
for ( const auto & b : rhs ) {
2020-01-03 23:37:58 +01:00
SigBit O = sigmap ( b ) ;
if ( O ! = b )
alias_map [ O ] = b ;
ci_bits . emplace_back ( b ) ;
undriven_bits . erase ( O ) ;
}
2019-04-17 20:08:42 +02:00
}
}
2019-12-07 01:19:10 +01:00
for ( auto bit : input_bits )
2019-12-31 03:24:29 +01:00
undriven_bits . erase ( bit ) ;
2019-12-07 01:19:10 +01:00
for ( auto bit : output_bits )
unused_bits . erase ( sigmap ( bit ) ) ;
2019-02-08 23:53:12 +01:00
for ( auto bit : unused_bits )
2019-11-22 22:24:28 +01:00
undriven_bits . erase ( bit ) ;
2019-05-26 23:14:13 +02:00
2019-12-31 08:29:14 +01:00
// Make all undriven bits a primary input
2019-12-31 18:59:17 +01:00
for ( auto bit : undriven_bits ) {
input_bits . insert ( bit ) ;
undriven_bits . erase ( bit ) ;
2019-05-26 23:14:13 +02:00
}
2019-02-08 23:53:12 +01:00
2020-04-16 01:16:30 +02:00
struct sort_by_port_id {
bool operator ( ) ( const RTLIL : : SigBit & a , const RTLIL : : SigBit & b ) const {
return a . wire - > port_id < b . wire - > port_id | |
( a . wire - > port_id = = b . wire - > port_id & & a . offset < b . offset ) ;
}
} ;
input_bits . sort ( sort_by_port_id ( ) ) ;
output_bits . sort ( sort_by_port_id ( ) ) ;
2020-01-03 21:30:22 +01:00
2019-02-08 23:53:12 +01:00
aig_map [ State : : S0 ] = 0 ;
aig_map [ State : : S1 ] = 1 ;
2020-01-03 21:30:22 +01:00
for ( const auto & bit : input_bits ) {
2019-02-15 20:51:21 +01:00
aig_m + + , aig_i + + ;
2019-05-31 01:03:22 +02:00
log_assert ( ! aig_map . count ( bit ) ) ;
2019-04-17 01:37:47 +02:00
aig_map [ bit ] = 2 * aig_m ;
2019-02-15 20:51:21 +01:00
}
2020-05-25 16:17:48 +02:00
for ( auto cell : ff_list ) {
2026-06-10 19:22:53 +02:00
const SigBit & q = sigmap ( cell - > getPort ( TW : : Q ) ) ;
2019-08-21 03:17:14 +02:00
aig_m + + , aig_i + + ;
2019-12-31 19:21:11 +01:00
log_assert ( ! aig_map . count ( q ) ) ;
aig_map [ q ] = 2 * aig_m ;
2019-08-21 03:17:14 +02:00
}
2019-12-30 23:31:42 +01:00
for ( auto & bit : ci_bits ) {
2019-02-08 23:53:12 +01:00
aig_m + + , aig_i + + ;
2020-01-14 21:25:45 +01:00
// 1'bx may exist here due to a box output
// that has been padded to its full width
if ( bit = = State : : Sx )
continue ;
2021-03-10 20:31:55 +01:00
if ( aig_map . count ( bit ) )
2021-03-30 07:01:57 +02:00
log_error ( " Visited AIG node more than once; this could be a combinatorial loop that has not been broken \n " ) ;
2020-01-06 22:34:45 +01:00
aig_map [ bit ] = 2 * aig_m ;
2019-02-08 23:53:12 +01:00
}
2019-12-28 00:35:19 +01:00
for ( auto bit : co_bits ) {
ordered_outputs [ bit ] = aig_o + + ;
2019-02-15 20:51:21 +01:00
aig_outputs . push_back ( bit2aig ( bit ) ) ;
}
2020-01-03 21:30:22 +01:00
for ( const auto & bit : output_bits ) {
2019-04-13 01:17:48 +02:00
ordered_outputs [ bit ] = aig_o + + ;
2020-01-14 04:07:55 +01:00
int aig ;
2020-03-06 19:51:47 +01:00
// Unlike bit2aig() which checks aig_map first for
// inout/scc bits, since aig_map will point to
2020-01-14 06:45:27 +01:00
// the PI, first attempt to find the NOT/AND driver
// before resorting to an aig_map lookup (which
// could be another PO)
2020-01-14 04:07:55 +01:00
if ( input_bits . count ( bit ) ) {
2020-01-14 06:45:27 +01:00
if ( not_map . count ( bit ) ) {
aig = bit2aig ( not_map . at ( bit ) ) ^ 1 ;
} else if ( and_map . count ( bit ) ) {
auto args = and_map . at ( bit ) ;
int a0 = bit2aig ( args . first ) ;
int a1 = bit2aig ( args . second ) ;
aig = mkgate ( a0 , a1 ) ;
}
else
aig = aig_map . at ( bit ) ;
2020-01-14 04:07:55 +01:00
}
else
aig = bit2aig ( bit ) ;
aig_outputs . push_back ( aig ) ;
2019-02-08 23:53:12 +01:00
}
2020-05-25 16:17:48 +02:00
for ( auto cell : ff_list ) {
2026-06-10 19:22:53 +02:00
const SigBit & d = sigmap ( cell - > getPort ( TW : : D ) ) ;
2019-08-21 03:17:14 +02:00
aig_o + + ;
2019-12-31 19:21:11 +01:00
aig_outputs . push_back ( aig_map . at ( d ) ) ;
2019-08-21 03:17:14 +02:00
}
2019-02-08 23:53:12 +01:00
}
2019-06-12 19:00:57 +02:00
void write_aiger ( std : : ostream & f , bool ascii_mode )
2019-02-08 23:53:12 +01:00
{
2019-02-14 23:52:47 +01:00
int aig_obc = aig_o ;
int aig_obcj = aig_obc ;
int aig_obcjf = aig_obcj ;
2019-02-08 23:53:12 +01:00
log_assert ( aig_m = = aig_i + aig_l + aig_a ) ;
log_assert ( aig_obcjf = = GetSize ( aig_outputs ) ) ;
2019-02-14 23:52:47 +01:00
f < < stringf ( " %s %d %d %d %d %d " , ascii_mode ? " aag " : " aig " , aig_m , aig_i , aig_l , aig_o , aig_a ) ;
f < < stringf ( " \n " ) ;
2019-02-08 23:53:12 +01:00
if ( ascii_mode )
{
for ( int i = 0 ; i < aig_i ; i + + )
f < < stringf ( " %d \n " , 2 * i + 2 ) ;
for ( int i = 0 ; i < aig_obc ; i + + )
f < < stringf ( " %d \n " , aig_outputs . at ( i ) ) ;
for ( int i = aig_obc ; i < aig_obcj ; i + + )
f < < stringf ( " 1 \n " ) ;
for ( int i = aig_obc ; i < aig_obcj ; i + + )
f < < stringf ( " %d \n " , aig_outputs . at ( i ) ) ;
for ( int i = aig_obcj ; i < aig_obcjf ; i + + )
f < < stringf ( " %d \n " , aig_outputs . at ( i ) ) ;
for ( int i = 0 ; i < aig_a ; i + + )
f < < stringf ( " %d %d %d \n " , 2 * ( aig_i + aig_l + i ) + 2 , aig_gates . at ( i ) . first , aig_gates . at ( i ) . second ) ;
}
else
{
for ( int i = 0 ; i < aig_obc ; i + + )
f < < stringf ( " %d \n " , aig_outputs . at ( i ) ) ;
for ( int i = aig_obc ; i < aig_obcj ; i + + )
f < < stringf ( " 1 \n " ) ;
for ( int i = aig_obc ; i < aig_obcj ; i + + )
f < < stringf ( " %d \n " , aig_outputs . at ( i ) ) ;
for ( int i = aig_obcj ; i < aig_obcjf ; i + + )
f < < stringf ( " %d \n " , aig_outputs . at ( i ) ) ;
for ( int i = 0 ; i < aig_a ; i + + ) {
int lhs = 2 * ( aig_i + aig_l + i ) + 2 ;
int rhs0 = aig_gates . at ( i ) . first ;
int rhs1 = aig_gates . at ( i ) . second ;
int delta0 = lhs - rhs0 ;
int delta1 = rhs0 - rhs1 ;
aiger_encode ( f , delta0 ) ;
aiger_encode ( f , delta1 ) ;
}
}
2019-04-12 23:13:11 +02:00
f < < " c " ;
2024-09-03 08:53:51 +02:00
auto write_buffer = [ ] ( std : : ostream & buffer , unsigned int u32 ) {
typedef unsigned char uchar ;
unsigned char u32_be [ 4 ] = {
( uchar ) ( u32 > > 24 ) , ( uchar ) ( u32 > > 16 ) , ( uchar ) ( u32 > > 8 ) , ( uchar ) u32
} ;
buffer . write ( ( char * ) u32_be , sizeof ( u32_be ) ) ;
2019-08-19 22:17:31 +02:00
} ;
std : : stringstream h_buffer ;
auto write_h_buffer = std : : bind ( write_buffer , std : : ref ( h_buffer ) , std : : placeholders : : _1 ) ;
write_h_buffer ( 1 ) ;
2020-05-25 16:17:48 +02:00
log_debug ( " ciNum = %d \n " , GetSize ( input_bits ) + GetSize ( ff_list ) + GetSize ( ci_bits ) ) ;
write_h_buffer ( GetSize ( input_bits ) + GetSize ( ff_list ) + GetSize ( ci_bits ) ) ;
log_debug ( " coNum = %d \n " , GetSize ( output_bits ) + GetSize ( ff_list ) + GetSize ( co_bits ) ) ;
write_h_buffer ( GetSize ( output_bits ) + GetSize ( ff_list ) + GetSize ( co_bits ) ) ;
log_debug ( " piNum = %d \n " , GetSize ( input_bits ) + GetSize ( ff_list ) ) ;
write_h_buffer ( GetSize ( input_bits ) + GetSize ( ff_list ) ) ;
log_debug ( " poNum = %d \n " , GetSize ( output_bits ) + GetSize ( ff_list ) ) ;
write_h_buffer ( GetSize ( output_bits ) + GetSize ( ff_list ) ) ;
2019-08-19 22:17:31 +02:00
log_debug ( " boxNum = %d \n " , GetSize ( box_list ) ) ;
2020-05-25 16:17:48 +02:00
write_h_buffer ( GetSize ( box_list ) ) ;
2019-08-19 22:17:31 +02:00
auto write_buffer_float = [ ] ( std : : stringstream & buffer , float f32 ) {
buffer . write ( reinterpret_cast < const char * > ( & f32 ) , sizeof ( f32 ) ) ;
} ;
std : : stringstream i_buffer ;
auto write_i_buffer = std : : bind ( write_buffer_float , std : : ref ( i_buffer ) , std : : placeholders : : _1 ) ;
for ( auto bit : input_bits )
write_i_buffer ( arrival_times . at ( bit , 0 ) ) ;
//std::stringstream o_buffer;
//auto write_o_buffer = std::bind(write_buffer_float, std::ref(o_buffer), std::placeholders::_1);
//for (auto bit : output_bits)
// write_o_buffer(0);
2019-04-17 00:01:45 +02:00
2020-05-25 16:17:48 +02:00
if ( ! box_list . empty ( ) | | ! ff_list . empty ( ) ) {
2020-01-12 02:26:25 +01:00
dict < IdString , std : : tuple < int , int , int > > cell_cache ;
2020-01-03 22:08:52 +01:00
2019-05-28 08:10:59 +02:00
int box_count = 0 ;
2019-04-17 00:01:45 +02:00
for ( auto cell : box_list ) {
2020-01-04 18:17:01 +01:00
log_assert ( cell ) ;
2020-05-14 06:56:06 +02:00
log_assert ( cell - > parameters . empty ( ) ) ;
2020-01-04 18:17:01 +01:00
2020-05-14 06:56:06 +02:00
auto r = cell_cache . insert ( cell - > type ) ;
2020-01-09 03:27:09 +01:00
auto & v = r . first - > second ;
if ( r . second ) {
2020-05-14 06:56:06 +02:00
RTLIL : : Module * box_module = design - > module ( cell - > type ) ;
log_assert ( box_module ) ;
2020-01-09 03:27:09 +01:00
int box_inputs = 0 , box_outputs = 0 ;
2020-05-14 06:56:06 +02:00
for ( auto port_name : box_module - > ports ) {
RTLIL : : Wire * w = box_module - > wire ( port_name ) ;
2020-01-12 02:26:25 +01:00
log_assert ( w ) ;
if ( w - > port_input )
box_inputs + = GetSize ( w ) ;
if ( w - > port_output )
box_outputs + = GetSize ( w ) ;
2019-04-17 00:01:45 +02:00
}
2019-05-22 01:19:23 +02:00
2020-01-12 02:26:25 +01:00
std : : get < 0 > ( v ) = box_inputs ;
std : : get < 1 > ( v ) = box_outputs ;
2020-05-14 06:56:06 +02:00
std : : get < 2 > ( v ) = box_module - > attributes . at ( ID : : abc9_box_id ) . as_int ( ) ;
2019-10-01 02:02:20 +02:00
}
2020-01-12 02:26:25 +01:00
write_h_buffer ( std : : get < 0 > ( v ) ) ;
2020-01-09 03:27:09 +01:00
write_h_buffer ( std : : get < 1 > ( v ) ) ;
write_h_buffer ( std : : get < 2 > ( v ) ) ;
2019-05-28 08:10:59 +02:00
write_h_buffer ( box_count + + ) ;
2019-04-13 03:16:25 +02:00
}
2019-04-17 00:01:45 +02:00
2019-06-15 18:07:03 +02:00
std : : stringstream r_buffer ;
auto write_r_buffer = std : : bind ( write_buffer , std : : ref ( r_buffer ) , std : : placeholders : : _1 ) ;
2020-05-25 16:17:48 +02:00
log_debug ( " flopNum = %d \n " , GetSize ( ff_list ) ) ;
write_r_buffer ( ff_list . size ( ) ) ;
2019-12-04 03:47:44 +01:00
std : : stringstream s_buffer ;
auto write_s_buffer = std : : bind ( write_buffer , std : : ref ( s_buffer ) , std : : placeholders : : _1 ) ;
2020-05-25 16:17:48 +02:00
write_s_buffer ( ff_list . size ( ) ) ;
2019-12-04 03:47:44 +01:00
2020-04-16 23:02:42 +02:00
dict < SigSpec , int > clk_to_mergeability ;
2020-05-25 16:17:48 +02:00
for ( const auto cell : ff_list ) {
2026-06-10 19:22:53 +02:00
const SigBit & d = sigmap ( cell - > getPort ( TW : : D ) ) ;
const SigBit & q = sigmap ( cell - > getPort ( TW : : Q ) ) ;
2020-01-02 21:36:54 +01:00
2026-06-10 19:22:53 +02:00
SigSpec clk_and_pol { sigmap ( cell - > getPort ( TW : : C ) ) , cell - > type [ 6 ] = = ' P ' ? State : : S1 : State : : S0 } ;
2020-04-16 23:02:42 +02:00
auto r = clk_to_mergeability . insert ( std : : make_pair ( clk_and_pol , clk_to_mergeability . size ( ) + 1 ) ) ;
int mergeability = r . first - > second ;
2019-12-04 03:47:44 +01:00
log_assert ( mergeability > 0 ) ;
2020-04-16 23:02:42 +02:00
write_r_buffer ( mergeability ) ;
2020-01-02 21:36:54 +01:00
2020-05-25 16:17:48 +02:00
State init = init_map . at ( q , State : : Sx ) ;
2026-05-08 09:01:43 +02:00
log_debug ( " Cell '%s' (type %s) has (* init *) value '%s'. \n " , cell , cell - > type . unescape ( ) , log_signal ( init ) ) ;
2020-04-13 18:38:07 +02:00
if ( init = = State : : S1 )
2020-01-02 21:36:54 +01:00
write_s_buffer ( 1 ) ;
else if ( init = = State : : S0 )
write_s_buffer ( 0 ) ;
else {
log_assert ( init = = State : : Sx ) ;
2020-04-14 01:20:15 +02:00
write_s_buffer ( 2 ) ;
2020-01-02 21:36:54 +01:00
}
2020-02-14 22:43:34 +01:00
// Use arrival time from output of flop box
2020-02-15 01:08:04 +01:00
write_i_buffer ( arrival_times . at ( d , 0 ) ) ;
2019-08-21 03:17:14 +02:00
//write_o_buffer(0);
}
2019-06-15 18:07:03 +02:00
f < < " r " ;
2019-08-19 22:17:31 +02:00
std : : string buffer_str = r_buffer . str ( ) ;
2024-09-03 08:53:51 +02:00
write_buffer ( f , buffer_str . size ( ) ) ;
2019-08-19 21:33:24 +02:00
f . write ( buffer_str . data ( ) , buffer_str . size ( ) ) ;
2019-08-21 03:17:14 +02:00
f < < " s " ;
buffer_str = s_buffer . str ( ) ;
2024-09-03 08:53:51 +02:00
write_buffer ( f , buffer_str . size ( ) ) ;
2019-08-21 03:17:14 +02:00
f . write ( buffer_str . data ( ) , buffer_str . size ( ) ) ;
2020-04-16 01:13:57 +02:00
RTLIL : : Design * holes_design ;
auto it = saved_designs . find ( " $abc9_holes " ) ;
if ( it ! = saved_designs . end ( ) )
holes_design = it - > second ;
else
holes_design = nullptr ;
2026-06-11 13:17:54 +02:00
RTLIL : : Module * holes_module = holes_design ? holes_design - > module ( module - > meta_ - > name ) : nullptr ;
2020-01-03 22:08:52 +01:00
if ( holes_module ) {
std : : stringstream a_buffer ;
2020-04-16 01:16:30 +02:00
XAigerWriter writer ( holes_module , false /* dff_mode */ ) ;
2020-01-03 22:08:52 +01:00
writer . write_aiger ( a_buffer , false /*ascii_mode*/ ) ;
f < < " a " ;
std : : string buffer_str = a_buffer . str ( ) ;
2024-09-03 08:53:51 +02:00
write_buffer ( f , buffer_str . size ( ) ) ;
2020-01-03 22:08:52 +01:00
f . write ( buffer_str . data ( ) , buffer_str . size ( ) ) ;
}
2019-04-17 00:01:45 +02:00
}
2019-04-12 23:13:11 +02:00
2019-08-19 22:17:31 +02:00
f < < " h " ;
std : : string buffer_str = h_buffer . str ( ) ;
2024-09-03 08:53:51 +02:00
write_buffer ( f , buffer_str . size ( ) ) ;
2019-08-19 22:17:31 +02:00
f . write ( buffer_str . data ( ) , buffer_str . size ( ) ) ;
f < < " i " ;
buffer_str = i_buffer . str ( ) ;
2024-09-03 08:53:51 +02:00
write_buffer ( f , buffer_str . size ( ) ) ;
2019-08-19 22:17:31 +02:00
f . write ( buffer_str . data ( ) , buffer_str . size ( ) ) ;
//f << "o";
//buffer_str = o_buffer.str();
//buffer_size_be = to_big_endian(buffer_str.size());
//f.write(reinterpret_cast<const char*>(&buffer_size_be), sizeof(buffer_size_be));
//f.write(buffer_str.data(), buffer_str.size());
2025-05-07 11:34:23 +02:00
f < < stringf ( " Generated by %s \n " , yosys_maybe_version ( ) ) ;
2019-12-31 00:35:33 +01:00
2020-05-14 03:02:05 +02:00
design - > scratchpad_set_int ( " write_xaiger.num_ands " , and_map . size ( ) ) ;
design - > scratchpad_set_int ( " write_xaiger.num_wires " , aig_map . size ( ) ) ;
design - > scratchpad_set_int ( " write_xaiger.num_inputs " , input_bits . size ( ) ) ;
design - > scratchpad_set_int ( " write_xaiger.num_outputs " , output_bits . size ( ) ) ;
2019-02-08 23:53:12 +01:00
}
2020-01-06 19:23:04 +01:00
void write_map ( std : : ostream & f )
2019-02-08 23:53:12 +01:00
{
dict < int , string > input_lines ;
dict < int , string > output_lines ;
for ( auto wire : module - > wires ( ) )
{
for ( int i = 0 ; i < GetSize ( wire ) ; i + + )
{
2019-02-17 07:22:17 +01:00
RTLIL : : SigBit b ( wire , i ) ;
2019-04-13 01:17:48 +02:00
if ( input_bits . count ( b ) ) {
2019-04-24 01:11:14 +02:00
int a = aig_map . at ( b ) ;
2019-02-08 23:53:12 +01:00
log_assert ( ( a & 1 ) = = 0 ) ;
2026-05-08 09:01:43 +02:00
input_lines [ a ] + = stringf ( " input %d %d %s \n " , ( a > > 1 ) - 1 , wire - > start_offset + i , wire ) ;
2019-02-08 23:53:12 +01:00
}
2019-04-13 01:17:48 +02:00
if ( output_bits . count ( b ) ) {
2019-02-17 07:22:17 +01:00
int o = ordered_outputs . at ( b ) ;
2026-05-08 09:01:43 +02:00
output_lines [ o ] + = stringf ( " output %d %d %s \n " , o - GetSize ( co_bits ) , wire - > start_offset + i , wire ) ;
2019-02-08 23:53:12 +01:00
}
}
}
input_lines . sort ( ) ;
for ( auto & it : input_lines )
f < < it . second ;
2019-05-28 08:10:59 +02:00
log_assert ( input_lines . size ( ) = = input_bits . size ( ) ) ;
2019-02-08 23:53:12 +01:00
2019-05-28 08:10:59 +02:00
int box_count = 0 ;
for ( auto cell : box_list )
2026-06-11 13:17:54 +02:00
f < < stringf ( " box %d %d %s \n " , box_count + + , 0 , cell - > module - > design - > twines . str ( cell - > meta_ - > name ) ) ;
2019-05-28 08:10:59 +02:00
2019-02-08 23:53:12 +01:00
output_lines . sort ( ) ;
for ( auto & it : output_lines )
f < < it . second ;
2019-05-28 08:10:59 +02:00
log_assert ( output_lines . size ( ) = = output_bits . size ( ) ) ;
2019-02-08 23:53:12 +01:00
}
} ;
2019-02-12 00:18:42 +01:00
struct XAigerBackend : public Backend {
XAigerBackend ( ) : Backend ( " xaiger " , " write design to XAIGER file " ) { }
2020-06-19 01:34:52 +02:00
void help ( ) override
2019-02-08 23:53:12 +01:00
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log ( " \n " ) ;
2019-02-12 00:18:42 +01:00
log ( " write_xaiger [options] [filename] \n " ) ;
2019-02-08 23:53:12 +01:00
log ( " \n " ) ;
2020-01-06 19:23:04 +01:00
log ( " Write the top module (according to the (* top *) attribute or if only one module \n " ) ;
2020-04-14 17:03:58 +02:00
log ( " is currently selected) to an XAIGER file. Any non $_NOT_, $_AND_, (optionally \n " ) ;
log ( " $_DFF_N_, $_DFF_P_), or non (* abc9_box *) cells will be converted into psuedo- \n " ) ;
2020-04-16 01:13:57 +02:00
log ( " inputs and pseudo-outputs. Whitebox contents will be taken from the equivalent \n " ) ;
log ( " module in the '$abc9_holes' design, if it exists. \n " ) ;
2019-02-08 23:53:12 +01:00
log ( " \n " ) ;
log ( " -ascii \n " ) ;
2019-04-19 02:43:13 +02:00
log ( " write ASCII version of AIGER format \n " ) ;
2019-02-08 23:53:12 +01:00
log ( " \n " ) ;
log ( " -map <filename> \n " ) ;
2019-10-07 22:09:13 +02:00
log ( " write an extra file with port and box symbols \n " ) ;
2019-02-08 23:53:12 +01:00
log ( " \n " ) ;
2020-04-09 23:26:52 +02:00
log ( " -dff \n " ) ;
log ( " write $_DFF_[NP]_ cells \n " ) ;
log ( " \n " ) ;
2019-02-08 23:53:12 +01:00
}
2020-06-19 01:34:52 +02:00
void execute ( std : : ostream * & f , std : : string filename , std : : vector < std : : string > args , RTLIL : : Design * design ) override
2019-02-08 23:53:12 +01:00
{
2020-04-09 23:26:52 +02:00
bool ascii_mode = false , dff_mode = false ;
2019-02-08 23:53:12 +01:00
std : : string map_filename ;
2019-02-12 00:18:42 +01:00
log_header ( design , " Executing XAIGER backend. \n " ) ;
2019-02-08 23:53:12 +01:00
size_t argidx ;
for ( argidx = 1 ; argidx < args . size ( ) ; argidx + + )
{
if ( args [ argidx ] = = " -ascii " ) {
ascii_mode = true ;
continue ;
}
if ( map_filename . empty ( ) & & args [ argidx ] = = " -map " & & argidx + 1 < args . size ( ) ) {
map_filename = args [ + + argidx ] ;
continue ;
}
2020-04-09 23:26:52 +02:00
if ( args [ argidx ] = = " -dff " ) {
dff_mode = true ;
continue ;
}
2019-02-08 23:53:12 +01:00
break ;
}
2019-09-28 09:28:51 +02:00
extra_args ( f , filename , args , argidx , ! ascii_mode ) ;
2019-02-08 23:53:12 +01:00
Module * top_module = design - > top_module ( ) ;
if ( top_module = = nullptr )
log_error ( " Can't find top module in current design! \n " ) ;
2020-01-06 19:23:04 +01:00
if ( ! design - > selected_whole_module ( top_module ) )
2026-05-08 09:01:43 +02:00
log_cmd_error ( " Can't handle partially selected module %s! \n " , top_module ) ;
2020-01-06 19:23:04 +01:00
if ( ! top_module - > processes . empty ( ) )
2026-05-08 09:01:43 +02:00
log_error ( " Found unmapped processes in module %s: unmapped processes are not supported in XAIGER backend! \n " , top_module ) ;
2020-01-06 19:23:04 +01:00
if ( ! top_module - > memories . empty ( ) )
2026-05-08 09:01:43 +02:00
log_error ( " Found unmapped memories in module %s: unmapped memories are not supported in XAIGER backend! \n " , top_module ) ;
2020-01-06 19:23:04 +01:00
2020-04-09 23:26:52 +02:00
XAigerWriter writer ( top_module , dff_mode ) ;
2019-06-12 19:00:57 +02:00
writer . write_aiger ( * f , ascii_mode ) ;
2019-02-08 23:53:12 +01:00
if ( ! map_filename . empty ( ) ) {
std : : ofstream mapf ;
mapf . open ( map_filename . c_str ( ) , std : : ofstream : : trunc ) ;
if ( mapf . fail ( ) )
2025-09-11 07:25:26 +02:00
log_error ( " Can't open file `%s' for writing: %s \n " , map_filename , strerror ( errno ) ) ;
2020-01-06 19:23:04 +01:00
writer . write_map ( mapf ) ;
2019-02-08 23:53:12 +01:00
}
}
2019-02-12 00:18:42 +01:00
} XAigerBackend ;
2019-02-08 23:53:12 +01:00
PRIVATE_NAMESPACE_END