Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
A
abc
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
lvzhengyang
abc
Commits
40cbacaf
Commit
40cbacaf
authored
Jan 26, 2015
by
Alan Mishchenko
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Several ongoing changes.
parent
65cd556b
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
156 additions
and
19 deletions
+156
-19
src/aig/gia/giaScript.c
+1
-1
src/base/cba/cba.h
+35
-16
src/base/cba/cbaBuild.c
+1
-1
src/base/cba/cbaNtk.c
+94
-1
src/base/cba/cbaPrs.h
+25
-0
No files found.
src/aig/gia/giaScript.c
View file @
40cbacaf
...
@@ -414,7 +414,7 @@ Gia_Man_t * Gia_ManAigSynch2( Gia_Man_t * pInit, void * pPars0, int nLutSize, in
...
@@ -414,7 +414,7 @@ Gia_Man_t * Gia_ManAigSynch2( Gia_Man_t * pInit, void * pPars0, int nLutSize, in
Gia_ManStop
(
pTemp
);
Gia_ManStop
(
pTemp
);
}
}
// perform balancing
// perform balancing
if
(
Gia_ManBufNum
(
pGia1
)
)
if
(
Gia_ManBufNum
(
pGia1
)
||
1
)
pGia2
=
Gia_ManAreaBalance
(
pGia1
,
0
,
ABC_INFINITY
,
0
,
0
);
pGia2
=
Gia_ManAreaBalance
(
pGia1
,
0
,
ABC_INFINITY
,
0
,
0
);
else
else
{
{
...
...
src/base/cba/cba.h
View file @
40cbacaf
...
@@ -49,12 +49,11 @@ typedef enum {
...
@@ -49,12 +49,11 @@ typedef enum {
CBA_OBJ_BO
,
// 2: box output
CBA_OBJ_BO
,
// 2: box output
CBA_OBJ_PI
,
// 3: input
CBA_OBJ_PI
,
// 3: input
CBA_OBJ_PO
,
// 4: output
CBA_OBJ_PO
,
// 4: output
CBA_OBJ_PIO
,
// 5: ioput
CBA_OBJ_NODE
,
// 5: node
CBA_OBJ_NODE
,
// 6: node
CBA_OBJ_BOX
,
// 6: box
CBA_OBJ_BOX
,
// 7: box
CBA_OBJ_LATCH
,
// 7: latch
CBA_OBJ_LATCH
,
// 8: latch
CBA_OBJ_CONCAT
,
// 8: concatenation
CBA_OBJ_CONCAT
,
// 9: concatenation
CBA_OBJ_UNKNOWN
// 9: unknown
CBA_OBJ_UNKNOWN
// 10: unknown
}
Cba_ObjType_t
;
}
Cba_ObjType_t
;
// Verilog predefined models
// Verilog predefined models
...
@@ -62,6 +61,8 @@ typedef enum {
...
@@ -62,6 +61,8 @@ typedef enum {
CBA_NODE_NONE
=
0
,
// 0: unused
CBA_NODE_NONE
=
0
,
// 0: unused
CBA_NODE_C0
,
// 1: constant 0
CBA_NODE_C0
,
// 1: constant 0
CBA_NODE_C1
,
// 2: constant 1
CBA_NODE_C1
,
// 2: constant 1
CBA_NODE_CX
,
// 2: constant X
CBA_NODE_CZ
,
// 2: constant Z
CBA_NODE_BUF
,
// 3: buffer
CBA_NODE_BUF
,
// 3: buffer
CBA_NODE_INV
,
// 4: inverter
CBA_NODE_INV
,
// 4: inverter
CBA_NODE_AND
,
// 5: AND
CBA_NODE_AND
,
// 5: AND
...
@@ -88,7 +89,6 @@ typedef enum {
...
@@ -88,7 +89,6 @@ typedef enum {
CBA_NODE_PENC
,
CBA_NODE_PENC
,
CBA_NODE_DEC
,
CBA_NODE_DEC
,
CBA_NODE_C
,
CBA_NODE_ADD
,
CBA_NODE_ADD
,
CBA_NODE_SUB
,
CBA_NODE_SUB
,
CBA_NODE_MUL
,
CBA_NODE_MUL
,
...
@@ -169,8 +169,6 @@ struct Cba_Ntk_t_
...
@@ -169,8 +169,6 @@ struct Cba_Ntk_t_
// attributes
// attributes
Vec_Int_t
vInstIds
;
// instance names (used by parser to store instance name as NameId)
Vec_Int_t
vInstIds
;
// instance names (used by parser to store instance name as NameId)
Vec_Int_t
vNameIds
;
// original names as NameId
Vec_Int_t
vNameIds
;
// original names as NameId
Vec_Int_t
vRangesL
;
// ranges as NameId
Vec_Int_t
vRangesR
;
// ranges as NameId
};
};
...
@@ -210,15 +208,12 @@ static inline void Cba_NtkSetHost( Cba_Ntk_t * p, int n, int i ) { p
...
@@ -210,15 +208,12 @@ static inline void Cba_NtkSetHost( Cba_Ntk_t * p, int n, int i ) { p
static
inline
Vec_Int_t
*
Cba_NtkCopies
(
Cba_Ntk_t
*
p
)
{
return
&
p
->
pDesign
->
vCopies
;
}
static
inline
Vec_Int_t
*
Cba_NtkCopies
(
Cba_Ntk_t
*
p
)
{
return
&
p
->
pDesign
->
vCopies
;
}
static
inline
int
Cba_NtkCopy
(
Cba_Ntk_t
*
p
,
int
i
)
{
return
Vec_IntEntry
(
Cba_NtkCopies
(
p
),
p
->
iObjStart
+
i
);
}
static
inline
int
Cba_NtkCopy
(
Cba_Ntk_t
*
p
,
int
i
)
{
return
Vec_IntEntry
(
Cba_NtkCopies
(
p
),
p
->
iObjStart
+
i
);
}
static
inline
void
Cba_NtkSetCopy
(
Cba_Ntk_t
*
p
,
int
i
,
int
x
)
{
Vec_IntWriteEntry
(
Cba_NtkCopies
(
p
),
p
->
iObjStart
+
i
,
x
);
}
static
inline
void
Cba_NtkSetCopy
(
Cba_Ntk_t
*
p
,
int
i
,
int
x
)
{
Vec_IntWriteEntry
(
Cba_NtkCopies
(
p
),
p
->
iObjStart
+
i
,
x
);
}
static
inline
int
Cba_NtkIsWordLevel
(
Cba_Ntk_t
*
p
)
{
return
Vec_IntSize
(
&
p
->
vRangesL
)
>
0
;
}
static
inline
Cba_ObjType_t
Cba_ObjType
(
Cba_Ntk_t
*
p
,
int
i
)
{
return
(
Cba_ObjType_t
)
Vec_IntEntry
(
&
p
->
vTypes
,
i
);
}
static
inline
Cba_ObjType_t
Cba_ObjType
(
Cba_Ntk_t
*
p
,
int
i
)
{
return
(
Cba_ObjType_t
)
Vec_IntEntry
(
&
p
->
vTypes
,
i
);
}
static
inline
int
Cba_ObjFuncId
(
Cba_Ntk_t
*
p
,
int
i
)
{
return
Vec_IntEntry
(
&
p
->
vFuncs
,
i
);
}
static
inline
int
Cba_ObjFuncId
(
Cba_Ntk_t
*
p
,
int
i
)
{
return
Vec_IntEntry
(
&
p
->
vFuncs
,
i
);
}
static
inline
int
Cba_ObjFaninId
(
Cba_Ntk_t
*
p
,
int
i
)
{
return
Vec_IntEntry
(
&
p
->
vFanins
,
i
);
}
static
inline
int
Cba_ObjFaninId
(
Cba_Ntk_t
*
p
,
int
i
)
{
return
Vec_IntEntry
(
&
p
->
vFanins
,
i
);
}
static
inline
int
Cba_ObjInstId
(
Cba_Ntk_t
*
p
,
int
i
)
{
return
Vec_IntEntry
(
&
p
->
vInstIds
,
i
);
}
static
inline
int
Cba_ObjInstId
(
Cba_Ntk_t
*
p
,
int
i
)
{
return
Vec_IntEntry
(
&
p
->
vInstIds
,
i
);
}
static
inline
int
Cba_ObjNameId
(
Cba_Ntk_t
*
p
,
int
i
)
{
return
Vec_IntEntry
(
&
p
->
vNameIds
,
i
);
}
static
inline
int
Cba_ObjNameId
(
Cba_Ntk_t
*
p
,
int
i
)
{
return
Vec_IntEntry
(
&
p
->
vNameIds
,
i
);
}
static
inline
int
Cba_ObjRangeLId
(
Cba_Ntk_t
*
p
,
int
i
)
{
return
Vec_IntEntry
(
&
p
->
vRangesL
,
i
);
}
static
inline
int
Cba_ObjRangeRId
(
Cba_Ntk_t
*
p
,
int
i
)
{
return
Vec_IntEntry
(
&
p
->
vRangesR
,
i
);
}
static
inline
int
Cba_ObjIsPi
(
Cba_Ntk_t
*
p
,
int
i
)
{
return
Cba_ObjType
(
p
,
i
)
==
CBA_OBJ_PI
;
}
static
inline
int
Cba_ObjIsPi
(
Cba_Ntk_t
*
p
,
int
i
)
{
return
Cba_ObjType
(
p
,
i
)
==
CBA_OBJ_PI
;
}
static
inline
int
Cba_ObjIsPo
(
Cba_Ntk_t
*
p
,
int
i
)
{
return
Cba_ObjType
(
p
,
i
)
==
CBA_OBJ_PO
;
}
static
inline
int
Cba_ObjIsPo
(
Cba_Ntk_t
*
p
,
int
i
)
{
return
Cba_ObjType
(
p
,
i
)
==
CBA_OBJ_PO
;
}
...
@@ -341,6 +336,33 @@ static inline void Cba_ManSetupArray( Cba_Man_t * p, Vec_Int_t * vTo, Vec_Int_t
...
@@ -341,6 +336,33 @@ static inline void Cba_ManSetupArray( Cba_Man_t * p, Vec_Int_t * vTo, Vec_Int_t
}
}
// constructors desctructors
// constructors desctructors
static
inline
int
Cba_ObjAdd
(
Cba_Ntk_t
*
p
,
int
i
,
Cba_ObjType_t
Type
,
int
Func
,
int
Fanin
,
int
NameId
)
{
if
(
Type
==
CBA_OBJ_PI
)
Vec_IntWriteEntry
(
&
p
->
vInputs
,
Func
,
i
);
if
(
Type
==
CBA_OBJ_PO
)
Vec_IntWriteEntry
(
&
p
->
vOutputs
,
Func
,
i
);
if
(
Type
>=
0
)
Vec_IntWriteEntry
(
&
p
->
vTypes
,
i
,
Type
);
if
(
Func
>=
0
)
Vec_IntWriteEntry
(
&
p
->
vFuncs
,
i
,
Func
);
if
(
Fanin
>=
0
)
Vec_IntWriteEntry
(
&
p
->
vFanins
,
i
,
Fanin
);
if
(
NameId
>=
0
)
Vec_IntWriteEntry
(
&
p
->
vNameIds
,
i
,
NameId
);
return
i
;
}
static
inline
int
Cba_BoxAdd
(
Cba_Ntk_t
*
p
,
Cba_Ntk_t
*
pBox
,
int
NameId
)
{
int
i
,
ObjId
;
for
(
i
=
0
;
i
<
Cba_NtkPiNum
(
pBox
);
i
++
)
Cba_ObjAdd
(
p
,
p
->
nObjs
++
,
CBA_OBJ_BI
,
i
,
-
1
,
-
1
);
ObjId
=
Cba_ObjAdd
(
p
,
p
->
nObjs
++
,
CBA_OBJ_BOX
,
Cba_NtkId
(
pBox
),
-
1
,
NameId
);
for
(
i
=
0
;
i
<
Cba_NtkPoNum
(
pBox
);
i
++
)
Cba_ObjAdd
(
p
,
p
->
nObjs
++
,
CBA_OBJ_BO
,
i
,
ObjId
,
-
1
);
Cba_NtkSetHost
(
pBox
,
Cba_NtkId
(
p
),
ObjId
);
return
ObjId
;
}
static
inline
Cba_Ntk_t
*
Cba_NtkAlloc
(
Cba_Man_t
*
p
,
char
*
pName
)
static
inline
Cba_Ntk_t
*
Cba_NtkAlloc
(
Cba_Man_t
*
p
,
char
*
pName
)
{
{
int
iModelId
=
Abc_NamStrFindOrAdd
(
p
->
pModels
,
pName
,
NULL
);
int
iModelId
=
Abc_NamStrFindOrAdd
(
p
->
pModels
,
pName
,
NULL
);
...
@@ -354,16 +376,13 @@ static inline Cba_Ntk_t * Cba_NtkAlloc( Cba_Man_t * p, char * pName )
...
@@ -354,16 +376,13 @@ static inline Cba_Ntk_t * Cba_NtkAlloc( Cba_Man_t * p, char * pName )
printf
(
"Model with name %s already exists.
\n
"
,
pName
);
printf
(
"Model with name %s already exists.
\n
"
,
pName
);
return
pNtk
;
return
pNtk
;
}
}
static
inline
void
Cba_NtkResize
(
Cba_Ntk_t
*
p
,
int
nObjs
,
int
fRange
)
static
inline
void
Cba_NtkResize
(
Cba_Ntk_t
*
p
,
int
nObjs
)
{
{
assert
(
Vec_IntSize
(
&
p
->
vTypes
)
==
0
);
assert
(
Vec_IntSize
(
&
p
->
vTypes
)
==
0
);
Cba_ManFetchArray
(
p
->
pDesign
,
&
p
->
vTypes
,
nObjs
);
Cba_ManFetchArray
(
p
->
pDesign
,
&
p
->
vTypes
,
nObjs
);
Cba_ManFetchArray
(
p
->
pDesign
,
&
p
->
vFuncs
,
nObjs
);
Cba_ManFetchArray
(
p
->
pDesign
,
&
p
->
vFuncs
,
nObjs
);
Cba_ManFetchArray
(
p
->
pDesign
,
&
p
->
vFanins
,
nObjs
);
Cba_ManFetchArray
(
p
->
pDesign
,
&
p
->
vFanins
,
nObjs
);
Cba_ManFetchArray
(
p
->
pDesign
,
&
p
->
vNameIds
,
nObjs
);
Cba_ManFetchArray
(
p
->
pDesign
,
&
p
->
vNameIds
,
nObjs
);
if
(
!
fRange
)
return
;
Cba_ManFetchArray
(
p
->
pDesign
,
&
p
->
vRangesL
,
nObjs
);
Cba_ManFetchArray
(
p
->
pDesign
,
&
p
->
vRangesR
,
nObjs
);
}
}
static
inline
Cba_Man_t
*
Cba_ManAlloc
(
Cba_Man_t
*
pOld
,
char
*
pFileName
)
static
inline
Cba_Man_t
*
Cba_ManAlloc
(
Cba_Man_t
*
pOld
,
char
*
pFileName
)
{
{
...
...
src/base/cba/cbaBuild.c
View file @
40cbacaf
...
@@ -169,7 +169,7 @@ Cba_Ntk_t * Cba_NtkBuild( Cba_Man_t * pNew, Cba_Ntk_t * pNtk, Vec_Int_t * vMap,
...
@@ -169,7 +169,7 @@ Cba_Ntk_t * Cba_NtkBuild( Cba_Man_t * pNew, Cba_Ntk_t * pNtk, Vec_Int_t * vMap,
// start network
// start network
pNtkNew
=
Cba_ManNtk
(
pNew
,
Cba_NtkId
(
pNtk
)
);
pNtkNew
=
Cba_ManNtk
(
pNew
,
Cba_NtkId
(
pNtk
)
);
Cba_NtkResize
(
pNtkNew
,
nObjCount
,
0
);
Cba_NtkResize
(
pNtkNew
,
nObjCount
);
// fill object information
// fill object information
Cba_NtkForEachPi
(
pNtk
,
NameId
,
i
)
Cba_NtkForEachPi
(
pNtk
,
NameId
,
i
)
...
...
src/base/cba/cbaNtk.c
View file @
40cbacaf
...
@@ -137,7 +137,7 @@ Cba_Man_t * Cba_ManDupStart( Cba_Man_t * p, Vec_Int_t * vNtkSizes )
...
@@ -137,7 +137,7 @@ Cba_Man_t * Cba_ManDupStart( Cba_Man_t * p, Vec_Int_t * vNtkSizes )
Cba_Ntk_t
*
pNtk
;
int
i
;
Cba_Ntk_t
*
pNtk
;
int
i
;
Cba_Man_t
*
pNew
=
Cba_ManClone
(
p
);
Cba_Man_t
*
pNew
=
Cba_ManClone
(
p
);
Cba_ManForEachNtk
(
p
,
pNtk
,
i
)
Cba_ManForEachNtk
(
p
,
pNtk
,
i
)
Cba_NtkResize
(
Cba_ManNtk
(
pNew
,
i
),
vNtkSizes
?
Vec_IntEntry
(
vNtkSizes
,
i
)
:
Cba_NtkObjNum
(
pNtk
)
,
Cba_NtkIsWordLevel
(
pNtk
)
);
Cba_NtkResize
(
Cba_ManNtk
(
pNew
,
i
),
vNtkSizes
?
Vec_IntEntry
(
vNtkSizes
,
i
)
:
Cba_NtkObjNum
(
pNtk
)
);
Vec_IntFill
(
&
p
->
vCopies
,
Cba_ManObjNum
(
p
),
-
1
);
Vec_IntFill
(
&
p
->
vCopies
,
Cba_ManObjNum
(
p
),
-
1
);
Cba_ManForEachNtk
(
p
,
pNtk
,
i
)
Cba_ManForEachNtk
(
p
,
pNtk
,
i
)
Cba_NtkDupStart
(
Cba_ManNtk
(
pNew
,
i
),
pNtk
);
Cba_NtkDupStart
(
Cba_ManNtk
(
pNew
,
i
),
pNtk
);
...
@@ -196,6 +196,99 @@ Cba_Man_t * Cba_ManDup( Cba_Man_t * p )
...
@@ -196,6 +196,99 @@ Cba_Man_t * Cba_ManDup( Cba_Man_t * p )
}
}
#if 0
/**Function*************************************************************
Synopsis [Count the number of objects.]
Description []
SideEffects []
SeeAlso []
***********************************************************************/
int Cba_NtkCountObj_rec( Cba_Ntk_t * p )
{
int iObj, Count = 0;
if ( p->nObjsRec >= 0 )
return p->nObjsRec;
Cba_NtkForEachBox( p, iObj )
Count += Cba_BoxIsPrim(p, iObj) ? 1 : Cba_NtkCountObj_rec( Cba_ObjBoxModel(p, iObj) );
return Count;
}
int Cba_ManCountObj( Cba_Man_t * p )
{
Cba_Ntk_t * pNtk; int i;
Cba_ManForEachNtk( p, pNtk, i )
pNtk->nObjsRec = -1;
return Cba_NtkCountObj_rec( Cba_ManRoot(p) );
}
/**Function*************************************************************
Synopsis []
Description []
SideEffects []
SeeAlso []
***********************************************************************/
void Cba_NtkCollapse_rec( Cba_Ntk_t * pNew, Cba_Ntk_t * p, Vec_Int_t * vSigs )
{
int i, k, iObj, iTerm;
// set PI copies
Cba_NtkCleanCopies( p );
assert( Vec_IntSize(vSigs) == Cba_NtkPiNum(p) );
Cba_NtkForEachPi( pRoot, iObj, i )
Cba_ObjSetCopy( pRoot, iObj, Vec_IntEntry(vSigs, i) );
// duplicate internal objects
Cba_ManForEachBox( p, iObj )
if ( Cba_BoxIsPrim(p, iObj) )
Cba_BoxDup( pNew, p, iObj );
// duplicate other modules
Cba_ManForEachBox( p, iObj )
if ( !Cba_BoxIsPrim(p, iObj) )
{
Vec_IntClear( vSigs );
Cba_BoxForEachBi( iObj, iTerm, k )
Vec_IntPush( vSigs, Cba_ObjCopy(p, Cba_ObjFanin(p, iTerm)) );
Cba_NtkCollapse_rec( pNew, Cba_ObjBoxModel(p, iObj), vSigs );
Cba_BoxForEachBo( iObj, iTerm, k )
Cba_ObjAddFanin( pNew, Cba_ObjCopy(p, iObj), Vec_IntEntry(vSigs, k) );
}
// connect objects
Cba_ManForEachObj( p, iObj )
if ( Cba_ObjType(p, iObj) == CBA_OBJ_BI || Cba_ObjType(p, iObj) == CBA_OBJ_BO )
Cba_ObjAddFanin( pNew, Cba_ObjCopy(p, iObj), Cba_ObjCopy(p, Cba_ObjFanin(p, iObj)) );
// collect POs
Vec_IntClear( vSigs );
Cba_NtkForEachPi( pRoot, iObj, i )
Vec_IntPush( vSigs, Cba_ObjCopy(p, Cba_ObjFanin(p, iObj)) );
}
Cba_Man_t * Cba_ManCollapse( Cba_Man_t * p )
{
Cba_Man_t * pNew = Cba_ManAlloc( NULL, Cba_ManName(p) );
Cba_Ntk_t * pRootNew = Cba_NtkAlloc( pNew, Cba_NtkName(pRoot) );
int i, iObj;
Vec_Int_t * vSigs = Vec_IntAlloc( 1000 );
Cba_Ntk_t * pRoot = Cba_ManRoot( p );
Cba_NtkForEachPi( pRoot, iObj, i )
Vec_IntPush( vSigns, Cba_ObjDup(pRootNew, pRoot, iObj) );
Cba_NtkCollapse_rec( pRootNew, pRoot, vSigns );
assert( Vec_IntSize(vSigns) == Cba_NtkPoNum(pRoot) );
Cba_NtkForEachPo( pRoot, iObj, i )
Cba_ObjAddFanin( pRootNew, Cba_ObjDup(pRootNew, pRoot, iObj), Vec_IntEntry(vSigns, i) );
Vec_IntFree( vSigs );
return pNew;
}
#endif
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
/// END OF FILE ///
/// END OF FILE ///
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
...
...
src/base/cba/cbaPrs.h
View file @
40cbacaf
...
@@ -48,6 +48,31 @@ typedef enum {
...
@@ -48,6 +48,31 @@ typedef enum {
/// BASIC TYPES ///
/// BASIC TYPES ///
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
// network
typedef
struct
Cba_Prn_t_
Cba_Prn_t
;
struct
Cba_Prn_t_
{
int
iModuleName
;
// interface
Vec_Int_t
vOrder
;
// signal names
Vec_Int_t
vInouts
;
// inouts
Vec_Int_t
vInputs
;
// inputs
Vec_Int_t
vOutputs
;
// outputs
Vec_Int_t
vWires
;
// wires
// signal ranges
Vec_Int_t
vInoutsR
;
// inouts
Vec_Int_t
vInputsR
;
// inputs
Vec_Int_t
vOutputsR
;
// outputs
Vec_Int_t
vWiresR
;
// wires
// objects
Vec_Int_t
vObjBegs
;
// object beginnings
Vec_Int_t
vObjects
;
// object data (ModuleId; InstId; FormNameId/ActNameId/ActRange)
// concatenations
Vec_Int_t
vConBegs
;
// concatenation beginnings
Vec_Int_t
vConcats
;
// concatenation data
};
// parser
// parser
typedef
struct
Cba_Prs_t_
Cba_Prs_t
;
typedef
struct
Cba_Prs_t_
Cba_Prs_t
;
struct
Cba_Prs_t_
struct
Cba_Prs_t_
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment