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
5a6205ec
Commit
5a6205ec
authored
Jun 30, 2013
by
Alan Mishchenko
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Updating new mapper.
parent
6ace5234
Show whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
138 additions
and
75 deletions
+138
-75
src/aig/gia/giaTest.c
+138
-75
No files found.
src/aig/gia/giaTest.c
View file @
5a6205ec
...
@@ -47,14 +47,14 @@ typedef struct Mig_Obj_t_ Mig_Obj_t;
...
@@ -47,14 +47,14 @@ typedef struct Mig_Obj_t_ Mig_Obj_t;
struct
Mig_Obj_t_
struct
Mig_Obj_t_
{
{
Mig_Fan_t
pFans
[
4
];
// fanins
Mig_Fan_t
pFans
[
4
];
// fanins
int
hCutBest
;
// cut best
/*
int
hCutList
;
// cut list
int nMapRefs; // exact mapping references
int nMapRefs; // exact mapping references
int nEstRefs; // estimated mapping references
int nEstRefs; // estimated mapping references
int mRequired; // required time
int mRequired; // required time
int mTime; // arrival time
int mTime; // arrival time
int mArea; // area
int mArea; // area
int mEdge; // edge
int mEdge; // edge
*/
};
};
typedef
struct
Mig_Man_t_
Mig_Man_t
;
typedef
struct
Mig_Man_t_
Mig_Man_t
;
...
@@ -77,6 +77,7 @@ struct Mig_Man_t_
...
@@ -77,6 +77,7 @@ struct Mig_Man_t_
Vec_Int_t
vRefs
;
// ref counters
Vec_Int_t
vRefs
;
// ref counters
Vec_Int_t
vRefs2
;
// ref counters
Vec_Int_t
vRefs2
;
// ref counters
Vec_Int_t
vSibls
;
// choice nodes
Vec_Int_t
vSibls
;
// choice nodes
void
*
pMan
;
// mapping manager
};
};
/*
/*
...
@@ -385,6 +386,7 @@ static inline int Mig_ManMemory( Mig_Man_t * p )
...
@@ -385,6 +386,7 @@ static inline int Mig_ManMemory( Mig_Man_t * p )
return
Vec_PtrSize
(
&
p
->
vPages
)
*
(
MIG_MASK
+
1
)
*
sizeof
(
Mig_Obj_t
);
return
Vec_PtrSize
(
&
p
->
vPages
)
*
(
MIG_MASK
+
1
)
*
sizeof
(
Mig_Obj_t
);
}
}
/**Function*************************************************************
/**Function*************************************************************
Synopsis []
Synopsis []
...
@@ -615,6 +617,15 @@ struct Mpm_Man_t_
...
@@ -615,6 +617,15 @@ struct Mpm_Man_t_
// Dsd_Man_t * pManDsd;
// Dsd_Man_t * pManDsd;
void
*
pManDsd
;
void
*
pManDsd
;
int
pPerm
[
MPM_VAR_MAX
];
int
pPerm
[
MPM_VAR_MAX
];
// mapping attributes
Vec_Int_t
vCutBests
;
// cut best
Vec_Int_t
vCutLists
;
// cut list
Vec_Int_t
vMapRefs
;
// exact mapping references
Vec_Int_t
vEstRefs
;
// estimated mapping references
Vec_Int_t
vRequireds
;
// required time
Vec_Int_t
vTimes
;
// arrival time
Vec_Int_t
vAreas
;
// area
Vec_Int_t
vEdges
;
// edge
// statistics
// statistics
int
nCutsMerged
;
int
nCutsMerged
;
abctime
timeFanin
;
abctime
timeFanin
;
...
@@ -627,11 +638,39 @@ struct Mpm_Man_t_
...
@@ -627,11 +638,39 @@ struct Mpm_Man_t_
abctime
timeTotal
;
abctime
timeTotal
;
};
};
static
inline
int
Mpm_ObjCutBest
(
Mpm_Man_t
*
p
,
Mig_Obj_t
*
pObj
)
{
return
Vec_IntEntry
(
&
p
->
vCutBests
,
Mig_ObjId
(
pObj
));
}
static
inline
void
Mpm_ObjSetCutBest
(
Mpm_Man_t
*
p
,
Mig_Obj_t
*
pObj
,
int
i
)
{
Vec_IntWriteEntry
(
&
p
->
vCutBests
,
Mig_ObjId
(
pObj
),
i
);
}
static
inline
int
Mpm_ObjCutList
(
Mpm_Man_t
*
p
,
Mig_Obj_t
*
pObj
)
{
return
Vec_IntEntry
(
&
p
->
vCutLists
,
Mig_ObjId
(
pObj
));
}
static
inline
int
*
Mpm_ObjCutListP
(
Mpm_Man_t
*
p
,
Mig_Obj_t
*
pObj
)
{
return
Vec_IntEntryP
(
&
p
->
vCutLists
,
Mig_ObjId
(
pObj
));
}
static
inline
void
Mpm_ObjSetCutList
(
Mpm_Man_t
*
p
,
Mig_Obj_t
*
pObj
,
int
i
)
{
Vec_IntWriteEntry
(
&
p
->
vCutLists
,
Mig_ObjId
(
pObj
),
i
);
}
static
inline
int
Mpm_ObjMapRef
(
Mpm_Man_t
*
p
,
Mig_Obj_t
*
pObj
)
{
return
Vec_IntEntry
(
&
p
->
vMapRefs
,
Mig_ObjId
(
pObj
));
}
static
inline
void
Mpm_ObjSetMapRef
(
Mpm_Man_t
*
p
,
Mig_Obj_t
*
pObj
,
int
i
)
{
Vec_IntWriteEntry
(
&
p
->
vMapRefs
,
Mig_ObjId
(
pObj
),
i
);
}
static
inline
void
Mpm_ManCleanMapRefs
(
Mpm_Man_t
*
p
)
{
Vec_IntFill
(
&
p
->
vMapRefs
,
Mig_ManObjNum
(
p
->
pMig
),
0
);
}
static
inline
int
Mpm_ObjEstRef
(
Mpm_Man_t
*
p
,
Mig_Obj_t
*
pObj
)
{
return
Vec_IntEntry
(
&
p
->
vEstRefs
,
Mig_ObjId
(
pObj
));
}
static
inline
void
Mpm_ObjSetEstRef
(
Mpm_Man_t
*
p
,
Mig_Obj_t
*
pObj
,
int
i
)
{
Vec_IntWriteEntry
(
&
p
->
vEstRefs
,
Mig_ObjId
(
pObj
),
i
);
}
static
inline
int
Mpm_ObjRequired
(
Mpm_Man_t
*
p
,
Mig_Obj_t
*
pObj
)
{
return
Vec_IntEntry
(
&
p
->
vRequireds
,
Mig_ObjId
(
pObj
));
}
static
inline
void
Mpm_ObjSetRequired
(
Mpm_Man_t
*
p
,
Mig_Obj_t
*
pObj
,
int
i
)
{
Vec_IntWriteEntry
(
&
p
->
vRequireds
,
Mig_ObjId
(
pObj
),
i
);
}
static
inline
void
Mpm_ManCleanRequired
(
Mpm_Man_t
*
p
)
{
Vec_IntFill
(
&
p
->
vRequireds
,
Mig_ManObjNum
(
p
->
pMig
),
ABC_INFINITY
);}
static
inline
int
Mpm_ObjTime
(
Mpm_Man_t
*
p
,
Mig_Obj_t
*
pObj
)
{
return
Vec_IntEntry
(
&
p
->
vTimes
,
Mig_ObjId
(
pObj
));
}
static
inline
void
Mpm_ObjSetTime
(
Mpm_Man_t
*
p
,
Mig_Obj_t
*
pObj
,
int
i
)
{
Vec_IntWriteEntry
(
&
p
->
vTimes
,
Mig_ObjId
(
pObj
),
i
);
}
static
inline
int
Mpm_ObjArea
(
Mpm_Man_t
*
p
,
Mig_Obj_t
*
pObj
)
{
return
Vec_IntEntry
(
&
p
->
vAreas
,
Mig_ObjId
(
pObj
));
}
static
inline
void
Mpm_ObjSetArea
(
Mpm_Man_t
*
p
,
Mig_Obj_t
*
pObj
,
int
i
)
{
Vec_IntWriteEntry
(
&
p
->
vAreas
,
Mig_ObjId
(
pObj
),
i
);
}
static
inline
int
Mpm_ObjEdge
(
Mpm_Man_t
*
p
,
Mig_Obj_t
*
pObj
)
{
return
Vec_IntEntry
(
&
p
->
vEdges
,
Mig_ObjId
(
pObj
));
}
static
inline
void
Mpm_ObjSetEdge
(
Mpm_Man_t
*
p
,
Mig_Obj_t
*
pObj
,
int
i
)
{
Vec_IntWriteEntry
(
&
p
->
vEdges
,
Mig_ObjId
(
pObj
),
i
);
}
// iterators over object cuts
// iterators over object cuts
#define Mpm_ObjForEachCut( p, pObj, hCut, pCut ) \
#define Mpm_ObjForEachCut( p, pObj, hCut, pCut ) \
for ( hCut =
pObj->hCutList
; hCut && (pCut = Mpm_CutFetch(p, hCut)); hCut = pCut->hNext )
for ( hCut =
Mpm_ObjCutList(p, pObj)
; hCut && (pCut = Mpm_CutFetch(p, hCut)); hCut = pCut->hNext )
#define Mpm_ObjForEachCutSafe( p, pObj, hCut, pCut, hNext ) \
#define Mpm_ObjForEachCutSafe( p, pObj, hCut, pCut, hNext ) \
for ( hCut =
pObj->hCutList
; hCut && (pCut = Mpm_CutFetch(p, hCut)) && ((hNext = pCut->hNext), 1); hCut = hNext )
for ( hCut =
Mpm_ObjCutList(p, pObj)
; hCut && (pCut = Mpm_CutFetch(p, hCut)) && ((hNext = pCut->hNext), 1); hCut = hNext )
// iterators over cut leaves
// iterators over cut leaves
#define Mpm_CutForEachLeafId( pCut, iLeafId, i ) \
#define Mpm_CutForEachLeafId( pCut, iLeafId, i ) \
...
@@ -666,9 +705,9 @@ static inline Mpm_Cut_t * Mpm_CutFetch( Mpm_Man_t * p, int h )
...
@@ -666,9 +705,9 @@ static inline Mpm_Cut_t * Mpm_CutFetch( Mpm_Man_t * p, int h )
assert
(
Mpm_CutWordNum
(
pCut
->
nLeaves
)
==
(
h
&
p
->
pManCuts
->
uMask
)
);
assert
(
Mpm_CutWordNum
(
pCut
->
nLeaves
)
==
(
h
&
p
->
pManCuts
->
uMask
)
);
return
pCut
;
return
pCut
;
}
}
static
inline
Mpm_Cut_t
*
Mpm_ObjCutBest
(
Mpm_Man_t
*
p
,
Mig_Obj_t
*
pObj
)
static
inline
Mpm_Cut_t
*
Mpm_ObjCutBest
P
(
Mpm_Man_t
*
p
,
Mig_Obj_t
*
pObj
)
{
{
return
Mpm_CutFetch
(
p
,
pObj
->
hCutBest
);
return
Mpm_CutFetch
(
p
,
Mpm_ObjCutBest
(
p
,
pObj
)
);
}
}
static
inline
int
Mpm_CutCreateZero
(
Mpm_Man_t
*
p
)
static
inline
int
Mpm_CutCreateZero
(
Mpm_Man_t
*
p
)
{
{
...
@@ -715,6 +754,7 @@ static inline int Mpm_CutCopySet( Mpm_Man_t * p, Mig_Obj_t * pObj, int fCompl )
...
@@ -715,6 +754,7 @@ static inline int Mpm_CutCopySet( Mpm_Man_t * p, Mig_Obj_t * pObj, int fCompl )
*
pList
=
0
;
*
pList
=
0
;
return
iList
;
return
iList
;
}
}
/*
static inline void Mpm_CutRef( Mpm_Man_t * p, int * pLeaves, int nLeaves )
static inline void Mpm_CutRef( Mpm_Man_t * p, int * pLeaves, int nLeaves )
{
{
int i;
int i;
...
@@ -727,6 +767,7 @@ static inline void Mpm_CutDeref( Mpm_Man_t * p, int * pLeaves, int nLeaves )
...
@@ -727,6 +767,7 @@ static inline void Mpm_CutDeref( Mpm_Man_t * p, int * pLeaves, int nLeaves )
for ( i = 0; i < nLeaves; i++ )
for ( i = 0; i < nLeaves; i++ )
Mig_ManObj( p->pMig, Abc_Lit2Var(pLeaves[i]) )->nMapRefs--;
Mig_ManObj( p->pMig, Abc_Lit2Var(pLeaves[i]) )->nMapRefs--;
}
}
*/
static
inline
void
Mpm_CutPrint
(
int
*
pLeaves
,
int
nLeaves
)
static
inline
void
Mpm_CutPrint
(
int
*
pLeaves
,
int
nLeaves
)
{
{
int
i
;
int
i
;
...
@@ -764,7 +805,7 @@ int Mpm_ManNodeIfToGia_rec( Gia_Man_t * pNew, Mpm_Man_t * pMan, Mig_Obj_t * pObj
...
@@ -764,7 +805,7 @@ int Mpm_ManNodeIfToGia_rec( Gia_Man_t * pNew, Mpm_Man_t * pMan, Mig_Obj_t * pObj
Mpm_Cut_t
*
pCut
;
Mpm_Cut_t
*
pCut
;
int
iFunc
,
iFunc0
,
iFunc1
;
int
iFunc
,
iFunc0
,
iFunc1
;
// get the best cut
// get the best cut
pCut
=
Mpm_ObjCutBest
(
pMan
,
pObj
);
pCut
=
Mpm_ObjCutBest
P
(
pMan
,
pObj
);
// if the cut is visited, return the result
// if the cut is visited, return the result
if
(
Mpm_CutDataInt
(
pCut
)
)
if
(
Mpm_CutDataInt
(
pCut
)
)
return
Mpm_CutDataInt
(
pCut
);
return
Mpm_CutDataInt
(
pCut
);
...
@@ -802,11 +843,11 @@ int Mpm_ManNodeIfToGia( Gia_Man_t * pNew, Mpm_Man_t * pMan, Mig_Obj_t * pObj, Ve
...
@@ -802,11 +843,11 @@ int Mpm_ManNodeIfToGia( Gia_Man_t * pNew, Mpm_Man_t * pMan, Mig_Obj_t * pObj, Ve
Mig_Obj_t
*
pFanin
;
Mig_Obj_t
*
pFanin
;
int
i
,
iRes
;
int
i
,
iRes
;
// get the best cut
// get the best cut
pCut
=
Mpm_ObjCutBest
(
pMan
,
pObj
);
pCut
=
Mpm_ObjCutBest
P
(
pMan
,
pObj
);
assert
(
pCut
->
nLeaves
>
1
);
assert
(
pCut
->
nLeaves
>
1
);
// set the leaf variables
// set the leaf variables
Mpm_CutForEachLeaf
(
pMan
->
pMig
,
pCut
,
pFanin
,
i
)
Mpm_CutForEachLeaf
(
pMan
->
pMig
,
pCut
,
pFanin
,
i
)
Mpm_CutSetDataInt
(
Mpm_ObjCutBest
(
pMan
,
pFanin
),
Vec_IntEntry
(
vLeaves
,
i
)
);
Mpm_CutSetDataInt
(
Mpm_ObjCutBest
P
(
pMan
,
pFanin
),
Vec_IntEntry
(
vLeaves
,
i
)
);
// recursively compute the function while collecting visited cuts
// recursively compute the function while collecting visited cuts
Vec_PtrClear
(
pMan
->
vTemp
);
Vec_PtrClear
(
pMan
->
vTemp
);
iRes
=
Mpm_ManNodeIfToGia_rec
(
pNew
,
pMan
,
pObj
,
pMan
->
vTemp
,
fHash
);
iRes
=
Mpm_ManNodeIfToGia_rec
(
pNew
,
pMan
,
pObj
,
pMan
->
vTemp
,
fHash
);
...
@@ -817,7 +858,7 @@ int Mpm_ManNodeIfToGia( Gia_Man_t * pNew, Mpm_Man_t * pMan, Mig_Obj_t * pObj, Ve
...
@@ -817,7 +858,7 @@ int Mpm_ManNodeIfToGia( Gia_Man_t * pNew, Mpm_Man_t * pMan, Mig_Obj_t * pObj, Ve
}
}
// clean the cuts
// clean the cuts
Mpm_CutForEachLeaf
(
pMan
->
pMig
,
pCut
,
pFanin
,
i
)
Mpm_CutForEachLeaf
(
pMan
->
pMig
,
pCut
,
pFanin
,
i
)
Mpm_CutSetDataInt
(
Mpm_ObjCutBest
(
pMan
,
pFanin
),
0
);
Mpm_CutSetDataInt
(
Mpm_ObjCutBest
P
(
pMan
,
pFanin
),
0
);
Vec_PtrForEachEntry
(
Mpm_Cut_t
*
,
pMan
->
vTemp
,
pCut
,
i
)
Vec_PtrForEachEntry
(
Mpm_Cut_t
*
,
pMan
->
vTemp
,
pCut
,
i
)
Mpm_CutSetDataInt
(
pCut
,
0
);
Mpm_CutSetDataInt
(
pCut
,
0
);
return
iRes
;
return
iRes
;
...
@@ -848,13 +889,13 @@ Gia_Man_t * Mpm_ManFromIfLogic( Mpm_Man_t * pMan )
...
@@ -848,13 +889,13 @@ Gia_Man_t * Mpm_ManFromIfLogic( Mpm_Man_t * pMan )
Mig_ManCleanCopy
(
pMan
->
pMig
);
Mig_ManCleanCopy
(
pMan
->
pMig
);
Mig_ManForEachObj
(
pMan
->
pMig
,
pObj
)
Mig_ManForEachObj
(
pMan
->
pMig
,
pObj
)
{
{
if
(
pObj
->
nMapRefs
==
0
&&
!
Mig_ObjIsTerm
(
pObj
)
)
if
(
!
Mpm_ObjMapRef
(
pMan
,
pObj
)
&&
!
Mig_ObjIsTerm
(
pObj
)
)
continue
;
continue
;
if
(
Mig_ObjIsNode
(
pObj
)
)
if
(
Mig_ObjIsNode
(
pObj
)
)
{
{
// collect leaves of the best cut
// collect leaves of the best cut
Vec_IntClear
(
vLeaves
);
Vec_IntClear
(
vLeaves
);
pCutBest
=
Mpm_ObjCutBest
(
pMan
,
pObj
);
pCutBest
=
Mpm_ObjCutBest
P
(
pMan
,
pObj
);
Mpm_CutForEachLeaf
(
pMan
->
pMig
,
pCutBest
,
pFanin
,
k
)
Mpm_CutForEachLeaf
(
pMan
->
pMig
,
pCutBest
,
pFanin
,
k
)
Vec_IntPush
(
vLeaves
,
Mig_ObjCopy
(
pFanin
)
);
Vec_IntPush
(
vLeaves
,
Mig_ObjCopy
(
pFanin
)
);
// perform one of the two types of mapping: with and without structures
// perform one of the two types of mapping: with and without structures
...
@@ -1021,35 +1062,38 @@ static inline word Mpm_CutGetSign( Mpm_Cut_t * pCut )
...
@@ -1021,35 +1062,38 @@ static inline word Mpm_CutGetSign( Mpm_Cut_t * pCut )
}
}
static
inline
int
Mpm_CutGetArrTime
(
Mpm_Man_t
*
p
,
Mpm_Cut_t
*
pCut
)
static
inline
int
Mpm_CutGetArrTime
(
Mpm_Man_t
*
p
,
Mpm_Cut_t
*
pCut
)
{
{
Mig_Obj_t
*
pLeaf
;
int
*
pDelays
=
p
->
pLibLut
->
pLutDelays
[
pCut
->
nLeaves
];
int
*
pDelays
=
p
->
pLibLut
->
pLutDelays
[
pCut
->
nLeaves
];
int
i
,
ArrTime
=
0
;
int
*
pmTimes
=
Vec_IntArray
(
&
p
->
vTimes
);
Mpm_CutForEachLeaf
(
p
->
pMig
,
pCut
,
pLeaf
,
i
)
int
i
,
iLeaf
,
ArrTime
=
0
;
ArrTime
=
Abc_MaxInt
(
ArrTime
,
pLeaf
->
mTime
+
pDelays
[
i
]
);
Mpm_CutForEachLeafId
(
pCut
,
iLeaf
,
i
)
ArrTime
=
Abc_MaxInt
(
ArrTime
,
pmTimes
[
iLeaf
]
+
pDelays
[
i
]
);
return
ArrTime
;
return
ArrTime
;
}
}
static
inline
void
Mpm_CutSetupInfo
(
Mpm_Man_t
*
p
,
Mpm_Cut_t
*
pCut
,
int
ArrTime
,
Mpm_Inf_t
*
pInfo
)
static
inline
void
Mpm_CutSetupInfo
(
Mpm_Man_t
*
p
,
Mpm_Cut_t
*
pCut
,
int
ArrTime
,
Mpm_Inf_t
*
pInfo
)
{
{
Mig_Obj_t
*
pLeaf
;
int
*
pmMapRefs
=
Vec_IntArray
(
&
p
->
vMapRefs
);
int
i
;
int
*
pmEstRefs
=
Vec_IntArray
(
&
p
->
vEstRefs
);
int
*
pmArea
=
Vec_IntArray
(
&
p
->
vAreas
);
int
*
pmEdge
=
Vec_IntArray
(
&
p
->
vEdges
);
int
i
,
iLeaf
;
memset
(
pInfo
,
0
,
sizeof
(
Mpm_Inf_t
)
);
memset
(
pInfo
,
0
,
sizeof
(
Mpm_Inf_t
)
);
pInfo
->
nLeaves
=
pCut
->
nLeaves
;
pInfo
->
nLeaves
=
pCut
->
nLeaves
;
pInfo
->
mTime
=
ArrTime
;
pInfo
->
mTime
=
ArrTime
;
pInfo
->
mArea
=
p
->
pLibLut
->
pLutAreas
[
pCut
->
nLeaves
];
pInfo
->
mArea
=
p
->
pLibLut
->
pLutAreas
[
pCut
->
nLeaves
];
pInfo
->
mEdge
=
MPM_UNIT_EDGE
*
pCut
->
nLeaves
;
pInfo
->
mEdge
=
MPM_UNIT_EDGE
*
pCut
->
nLeaves
;
Mpm_CutForEachLeaf
(
p
->
pMig
,
pCut
,
p
Leaf
,
i
)
Mpm_CutForEachLeaf
Id
(
pCut
,
i
Leaf
,
i
)
{
{
if
(
p
->
fMainRun
&&
p
Leaf
->
nMapRefs
==
0
)
// not used in the mapping
if
(
p
->
fMainRun
&&
p
mMapRefs
[
iLeaf
]
==
0
)
// not used in the mapping
{
{
pInfo
->
mArea
+=
p
Leaf
->
mArea
;
pInfo
->
mArea
+=
p
mArea
[
iLeaf
]
;
pInfo
->
mEdge
+=
p
Leaf
->
mEdge
;
pInfo
->
mEdge
+=
p
mEdge
[
iLeaf
]
;
}
}
else
else
{
{
assert
(
p
Leaf
->
nEstRefs
>
0
);
assert
(
p
mEstRefs
[
iLeaf
]
>
0
);
pInfo
->
mArea
+=
MPM_UNIT_REFS
*
p
Leaf
->
mArea
/
pLeaf
->
nEstRefs
;
pInfo
->
mArea
+=
MPM_UNIT_REFS
*
p
mArea
[
iLeaf
]
/
pmEstRefs
[
iLeaf
]
;
pInfo
->
mEdge
+=
MPM_UNIT_REFS
*
p
Leaf
->
mEdge
/
pLeaf
->
nEstRefs
;
pInfo
->
mEdge
+=
MPM_UNIT_REFS
*
p
mEdge
[
iLeaf
]
/
pmEstRefs
[
iLeaf
]
;
// pInfo->mAveRefs += MPM_UNIT_EDGE * p
Leaf->nMapRefs
;
// pInfo->mAveRefs += MPM_UNIT_EDGE * p
mMapRefs[iLeaf]
;
}
}
pInfo
->
uSign
|=
((
word
)
1
<<
(
Abc_Lit2Var
(
pCut
->
pLeaves
[
i
])
&
0x3F
));
pInfo
->
uSign
|=
((
word
)
1
<<
(
Abc_Lit2Var
(
pCut
->
pLeaves
[
i
])
&
0x3F
));
}
}
...
@@ -1246,7 +1290,7 @@ void Mpm_ObjTranslateCutsFromStore( Mpm_Man_t * p, Mig_Obj_t * pObj, int fAddUni
...
@@ -1246,7 +1290,7 @@ void Mpm_ObjTranslateCutsFromStore( Mpm_Man_t * p, Mig_Obj_t * pObj, int fAddUni
{
{
Mpm_Cut_t
*
pCut
;
Mpm_Cut_t
*
pCut
;
Mpm_Uni_t
*
pUnit
;
Mpm_Uni_t
*
pUnit
;
int
i
,
*
pList
=
&
pObj
->
hCutList
;
int
i
,
*
pList
=
Mpm_ObjCutListP
(
p
,
pObj
)
;
assert
(
p
->
nCutStore
>
0
&&
p
->
nCutStore
<=
p
->
nNumCuts
);
assert
(
p
->
nCutStore
>
0
&&
p
->
nCutStore
<=
p
->
nNumCuts
);
// translate cuts
// translate cuts
*
pList
=
0
;
*
pList
=
0
;
...
@@ -1272,53 +1316,45 @@ void Mpm_ObjTranslateCutsFromStore( Mpm_Man_t * p, Mig_Obj_t * pObj, int fAddUni
...
@@ -1272,53 +1316,45 @@ void Mpm_ObjTranslateCutsFromStore( Mpm_Man_t * p, Mig_Obj_t * pObj, int fAddUni
SeeAlso []
SeeAlso []
***********************************************************************/
***********************************************************************/
static
inline
void
Mpm_ManResetRequired
(
Mpm_Man_t
*
p
)
{
Mig_Obj_t
*
pObj
;
Mig_ManForEachObj
(
p
->
pMig
,
pObj
)
{
pObj
->
mRequired
=
ABC_INFINITY
;
pObj
->
nMapRefs
=
0
;
}
}
static
inline
int
Mpm_ManFindArrivalMax
(
Mpm_Man_t
*
p
)
static
inline
int
Mpm_ManFindArrivalMax
(
Mpm_Man_t
*
p
)
{
{
int
*
pmTimes
=
Vec_IntArray
(
&
p
->
vTimes
);
Mig_Obj_t
*
pObj
;
Mig_Obj_t
*
pObj
;
int
i
,
ArrMax
=
0
;
int
i
,
ArrMax
=
0
;
Mig_ManForEachCo
(
p
->
pMig
,
pObj
,
i
)
Mig_ManForEachCo
(
p
->
pMig
,
pObj
,
i
)
ArrMax
=
Abc_MaxInt
(
ArrMax
,
Mig_ObjFanin0
(
pObj
)
->
mTime
);
ArrMax
=
Abc_MaxInt
(
ArrMax
,
pmTimes
[
Mig_ObjFaninId0
(
pObj
)
]
);
return
ArrMax
;
return
ArrMax
;
}
}
static
inline
void
Mpm_ManFinalizeRound
(
Mpm_Man_t
*
p
)
static
inline
void
Mpm_ManFinalizeRound
(
Mpm_Man_t
*
p
)
{
{
int
*
pMapRefs
=
Vec_IntArray
(
&
p
->
vMapRefs
);
int
*
pRequired
=
Vec_IntArray
(
&
p
->
vRequireds
);
Mig_Obj_t
*
pObj
;
Mig_Obj_t
*
pObj
;
Mig_Obj_t
*
pFanin
;
Mpm_Cut_t
*
pCut
;
Mpm_Cut_t
*
pCut
;
int
*
pDelays
;
int
*
pDelays
;
int
i
,
Required
;
int
i
,
iLeaf
;
p
->
GloArea
=
0
;
p
->
GloArea
=
0
;
p
->
GloEdge
=
0
;
p
->
GloEdge
=
0
;
p
->
GloRequired
=
Mpm_ManFindArrivalMax
(
p
);
p
->
GloRequired
=
Mpm_ManFindArrivalMax
(
p
);
Mpm_ManResetRequired
(
p
);
Mpm_ManCleanMapRefs
(
p
);
Mpm_ManCleanRequired
(
p
);
Mig_ManForEachObjReverse
(
p
->
pMig
,
pObj
)
Mig_ManForEachObjReverse
(
p
->
pMig
,
pObj
)
{
{
if
(
Mig_ObjIsCo
(
pObj
)
)
if
(
Mig_ObjIsCo
(
pObj
)
)
{
{
pFanin
=
Mig_ObjFanin0
(
pObj
);
pRequired
[
Mig_ObjFaninId0
(
pObj
)]
=
p
->
GloRequired
;
pFanin
->
mRequired
=
p
->
GloRequired
;
pMapRefs
[
Mig_ObjFaninId0
(
pObj
)]
++
;
pFanin
->
nMapRefs
++
;
}
}
else
if
(
Mig_ObjIsNode
(
pObj
)
)
else
if
(
Mig_ObjIsNode
(
pObj
)
)
{
{
if
(
p
Obj
->
nMapRefs
>
0
)
if
(
p
MapRefs
[
Mig_ObjId
(
pObj
)]
)
{
{
pCut
=
Mpm_ObjCutBest
(
p
,
pObj
);
pCut
=
Mpm_ObjCutBestP
(
p
,
pObj
);
pDelays
=
p
->
pLibLut
->
pLutDelays
[
pCut
->
nLeaves
];
pDelays
=
p
->
pLibLut
->
pLutDelays
[
pCut
->
nLeaves
];
Required
=
pObj
->
mRequired
;
Mpm_CutForEachLeafId
(
pCut
,
iLeaf
,
i
)
Mpm_CutForEachLeaf
(
p
->
pMig
,
pCut
,
pFanin
,
i
)
{
{
p
Fanin
->
mRequired
=
Abc_MinInt
(
pFanin
->
mRequired
,
Required
-
pDelays
[
i
]
);
p
Required
[
iLeaf
]
=
Abc_MinInt
(
pRequired
[
iLeaf
],
pRequired
[
Mig_ObjId
(
pObj
)]
-
pDelays
[
i
]
);
p
Fanin
->
nMapRefs
++
;
p
MapRefs
[
iLeaf
]
++
;
}
}
p
->
GloArea
+=
p
->
pLibLut
->
pLutAreas
[
pCut
->
nLeaves
];
p
->
GloArea
+=
p
->
pLibLut
->
pLutAreas
[
pCut
->
nLeaves
];
p
->
GloEdge
+=
pCut
->
nLeaves
;
p
->
GloEdge
+=
pCut
->
nLeaves
;
...
@@ -1328,8 +1364,9 @@ static inline void Mpm_ManFinalizeRound( Mpm_Man_t * p )
...
@@ -1328,8 +1364,9 @@ static inline void Mpm_ManFinalizeRound( Mpm_Man_t * p )
{
{
}
}
// pObj->EstRefs = (float)((2.0 * pObj->EstRefs + pObj->nRefs) / 3.0);
// pObj->EstRefs = (float)((2.0 * pObj->EstRefs + pObj->nRefs) / 3.0);
if
(
p
->
fMainRun
)
pObj
->
nEstRefs
=
(
2
*
pObj
->
nEstRefs
+
MPM_UNIT_REFS
*
pObj
->
nMapRefs
)
/
3
;
// if ( p->fMainRun )
// pObj->nEstRefs = (2 * pObj->nEstRefs + MPM_UNIT_REFS * pObj->nMapRefs) / 3;
}
}
p
->
GloArea
/=
MPM_UNIT_AREA
;
p
->
GloArea
/=
MPM_UNIT_AREA
;
}
}
...
@@ -1400,8 +1437,18 @@ static inline Mpm_Man_t * Mpm_ManStart( Mig_Man_t * pMig, Mpm_LibLut_t * pLib, i
...
@@ -1400,8 +1437,18 @@ static inline Mpm_Man_t * Mpm_ManStart( Mig_Man_t * pMig, Mpm_LibLut_t * pLib, i
p
->
pCutTemp
=
(
Mpm_Cut_t
*
)
ABC_CALLOC
(
word
,
Mpm_CutWordNum
(
p
->
nLutSize
)
);
p
->
pCutTemp
=
(
Mpm_Cut_t
*
)
ABC_CALLOC
(
word
,
Mpm_CutWordNum
(
p
->
nLutSize
)
);
Vec_StrGrow
(
&
p
->
vObjShared
,
32
);
Vec_StrGrow
(
&
p
->
vObjShared
,
32
);
p
->
vTemp
=
Vec_PtrAlloc
(
1000
);
p
->
vTemp
=
Vec_PtrAlloc
(
1000
);
// mapping attributes
Vec_IntFill
(
&
p
->
vCutBests
,
Mig_ManObjNum
(
pMig
),
0
);
Vec_IntFill
(
&
p
->
vCutLists
,
Mig_ManObjNum
(
pMig
),
0
);
Vec_IntFill
(
&
p
->
vMapRefs
,
Mig_ManObjNum
(
pMig
),
0
);
Vec_IntFill
(
&
p
->
vEstRefs
,
Mig_ManObjNum
(
pMig
),
0
);
Vec_IntFill
(
&
p
->
vRequireds
,
Mig_ManObjNum
(
pMig
),
0
);
Vec_IntFill
(
&
p
->
vTimes
,
Mig_ManObjNum
(
pMig
),
0
);
Vec_IntFill
(
&
p
->
vAreas
,
Mig_ManObjNum
(
pMig
),
0
);
Vec_IntFill
(
&
p
->
vEdges
,
Mig_ManObjNum
(
pMig
),
0
);
// start DSD manager
// start DSD manager
p
->
pManDsd
=
NULL
;
p
->
pManDsd
=
NULL
;
pMig
->
pMan
=
p
;
return
p
;
return
p
;
}
}
static
inline
void
Mpm_ManStop
(
Mpm_Man_t
*
p
)
static
inline
void
Mpm_ManStop
(
Mpm_Man_t
*
p
)
...
@@ -1412,6 +1459,15 @@ static inline void Mpm_ManStop( Mpm_Man_t * p )
...
@@ -1412,6 +1459,15 @@ static inline void Mpm_ManStop( Mpm_Man_t * p )
ABC_FREE
(
p
->
vObjShared
.
pArray
);
ABC_FREE
(
p
->
vObjShared
.
pArray
);
ABC_FREE
(
p
->
pCutTemp
);
ABC_FREE
(
p
->
pCutTemp
);
ABC_FREE
(
p
->
pObjPres
);
ABC_FREE
(
p
->
pObjPres
);
// mapping attributes
ABC_FREE
(
p
->
vCutBests
.
pArray
);
ABC_FREE
(
p
->
vCutLists
.
pArray
);
ABC_FREE
(
p
->
vMapRefs
.
pArray
);
ABC_FREE
(
p
->
vEstRefs
.
pArray
);
ABC_FREE
(
p
->
vRequireds
.
pArray
);
ABC_FREE
(
p
->
vTimes
.
pArray
);
ABC_FREE
(
p
->
vAreas
.
pArray
);
ABC_FREE
(
p
->
vEdges
.
pArray
);
ABC_FREE
(
p
);
ABC_FREE
(
p
);
}
}
static
inline
void
Mpm_ManPrintStatsInit
(
Mpm_Man_t
*
p
)
static
inline
void
Mpm_ManPrintStatsInit
(
Mpm_Man_t
*
p
)
...
@@ -1465,7 +1521,7 @@ static inline void Mpm_ObjRecycleCuts( Mpm_Man_t * p, Mig_Obj_t * pObj )
...
@@ -1465,7 +1521,7 @@ static inline void Mpm_ObjRecycleCuts( Mpm_Man_t * p, Mig_Obj_t * pObj )
int
hCut
,
hNext
;
int
hCut
,
hNext
;
Mpm_ObjForEachCutSafe
(
p
,
pObj
,
hCut
,
pCut
,
hNext
)
Mpm_ObjForEachCutSafe
(
p
,
pObj
,
hCut
,
pCut
,
hNext
)
Mmr_StepRecycle
(
p
->
pManCuts
,
hCut
);
Mmr_StepRecycle
(
p
->
pManCuts
,
hCut
);
pObj
->
hCutList
=
0
;
Mpm_ObjSetCutList
(
p
,
pObj
,
0
)
;
}
}
static
inline
void
Mpm_ObjDerefFaninCuts
(
Mpm_Man_t
*
p
,
Mig_Obj_t
*
pObj
)
static
inline
void
Mpm_ObjDerefFaninCuts
(
Mpm_Man_t
*
p
,
Mig_Obj_t
*
pObj
)
{
{
...
@@ -1618,25 +1674,27 @@ int Mpm_ManDeriveCuts( Mpm_Man_t * p, Mig_Obj_t * pObj )
...
@@ -1618,25 +1674,27 @@ int Mpm_ManDeriveCuts( Mpm_Man_t * p, Mig_Obj_t * pObj )
// static int Flag = 0;
// static int Flag = 0;
Mpm_Cut_t
*
pCuts
[
3
];
Mpm_Cut_t
*
pCuts
[
3
];
Mpm_Uni_t
*
pUnit
;
Mpm_Uni_t
*
pUnit
;
int
c0
,
c1
,
c2
;
int
Required
=
Mpm_ObjRequired
(
p
,
pObj
);
int
c0
,
c1
,
c2
,
hCutBest
;
#ifdef MIG_RUNTIME
#ifdef MIG_RUNTIME
abctime
clk
;
abctime
clk
;
#endif
#endif
Mpm_ManPrepareCutStore
(
p
);
Mpm_ManPrepareCutStore
(
p
);
// check that the best cut is ok
// check that the best cut is ok
assert
(
pObj
->
hCutList
==
0
);
hCutBest
=
Mpm_ObjCutBest
(
p
,
pObj
);
if
(
pObj
->
hCutBest
>
0
)
// cut list is assigned
assert
(
Mpm_ObjCutList
(
p
,
pObj
)
==
0
);
if
(
hCutBest
>
0
)
// cut list is assigned
{
{
Mpm_Cut_t
*
pCut
=
Mpm_ObjCutBest
(
p
,
pObj
);
assert
(
pCut
->
hNext
==
0
);
Mpm_Cut_t
*
pCut
=
Mpm_ObjCutBest
P
(
p
,
pObj
);
pObj
->
mTime
=
Mpm_CutGetArrTime
(
p
,
pCut
);
assert
(
pCut
->
hNext
==
0
);
if
(
pObj
->
mTime
>
pObj
->
m
Required
)
if
(
Mpm_ObjTime
(
p
,
pObj
)
>
Required
)
printf
(
"Arrival time (%d) exceeds required time (%d) at object %d.
\n
"
,
pObj
->
mTime
,
pObj
->
m
Required
,
Mig_ObjId
(
pObj
)
);
printf
(
"Arrival time (%d) exceeds required time (%d) at object %d.
\n
"
,
Mpm_ObjTime
(
p
,
pObj
),
Required
,
Mig_ObjId
(
pObj
)
);
if
(
p
->
fMainRun
)
if
(
p
->
fMainRun
)
Mpm_ObjAddCutToStore
(
p
,
pCut
,
pObj
->
mTime
);
Mpm_ObjAddCutToStore
(
p
,
pCut
,
Mpm_ObjTime
(
p
,
pObj
)
);
}
}
// start storage with choice cuts
// start storage with choice cuts
if
(
p
->
pMig
->
vSibls
.
nSize
&&
Mig_ObjSiblId
(
pObj
)
)
if
(
p
->
pMig
->
vSibls
.
nSize
&&
Mig_ObjSiblId
(
pObj
)
)
Mpm_ObjAddChoiceCutsToStore
(
p
,
Mig_ObjSibl
(
pObj
),
pObj
->
m
Required
);
Mpm_ObjAddChoiceCutsToStore
(
p
,
Mig_ObjSibl
(
pObj
),
Required
);
// compute signatures for fanin cuts
// compute signatures for fanin cuts
#ifdef MIG_RUNTIME
#ifdef MIG_RUNTIME
clk
=
Abc_Clock
();
clk
=
Abc_Clock
();
...
@@ -1656,7 +1714,7 @@ clk = Abc_Clock();
...
@@ -1656,7 +1714,7 @@ clk = Abc_Clock();
for
(
c0
=
0
;
c0
<
p
->
nCuts
[
0
]
&&
(
pCuts
[
0
]
=
p
->
pCuts
[
0
][
c0
]);
c0
++
)
for
(
c0
=
0
;
c0
<
p
->
nCuts
[
0
]
&&
(
pCuts
[
0
]
=
p
->
pCuts
[
0
][
c0
]);
c0
++
)
for
(
c1
=
0
;
c1
<
p
->
nCuts
[
1
]
&&
(
pCuts
[
1
]
=
p
->
pCuts
[
1
][
c1
]);
c1
++
)
for
(
c1
=
0
;
c1
<
p
->
nCuts
[
1
]
&&
(
pCuts
[
1
]
=
p
->
pCuts
[
1
][
c1
]);
c1
++
)
if
(
Abc_TtCountOnes
(
p
->
pSigns
[
0
][
c0
]
|
p
->
pSigns
[
1
][
c1
])
<=
p
->
nLutSize
)
if
(
Abc_TtCountOnes
(
p
->
pSigns
[
0
][
c0
]
|
p
->
pSigns
[
1
][
c1
])
<=
p
->
nLutSize
)
if
(
!
Mpm_ManDeriveCutNew
(
p
,
pCuts
,
pObj
->
m
Required
)
)
if
(
!
Mpm_ManDeriveCutNew
(
p
,
pCuts
,
Required
)
)
goto
finish
;
goto
finish
;
}
}
else
if
(
Mig_ObjIsNode3
(
pObj
)
)
else
if
(
Mig_ObjIsNode3
(
pObj
)
)
...
@@ -1666,7 +1724,7 @@ clk = Abc_Clock();
...
@@ -1666,7 +1724,7 @@ clk = Abc_Clock();
for
(
c1
=
0
;
c1
<
p
->
nCuts
[
1
]
&&
(
pCuts
[
1
]
=
p
->
pCuts
[
1
][
c1
]);
c1
++
)
for
(
c1
=
0
;
c1
<
p
->
nCuts
[
1
]
&&
(
pCuts
[
1
]
=
p
->
pCuts
[
1
][
c1
]);
c1
++
)
for
(
c2
=
0
;
c2
<
p
->
nCuts
[
2
]
&&
(
pCuts
[
2
]
=
p
->
pCuts
[
2
][
c2
]);
c2
++
)
for
(
c2
=
0
;
c2
<
p
->
nCuts
[
2
]
&&
(
pCuts
[
2
]
=
p
->
pCuts
[
2
][
c2
]);
c2
++
)
if
(
Abc_TtCountOnes
(
p
->
pSigns
[
0
][
c0
]
|
p
->
pSigns
[
1
][
c1
]
|
p
->
pSigns
[
2
][
c2
])
<=
p
->
nLutSize
)
if
(
Abc_TtCountOnes
(
p
->
pSigns
[
0
][
c0
]
|
p
->
pSigns
[
1
][
c1
]
|
p
->
pSigns
[
2
][
c2
])
<=
p
->
nLutSize
)
if
(
!
Mpm_ManDeriveCutNew
(
p
,
pCuts
,
pObj
->
m
Required
)
)
if
(
!
Mpm_ManDeriveCutNew
(
p
,
pCuts
,
Required
)
)
goto
finish
;
goto
finish
;
}
}
else
assert
(
0
);
else
assert
(
0
);
...
@@ -1681,18 +1739,19 @@ finish:
...
@@ -1681,18 +1739,19 @@ finish:
// save best cut
// save best cut
assert
(
p
->
nCutStore
>
0
);
assert
(
p
->
nCutStore
>
0
);
pUnit
=
p
->
pCutStore
[
0
];
pUnit
=
p
->
pCutStore
[
0
];
if
(
pUnit
->
Inf
.
mTime
<=
pObj
->
m
Required
)
if
(
pUnit
->
Inf
.
mTime
<=
Required
)
{
{
Mpm_Cut_t
*
pCut
;
Mpm_Cut_t
*
pCut
;
if
(
pObj
->
hCutBest
)
if
(
hCutBest
)
Mmr_StepRecycle
(
p
->
pManCuts
,
pObj
->
hCutBest
);
Mmr_StepRecycle
(
p
->
pManCuts
,
hCutBest
);
pObj
->
hCutBest
=
Mpm_CutCreate
(
p
,
pUnit
->
pLeaves
,
pUnit
->
nLeaves
,
pUnit
->
fUseless
,
&
pCut
);
hCutBest
=
Mpm_CutCreate
(
p
,
pUnit
->
pLeaves
,
pUnit
->
nLeaves
,
pUnit
->
fUseless
,
&
pCut
);
pObj
->
mTime
=
pUnit
->
Inf
.
mTime
;
Mpm_ObjSetCutBest
(
p
,
pObj
,
hCutBest
);
pObj
->
mArea
=
pUnit
->
Inf
.
mArea
;
Mpm_ObjSetTime
(
p
,
pObj
,
pUnit
->
Inf
.
mTime
);
pObj
->
mEdge
=
pUnit
->
Inf
.
mEdge
;
Mpm_ObjSetArea
(
p
,
pObj
,
pUnit
->
Inf
.
mArea
);
Mpm_ObjSetEdge
(
p
,
pObj
,
pUnit
->
Inf
.
mEdge
);
}
}
else
assert
(
!
p
->
fMainRun
);
else
assert
(
!
p
->
fMainRun
);
assert
(
pObj
->
hCutBest
>
0
);
assert
(
hCutBest
>
0
);
// transform internal storage into regular cuts
// transform internal storage into regular cuts
Mpm_ObjTranslateCutsFromStore
(
p
,
pObj
,
Mig_ObjRefNum
(
pObj
)
>
0
);
Mpm_ObjTranslateCutsFromStore
(
p
,
pObj
,
Mig_ObjRefNum
(
pObj
)
>
0
);
// dereference fanin cuts and reference node
// dereference fanin cuts and reference node
...
@@ -1743,15 +1802,19 @@ Gia_Man_t * Mpm_ManPerformTest( Mig_Man_t * pMig )
...
@@ -1743,15 +1802,19 @@ Gia_Man_t * Mpm_ManPerformTest( Mig_Man_t * pMig )
Mpm_LibLut_t
*
pLib
;
Mpm_LibLut_t
*
pLib
;
Mpm_Man_t
*
p
;
Mpm_Man_t
*
p
;
Mig_Obj_t
*
pObj
;
Mig_Obj_t
*
pObj
;
int
i
;
int
i
,
hCut
;
pLib
=
Mpm_LibLutSetSimple
(
6
);
pLib
=
Mpm_LibLutSetSimple
(
6
);
p
=
Mpm_ManStart
(
pMig
,
pLib
,
8
);
p
=
Mpm_ManStart
(
pMig
,
pLib
,
8
);
Mpm_ManPrintStatsInit
(
p
);
Mpm_ManPrintStatsInit
(
p
);
Mpm_Man
Reset
Required
(
p
);
Mpm_Man
Clean
Required
(
p
);
Mig_ManForEachCi
(
p
->
pMig
,
pObj
,
i
)
Mig_ManForEachCi
(
p
->
pMig
,
pObj
,
i
)
pObj
->
hCutList
=
pObj
->
hCutBest
=
Mpm_CutCreateUnit
(
p
,
Mig_ObjId
(
pObj
)
);
{
hCut
=
Mpm_CutCreateUnit
(
p
,
Mig_ObjId
(
pObj
)
);
Mpm_ObjSetCutBest
(
p
,
pObj
,
hCut
);
Mpm_ObjSetCutList
(
p
,
pObj
,
hCut
);
}
Mig_ManForEachCand
(
p
->
pMig
,
pObj
)
Mig_ManForEachCand
(
p
->
pMig
,
pObj
)
pObj
->
nEstRefs
=
MPM_UNIT_REFS
*
Mig_ObjRefNum
(
pObj
);
Mpm_ObjSetEstRef
(
p
,
pObj
,
MPM_UNIT_REFS
*
Mig_ObjRefNum
(
pObj
)
);
Mpm_ManPerform
(
p
);
Mpm_ManPerform
(
p
);
Mpm_ManPrintStats
(
p
);
Mpm_ManPrintStats
(
p
);
pNew
=
Mpm_ManFromIfLogic
(
p
);
pNew
=
Mpm_ManFromIfLogic
(
p
);
...
...
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