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
69bd3554
Commit
69bd3554
authored
Sep 26, 2014
by
Alan Mishchenko
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Support for sequential designs in word-level Verilog.
parent
6aa1c94e
Show whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
189 additions
and
72 deletions
+189
-72
src/base/wlc/wlc.h
+25
-7
src/base/wlc/wlcBlast.c
+23
-14
src/base/wlc/wlcNtk.c
+33
-28
src/base/wlc/wlcReadVer.c
+74
-14
src/base/wlc/wlcWriteVer.c
+34
-9
No files found.
src/base/wlc/wlc.h
View file @
69bd3554
...
...
@@ -43,10 +43,10 @@ ABC_NAMESPACE_HEADER_START
typedef
enum
{
WLC_OBJ_NONE
=
0
,
// 00: unknown
WLC_OBJ_PI
,
// 01: primary input
WLC_OBJ_PO
,
// 02: primary output
WLC_OBJ_
BO
,
// 03: box
output
WLC_OBJ_
BI
,
// 04: box input
WLC_OBJ_FF
,
// 05: flop
WLC_OBJ_PO
,
// 02: primary output
(unused)
WLC_OBJ_
FO
,
// 03: flop
output
WLC_OBJ_
FI
,
// 04: flop input (unused)
WLC_OBJ_FF
,
// 05: flop
(unused)
WLC_OBJ_CONST
,
// 06: constant
WLC_OBJ_BUF
,
// 07: buffer
WLC_OBJ_MUX
,
// 08: multiplexer
...
...
@@ -88,6 +88,10 @@ typedef enum {
}
Wlc_ObjType_t
;
// Unlike AIG managers and logic networks in ABC, this network treats POs and FIs
// as attributes of internal nodes and *not* as separate types of objects.
////////////////////////////////////////////////////////////////////////
/// BASIC TYPES ///
////////////////////////////////////////////////////////////////////////
...
...
@@ -98,7 +102,9 @@ struct Wlc_Obj_t_ // 16 bytes
unsigned
Type
:
6
;
// node type
unsigned
Signed
:
1
;
// signed
unsigned
Mark
:
1
;
// user mark
unsigned
nFanins
:
24
;
// fanin count
unsigned
fIsPo
:
1
;
// this is PO
unsigned
fIsFi
:
1
;
// this is FI
unsigned
nFanins
:
22
;
// fanin count
unsigned
End
:
16
;
// range end
unsigned
Beg
:
16
;
// range begin
union
{
int
Fanins
[
2
];
// fanin IDs
...
...
@@ -138,7 +144,7 @@ static inline int Wlc_NtkPiNum( Wlc_Ntk_t * p )
static
inline
int
Wlc_NtkPoNum
(
Wlc_Ntk_t
*
p
)
{
return
Vec_IntSize
(
&
p
->
vPos
);
}
static
inline
int
Wlc_NtkCiNum
(
Wlc_Ntk_t
*
p
)
{
return
Vec_IntSize
(
&
p
->
vCis
);
}
static
inline
int
Wlc_NtkCoNum
(
Wlc_Ntk_t
*
p
)
{
return
Vec_IntSize
(
&
p
->
vCos
);
}
static
inline
int
Wlc_NtkFfNum
(
Wlc_Ntk_t
*
p
)
{
return
Vec_IntSize
(
&
p
->
v
Ffs
);
}
static
inline
int
Wlc_NtkFfNum
(
Wlc_Ntk_t
*
p
)
{
return
Vec_IntSize
(
&
p
->
v
Cis
)
-
Vec_IntSize
(
&
p
->
vPis
);
}
static
inline
Wlc_Obj_t
*
Wlc_NtkObj
(
Wlc_Ntk_t
*
p
,
int
Id
)
{
assert
(
Id
>
0
&&
Id
<
p
->
nObjsAlloc
);
return
p
->
pObjs
+
Id
;
}
static
inline
Wlc_Obj_t
*
Wlc_NtkPi
(
Wlc_Ntk_t
*
p
,
int
i
)
{
return
Wlc_NtkObj
(
p
,
Vec_IntEntry
(
&
p
->
vPis
,
i
)
);
}
...
...
@@ -147,8 +153,13 @@ static inline Wlc_Obj_t * Wlc_NtkCi( Wlc_Ntk_t * p, int i )
static
inline
Wlc_Obj_t
*
Wlc_NtkCo
(
Wlc_Ntk_t
*
p
,
int
i
)
{
return
Wlc_NtkObj
(
p
,
Vec_IntEntry
(
&
p
->
vCos
,
i
)
);
}
static
inline
Wlc_Obj_t
*
Wlc_NtkFf
(
Wlc_Ntk_t
*
p
,
int
i
)
{
return
Wlc_NtkObj
(
p
,
Vec_IntEntry
(
&
p
->
vFfs
,
i
)
);
}
static
inline
int
Wlc_ObjIsPi
(
Wlc_Obj_t
*
p
)
{
return
p
->
Type
==
WLC_OBJ_PI
;
}
static
inline
int
Wlc_ObjIsPo
(
Wlc_Obj_t
*
p
)
{
return
p
->
fIsPo
;
}
static
inline
int
Wlc_ObjIsCi
(
Wlc_Obj_t
*
p
)
{
return
p
->
Type
==
WLC_OBJ_PI
||
p
->
Type
==
WLC_OBJ_FO
;
}
static
inline
int
Wlc_ObjIsCo
(
Wlc_Obj_t
*
p
)
{
return
p
->
fIsPo
||
p
->
fIsFi
;
}
static
inline
int
Wlc_ObjId
(
Wlc_Ntk_t
*
p
,
Wlc_Obj_t
*
pObj
)
{
return
pObj
-
p
->
pObjs
;
}
static
inline
int
Wlc_Obj
PioId
(
Wlc_Obj_t
*
p
)
{
assert
(
p
->
Type
==
WLC_OBJ_PI
||
p
->
Type
==
WLC_OBJ_PO
);
return
p
->
Fanins
[
1
];
}
static
inline
int
Wlc_Obj
CiId
(
Wlc_Obj_t
*
p
)
{
assert
(
Wlc_ObjIsCi
(
p
)
);
return
p
->
Fanins
[
1
];
}
static
inline
int
Wlc_ObjFaninNum
(
Wlc_Obj_t
*
p
)
{
return
p
->
nFanins
;
}
static
inline
int
Wlc_ObjHasArray
(
Wlc_Obj_t
*
p
)
{
return
p
->
nFanins
>
2
||
p
->
Type
==
WLC_OBJ_CONST
;
}
static
inline
int
*
Wlc_ObjFanins
(
Wlc_Obj_t
*
p
)
{
return
Wlc_ObjHasArray
(
p
)
?
p
->
pFanins
[
0
]
:
p
->
Fanins
;
}
...
...
@@ -174,12 +185,15 @@ static inline void Wlc_NtkCleanCopy( Wlc_Ntk_t * p )
static
inline
int
Wlc_NtkHasCopy
(
Wlc_Ntk_t
*
p
)
{
return
Vec_IntSize
(
&
p
->
vCopies
)
>
0
;
}
static
inline
void
Wlc_ObjSetCopy
(
Wlc_Ntk_t
*
p
,
int
iObj
,
int
i
)
{
Vec_IntWriteEntry
(
&
p
->
vCopies
,
iObj
,
i
);
}
static
inline
int
Wlc_ObjCopy
(
Wlc_Ntk_t
*
p
,
int
iObj
)
{
return
Vec_IntEntry
(
&
p
->
vCopies
,
iObj
);
}
static
inline
Wlc_Obj_t
*
Wlc_ObjCopyObj
(
Wlc_Ntk_t
*
pNew
,
Wlc_Ntk_t
*
p
,
Wlc_Obj_t
*
pObj
)
{
return
Wlc_NtkObj
(
pNew
,
Wlc_ObjCopy
(
p
,
Wlc_ObjId
(
p
,
pObj
)));}
static
inline
void
Wlc_NtkCleanNameId
(
Wlc_Ntk_t
*
p
)
{
Vec_IntFill
(
&
p
->
vNameIds
,
p
->
nObjsAlloc
,
0
);
}
static
inline
int
Wlc_NtkHasNameId
(
Wlc_Ntk_t
*
p
)
{
return
Vec_IntSize
(
&
p
->
vNameIds
)
>
0
;
}
static
inline
void
Wlc_ObjSetNameId
(
Wlc_Ntk_t
*
p
,
int
iObj
,
int
i
)
{
Vec_IntWriteEntry
(
&
p
->
vNameIds
,
iObj
,
i
);
}
static
inline
int
Wlc_ObjNameId
(
Wlc_Ntk_t
*
p
,
int
iObj
)
{
return
Vec_IntEntry
(
&
p
->
vNameIds
,
iObj
);
}
static
inline
Wlc_Obj_t
*
Wlc_ObjFoToFi
(
Wlc_Ntk_t
*
p
,
Wlc_Obj_t
*
pObj
)
{
assert
(
pObj
->
Type
==
WLC_OBJ_FO
);
return
Wlc_NtkCo
(
p
,
Wlc_NtkCoNum
(
p
)
-
Wlc_NtkCiNum
(
p
)
+
Wlc_ObjCiId
(
pObj
));
}
////////////////////////////////////////////////////////////////////////
/// MACRO DEFINITIONS ///
////////////////////////////////////////////////////////////////////////
...
...
@@ -198,6 +212,8 @@ static inline int Wlc_ObjNameId( Wlc_Ntk_t * p, int iObj )
for ( i = 0; (i < Wlc_NtkCiNum(p)) && (((pCi) = Wlc_NtkCi(p, i)), 1); i++ )
#define Wlc_NtkForEachCo( p, pCo, i ) \
for ( i = 0; (i < Wlc_NtkCoNum(p)) && (((pCo) = Wlc_NtkCo(p, i)), 1); i++ )
#define Wlc_NtkForEachFf( p, pFf, i ) \
for ( i = 0; (i < Vec_IntSize(&p->vFfs)) && (((pFf) = Wlc_NtkFf(p, i)), 1); i++ )
#define Wlc_ObjForEachFanin( pObj, iFanin, i ) \
for ( i = 0; (i < Wlc_ObjFaninNum(pObj)) && (((iFanin) = Wlc_ObjFaninId(pObj, i)), 1); i++ )
...
...
@@ -214,6 +230,8 @@ extern Gia_Man_t * Wlc_NtkBitBlast( Wlc_Ntk_t * p );
/*=== wlcNtk.c ========================================================*/
extern
Wlc_Ntk_t
*
Wlc_NtkAlloc
(
char
*
pName
,
int
nObjsAlloc
);
extern
int
Wlc_ObjAlloc
(
Wlc_Ntk_t
*
p
,
int
Type
,
int
Signed
,
int
End
,
int
Beg
);
extern
void
Wlc_ObjSetCi
(
Wlc_Ntk_t
*
p
,
Wlc_Obj_t
*
pObj
);
extern
void
Wlc_ObjSetCo
(
Wlc_Ntk_t
*
p
,
Wlc_Obj_t
*
pObj
,
int
fFlopInput
);
extern
char
*
Wlc_ObjName
(
Wlc_Ntk_t
*
p
,
int
iObj
);
extern
void
Wlc_ObjUpdateType
(
Wlc_Ntk_t
*
p
,
Wlc_Obj_t
*
pObj
,
int
Type
);
extern
void
Wlc_ObjAddFanins
(
Wlc_Ntk_t
*
p
,
Wlc_Obj_t
*
pObj
,
Vec_Int_t
*
vFanins
);
...
...
src/base/wlc/wlcBlast.c
View file @
69bd3554
...
...
@@ -343,6 +343,7 @@ Gia_Man_t * Wlc_NtkBitBlast( Wlc_Ntk_t * p )
int
nBits
=
Wlc_NtkPrepareBits
(
p
);
int
nRange
,
nRange0
,
nRange1
,
nRange2
;
int
i
,
k
,
b
,
iFanin
,
iLit
,
*
pFans0
,
*
pFans1
,
*
pFans2
;
int
nFFins
=
0
,
nFFouts
=
0
;
vBits
=
Vec_IntAlloc
(
nBits
);
vTemp0
=
Vec_IntAlloc
(
1000
);
vTemp1
=
Vec_IntAlloc
(
1000
);
...
...
@@ -357,8 +358,8 @@ Gia_Man_t * Wlc_NtkBitBlast( Wlc_Ntk_t * p )
// create primary inputs
Wlc_NtkForEachObj
(
p
,
pObj
,
i
)
{
int
nAndPrev
=
Gia_ManObjNum
(
pNew
);
// char * pName = Wlc_ObjName(p, i);
int
nAndPrev
=
Gia_ManAndNum
(
pNew
);
nRange
=
Wlc_ObjRange
(
pObj
);
nRange0
=
Wlc_ObjFaninNum
(
pObj
)
>
0
?
Wlc_ObjRange
(
Wlc_ObjFanin0
(
p
,
pObj
)
)
:
-
1
;
nRange1
=
Wlc_ObjFaninNum
(
pObj
)
>
1
?
Wlc_ObjRange
(
Wlc_ObjFanin1
(
p
,
pObj
)
)
:
-
1
;
...
...
@@ -367,14 +368,16 @@ Gia_Man_t * Wlc_NtkBitBlast( Wlc_Ntk_t * p )
pFans1
=
Wlc_ObjFaninNum
(
pObj
)
>
1
?
Vec_IntEntryP
(
vBits
,
Wlc_ObjCopy
(
p
,
Wlc_ObjFaninId1
(
pObj
))
)
:
NULL
;
pFans2
=
Wlc_ObjFaninNum
(
pObj
)
>
2
?
Vec_IntEntryP
(
vBits
,
Wlc_ObjCopy
(
p
,
Wlc_ObjFaninId2
(
pObj
))
)
:
NULL
;
Vec_IntClear
(
vRes
);
if
(
pObj
->
Type
==
WLC_OBJ_PI
)
if
(
Wlc_ObjIsCi
(
pObj
)
)
{
for
(
k
=
0
;
k
<
nRange
;
k
++
)
Vec_IntPush
(
vRes
,
Gia_ManAppendCi
(
pNew
)
);
if
(
pObj
->
Type
==
WLC_OBJ_FO
)
nFFouts
+=
Vec_IntSize
(
vRes
);
}
else
if
(
pObj
->
Type
==
WLC_OBJ_
PO
||
pObj
->
Type
==
WLC_OBJ_
BUF
)
else
if
(
pObj
->
Type
==
WLC_OBJ_BUF
)
{
if
(
pObj
->
Type
==
WLC_OBJ_BUF
&&
pObj
->
Signed
&&
!
Wlc_ObjFanin0
(
p
,
pObj
)
->
Signed
)
// unsign->sign
if
(
pObj
->
Signed
&&
!
Wlc_ObjFanin0
(
p
,
pObj
)
->
Signed
)
// unsign->sign
{
int
nRangeMax
=
Abc_MaxInt
(
nRange0
,
nRange
);
int
*
pArg0
=
Wlc_VecLoadFanins
(
vTemp0
,
pFans0
,
nRange0
,
nRangeMax
,
0
);
...
...
@@ -533,19 +536,23 @@ Gia_Man_t * Wlc_NtkBitBlast( Wlc_Ntk_t * p )
Vec_IntPush
(
vRes
,
Wlc_BlastReduction
(
pNew
,
pFans0
,
nRange
,
pObj
->
Type
)
);
else
if
(
pObj
->
Type
==
WLC_OBJ_ARI_ADD
||
pObj
->
Type
==
WLC_OBJ_ARI_SUB
)
{
int
*
pArg0
=
Wlc_VecLoadFanins
(
vRes
,
pFans0
,
nRange0
,
nRange
,
Wlc_ObjFanin0
(
p
,
pObj
)
->
Signed
);
int
*
pArg1
=
Wlc_VecLoadFanins
(
vTemp1
,
pFans1
,
nRange1
,
nRange
,
Wlc_ObjFanin1
(
p
,
pObj
)
->
Signed
);
int
nRangeMax
=
Abc_MaxInt
(
nRange
,
Abc_MaxInt
(
nRange0
,
nRange1
)
);
int
*
pArg0
=
Wlc_VecLoadFanins
(
vRes
,
pFans0
,
nRange0
,
nRangeMax
,
Wlc_ObjFanin0
(
p
,
pObj
)
->
Signed
);
int
*
pArg1
=
Wlc_VecLoadFanins
(
vTemp1
,
pFans1
,
nRange1
,
nRangeMax
,
Wlc_ObjFanin1
(
p
,
pObj
)
->
Signed
);
if
(
pObj
->
Type
==
WLC_OBJ_ARI_ADD
)
Wlc_BlastAdder
(
pNew
,
pArg0
,
pArg1
,
nRange
);
// result is in pFan0 (vRes)
else
Wlc_BlastSubtract
(
pNew
,
pArg0
,
pArg1
,
nRange
);
// result is in pFan0 (vRes)
Vec_IntShrink
(
vRes
,
nRange
);
}
else
if
(
pObj
->
Type
==
WLC_OBJ_ARI_MULTI
)
{
int
*
pArg0
=
Wlc_VecLoadFanins
(
vTemp0
,
pFans0
,
nRange0
,
nRange
,
Wlc_ObjFanin0
(
p
,
pObj
)
->
Signed
);
int
*
pArg1
=
Wlc_VecLoadFanins
(
vTemp1
,
pFans1
,
nRange1
,
nRange
,
Wlc_ObjFanin1
(
p
,
pObj
)
->
Signed
);
int
nRangeMax
=
Abc_MaxInt
(
nRange
,
Abc_MaxInt
(
nRange0
,
nRange1
)
);
int
*
pArg0
=
Wlc_VecLoadFanins
(
vTemp0
,
pFans0
,
nRange0
,
nRangeMax
,
Wlc_ObjFanin0
(
p
,
pObj
)
->
Signed
);
int
*
pArg1
=
Wlc_VecLoadFanins
(
vTemp1
,
pFans1
,
nRange1
,
nRangeMax
,
Wlc_ObjFanin1
(
p
,
pObj
)
->
Signed
);
assert
(
nRange0
<=
nRange
&&
nRange1
<=
nRange
);
Wlc_BlastMultiplier
(
pNew
,
pArg0
,
pArg1
,
nRange
,
vTemp2
,
vRes
);
Vec_IntShrink
(
vRes
,
nRange
);
}
else
if
(
pObj
->
Type
==
WLC_OBJ_ARI_DIVIDE
||
pObj
->
Type
==
WLC_OBJ_ARI_MODULUS
)
{
...
...
@@ -567,8 +574,7 @@ Gia_Man_t * Wlc_NtkBitBlast( Wlc_Ntk_t * p )
assert
(
Vec_IntSize
(
vBits
)
==
Wlc_ObjCopy
(
p
,
i
)
);
Vec_IntAppend
(
vBits
,
vRes
);
pPrev
=
pObj
;
if
(
pObj
->
Type
!=
WLC_OBJ_PI
&&
pObj
->
Type
!=
WLC_OBJ_PO
)
p
->
nAnds
[
pObj
->
Type
]
+=
Gia_ManObjNum
(
pNew
)
-
nAndPrev
;
p
->
nAnds
[
pObj
->
Type
]
+=
Gia_ManAndNum
(
pNew
)
-
nAndPrev
;
}
p
->
nAnds
[
0
]
=
Gia_ManAndNum
(
pNew
);
assert
(
nBits
==
Vec_IntSize
(
vBits
)
);
...
...
@@ -576,18 +582,21 @@ Gia_Man_t * Wlc_NtkBitBlast( Wlc_Ntk_t * p )
Vec_IntFree
(
vTemp1
);
Vec_IntFree
(
vTemp2
);
Vec_IntFree
(
vRes
);
// create
P
Os
Wlc_NtkForEach
P
o
(
p
,
pObj
,
i
)
// create
C
Os
Wlc_NtkForEach
C
o
(
p
,
pObj
,
i
)
{
nRange
=
Wlc_ObjRange
(
pObj
);
nRange0
=
Wlc_ObjFaninNum
(
pObj
)
>
0
?
Wlc_ObjRange
(
Wlc_ObjFanin0
(
p
,
pObj
)
)
:
-
1
;
assert
(
nRange
==
nRange0
);
pFans0
=
Vec_IntEntryP
(
vBits
,
Wlc_ObjCopy
(
p
,
Wlc_ObjId
(
p
,
pObj
))
);
for
(
k
=
0
;
k
<
nRange
;
k
++
)
Gia_ManAppendCo
(
pNew
,
pFans0
[
k
]
);
if
(
pObj
->
fIsFi
)
nFFins
+=
nRange
;
}
Vec_IntFree
(
vBits
);
Vec_IntErase
(
&
p
->
vCopies
);
// set the number of registers
assert
(
nFFins
==
nFFouts
);
Gia_ManSetRegNum
(
pNew
,
nFFins
);
// finalize and cleanup
pNew
=
Gia_ManCleanup
(
pTemp
=
pNew
);
Gia_ManStop
(
pTemp
);
...
...
src/base/wlc/wlcNtk.c
View file @
69bd3554
...
...
@@ -107,9 +107,30 @@ Wlc_Ntk_t * Wlc_NtkAlloc( char * pName, int nObjsAlloc )
p
->
iObj
=
1
;
return
p
;
}
void
Wlc_ObjSetCi
(
Wlc_Ntk_t
*
p
,
Wlc_Obj_t
*
pObj
)
{
assert
(
Wlc_ObjIsCi
(
pObj
)
);
assert
(
Wlc_ObjFaninNum
(
pObj
)
==
0
);
pObj
->
Fanins
[
1
]
=
Vec_IntSize
(
&
p
->
vCis
);
Vec_IntPush
(
&
p
->
vCis
,
Wlc_ObjId
(
p
,
pObj
)
);
if
(
pObj
->
Type
==
WLC_OBJ_PI
)
Vec_IntPush
(
&
p
->
vPis
,
Wlc_ObjId
(
p
,
pObj
)
);
}
void
Wlc_ObjSetCo
(
Wlc_Ntk_t
*
p
,
Wlc_Obj_t
*
pObj
,
int
fFlopInput
)
{
// pObj->Fanins[1] = Vec_IntSize(&p->vCos);
Vec_IntPush
(
&
p
->
vCos
,
Wlc_ObjId
(
p
,
pObj
)
);
if
(
!
fFlopInput
)
Vec_IntPush
(
&
p
->
vPos
,
Wlc_ObjId
(
p
,
pObj
)
);
if
(
fFlopInput
)
pObj
->
fIsFi
=
1
;
else
pObj
->
fIsPo
=
1
;
}
int
Wlc_ObjAlloc
(
Wlc_Ntk_t
*
p
,
int
Type
,
int
Signed
,
int
End
,
int
Beg
)
{
Wlc_Obj_t
*
pObj
;
assert
(
Type
!=
WLC_OBJ_PO
&&
Type
!=
WLC_OBJ_FI
);
if
(
p
->
iObj
==
p
->
nObjsAlloc
)
{
p
->
pObjs
=
ABC_REALLOC
(
Wlc_Obj_t
,
p
->
pObjs
,
2
*
p
->
nObjsAlloc
);
...
...
@@ -121,16 +142,8 @@ int Wlc_ObjAlloc( Wlc_Ntk_t * p, int Type, int Signed, int End, int Beg )
pObj
->
Signed
=
Signed
;
pObj
->
End
=
End
;
pObj
->
Beg
=
Beg
;
if
(
Type
==
WLC_OBJ_PI
)
{
pObj
->
Fanins
[
1
]
=
Vec_IntSize
(
&
p
->
vPis
);
Vec_IntPush
(
&
p
->
vPis
,
p
->
iObj
);
}
else
if
(
Type
==
WLC_OBJ_PO
)
{
pObj
->
Fanins
[
1
]
=
Vec_IntSize
(
&
p
->
vPos
);
Vec_IntPush
(
&
p
->
vPos
,
p
->
iObj
);
}
if
(
Wlc_ObjIsCi
(
pObj
)
)
Wlc_ObjSetCi
(
p
,
pObj
);
p
->
nObjs
[
Type
]
++
;
return
p
->
iObj
++
;
}
...
...
@@ -144,13 +157,7 @@ char * Wlc_ObjName( Wlc_Ntk_t * p, int iObj )
}
void
Wlc_ObjUpdateType
(
Wlc_Ntk_t
*
p
,
Wlc_Obj_t
*
pObj
,
int
Type
)
{
if
(
pObj
->
Type
==
WLC_OBJ_PO
)
{
// if ( Type != WLC_OBJ_BUF )
// printf( "Primary outputs should be driven by buffers.\n" );
assert
(
Type
==
WLC_OBJ_BUF
);
return
;
}
assert
(
pObj
->
Type
==
WLC_OBJ_NONE
);
p
->
nObjs
[
pObj
->
Type
]
--
;
pObj
->
Type
=
Type
;
p
->
nObjs
[
pObj
->
Type
]
++
;
...
...
@@ -274,20 +281,18 @@ void Wlc_NtkPrintDistrib( Wlc_Ntk_t * p, int fVerbose )
Wlc_NtkForEachObj
(
p
,
pObj
,
i
)
{
// char * pName = Wlc_ObjName(p, i);
// if ( pObj->Type == WLC_OBJ_ARI_MULTI )
if
(
Wlc_ObjSign
(
pObj
)
>
0x1FFFFF
)
printf
(
"Object %6d has range %d, which is reduced to %d in the statistics.
\n
"
,
i
,
Wlc_ObjRange
(
pObj
),
Wlc_ObjRange
(
pObj
)
&
0xFFFFF
);
if
(
pObj
->
Beg
)
printf
(
"Object %6d has non-standard range %d=[%d:%d]
\n
"
,
i
,
Wlc_ObjRange
(
pObj
),
pObj
->
End
,
pObj
->
Beg
);
// 0-input types
if
(
pObj
->
Type
==
WLC_OBJ_PI
||
pObj
->
Type
==
WLC_OBJ_CONST
||
pObj
->
Type
==
WLC_OBJ_BIT_CONCAT
)
if
(
Wlc_ObjIsCi
(
pObj
)
||
pObj
->
Type
==
WLC_OBJ_CONST
||
pObj
->
Type
==
WLC_OBJ_BIT_CONCAT
)
Sign
=
Wlc_NtkPrintDistribMakeSign
(
Wlc_ObjSign
(
pObj
),
0
,
0
);
// 1-input types
else
if
(
pObj
->
Type
==
WLC_OBJ_BUF
||
pObj
->
Type
==
WLC_OBJ_
PO
||
pObj
->
Type
==
WLC_OBJ_BI
||
else
if
(
pObj
->
Type
==
WLC_OBJ_BUF
||
pObj
->
Type
==
WLC_OBJ_
BIT_SELECT
||
pObj
->
Type
==
WLC_OBJ_TABLE
||
pObj
->
Type
==
WLC_OBJ_BIT_ZEROPAD
||
pObj
->
Type
==
WLC_OBJ_BIT_SIGNEXT
||
pObj
->
Type
==
WLC_OBJ_BIT_NOT
||
pObj
->
Type
==
WLC_OBJ_LOGIC_NOT
||
pObj
->
Type
==
WLC_OBJ_ARI_MINUS
||
pObj
->
Type
==
WLC_OBJ_BIT_SELECT
||
pObj
->
Type
==
WLC_OBJ_TABLE
)
pObj
->
Type
==
WLC_OBJ_BIT_NOT
||
pObj
->
Type
==
WLC_OBJ_LOGIC_NOT
||
pObj
->
Type
==
WLC_OBJ_ARI_MINUS
)
Sign
=
Wlc_NtkPrintDistribMakeSign
(
Wlc_ObjSign
(
pObj
),
Wlc_ObjSign
(
Wlc_ObjFanin0
(
p
,
pObj
)),
0
);
// 2-input types (including MUX)
else
...
...
@@ -296,7 +301,7 @@ void Wlc_NtkPrintDistrib( Wlc_Ntk_t * p, int fVerbose )
Wlc_NtkPrintDistribAddOne
(
vTypes
,
vOccurs
,
pObj
->
Type
,
Sign
);
}
// print by occurrence
printf
(
"
Format: type ID : occurance name ... (occurrence)<output_range>=<input_range>.<input_range>
\n
"
);
printf
(
"
ID : name occurrence (occurrence)<output_range>=<input_range>.<input_range> ...
\n
"
);
for
(
i
=
0
;
i
<
WLC_OBJ_NUMBER
;
i
++
)
{
Vec_Wrd_t
*
vType
=
(
Vec_Wrd_t
*
)
Vec_PtrEntry
(
vTypes
,
i
);
...
...
@@ -436,12 +441,12 @@ Wlc_Ntk_t * Wlc_NtkDupDfs( Wlc_Ntk_t * p )
Wlc_NtkCleanCopy
(
p
);
vFanins
=
Vec_IntAlloc
(
100
);
pNew
=
Wlc_NtkAlloc
(
p
->
pName
,
p
->
nObjsAlloc
);
Wlc_NtkForEachPi
(
p
,
pObj
,
i
)
Wlc_ObjDup
(
pNew
,
p
,
Wlc_ObjId
(
p
,
pObj
),
vFanins
);
Wlc_NtkForEachPo
(
p
,
pObj
,
i
)
Wlc_NtkDupDfs_rec
(
pNew
,
p
,
Wlc_ObjFaninId0
(
pObj
),
vFanins
);
Wlc_NtkForEachPo
(
p
,
pObj
,
i
)
Wlc_NtkForEachCi
(
p
,
pObj
,
i
)
Wlc_ObjDup
(
pNew
,
p
,
Wlc_ObjId
(
p
,
pObj
),
vFanins
);
Wlc_NtkForEachCo
(
p
,
pObj
,
i
)
Wlc_NtkDupDfs_rec
(
pNew
,
p
,
Wlc_ObjId
(
p
,
pObj
),
vFanins
);
Wlc_NtkForEachCo
(
p
,
pObj
,
i
)
Wlc_ObjSetCo
(
pNew
,
Wlc_ObjCopyObj
(
pNew
,
p
,
pObj
),
pObj
->
fIsFi
);
Vec_IntFree
(
vFanins
);
return
pNew
;
}
...
...
src/base/wlc/wlcReadVer.c
View file @
69bd3554
...
...
@@ -658,11 +658,11 @@ static inline int Wlc_PrsFindDefinition( Wlc_Prs_t * p, char * pStr, Vec_Int_t *
int
Wlc_PrsReadDeclaration
(
Wlc_Prs_t
*
p
,
char
*
pStart
)
{
int
fFound
=
0
,
Type
=
WLC_OBJ_NONE
,
iObj
;
int
Signed
=
0
,
Beg
=
0
,
End
=
0
,
NameId
;
int
Signed
=
0
,
Beg
=
0
,
End
=
0
,
NameId
,
fIsPo
=
0
;
if
(
Wlc_PrsStrCmp
(
pStart
,
"input"
)
)
Type
=
WLC_OBJ_PI
,
pStart
+=
strlen
(
"input"
)
;
pStart
+=
strlen
(
"input"
),
Type
=
WLC_OBJ_PI
;
else
if
(
Wlc_PrsStrCmp
(
pStart
,
"output"
)
)
Type
=
WLC_OBJ_PO
,
pStart
+=
strlen
(
"output"
)
;
pStart
+=
strlen
(
"output"
),
fIsPo
=
1
;
pStart
=
Wlc_PrsSkipSpaces
(
pStart
);
if
(
Wlc_PrsStrCmp
(
pStart
,
"wire"
)
)
pStart
+=
strlen
(
"wire"
);
...
...
@@ -685,6 +685,7 @@ int Wlc_PrsReadDeclaration( Wlc_Prs_t * p, char * pStart )
if
(
fFound
)
return
Wlc_PrsWriteErrorMessage
(
p
,
pStart
,
"Name %s is declared more than once."
,
pName
);
iObj
=
Wlc_ObjAlloc
(
p
->
pNtk
,
Type
,
Signed
,
End
,
Beg
);
if
(
fIsPo
)
Wlc_ObjSetCo
(
p
->
pNtk
,
Wlc_NtkObj
(
p
->
pNtk
,
iObj
),
0
);
assert
(
iObj
==
NameId
);
// check next definition
pStart
=
Wlc_PrsSkipSpaces
(
pStart
);
...
...
@@ -707,6 +708,7 @@ int Wlc_PrsReadDeclaration( Wlc_Prs_t * p, char * pStart )
}
int
Wlc_PrsDerive
(
Wlc_Prs_t
*
p
)
{
Wlc_Obj_t
*
pObj
;
char
*
pStart
,
*
pName
;
int
i
;
// go through the directives
...
...
@@ -719,9 +721,21 @@ startword:
pName
=
strtok
(
pStart
+
strlen
(
"module"
),
"
\r\n\t
(,)"
);
if
(
pName
==
NULL
)
return
Wlc_PrsWriteErrorMessage
(
p
,
pStart
,
"Cannot read model name."
);
// THIS IS A HACK to skip definitions of modules beginning with "CPL_"
if
(
Wlc_PrsStrCmp
(
pName
,
"CPL_"
)
)
{
while
(
++
i
<
Vec_IntSize
(
p
->
vStarts
)
)
{
pStart
=
Wlc_PrsStr
(
p
,
Vec_IntEntry
(
p
->
vStarts
,
i
));
pStart
=
strstr
(
pStart
,
"endmodule"
);
if
(
pStart
!=
NULL
)
break
;
}
continue
;
}
if
(
Wlc_PrsStrCmp
(
pName
,
"table"
)
)
{
// THIS IS A HACK
TO DETECT table
s
// THIS IS A HACK
to detect table module description
s
int
Width1
=
-
1
,
Width2
=
-
1
;
int
v
,
b
,
Value
,
nBits
,
nInts
;
unsigned
*
pTable
;
...
...
@@ -784,6 +798,14 @@ startword:
Vec_Int_t
*
vTemp
=
Vec_IntStartNatural
(
Wlc_NtkObjNumMax
(
p
->
pNtk
)
);
Vec_IntAppend
(
&
p
->
pNtk
->
vNameIds
,
vTemp
);
Vec_IntFree
(
vTemp
);
// move FO/FI to be part of CI/CO
assert
(
(
Vec_IntSize
(
&
p
->
pNtk
->
vFfs
)
&
1
)
==
0
);
Wlc_NtkForEachFf
(
p
->
pNtk
,
pObj
,
i
)
if
(
i
&
1
)
Wlc_ObjSetCo
(
p
->
pNtk
,
pObj
,
1
);
else
Wlc_ObjSetCi
(
p
->
pNtk
,
pObj
);
Vec_IntClear
(
&
p
->
pNtk
->
vFfs
);
break
;
}
// these are read as part of the interface
...
...
@@ -807,7 +829,7 @@ startword:
Type
=
Wlc_PrsFindDefinition
(
p
,
pStart
,
p
->
vFanins
);
if
(
Type
)
{
Wlc_Obj_t
*
pObj
=
Wlc_NtkObj
(
p
->
pNtk
,
NameId
);
pObj
=
Wlc_NtkObj
(
p
->
pNtk
,
NameId
);
Wlc_ObjUpdateType
(
p
->
pNtk
,
pObj
,
Type
);
Wlc_ObjAddFanins
(
p
->
pNtk
,
pObj
,
p
->
vFanins
);
}
...
...
@@ -816,7 +838,7 @@ startword:
}
else
if
(
Wlc_PrsStrCmp
(
pStart
,
"table"
)
)
{
// THIS IS A HACK
TO DETECT
tables
// THIS IS A HACK
to detect
tables
int
NameId
,
fFound
,
iTable
=
atoi
(
pStart
+
strlen
(
"table"
)
);
// find opening
pStart
=
Wlc_PrsFindSymbol
(
pStart
,
'('
);
...
...
@@ -844,15 +866,13 @@ startword:
NameId
=
Abc_NamStrFindOrAdd
(
p
->
pNtk
->
pManName
,
pName
,
&
fFound
);
if
(
!
fFound
)
return
Wlc_PrsWriteErrorMessage
(
p
,
pStart
,
"Name %s is not declared."
,
pName
);
{
Wlc_Obj_t
*
pObj
=
Wlc_NtkObj
(
p
->
pNtk
,
NameId
);
pObj
=
Wlc_NtkObj
(
p
->
pNtk
,
NameId
);
Wlc_ObjUpdateType
(
p
->
pNtk
,
pObj
,
WLC_OBJ_TABLE
);
Wlc_ObjAddFanins
(
p
->
pNtk
,
pObj
,
p
->
vFanins
);
}
}
else
if
(
Wlc_PrsStrCmp
(
pStart
,
"always"
)
)
{
// THIS IS A HACK
TO DETECT tables
// THIS IS A HACK
to detect always statement representing combinational MUX
int
NameId
,
NameIdOut
=
-
1
,
fFound
;
// find control
pStart
=
Wlc_PrsFindWord
(
pStart
,
"case"
,
&
fFound
);
...
...
@@ -918,8 +938,7 @@ startword:
break
;
}
// check range of the control
{
Wlc_Obj_t
*
pObj
=
Wlc_NtkObj
(
p
->
pNtk
,
Vec_IntEntry
(
p
->
vFanins
,
0
)
);
pObj
=
Wlc_NtkObj
(
p
->
pNtk
,
Vec_IntEntry
(
p
->
vFanins
,
0
)
);
if
(
(
1
<<
Wlc_ObjRange
(
pObj
))
!=
Vec_IntSize
(
p
->
vFanins
)
-
1
)
return
Wlc_PrsWriteErrorMessage
(
p
,
pStart
,
"The number of values in the case statement is wrong."
,
pName
);
pObj
=
Wlc_NtkObj
(
p
->
pNtk
,
NameIdOut
);
...
...
@@ -927,9 +946,50 @@ startword:
Wlc_ObjAddFanins
(
p
->
pNtk
,
pObj
,
p
->
vFanins
);
goto
startword
;
}
}
// else if ( Wlc_PrsStrCmp( pStart, "CPL_FF" ) )
else
if
(
Wlc_PrsStrCmp
(
pStart
,
"CPL_FF"
)
)
{
int
NameId
=
-
1
,
NameIdOut
=
-
1
,
fFound
,
nBits
=
1
,
fFlopOut
;
pStart
+=
strlen
(
"CPL_FF"
);
if
(
pStart
[
0
]
==
'#'
)
nBits
=
atoi
(
pStart
+
1
);
// read names
while
(
1
)
{
pStart
=
Wlc_PrsFindSymbol
(
pStart
,
'.'
);
if
(
pStart
==
NULL
)
break
;
pStart
=
Wlc_PrsSkipSpaces
(
pStart
+
1
);
if
(
pStart
[
0
]
!=
'd'
&&
(
pStart
[
0
]
!=
'q'
||
pStart
[
1
]
==
'b'
)
)
continue
;
fFlopOut
=
(
pStart
[
0
]
==
'd'
);
pStart
=
Wlc_PrsFindSymbol
(
pStart
,
'('
);
if
(
pStart
==
NULL
)
return
Wlc_PrsWriteErrorMessage
(
p
,
pStart
,
"Cannot read opening paranthesis in the flop description."
);
pStart
=
Wlc_PrsFindName
(
pStart
+
1
,
&
pName
);
if
(
pStart
==
NULL
)
return
Wlc_PrsWriteErrorMessage
(
p
,
pStart
,
"Cannot read name inside flop description."
);
if
(
fFlopOut
)
NameIdOut
=
Abc_NamStrFindOrAdd
(
p
->
pNtk
->
pManName
,
pName
,
&
fFound
);
else
NameId
=
Abc_NamStrFindOrAdd
(
p
->
pNtk
->
pManName
,
pName
,
&
fFound
);
if
(
!
fFound
)
return
Wlc_PrsWriteErrorMessage
(
p
,
pStart
,
"Name %s is not declared."
,
pName
);
}
if
(
NameId
==
-
1
||
NameIdOut
==
-
1
)
return
Wlc_PrsWriteErrorMessage
(
p
,
pStart
,
"Name of flop input or flop output is missing."
);
// create flop output
pObj
=
Wlc_NtkObj
(
p
->
pNtk
,
NameId
);
Wlc_ObjUpdateType
(
p
->
pNtk
,
pObj
,
WLC_OBJ_FO
);
Vec_IntPush
(
&
p
->
pNtk
->
vFfs
,
NameId
);
if
(
nBits
!=
Wlc_ObjRange
(
pObj
)
)
printf
(
"Warning! Flop input has bit-width (%d) that differs from the declaration (%d)
\n
"
,
nBits
,
Wlc_ObjRange
(
pObj
)
);
// create flop input
pObj
=
Wlc_NtkObj
(
p
->
pNtk
,
NameIdOut
);
Vec_IntPush
(
&
p
->
pNtk
->
vFfs
,
NameIdOut
);
if
(
nBits
!=
Wlc_ObjRange
(
pObj
)
)
printf
(
"Warning! Flop output has bit-width (%d) that differs from the declaration (%d)
\n
"
,
nBits
,
Wlc_ObjRange
(
pObj
)
);
}
else
if
(
pStart
[
0
]
!=
'`'
)
{
pStart
=
Wlc_PrsFindName
(
pStart
,
&
pName
);
return
Wlc_PrsWriteErrorMessage
(
p
,
pStart
,
"Cannot read line beginning with %s."
,
pName
);
...
...
src/base/wlc/wlcWriteVer.c
View file @
69bd3554
...
...
@@ -151,17 +151,21 @@ void Wlc_WriteVerInt( FILE * pFile, Wlc_Ntk_t * p )
sprintf
(
Range
,
"%s[%d:%d]%*s"
,
pObj
->
Signed
?
"signed "
:
" "
,
pObj
->
End
,
pObj
->
Beg
,
8
-
nDigits
,
""
);
fprintf
(
pFile
,
" "
);
if
(
pObj
->
Type
==
WLC_OBJ_PI
)
fprintf
(
pFile
,
"input wire %s %s"
,
Range
,
pName
);
else
if
(
pObj
->
Type
==
WLC_OBJ_PO
)
fprintf
(
pFile
,
"output wire %s %-16s = %s"
,
Range
,
pName
,
pName0
);
fprintf
(
pFile
,
"input "
);
else
if
(
pObj
->
fIsPo
)
fprintf
(
pFile
,
"output "
);
else
fprintf
(
pFile
,
" "
);
if
(
Wlc_ObjIsCi
(
pObj
)
)
fprintf
(
pFile
,
"wire %s %s"
,
Range
,
pName
);
else
if
(
pObj
->
Type
==
WLC_OBJ_TABLE
)
{
// wire [3:0] s4972; table0 s4972_Index(s4971, s4972);
fprintf
(
pFile
,
"
wire %s %s ; table%d s%d_Index(%s, %s)"
,
Range
,
pName
,
Wlc_ObjTableId
(
pObj
),
i
,
pName0
,
pName
);
fprintf
(
pFile
,
"wire %s %s ; table%d s%d_Index(%s, %s)"
,
Range
,
pName
,
Wlc_ObjTableId
(
pObj
),
i
,
pName0
,
pName
);
}
else
if
(
pObj
->
Type
==
WLC_OBJ_CONST
)
{
fprintf
(
pFile
,
"
wire %s %-16s = %d
\'
%sh"
,
Range
,
pName
,
Wlc_ObjRange
(
pObj
),
pObj
->
Signed
?
"s"
:
""
);
fprintf
(
pFile
,
"wire %s %-16s = %d
\'
%sh"
,
Range
,
pName
,
Wlc_ObjRange
(
pObj
),
pObj
->
Signed
?
"s"
:
""
);
Abc_TtPrintHexArrayRev
(
pFile
,
(
word
*
)
Wlc_ObjConstValue
(
pObj
),
(
Wlc_ObjRange
(
pObj
)
+
3
)
/
4
);
}
else
if
(
pObj
->
Type
==
WLC_OBJ_ROTATE_R
||
pObj
->
Type
==
WLC_OBJ_ROTATE_L
)
...
...
@@ -172,7 +176,7 @@ void Wlc_WriteVerInt( FILE * pFile, Wlc_Ntk_t * p )
int
Num1
=
Wlc_ObjRange
(
pObj
)
-
Num0
;
assert
(
pShift
->
Type
==
WLC_OBJ_CONST
);
assert
(
Num0
>
0
&&
Num0
<
Wlc_ObjRange
(
pObj
)
);
fprintf
(
pFile
,
"
wire %s %-16s = "
,
Range
,
Wlc_ObjName
(
p
,
i
)
);
fprintf
(
pFile
,
"wire %s %-16s = "
,
Range
,
Wlc_ObjName
(
p
,
i
)
);
if
(
pObj
->
Type
==
WLC_OBJ_ROTATE_R
)
fprintf
(
pFile
,
"(%s >> %d) | (%s << %d)"
,
pName0
,
Num0
,
pName0
,
Num1
);
else
...
...
@@ -180,7 +184,7 @@ void Wlc_WriteVerInt( FILE * pFile, Wlc_Ntk_t * p )
}
else
if
(
pObj
->
Type
==
WLC_OBJ_MUX
&&
Wlc_ObjFaninNum
(
pObj
)
>
3
)
{
fprintf
(
pFile
,
"
reg %s ;
\n
"
,
pName
);
fprintf
(
pFile
,
"reg %s ;
\n
"
,
pName
);
fprintf
(
pFile
,
" "
);
fprintf
(
pFile
,
"always @( "
);
Wlc_ObjForEachFanin
(
pObj
,
iFanin
,
k
)
...
...
@@ -204,11 +208,11 @@ void Wlc_WriteVerInt( FILE * pFile, Wlc_Ntk_t * p )
}
else
{
fprintf
(
pFile
,
"
wire %s %-16s = "
,
Range
,
Wlc_ObjName
(
p
,
i
)
);
fprintf
(
pFile
,
"wire %s %-16s = "
,
Range
,
Wlc_ObjName
(
p
,
i
)
);
if
(
pObj
->
Type
==
WLC_OBJ_BUF
)
fprintf
(
pFile
,
"%s"
,
pName0
);
else
if
(
pObj
->
Type
==
WLC_OBJ_MUX
)
fprintf
(
pFile
,
"%s ? %s : %s"
,
pName0
,
Wlc_ObjName
(
p
,
Wlc_ObjFaninId
1
(
pObj
)),
Wlc_ObjName
(
p
,
Wlc_ObjFaninId2
(
pObj
))
);
fprintf
(
pFile
,
"%s ? %s : %s"
,
pName0
,
Wlc_ObjName
(
p
,
Wlc_ObjFaninId
2
(
pObj
)),
Wlc_ObjName
(
p
,
Wlc_ObjFaninId1
(
pObj
))
);
else
if
(
pObj
->
Type
==
WLC_OBJ_BIT_NOT
)
fprintf
(
pFile
,
"~%s"
,
pName0
);
else
if
(
pObj
->
Type
==
WLC_OBJ_LOGIC_NOT
)
...
...
@@ -283,6 +287,27 @@ void Wlc_WriteVerInt( FILE * pFile, Wlc_Ntk_t * p )
}
fprintf
(
pFile
,
" ;
\n
"
);
}
Wlc_NtkForEachCi
(
p
,
pObj
,
i
)
{
char
*
pName
=
Wlc_ObjName
(
p
,
Wlc_ObjId
(
p
,
pObj
));
assert
(
i
==
Wlc_ObjCiId
(
pObj
)
);
if
(
pObj
->
Type
==
WLC_OBJ_PI
)
continue
;
// CPL_FF#9 I_32890_reg ( .q ( E_42304 ) , .qbar ( ) , .d ( E_42305 ) , .clk ( E_65040 ) ,
// .arst ( E_65037 ) , .arstval ( E_62126 ) );
fprintf
(
pFile
,
" "
);
fprintf
(
pFile
,
"CPL_FF"
);
if
(
Wlc_ObjRange
(
pObj
)
>
1
)
fprintf
(
pFile
,
"#%d"
,
Wlc_ObjRange
(
pObj
)
);
fprintf
(
pFile
,
" %reg%d ("
,
i
);
fprintf
(
pFile
,
" .q( %s ),"
,
pName
);
fprintf
(
pFile
,
" .qbar(),"
);
fprintf
(
pFile
,
" .d( %s ),"
,
Wlc_ObjName
(
p
,
Wlc_ObjId
(
p
,
Wlc_ObjFoToFi
(
p
,
pObj
)))
);
fprintf
(
pFile
,
" .clk( %s ),"
,
"1
\'
b0"
);
fprintf
(
pFile
,
" .arst( %s ),"
,
"1
\'
b0"
);
fprintf
(
pFile
,
" .arstval( %s )"
,
"1
\'
b0"
);
fprintf
(
pFile
,
" ) ;
\n
"
);
}
fprintf
(
pFile
,
"endmodule
\n\n
"
);
}
void
Wlc_WriteVer
(
Wlc_Ntk_t
*
p
,
char
*
pFileName
)
...
...
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