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
3f35ac81
Commit
3f35ac81
authored
Dec 05, 2017
by
Alan Mishchenko
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
New command 'lutexact'.
parent
e5d83352
Show whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
857 additions
and
5 deletions
+857
-5
src/base/abci/abc.c
+104
-0
src/sat/bmc/bmcMaj.c
+371
-2
src/sat/bmc/bmcMaj2.c
+382
-3
No files found.
src/base/abci/abc.c
View file @
3f35ac81
...
...
@@ -151,6 +151,7 @@ static int Abc_CommandBmsStop ( Abc_Frame_t * pAbc, int argc, cha
static
int
Abc_CommandBmsPs
(
Abc_Frame_t
*
pAbc
,
int
argc
,
char
**
argv
);
static
int
Abc_CommandMajExact
(
Abc_Frame_t
*
pAbc
,
int
argc
,
char
**
argv
);
static
int
Abc_CommandTwoExact
(
Abc_Frame_t
*
pAbc
,
int
argc
,
char
**
argv
);
static
int
Abc_CommandLutExact
(
Abc_Frame_t
*
pAbc
,
int
argc
,
char
**
argv
);
static
int
Abc_CommandLogic
(
Abc_Frame_t
*
pAbc
,
int
argc
,
char
**
argv
);
static
int
Abc_CommandComb
(
Abc_Frame_t
*
pAbc
,
int
argc
,
char
**
argv
);
...
...
@@ -819,6 +820,7 @@ void Abc_Init( Abc_Frame_t * pAbc )
Cmd_CommandAdd
(
pAbc
,
"Exact synthesis"
,
"bms_ps"
,
Abc_CommandBmsPs
,
0
);
Cmd_CommandAdd
(
pAbc
,
"Exact synthesis"
,
"majexact"
,
Abc_CommandMajExact
,
0
);
Cmd_CommandAdd
(
pAbc
,
"Exact synthesis"
,
"twoexact"
,
Abc_CommandTwoExact
,
0
);
Cmd_CommandAdd
(
pAbc
,
"Exact synthesis"
,
"lutexact"
,
Abc_CommandLutExact
,
0
);
Cmd_CommandAdd
(
pAbc
,
"Various"
,
"logic"
,
Abc_CommandLogic
,
1
);
Cmd_CommandAdd
(
pAbc
,
"Various"
,
"comb"
,
Abc_CommandComb
,
1
);
...
...
@@ -8258,6 +8260,108 @@ usage:
SeeAlso []
***********************************************************************/
int
Abc_CommandLutExact
(
Abc_Frame_t
*
pAbc
,
int
argc
,
char
**
argv
)
{
extern
void
Exa3_ManExactSynthesis
(
char
*
pTtStr
,
int
nVars
,
int
nNodes
,
int
nLutSize
,
int
fVerbose
);
extern
void
Exa3_ManExactSynthesis2
(
char
*
pTtStr
,
int
nVars
,
int
nNodes
,
int
nLutSize
,
int
fVerbose
);
int
c
,
nVars
=
5
,
nNodes
=
5
,
nLutSize
=
3
,
fGlucose
=
0
,
fVerbose
=
1
;
char
*
pTtStr
=
NULL
;
Extra_UtilGetoptReset
();
while
(
(
c
=
Extra_UtilGetopt
(
argc
,
argv
,
"INKgvh"
)
)
!=
EOF
)
{
switch
(
c
)
{
case
'I'
:
if
(
globalUtilOptind
>=
argc
)
{
Abc_Print
(
-
1
,
"Command line switch
\"
-I
\"
should be followed by an integer.
\n
"
);
goto
usage
;
}
nVars
=
atoi
(
argv
[
globalUtilOptind
]);
globalUtilOptind
++
;
if
(
nVars
<
0
)
goto
usage
;
break
;
case
'N'
:
if
(
globalUtilOptind
>=
argc
)
{
Abc_Print
(
-
1
,
"Command line switch
\"
-N
\"
should be followed by an integer.
\n
"
);
goto
usage
;
}
nNodes
=
atoi
(
argv
[
globalUtilOptind
]);
globalUtilOptind
++
;
if
(
nNodes
<
0
)
goto
usage
;
break
;
case
'K'
:
if
(
globalUtilOptind
>=
argc
)
{
Abc_Print
(
-
1
,
"Command line switch
\"
-K
\"
should be followed by an integer.
\n
"
);
goto
usage
;
}
nLutSize
=
atoi
(
argv
[
globalUtilOptind
]);
globalUtilOptind
++
;
if
(
nLutSize
<
0
)
goto
usage
;
break
;
case
'g'
:
fGlucose
^=
1
;
break
;
case
'v'
:
fVerbose
^=
1
;
break
;
case
'h'
:
goto
usage
;
default:
goto
usage
;
}
}
if
(
argc
==
globalUtilOptind
+
1
)
pTtStr
=
argv
[
globalUtilOptind
];
if
(
pTtStr
==
NULL
)
{
Abc_Print
(
-
1
,
"Truth table should be given on the command line.
\n
"
);
return
1
;
}
if
(
nVars
>
10
)
{
Abc_Print
(
-
1
,
"Function should not have more than 10 inputs.
\n
"
);
return
1
;
}
if
(
nLutSize
>
6
)
{
Abc_Print
(
-
1
,
"Node size should not be more than 6 inputs.
\n
"
);
return
1
;
}
if
(
fGlucose
)
Exa3_ManExactSynthesis
(
pTtStr
,
nVars
,
nNodes
,
nLutSize
,
fVerbose
);
else
Exa3_ManExactSynthesis2
(
pTtStr
,
nVars
,
nNodes
,
nLutSize
,
fVerbose
);
return
0
;
usage:
Abc_Print
(
-
2
,
"usage: lutexact [-INK <num>] [-fcgvh] <hex>
\n
"
);
Abc_Print
(
-
2
,
"
\t
exact synthesis of multi-input function using two-input gates
\n
"
);
Abc_Print
(
-
2
,
"
\t
-I <num> : the number of input variables [default = %d]
\n
"
,
nVars
);
Abc_Print
(
-
2
,
"
\t
-N <num> : the number of K-input nodes [default = %d]
\n
"
,
nNodes
);
Abc_Print
(
-
2
,
"
\t
-K <num> : the number of node fanins [default = %d]
\n
"
,
nLutSize
);
Abc_Print
(
-
2
,
"
\t
-g : toggle using Glucose 3.0 by Gilles Audemard and Laurent Simon [default = %s]
\n
"
,
fGlucose
?
"yes"
:
"no"
);
Abc_Print
(
-
2
,
"
\t
-v : toggle verbose printout [default = %s]
\n
"
,
fVerbose
?
"yes"
:
"no"
);
Abc_Print
(
-
2
,
"
\t
-h : print the command usage
\n
"
);
Abc_Print
(
-
2
,
"
\t
<hex> : truth table in hex notation
\n
"
);
return
1
;
}
/**Function*************************************************************
Synopsis []
Description []
SideEffects []
SeeAlso []
***********************************************************************/
int
Abc_CommandLogic
(
Abc_Frame_t
*
pAbc
,
int
argc
,
char
**
argv
)
{
Abc_Ntk_t
*
pNtk
,
*
pNtkRes
;
src/sat/bmc/bmcMaj.c
View file @
3f35ac81
...
...
@@ -118,7 +118,7 @@ int Maj_ManMarkup( Maj_Man_t * p )
}
}
}
//
printf( "The number of parameter variables = %d.\n", p->iVar );
printf
(
"The number of parameter variables = %d.
\n
"
,
p
->
iVar
);
return
p
->
iVar
;
// printout
for
(
i
=
p
->
nVars
+
2
;
i
<
p
->
nObjs
;
i
++
)
...
...
@@ -445,7 +445,7 @@ int Exa_ManMarkup( Exa_Man_t * p )
}
}
}
//
printf( "The number of parameter variables = %d.\n", p->iVar );
printf
(
"The number of parameter variables = %d.
\n
"
,
p
->
iVar
);
return
p
->
iVar
;
// printout
for
(
i
=
p
->
nVars
;
i
<
p
->
nObjs
;
i
++
)
...
...
@@ -736,6 +736,375 @@ void Exa_ManExactSynthesis( char * pTtStr, int nVars, int nNodes, int fVerbose )
Abc_PrintTime
(
1
,
"Total runtime"
,
Abc_Clock
()
-
clkTotal
);
}
typedef
struct
Exa3_Man_t_
Exa3_Man_t
;
struct
Exa3_Man_t_
{
int
nVars
;
// inputs
int
nNodes
;
// internal nodes
int
nLutSize
;
// lut size
int
LutMask
;
// lut mask
int
nObjs
;
// total objects (nVars inputs + nNodes internal nodes)
int
nWords
;
// the truth table size in 64-bit words
int
iVar
;
// the next available SAT variable
word
*
pTruth
;
// truth table
Vec_Wrd_t
*
vInfo
;
// nVars + nNodes + 1
int
VarMarks
[
MAJ_NOBJS
][
6
][
MAJ_NOBJS
];
// variable marks
int
VarVals
[
MAJ_NOBJS
];
// values of the first nVars variables
Vec_Wec_t
*
vOutLits
;
// output vars
bmcg_sat_solver
*
pSat
;
// SAT solver
};
static
inline
word
*
Exa3_ManTruth
(
Exa3_Man_t
*
p
,
int
v
)
{
return
Vec_WrdEntryP
(
p
->
vInfo
,
p
->
nWords
*
v
);
}
/**Function*************************************************************
Synopsis []
Description []
SideEffects []
SeeAlso []
***********************************************************************/
static
Vec_Wrd_t
*
Exa3_ManTruthTables
(
Exa3_Man_t
*
p
)
{
Vec_Wrd_t
*
vInfo
=
p
->
vInfo
=
Vec_WrdStart
(
p
->
nWords
*
(
p
->
nObjs
+
1
)
);
int
i
;
for
(
i
=
0
;
i
<
p
->
nVars
;
i
++
)
Abc_TtIthVar
(
Exa3_ManTruth
(
p
,
i
),
i
,
p
->
nVars
);
//Dau_DsdPrintFromTruth( Exa3_ManTruth(p, p->nObjs), p->nVars );
return
vInfo
;
}
static
int
Exa3_ManMarkup
(
Exa3_Man_t
*
p
)
{
int
i
,
k
,
j
;
assert
(
p
->
nObjs
<=
MAJ_NOBJS
);
// assign variables for truth tables
p
->
iVar
=
1
+
p
->
LutMask
*
p
->
nNodes
;
for
(
i
=
p
->
nVars
;
i
<
p
->
nObjs
;
i
++
)
{
for
(
k
=
0
;
k
<
p
->
nLutSize
;
k
++
)
{
for
(
j
=
0
;
j
<
i
-
k
;
j
++
)
{
Vec_WecPush
(
p
->
vOutLits
,
j
,
Abc_Var2Lit
(
p
->
iVar
,
0
)
);
p
->
VarMarks
[
i
][
k
][
j
]
=
p
->
iVar
++
;
}
}
}
printf
(
"The number of parameter variables = %d.
\n
"
,
p
->
iVar
);
return
p
->
iVar
;
// printout
for
(
i
=
p
->
nVars
;
i
<
p
->
nObjs
;
i
++
)
{
printf
(
"Node %d
\n
"
,
i
);
for
(
j
=
0
;
j
<
p
->
nObjs
;
j
++
)
{
for
(
k
=
0
;
k
<
p
->
nLutSize
;
k
++
)
printf
(
"%3d "
,
p
->
VarMarks
[
i
][
k
][
j
]
);
printf
(
"
\n
"
);
}
}
return
p
->
iVar
;
}
static
Exa3_Man_t
*
Exa3_ManAlloc
(
int
nVars
,
int
nNodes
,
int
nLutSize
,
word
*
pTruth
)
{
Exa3_Man_t
*
p
=
ABC_CALLOC
(
Exa3_Man_t
,
1
);
p
->
nVars
=
nVars
;
p
->
nNodes
=
nNodes
;
p
->
nLutSize
=
nLutSize
;
p
->
LutMask
=
(
1
<<
nLutSize
)
-
1
;
p
->
nObjs
=
nVars
+
nNodes
;
p
->
nWords
=
Abc_TtWordNum
(
nVars
);
p
->
pTruth
=
pTruth
;
p
->
vOutLits
=
Vec_WecStart
(
p
->
nObjs
);
p
->
iVar
=
Exa3_ManMarkup
(
p
);
p
->
vInfo
=
Exa3_ManTruthTables
(
p
);
p
->
pSat
=
bmcg_sat_solver_start
();
bmcg_sat_solver_set_nvars
(
p
->
pSat
,
p
->
iVar
);
return
p
;
}
static
void
Exa3_ManFree
(
Exa3_Man_t
*
p
)
{
bmcg_sat_solver_stop
(
p
->
pSat
);
Vec_WrdFree
(
p
->
vInfo
);
Vec_WecFree
(
p
->
vOutLits
);
ABC_FREE
(
p
);
}
/**Function*************************************************************
Synopsis []
Description []
SideEffects []
SeeAlso []
***********************************************************************/
static
inline
int
Exa3_ManFindFanin
(
Exa3_Man_t
*
p
,
int
i
,
int
k
)
{
int
j
,
Count
=
0
,
iVar
=
-
1
;
for
(
j
=
0
;
j
<
p
->
nObjs
;
j
++
)
if
(
p
->
VarMarks
[
i
][
k
][
j
]
&&
bmcg_sat_solver_read_cex_varvalue
(
p
->
pSat
,
p
->
VarMarks
[
i
][
k
][
j
])
)
{
iVar
=
j
;
Count
++
;
}
assert
(
Count
==
1
);
return
iVar
;
}
static
inline
int
Exa3_ManEval
(
Exa3_Man_t
*
p
)
{
static
int
Flag
=
0
;
int
i
,
k
,
j
,
iMint
;
word
*
pFanins
[
6
];
for
(
i
=
p
->
nVars
;
i
<
p
->
nObjs
;
i
++
)
{
int
iVarStart
=
1
+
p
->
LutMask
*
(
i
-
p
->
nVars
);
for
(
k
=
0
;
k
<
p
->
nLutSize
;
k
++
)
pFanins
[
k
]
=
Exa3_ManTruth
(
p
,
Exa3_ManFindFanin
(
p
,
i
,
k
)
);
Abc_TtConst0
(
Exa3_ManTruth
(
p
,
i
),
p
->
nWords
);
for
(
k
=
1
;
k
<=
p
->
LutMask
;
k
++
)
{
if
(
!
bmcg_sat_solver_read_cex_varvalue
(
p
->
pSat
,
iVarStart
+
k
-
1
)
)
continue
;
// Abc_TtAndCompl( Exa3_ManTruth(p, p->nObjs), pFanins[0], !(k&1), pFanins[1], !(k>>1), p->nWords );
Abc_TtConst1
(
Exa3_ManTruth
(
p
,
p
->
nObjs
),
p
->
nWords
);
for
(
j
=
0
;
j
<
p
->
nLutSize
;
j
++
)
Abc_TtAndCompl
(
Exa3_ManTruth
(
p
,
p
->
nObjs
),
Exa3_ManTruth
(
p
,
p
->
nObjs
),
0
,
pFanins
[
j
],
!
((
k
>>
j
)
&
1
),
p
->
nWords
);
Abc_TtOr
(
Exa3_ManTruth
(
p
,
i
),
Exa3_ManTruth
(
p
,
i
),
Exa3_ManTruth
(
p
,
p
->
nObjs
),
p
->
nWords
);
}
}
if
(
Flag
&&
p
->
nVars
>=
6
)
iMint
=
Abc_TtFindLastDiffBit
(
Exa3_ManTruth
(
p
,
p
->
nObjs
-
1
),
p
->
pTruth
,
p
->
nVars
);
else
iMint
=
Abc_TtFindFirstDiffBit
(
Exa3_ManTruth
(
p
,
p
->
nObjs
-
1
),
p
->
pTruth
,
p
->
nVars
);
//Flag ^= 1;
assert
(
iMint
<
(
1
<<
p
->
nVars
)
);
return
iMint
;
}
/**Function*************************************************************
Synopsis []
Description []
SideEffects []
SeeAlso []
***********************************************************************/
static
void
Exa3_ManPrintSolution
(
Exa3_Man_t
*
p
,
int
fCompl
)
{
int
i
,
k
,
iVar
;
printf
(
"Realization of given %d-input function using %d %d-input LUTs:
\n
"
,
p
->
nVars
,
p
->
nNodes
,
p
->
nLutSize
);
for
(
i
=
p
->
nObjs
-
1
;
i
>=
p
->
nVars
;
i
--
)
{
int
Val
,
iVarStart
=
1
+
p
->
LutMask
*
(
i
-
p
->
nVars
);
printf
(
"%02d = %d
\'
b"
,
i
,
1
<<
p
->
nLutSize
);
for
(
k
=
p
->
LutMask
-
1
;
k
>=
0
;
k
--
)
{
Val
=
bmcg_sat_solver_read_cex_varvalue
(
p
->
pSat
,
iVarStart
+
k
);
if
(
i
==
p
->
nObjs
-
1
&&
fCompl
)
printf
(
"%d"
,
!
Val
);
else
printf
(
"%d"
,
Val
);
}
if
(
i
==
p
->
nObjs
-
1
&&
fCompl
)
printf
(
"1("
);
else
printf
(
"0("
);
for
(
k
=
p
->
nLutSize
-
1
;
k
>=
0
;
k
--
)
{
iVar
=
Exa3_ManFindFanin
(
p
,
i
,
k
);
if
(
iVar
>=
0
&&
iVar
<
p
->
nVars
)
printf
(
" %c"
,
'a'
+
iVar
);
else
printf
(
" %02d"
,
iVar
);
}
printf
(
" )
\n
"
);
}
}
/**Function*************************************************************
Synopsis []
Description []
SideEffects []
SeeAlso []
***********************************************************************/
static
int
Exa3_ManAddCnfStart
(
Exa3_Man_t
*
p
)
{
int
pLits
[
MAJ_NOBJS
],
pLits2
[
2
],
i
,
j
,
k
,
n
,
m
;
// input constraints
for
(
i
=
p
->
nVars
;
i
<
p
->
nObjs
;
i
++
)
{
int
iVarStart
=
1
+
p
->
LutMask
*
(
i
-
p
->
nVars
);
for
(
k
=
0
;
k
<
p
->
nLutSize
;
k
++
)
{
int
nLits
=
0
;
for
(
j
=
0
;
j
<
p
->
nObjs
;
j
++
)
if
(
p
->
VarMarks
[
i
][
k
][
j
]
)
pLits
[
nLits
++
]
=
Abc_Var2Lit
(
p
->
VarMarks
[
i
][
k
][
j
],
0
);
assert
(
nLits
>
0
);
// input uniqueness
if
(
!
bmcg_sat_solver_addclause
(
p
->
pSat
,
pLits
,
nLits
)
)
return
0
;
for
(
n
=
0
;
n
<
nLits
;
n
++
)
for
(
m
=
n
+
1
;
m
<
nLits
;
m
++
)
{
pLits2
[
0
]
=
Abc_LitNot
(
pLits
[
n
]);
pLits2
[
1
]
=
Abc_LitNot
(
pLits
[
m
]);
if
(
!
bmcg_sat_solver_addclause
(
p
->
pSat
,
pLits2
,
2
)
)
return
0
;
}
if
(
k
==
p
->
nLutSize
-
1
)
break
;
// symmetry breaking
for
(
j
=
0
;
j
<
p
->
nObjs
;
j
++
)
if
(
p
->
VarMarks
[
i
][
k
][
j
]
)
for
(
n
=
j
;
n
<
p
->
nObjs
;
n
++
)
if
(
p
->
VarMarks
[
i
][
k
+
1
][
n
]
)
{
pLits2
[
0
]
=
Abc_Var2Lit
(
p
->
VarMarks
[
i
][
k
][
j
],
1
);
pLits2
[
1
]
=
Abc_Var2Lit
(
p
->
VarMarks
[
i
][
k
+
1
][
n
],
1
);
if
(
!
bmcg_sat_solver_addclause
(
p
->
pSat
,
pLits2
,
2
)
)
return
0
;
}
}
if
(
p
->
nLutSize
!=
2
)
continue
;
// two-input functions
for
(
k
=
0
;
k
<
3
;
k
++
)
{
pLits
[
0
]
=
Abc_Var2Lit
(
iVarStart
,
k
==
1
);
pLits
[
1
]
=
Abc_Var2Lit
(
iVarStart
+
1
,
k
==
2
);
pLits
[
2
]
=
Abc_Var2Lit
(
iVarStart
+
2
,
k
!=
0
);
if
(
!
bmcg_sat_solver_addclause
(
p
->
pSat
,
pLits
,
3
)
)
return
0
;
}
}
// outputs should be used
for
(
i
=
0
;
i
<
p
->
nObjs
-
1
;
i
++
)
{
Vec_Int_t
*
vArray
=
Vec_WecEntry
(
p
->
vOutLits
,
i
);
assert
(
Vec_IntSize
(
vArray
)
>
0
);
if
(
!
bmcg_sat_solver_addclause
(
p
->
pSat
,
Vec_IntArray
(
vArray
),
Vec_IntSize
(
vArray
)
)
)
return
0
;
}
return
1
;
}
static
int
Exa3_ManAddCnf
(
Exa3_Man_t
*
p
,
int
iMint
)
{
// save minterm values
int
i
,
k
,
n
,
j
,
Value
=
Abc_TtGetBit
(
p
->
pTruth
,
iMint
);
for
(
i
=
0
;
i
<
p
->
nVars
;
i
++
)
p
->
VarVals
[
i
]
=
(
iMint
>>
i
)
&
1
;
// sat_solver_setnvars( p->pSat, p->iVar + (p->nLutSize+1)*p->nNodes );
bmcg_sat_solver_set_nvars
(
p
->
pSat
,
p
->
iVar
+
(
p
->
nLutSize
+
1
)
*
p
->
nNodes
);
//printf( "Adding clauses for minterm %d with value %d.\n", iMint, Value );
for
(
i
=
p
->
nVars
;
i
<
p
->
nObjs
;
i
++
)
{
// fanin connectivity
int
iVarStart
=
1
+
p
->
LutMask
*
(
i
-
p
->
nVars
);
int
iBaseSatVarI
=
p
->
iVar
+
(
p
->
nLutSize
+
1
)
*
(
i
-
p
->
nVars
);
for
(
k
=
0
;
k
<
p
->
nLutSize
;
k
++
)
{
for
(
j
=
0
;
j
<
p
->
nObjs
;
j
++
)
if
(
p
->
VarMarks
[
i
][
k
][
j
]
)
{
int
iBaseSatVarJ
=
p
->
iVar
+
(
p
->
nLutSize
+
1
)
*
(
j
-
p
->
nVars
);
for
(
n
=
0
;
n
<
2
;
n
++
)
{
int
pLits
[
3
],
nLits
=
0
;
pLits
[
nLits
++
]
=
Abc_Var2Lit
(
p
->
VarMarks
[
i
][
k
][
j
],
1
);
pLits
[
nLits
++
]
=
Abc_Var2Lit
(
iBaseSatVarI
+
k
,
n
);
if
(
j
>=
p
->
nVars
)
pLits
[
nLits
++
]
=
Abc_Var2Lit
(
iBaseSatVarJ
+
p
->
nLutSize
,
!
n
);
else
if
(
p
->
VarVals
[
j
]
==
n
)
continue
;
if
(
!
bmcg_sat_solver_addclause
(
p
->
pSat
,
pLits
,
nLits
)
)
return
0
;
}
}
}
// node functionality
for
(
n
=
0
;
n
<
2
;
n
++
)
{
if
(
i
==
p
->
nObjs
-
1
&&
n
==
Value
)
continue
;
for
(
k
=
0
;
k
<=
p
->
LutMask
;
k
++
)
{
int
pLits
[
8
],
nLits
=
0
;
if
(
k
==
0
&&
n
==
1
)
continue
;
//pLits[nLits++] = Abc_Var2Lit( iBaseSatVarI + 0, (k&1) );
//pLits[nLits++] = Abc_Var2Lit( iBaseSatVarI + 1, (k>>1) );
//if ( i != p->nObjs - 1 ) pLits[nLits++] = Abc_Var2Lit( iBaseSatVarI + 2, !n );
for
(
j
=
0
;
j
<
p
->
nLutSize
;
j
++
)
pLits
[
nLits
++
]
=
Abc_Var2Lit
(
iBaseSatVarI
+
j
,
(
k
>>
j
)
&
1
);
if
(
i
!=
p
->
nObjs
-
1
)
pLits
[
nLits
++
]
=
Abc_Var2Lit
(
iBaseSatVarI
+
j
,
!
n
);
if
(
k
>
0
)
pLits
[
nLits
++
]
=
Abc_Var2Lit
(
iVarStart
+
k
-
1
,
n
);
assert
(
nLits
<=
p
->
nLutSize
+
2
);
if
(
!
bmcg_sat_solver_addclause
(
p
->
pSat
,
pLits
,
nLits
)
)
return
0
;
}
}
}
p
->
iVar
+=
(
p
->
nLutSize
+
1
)
*
p
->
nNodes
;
return
1
;
}
void
Exa3_ManExactSynthesis
(
char
*
pTtStr
,
int
nVars
,
int
nNodes
,
int
nLutSize
,
int
fVerbose
)
{
int
i
,
status
,
iMint
=
1
;
abctime
clkTotal
=
Abc_Clock
();
Exa3_Man_t
*
p
;
int
fCompl
=
0
;
word
pTruth
[
16
];
Abc_TtReadHex
(
pTruth
,
pTtStr
);
assert
(
nVars
<=
10
);
assert
(
nLutSize
<=
6
);
p
=
Exa3_ManAlloc
(
nVars
,
nNodes
,
nLutSize
,
pTruth
);
if
(
pTruth
[
0
]
&
1
)
{
fCompl
=
1
;
Abc_TtNot
(
pTruth
,
p
->
nWords
);
}
status
=
Exa3_ManAddCnfStart
(
p
);
assert
(
status
);
printf
(
"Running exact synthesis for %d-input function with %d %d-input LUTs...
\n
"
,
p
->
nVars
,
p
->
nNodes
,
p
->
nLutSize
);
for
(
i
=
0
;
iMint
!=
-
1
;
i
++
)
{
abctime
clk
=
Abc_Clock
();
if
(
!
Exa3_ManAddCnf
(
p
,
iMint
)
)
break
;
status
=
bmcg_sat_solver_solve
(
p
->
pSat
,
NULL
,
0
);
if
(
fVerbose
)
{
printf
(
"Iter %3d : "
,
i
);
Extra_PrintBinary
(
stdout
,
(
unsigned
*
)
&
iMint
,
p
->
nVars
);
printf
(
" Var =%5d "
,
p
->
iVar
);
printf
(
"Cla =%6d "
,
bmcg_sat_solver_clausenum
(
p
->
pSat
)
);
printf
(
"Conf =%9d "
,
bmcg_sat_solver_conflictnum
(
p
->
pSat
)
);
Abc_PrintTime
(
1
,
"Time"
,
Abc_Clock
()
-
clk
);
}
if
(
status
==
GLUCOSE_UNSAT
)
{
printf
(
"The problem has no solution.
\n
"
);
break
;
}
iMint
=
Exa3_ManEval
(
p
);
}
if
(
iMint
==
-
1
)
Exa3_ManPrintSolution
(
p
,
fCompl
);
Exa3_ManFree
(
p
);
Abc_PrintTime
(
1
,
"Total runtime"
,
Abc_Clock
()
-
clkTotal
);
}
////////////////////////////////////////////////////////////////////////
/// END OF FILE ///
////////////////////////////////////////////////////////////////////////
...
...
src/sat/bmc/bmcMaj2.c
View file @
3f35ac81
...
...
@@ -198,7 +198,7 @@ static int Maj_ManMarkup( Maj_Man_t * p )
}
}
}
//
printf( "The number of parameter variables = %d.\n", p->iVar );
printf
(
"The number of parameter variables = %d.
\n
"
,
p
->
iVar
);
if
(
!
p
->
fVerbose
)
return
p
->
iVar
;
// printout
...
...
@@ -540,7 +540,6 @@ static int Exa_ManMarkup( Exa_Man_t * p )
assert
(
p
->
nObjs
<=
MAJ_NOBJS
);
// assign variables for truth tables
p
->
iVar
=
1
+
p
->
nNodes
*
3
;
// assign variables for other nodes
for
(
i
=
p
->
nVars
;
i
<
p
->
nObjs
;
i
++
)
{
for
(
k
=
0
;
k
<
2
;
k
++
)
...
...
@@ -552,7 +551,7 @@ static int Exa_ManMarkup( Exa_Man_t * p )
}
}
}
//
printf( "The number of parameter variables = %d.\n", p->iVar );
printf
(
"The number of parameter variables = %d.
\n
"
,
p
->
iVar
);
return
p
->
iVar
;
// printout
for
(
i
=
p
->
nVars
;
i
<
p
->
nObjs
;
i
++
)
...
...
@@ -843,6 +842,386 @@ void Exa_ManExactSynthesis2( char * pTtStr, int nVars, int nNodes, int fVerbose
Abc_PrintTime
(
1
,
"Total runtime"
,
Abc_Clock
()
-
clkTotal
);
}
typedef
struct
Exa3_Man_t_
Exa3_Man_t
;
struct
Exa3_Man_t_
{
int
nVars
;
// inputs
int
nNodes
;
// internal nodes
int
nLutSize
;
// lut size
int
LutMask
;
// lut mask
int
nObjs
;
// total objects (nVars inputs + nNodes internal nodes)
int
nWords
;
// the truth table size in 64-bit words
int
iVar
;
// the next available SAT variable
word
*
pTruth
;
// truth table
Vec_Wrd_t
*
vInfo
;
// nVars + nNodes + 1
int
VarMarks
[
MAJ_NOBJS
][
6
][
MAJ_NOBJS
];
// variable marks
int
VarVals
[
MAJ_NOBJS
];
// values of the first nVars variables
Vec_Wec_t
*
vOutLits
;
// output vars
sat_solver
*
pSat
;
// SAT solver
};
static
inline
word
*
Exa3_ManTruth
(
Exa3_Man_t
*
p
,
int
v
)
{
return
Vec_WrdEntryP
(
p
->
vInfo
,
p
->
nWords
*
v
);
}
/**Function*************************************************************
Synopsis []
Description []
SideEffects []
SeeAlso []
***********************************************************************/
static
Vec_Wrd_t
*
Exa3_ManTruthTables
(
Exa3_Man_t
*
p
)
{
Vec_Wrd_t
*
vInfo
=
p
->
vInfo
=
Vec_WrdStart
(
p
->
nWords
*
(
p
->
nObjs
+
1
)
);
int
i
;
for
(
i
=
0
;
i
<
p
->
nVars
;
i
++
)
Abc_TtIthVar
(
Exa3_ManTruth
(
p
,
i
),
i
,
p
->
nVars
);
//Dau_DsdPrintFromTruth( Exa3_ManTruth(p, p->nObjs), p->nVars );
return
vInfo
;
}
static
int
Exa3_ManMarkup
(
Exa3_Man_t
*
p
)
{
int
i
,
k
,
j
;
assert
(
p
->
nObjs
<=
MAJ_NOBJS
);
// assign variables for truth tables
p
->
iVar
=
1
+
p
->
LutMask
*
p
->
nNodes
;
for
(
i
=
p
->
nVars
;
i
<
p
->
nObjs
;
i
++
)
{
for
(
k
=
0
;
k
<
p
->
nLutSize
;
k
++
)
{
for
(
j
=
0
;
j
<
i
-
k
;
j
++
)
{
Vec_WecPush
(
p
->
vOutLits
,
j
,
Abc_Var2Lit
(
p
->
iVar
,
0
)
);
p
->
VarMarks
[
i
][
k
][
j
]
=
p
->
iVar
++
;
}
}
}
printf
(
"The number of parameter variables = %d.
\n
"
,
p
->
iVar
);
return
p
->
iVar
;
// printout
for
(
i
=
p
->
nVars
;
i
<
p
->
nObjs
;
i
++
)
{
printf
(
"Node %d
\n
"
,
i
);
for
(
j
=
0
;
j
<
p
->
nObjs
;
j
++
)
{
for
(
k
=
0
;
k
<
p
->
nLutSize
;
k
++
)
printf
(
"%3d "
,
p
->
VarMarks
[
i
][
k
][
j
]
);
printf
(
"
\n
"
);
}
}
return
p
->
iVar
;
}
static
Exa3_Man_t
*
Exa3_ManAlloc
(
int
nVars
,
int
nNodes
,
int
nLutSize
,
word
*
pTruth
)
{
Exa3_Man_t
*
p
=
ABC_CALLOC
(
Exa3_Man_t
,
1
);
p
->
nVars
=
nVars
;
p
->
nNodes
=
nNodes
;
p
->
nLutSize
=
nLutSize
;
p
->
LutMask
=
(
1
<<
nLutSize
)
-
1
;
p
->
nObjs
=
nVars
+
nNodes
;
p
->
nWords
=
Abc_TtWordNum
(
nVars
);
p
->
pTruth
=
pTruth
;
p
->
vOutLits
=
Vec_WecStart
(
p
->
nObjs
);
p
->
iVar
=
Exa3_ManMarkup
(
p
);
p
->
vInfo
=
Exa3_ManTruthTables
(
p
);
p
->
pSat
=
sat_solver_new
();
sat_solver_setnvars
(
p
->
pSat
,
p
->
iVar
);
return
p
;
}
static
void
Exa3_ManFree
(
Exa3_Man_t
*
p
)
{
sat_solver_delete
(
p
->
pSat
);
Vec_WrdFree
(
p
->
vInfo
);
Vec_WecFree
(
p
->
vOutLits
);
ABC_FREE
(
p
);
}
/**Function*************************************************************
Synopsis []
Description []
SideEffects []
SeeAlso []
***********************************************************************/
static
inline
int
Exa3_ManFindFanin
(
Exa3_Man_t
*
p
,
int
i
,
int
k
)
{
int
j
,
Count
=
0
,
iVar
=
-
1
;
for
(
j
=
0
;
j
<
p
->
nObjs
;
j
++
)
if
(
p
->
VarMarks
[
i
][
k
][
j
]
&&
sat_solver_var_value
(
p
->
pSat
,
p
->
VarMarks
[
i
][
k
][
j
])
)
{
iVar
=
j
;
Count
++
;
}
assert
(
Count
==
1
);
return
iVar
;
}
static
inline
int
Exa3_ManEval
(
Exa3_Man_t
*
p
)
{
static
int
Flag
=
0
;
int
i
,
k
,
j
,
iMint
;
word
*
pFanins
[
6
];
for
(
i
=
p
->
nVars
;
i
<
p
->
nObjs
;
i
++
)
{
int
iVarStart
=
1
+
p
->
LutMask
*
(
i
-
p
->
nVars
);
for
(
k
=
0
;
k
<
p
->
nLutSize
;
k
++
)
pFanins
[
k
]
=
Exa3_ManTruth
(
p
,
Exa3_ManFindFanin
(
p
,
i
,
k
)
);
Abc_TtConst0
(
Exa3_ManTruth
(
p
,
i
),
p
->
nWords
);
for
(
k
=
1
;
k
<=
p
->
LutMask
;
k
++
)
{
if
(
!
sat_solver_var_value
(
p
->
pSat
,
iVarStart
+
k
-
1
)
)
continue
;
// Abc_TtAndCompl( Exa3_ManTruth(p, p->nObjs), pFanins[0], !(k&1), pFanins[1], !(k>>1), p->nWords );
Abc_TtConst1
(
Exa3_ManTruth
(
p
,
p
->
nObjs
),
p
->
nWords
);
for
(
j
=
0
;
j
<
p
->
nLutSize
;
j
++
)
Abc_TtAndCompl
(
Exa3_ManTruth
(
p
,
p
->
nObjs
),
Exa3_ManTruth
(
p
,
p
->
nObjs
),
0
,
pFanins
[
j
],
!
((
k
>>
j
)
&
1
),
p
->
nWords
);
Abc_TtOr
(
Exa3_ManTruth
(
p
,
i
),
Exa3_ManTruth
(
p
,
i
),
Exa3_ManTruth
(
p
,
p
->
nObjs
),
p
->
nWords
);
}
}
if
(
Flag
&&
p
->
nVars
>=
6
)
iMint
=
Abc_TtFindLastDiffBit
(
Exa3_ManTruth
(
p
,
p
->
nObjs
-
1
),
p
->
pTruth
,
p
->
nVars
);
else
iMint
=
Abc_TtFindFirstDiffBit
(
Exa3_ManTruth
(
p
,
p
->
nObjs
-
1
),
p
->
pTruth
,
p
->
nVars
);
//Flag ^= 1;
assert
(
iMint
<
(
1
<<
p
->
nVars
)
);
return
iMint
;
}
/**Function*************************************************************
Synopsis []
Description []
SideEffects []
SeeAlso []
***********************************************************************/
static
void
Exa3_ManPrintSolution
(
Exa3_Man_t
*
p
,
int
fCompl
)
{
int
i
,
k
,
iVar
;
printf
(
"Realization of given %d-input function using %d %d-input LUTs:
\n
"
,
p
->
nVars
,
p
->
nNodes
,
p
->
nLutSize
);
for
(
i
=
p
->
nObjs
-
1
;
i
>=
p
->
nVars
;
i
--
)
{
int
Val
,
iVarStart
=
1
+
p
->
LutMask
*
(
i
-
p
->
nVars
);
// int Val1 = sat_solver_var_value(p->pSat, iVarStart);
// int Val2 = sat_solver_var_value(p->pSat, iVarStart+1);
// int Val3 = sat_solver_var_value(p->pSat, iVarStart+2);
// if ( i == p->nObjs - 1 && fCompl )
// printf( "%02d = 4\'b%d%d%d1(", i, !Val3, !Val2, !Val1 );
// else
// printf( "%02d = 4\'b%d%d%d0(", i, Val3, Val2, Val1 );
printf
(
"%02d = %d
\'
b"
,
i
,
1
<<
p
->
nLutSize
);
for
(
k
=
p
->
LutMask
-
1
;
k
>=
0
;
k
--
)
{
Val
=
sat_solver_var_value
(
p
->
pSat
,
iVarStart
+
k
);
if
(
i
==
p
->
nObjs
-
1
&&
fCompl
)
printf
(
"%d"
,
!
Val
);
else
printf
(
"%d"
,
Val
);
}
if
(
i
==
p
->
nObjs
-
1
&&
fCompl
)
printf
(
"1("
);
else
printf
(
"0("
);
for
(
k
=
p
->
nLutSize
-
1
;
k
>=
0
;
k
--
)
{
iVar
=
Exa3_ManFindFanin
(
p
,
i
,
k
);
if
(
iVar
>=
0
&&
iVar
<
p
->
nVars
)
printf
(
" %c"
,
'a'
+
iVar
);
else
printf
(
" %02d"
,
iVar
);
}
printf
(
" )
\n
"
);
}
}
/**Function*************************************************************
Synopsis []
Description []
SideEffects []
SeeAlso []
***********************************************************************/
static
int
Exa3_ManAddCnfStart
(
Exa3_Man_t
*
p
)
{
int
pLits
[
MAJ_NOBJS
],
pLits2
[
2
],
i
,
j
,
k
,
n
,
m
;
// input constraints
for
(
i
=
p
->
nVars
;
i
<
p
->
nObjs
;
i
++
)
{
int
iVarStart
=
1
+
p
->
LutMask
*
(
i
-
p
->
nVars
);
for
(
k
=
0
;
k
<
p
->
nLutSize
;
k
++
)
{
int
nLits
=
0
;
for
(
j
=
0
;
j
<
p
->
nObjs
;
j
++
)
if
(
p
->
VarMarks
[
i
][
k
][
j
]
)
pLits
[
nLits
++
]
=
Abc_Var2Lit
(
p
->
VarMarks
[
i
][
k
][
j
],
0
);
assert
(
nLits
>
0
);
// input uniqueness
if
(
!
sat_solver_addclause
(
p
->
pSat
,
pLits
,
pLits
+
nLits
)
)
return
0
;
for
(
n
=
0
;
n
<
nLits
;
n
++
)
for
(
m
=
n
+
1
;
m
<
nLits
;
m
++
)
{
pLits2
[
0
]
=
Abc_LitNot
(
pLits
[
n
]);
pLits2
[
1
]
=
Abc_LitNot
(
pLits
[
m
]);
if
(
!
sat_solver_addclause
(
p
->
pSat
,
pLits2
,
pLits2
+
2
)
)
return
0
;
}
if
(
k
==
p
->
nLutSize
-
1
)
break
;
// symmetry breaking
for
(
j
=
0
;
j
<
p
->
nObjs
;
j
++
)
if
(
p
->
VarMarks
[
i
][
k
][
j
]
)
for
(
n
=
j
;
n
<
p
->
nObjs
;
n
++
)
if
(
p
->
VarMarks
[
i
][
k
+
1
][
n
]
)
{
pLits2
[
0
]
=
Abc_Var2Lit
(
p
->
VarMarks
[
i
][
k
][
j
],
1
);
pLits2
[
1
]
=
Abc_Var2Lit
(
p
->
VarMarks
[
i
][
k
+
1
][
n
],
1
);
if
(
!
sat_solver_addclause
(
p
->
pSat
,
pLits2
,
pLits2
+
2
)
)
return
0
;
}
}
if
(
p
->
nLutSize
!=
2
)
continue
;
// two-input functions
for
(
k
=
0
;
k
<
3
;
k
++
)
{
pLits
[
0
]
=
Abc_Var2Lit
(
iVarStart
,
k
==
1
);
pLits
[
1
]
=
Abc_Var2Lit
(
iVarStart
+
1
,
k
==
2
);
pLits
[
2
]
=
Abc_Var2Lit
(
iVarStart
+
2
,
k
!=
0
);
if
(
!
sat_solver_addclause
(
p
->
pSat
,
pLits
,
pLits
+
3
)
)
return
0
;
}
}
// outputs should be used
for
(
i
=
0
;
i
<
p
->
nObjs
-
1
;
i
++
)
{
Vec_Int_t
*
vArray
=
Vec_WecEntry
(
p
->
vOutLits
,
i
);
assert
(
Vec_IntSize
(
vArray
)
>
0
);
if
(
!
sat_solver_addclause
(
p
->
pSat
,
Vec_IntArray
(
vArray
),
Vec_IntLimit
(
vArray
)
)
)
return
0
;
}
return
1
;
}
static
int
Exa3_ManAddCnf
(
Exa3_Man_t
*
p
,
int
iMint
)
{
// save minterm values
int
i
,
k
,
n
,
j
,
Value
=
Abc_TtGetBit
(
p
->
pTruth
,
iMint
);
for
(
i
=
0
;
i
<
p
->
nVars
;
i
++
)
p
->
VarVals
[
i
]
=
(
iMint
>>
i
)
&
1
;
sat_solver_setnvars
(
p
->
pSat
,
p
->
iVar
+
(
p
->
nLutSize
+
1
)
*
p
->
nNodes
);
//printf( "Adding clauses for minterm %d with value %d.\n", iMint, Value );
for
(
i
=
p
->
nVars
;
i
<
p
->
nObjs
;
i
++
)
{
// fanin connectivity
int
iVarStart
=
1
+
p
->
LutMask
*
(
i
-
p
->
nVars
);
int
iBaseSatVarI
=
p
->
iVar
+
(
p
->
nLutSize
+
1
)
*
(
i
-
p
->
nVars
);
for
(
k
=
0
;
k
<
p
->
nLutSize
;
k
++
)
{
for
(
j
=
0
;
j
<
p
->
nObjs
;
j
++
)
if
(
p
->
VarMarks
[
i
][
k
][
j
]
)
{
int
iBaseSatVarJ
=
p
->
iVar
+
(
p
->
nLutSize
+
1
)
*
(
j
-
p
->
nVars
);
for
(
n
=
0
;
n
<
2
;
n
++
)
{
int
pLits
[
3
],
nLits
=
0
;
pLits
[
nLits
++
]
=
Abc_Var2Lit
(
p
->
VarMarks
[
i
][
k
][
j
],
1
);
pLits
[
nLits
++
]
=
Abc_Var2Lit
(
iBaseSatVarI
+
k
,
n
);
if
(
j
>=
p
->
nVars
)
pLits
[
nLits
++
]
=
Abc_Var2Lit
(
iBaseSatVarJ
+
p
->
nLutSize
,
!
n
);
else
if
(
p
->
VarVals
[
j
]
==
n
)
continue
;
if
(
!
sat_solver_addclause
(
p
->
pSat
,
pLits
,
pLits
+
nLits
)
)
return
0
;
}
}
}
// node functionality
for
(
n
=
0
;
n
<
2
;
n
++
)
{
if
(
i
==
p
->
nObjs
-
1
&&
n
==
Value
)
continue
;
for
(
k
=
0
;
k
<=
p
->
LutMask
;
k
++
)
{
int
pLits
[
8
],
nLits
=
0
;
if
(
k
==
0
&&
n
==
1
)
continue
;
//pLits[nLits++] = Abc_Var2Lit( iBaseSatVarI + 0, (k&1) );
//pLits[nLits++] = Abc_Var2Lit( iBaseSatVarI + 1, (k>>1) );
//if ( i != p->nObjs - 1 ) pLits[nLits++] = Abc_Var2Lit( iBaseSatVarI + 2, !n );
for
(
j
=
0
;
j
<
p
->
nLutSize
;
j
++
)
pLits
[
nLits
++
]
=
Abc_Var2Lit
(
iBaseSatVarI
+
j
,
(
k
>>
j
)
&
1
);
if
(
i
!=
p
->
nObjs
-
1
)
pLits
[
nLits
++
]
=
Abc_Var2Lit
(
iBaseSatVarI
+
j
,
!
n
);
if
(
k
>
0
)
pLits
[
nLits
++
]
=
Abc_Var2Lit
(
iVarStart
+
k
-
1
,
n
);
assert
(
nLits
<=
p
->
nLutSize
+
2
);
if
(
!
sat_solver_addclause
(
p
->
pSat
,
pLits
,
pLits
+
nLits
)
)
return
0
;
}
}
}
p
->
iVar
+=
(
p
->
nLutSize
+
1
)
*
p
->
nNodes
;
return
1
;
}
void
Exa3_ManExactSynthesis2
(
char
*
pTtStr
,
int
nVars
,
int
nNodes
,
int
nLutSize
,
int
fVerbose
)
{
int
i
,
status
,
iMint
=
1
;
abctime
clkTotal
=
Abc_Clock
();
Exa3_Man_t
*
p
;
int
fCompl
=
0
;
word
pTruth
[
16
];
Abc_TtReadHex
(
pTruth
,
pTtStr
);
assert
(
nVars
<=
10
);
assert
(
nLutSize
<=
6
);
p
=
Exa3_ManAlloc
(
nVars
,
nNodes
,
nLutSize
,
pTruth
);
if
(
pTruth
[
0
]
&
1
)
{
fCompl
=
1
;
Abc_TtNot
(
pTruth
,
p
->
nWords
);
}
status
=
Exa3_ManAddCnfStart
(
p
);
assert
(
status
);
printf
(
"Running exact synthesis for %d-input function with %d %d-input LUTs...
\n
"
,
p
->
nVars
,
p
->
nNodes
,
p
->
nLutSize
);
for
(
i
=
0
;
iMint
!=
-
1
;
i
++
)
{
abctime
clk
=
Abc_Clock
();
if
(
!
Exa3_ManAddCnf
(
p
,
iMint
)
)
break
;
status
=
sat_solver_solve
(
p
->
pSat
,
NULL
,
NULL
,
0
,
0
,
0
,
0
);
if
(
fVerbose
)
{
printf
(
"Iter %3d : "
,
i
);
Extra_PrintBinary
(
stdout
,
(
unsigned
*
)
&
iMint
,
p
->
nVars
);
printf
(
" Var =%5d "
,
p
->
iVar
);
printf
(
"Cla =%6d "
,
sat_solver_nclauses
(
p
->
pSat
)
);
printf
(
"Conf =%9d "
,
sat_solver_nconflicts
(
p
->
pSat
)
);
Abc_PrintTime
(
1
,
"Time"
,
Abc_Clock
()
-
clk
);
}
if
(
status
==
l_False
)
{
printf
(
"The problem has no solution.
\n
"
);
break
;
}
iMint
=
Exa3_ManEval
(
p
);
}
if
(
iMint
==
-
1
)
Exa3_ManPrintSolution
(
p
,
fCompl
);
Exa3_ManFree
(
p
);
Abc_PrintTime
(
1
,
"Total runtime"
,
Abc_Clock
()
-
clkTotal
);
}
////////////////////////////////////////////////////////////////////////
/// END OF FILE ///
////////////////////////////////////////////////////////////////////////
...
...
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