1999-11-01 03:07:40 +01:00
|
|
|
/*
|
2000-02-23 03:56:53 +01:00
|
|
|
* Copyright (c) 1999-2000 Stephen Williams (steve@icarus.com)
|
1999-11-01 03:07:40 +01:00
|
|
|
*
|
|
|
|
|
* This source code is free software; you can redistribute it
|
|
|
|
|
* and/or modify it in source code form under the terms of the GNU
|
|
|
|
|
* General Public License as published by the Free Software
|
|
|
|
|
* Foundation; either version 2 of the License, or (at your option)
|
|
|
|
|
* any later version.
|
|
|
|
|
*
|
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
|
*
|
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
|
|
|
|
*/
|
2000-02-23 03:56:53 +01:00
|
|
|
#if !defined(WINNT) && !defined(macintosh)
|
2000-04-12 22:02:52 +02:00
|
|
|
#ident "$Id: synth.cc,v 1.8 2000/04/12 20:02:53 steve Exp $"
|
1999-11-01 03:07:40 +01:00
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* The synth function searches the behavioral description for
|
|
|
|
|
* patterns that are known to represent LPM library components. This
|
|
|
|
|
* is especially interesting for the sequential components such as
|
|
|
|
|
* flip flops and latches. As threads are transformed into components,
|
|
|
|
|
* the design is rewritten.
|
1999-12-01 07:06:16 +01:00
|
|
|
*/
|
|
|
|
|
# include "functor.h"
|
|
|
|
|
# include "netlist.h"
|
|
|
|
|
|
2000-04-12 22:02:52 +02:00
|
|
|
/*
|
|
|
|
|
* This functor scans the behavioral code, looking for expressions to
|
|
|
|
|
* synthesize. Although it uses the proc_match_t class, it doesn't
|
|
|
|
|
* actually match anything, but transforms expressions into structural
|
|
|
|
|
* netlists. The product of this should be a process where all the
|
|
|
|
|
* expressions have been reduced to a signal ident, which references
|
|
|
|
|
* the NetNet of the now synthesized expression.
|
|
|
|
|
*/
|
|
|
|
|
class do_expr : public proc_match_t {
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
do_expr(Design*d)
|
|
|
|
|
: des_(d) { }
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
|
|
|
|
|
Design*des_;
|
|
|
|
|
|
|
|
|
|
virtual int assign(NetAssign*);
|
|
|
|
|
virtual int event_wait(NetEvWait*);
|
|
|
|
|
//virtual int assign_mem(NetAssignMem*);
|
|
|
|
|
//virtual int condit(NetCondit*);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int do_expr::assign(NetAssign*stmt)
|
|
|
|
|
{
|
|
|
|
|
if (dynamic_cast<NetESignal*>(stmt->rval()))
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
NetNet*tmp = stmt->rval()->synthesize(des_);
|
|
|
|
|
if (tmp == 0)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
NetESignal*tmpe = new NetESignal(tmp);
|
|
|
|
|
stmt->set_rval(tmpe);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int do_expr::event_wait(NetEvWait*stmt)
|
|
|
|
|
{
|
|
|
|
|
NetProc*tmp = stmt->statement();
|
|
|
|
|
if (tmp)
|
|
|
|
|
return tmp->match_proc(this);
|
|
|
|
|
else
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
1999-12-01 07:06:16 +01:00
|
|
|
/*
|
|
|
|
|
* This transform recognizes the following patterns:
|
1999-11-01 03:07:40 +01:00
|
|
|
*
|
|
|
|
|
* always @(posedge CLK) Q = D
|
|
|
|
|
* always @(negedge CLK) Q = D
|
|
|
|
|
*
|
|
|
|
|
* always @(posedge CLK) if (CE) Q = D;
|
|
|
|
|
* always @(negedge CLK) if (CE) Q = D;
|
|
|
|
|
*
|
1999-12-05 03:24:08 +01:00
|
|
|
* These are memory assignments:
|
|
|
|
|
*
|
|
|
|
|
* always @(posedge CLK) M[a] = D
|
|
|
|
|
* always @(negedge CLK) M[a] = D
|
|
|
|
|
*
|
|
|
|
|
* always @(posedge CLK) if (CE) M[a] = D;
|
|
|
|
|
* always @(negedge CLK) if (CE) M[a] = D;
|
|
|
|
|
*
|
1999-11-01 03:07:40 +01:00
|
|
|
* The r-value of the assignments must be identifiers (i.e. wires or
|
|
|
|
|
* registers) and the CE must be single-bit identifiers. The generated
|
|
|
|
|
* device will be wide enough to accomodate Q and D.
|
|
|
|
|
*/
|
1999-12-01 07:06:16 +01:00
|
|
|
class match_dff : public proc_match_t {
|
1999-11-01 03:07:40 +01:00
|
|
|
|
|
|
|
|
public:
|
1999-12-01 07:06:16 +01:00
|
|
|
match_dff(Design*d, NetProcTop*t)
|
2000-04-12 22:02:52 +02:00
|
|
|
: des_(d), top_(t), wclk_(0), eclk_(0), pclk_(0), con_(0), ce_(0),
|
1999-12-05 03:24:08 +01:00
|
|
|
asn_(0), asm_(0), d_(0)
|
1999-12-01 07:06:16 +01:00
|
|
|
{ }
|
1999-11-01 03:07:40 +01:00
|
|
|
|
1999-12-01 07:06:16 +01:00
|
|
|
~match_dff() { }
|
|
|
|
|
|
|
|
|
|
void make_it();
|
1999-11-01 03:07:40 +01:00
|
|
|
|
1999-12-01 07:06:16 +01:00
|
|
|
private:
|
1999-12-05 03:24:08 +01:00
|
|
|
void make_dff_();
|
|
|
|
|
void make_ram_();
|
|
|
|
|
|
1999-12-01 07:06:16 +01:00
|
|
|
virtual int assign(NetAssign*);
|
1999-12-05 03:24:08 +01:00
|
|
|
virtual int assign_mem(NetAssignMem*);
|
1999-12-01 07:06:16 +01:00
|
|
|
virtual int condit(NetCondit*);
|
2000-04-12 22:02:52 +02:00
|
|
|
virtual int event_wait(NetEvWait*);
|
1999-11-01 03:07:40 +01:00
|
|
|
|
1999-12-01 07:06:16 +01:00
|
|
|
Design*des_;
|
|
|
|
|
NetProcTop*top_;
|
2000-04-12 22:02:52 +02:00
|
|
|
|
|
|
|
|
NetEvWait*wclk_;
|
|
|
|
|
NetEvent *eclk_;
|
|
|
|
|
NetEvProbe*pclk_;
|
1999-11-01 03:07:40 +01:00
|
|
|
|
1999-12-01 07:06:16 +01:00
|
|
|
NetCondit *con_;
|
|
|
|
|
NetNet*ce_;
|
1999-12-05 03:24:08 +01:00
|
|
|
|
1999-12-01 07:06:16 +01:00
|
|
|
NetAssign *asn_;
|
1999-12-05 03:24:08 +01:00
|
|
|
NetAssignMem*asm_;
|
1999-11-01 03:07:40 +01:00
|
|
|
|
1999-12-01 07:06:16 +01:00
|
|
|
NetNet*d_;
|
1999-11-01 03:07:40 +01:00
|
|
|
};
|
|
|
|
|
|
1999-12-01 07:06:16 +01:00
|
|
|
int match_dff::assign(NetAssign*as)
|
|
|
|
|
{
|
|
|
|
|
if (!pclk_)
|
|
|
|
|
return 0;
|
1999-12-05 03:24:08 +01:00
|
|
|
if (asn_ || asm_)
|
1999-12-01 07:06:16 +01:00
|
|
|
return 0;
|
1999-11-01 03:07:40 +01:00
|
|
|
|
1999-12-01 07:06:16 +01:00
|
|
|
asn_ = as;
|
|
|
|
|
d_ = asn_->rval()->synthesize(des_);
|
|
|
|
|
if (d_ == 0)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
1999-12-05 03:24:08 +01:00
|
|
|
int match_dff::assign_mem(NetAssignMem*as)
|
|
|
|
|
{
|
|
|
|
|
if (!pclk_)
|
|
|
|
|
return 0;
|
|
|
|
|
if (asn_ || asm_)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
asm_ = as;
|
|
|
|
|
d_ = asm_->rval()->synthesize(des_);
|
|
|
|
|
if (d_ == 0)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
1999-12-01 07:06:16 +01:00
|
|
|
int match_dff::condit(NetCondit*co)
|
1999-11-01 03:07:40 +01:00
|
|
|
{
|
1999-12-01 07:06:16 +01:00
|
|
|
if (!pclk_)
|
|
|
|
|
return 0;
|
1999-12-05 03:24:08 +01:00
|
|
|
if (con_ || asn_ || asm_)
|
1999-12-01 07:06:16 +01:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
con_ = co;
|
|
|
|
|
if (con_->else_clause())
|
|
|
|
|
return 0;
|
|
|
|
|
ce_ = con_->expr()->synthesize(des_);
|
|
|
|
|
if (ce_ == 0)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
return con_->if_clause()->match_proc(this);
|
1999-11-01 03:07:40 +01:00
|
|
|
}
|
|
|
|
|
|
2000-04-12 22:02:52 +02:00
|
|
|
int match_dff::event_wait(NetEvWait*evw)
|
1999-11-01 03:07:40 +01:00
|
|
|
{
|
2000-04-12 22:02:52 +02:00
|
|
|
if (evw->nevents() != 1)
|
1999-12-01 07:06:16 +01:00
|
|
|
return 0;
|
|
|
|
|
|
2000-04-12 22:02:52 +02:00
|
|
|
wclk_ = evw;
|
|
|
|
|
eclk_ = evw->event(0);
|
1999-11-01 03:07:40 +01:00
|
|
|
|
2000-04-12 22:02:52 +02:00
|
|
|
if (eclk_->nprobe() != 1)
|
1999-12-01 07:06:16 +01:00
|
|
|
return 0;
|
2000-04-02 06:26:06 +02:00
|
|
|
|
2000-04-12 22:02:52 +02:00
|
|
|
pclk_ = eclk_->probe(0);
|
|
|
|
|
|
|
|
|
|
return wclk_->statement()->match_proc(this);
|
1999-11-01 03:07:40 +01:00
|
|
|
}
|
|
|
|
|
|
1999-12-01 07:06:16 +01:00
|
|
|
void match_dff::make_it()
|
1999-12-05 03:24:08 +01:00
|
|
|
{
|
|
|
|
|
if (asn_) {
|
|
|
|
|
assert(asm_ == 0);
|
|
|
|
|
make_dff_();
|
|
|
|
|
} else {
|
|
|
|
|
assert(asm_);
|
|
|
|
|
make_ram_();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void match_dff::make_dff_()
|
1999-11-01 03:07:40 +01:00
|
|
|
{
|
|
|
|
|
NetFF*ff = new NetFF(asn_->name(), asn_->pin_count());
|
|
|
|
|
|
|
|
|
|
for (unsigned idx = 0 ; idx < ff->width() ; idx += 1) {
|
1999-12-01 07:06:16 +01:00
|
|
|
connect(ff->pin_Data(idx), d_->pin(idx));
|
1999-11-01 03:07:40 +01:00
|
|
|
connect(ff->pin_Q(idx), asn_->pin(idx));
|
|
|
|
|
}
|
|
|
|
|
|
2000-04-12 22:02:52 +02:00
|
|
|
connect(ff->pin_Clock(), pclk_->pin(0));
|
1999-12-01 07:06:16 +01:00
|
|
|
if (ce_) connect(ff->pin_Enable(), ce_->pin(0));
|
1999-11-01 03:07:40 +01:00
|
|
|
|
1999-12-01 07:06:16 +01:00
|
|
|
ff->attribute("LPM_FFType", "DFF");
|
2000-04-12 22:02:52 +02:00
|
|
|
if (pclk_->edge() == NetEvProbe::NEGEDGE)
|
1999-11-01 03:07:40 +01:00
|
|
|
ff->attribute("Clock:LPM_Polarity", "INVERT");
|
|
|
|
|
|
1999-12-01 07:06:16 +01:00
|
|
|
des_->add_node(ff);
|
1999-11-01 03:07:40 +01:00
|
|
|
}
|
|
|
|
|
|
1999-12-05 03:24:08 +01:00
|
|
|
void match_dff::make_ram_()
|
|
|
|
|
{
|
|
|
|
|
NetMemory*mem = asm_->memory();
|
|
|
|
|
NetNet*adr = asm_->index();
|
|
|
|
|
NetRamDq*ram = new NetRamDq(des_->local_symbol(mem->name()), mem,
|
|
|
|
|
adr->pin_count());
|
|
|
|
|
for (unsigned idx = 0 ; idx < adr->pin_count() ; idx += 1)
|
|
|
|
|
connect(adr->pin(idx), ram->pin_Address(idx));
|
|
|
|
|
|
|
|
|
|
for (unsigned idx = 0 ; idx < ram->width() ; idx += 1)
|
|
|
|
|
connect(ram->pin_Data(idx), d_->pin(idx));
|
|
|
|
|
|
|
|
|
|
if (ce_)
|
|
|
|
|
connect(ram->pin_WE(), ce_->pin(0));
|
|
|
|
|
|
2000-04-12 22:02:52 +02:00
|
|
|
assert(pclk_->edge() == NetEvProbe::POSEDGE);
|
|
|
|
|
connect(ram->pin_InClock(), pclk_->pin(0));
|
1999-12-05 03:24:08 +01:00
|
|
|
|
|
|
|
|
ram->absorb_partners();
|
|
|
|
|
des_->add_node(ram);
|
|
|
|
|
}
|
|
|
|
|
|
2000-02-13 05:35:43 +01:00
|
|
|
class match_block : public proc_match_t {
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
match_block(Design*d, NetProcTop*t)
|
|
|
|
|
: des_(d), top_(t)
|
|
|
|
|
{ }
|
|
|
|
|
|
|
|
|
|
~match_block() { }
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
|
|
|
|
|
Design*des_;
|
|
|
|
|
NetProcTop*top_;
|
|
|
|
|
};
|
|
|
|
|
|
1999-12-01 07:06:16 +01:00
|
|
|
class synth_f : public functor_t {
|
1999-11-01 03:07:40 +01:00
|
|
|
|
1999-12-01 07:06:16 +01:00
|
|
|
public:
|
|
|
|
|
void process(class Design*, class NetProcTop*);
|
1999-11-01 03:07:40 +01:00
|
|
|
|
1999-12-01 07:06:16 +01:00
|
|
|
private:
|
|
|
|
|
void proc_always_(class Design*);
|
1999-11-01 03:07:40 +01:00
|
|
|
|
1999-12-01 07:06:16 +01:00
|
|
|
NetProcTop*top_;
|
|
|
|
|
};
|
1999-11-01 03:07:40 +01:00
|
|
|
|
|
|
|
|
|
1999-12-01 07:06:16 +01:00
|
|
|
/*
|
|
|
|
|
* Look at a process, and divide the problem into always and initial
|
|
|
|
|
* threads.
|
|
|
|
|
*/
|
|
|
|
|
void synth_f::process(class Design*des, class NetProcTop*top)
|
|
|
|
|
{
|
|
|
|
|
switch (top->type()) {
|
|
|
|
|
case NetProcTop::KALWAYS:
|
|
|
|
|
top_ = top;
|
|
|
|
|
proc_always_(des);
|
1999-11-01 03:07:40 +01:00
|
|
|
break;
|
|
|
|
|
}
|
1999-12-01 07:06:16 +01:00
|
|
|
}
|
1999-11-01 03:07:40 +01:00
|
|
|
|
1999-12-01 07:06:16 +01:00
|
|
|
/*
|
|
|
|
|
* An "always ..." statement has been found.
|
|
|
|
|
*/
|
|
|
|
|
void synth_f::proc_always_(class Design*des)
|
|
|
|
|
{
|
2000-04-12 22:02:52 +02:00
|
|
|
do_expr expr_pat(des);
|
|
|
|
|
top_->statement()->match_proc(&expr_pat);
|
|
|
|
|
|
1999-12-01 07:06:16 +01:00
|
|
|
match_dff dff_pat(des, top_);
|
|
|
|
|
if (top_->statement()->match_proc(&dff_pat)) {
|
|
|
|
|
dff_pat.make_it();
|
|
|
|
|
des->delete_process(top_);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2000-02-13 05:35:43 +01:00
|
|
|
|
|
|
|
|
match_block block_pat(des, top_);
|
|
|
|
|
if (top_->statement()->match_proc(&block_pat)) {
|
|
|
|
|
cerr << "XXXX: recurse and return here" << endl;
|
|
|
|
|
}
|
1999-11-01 03:07:40 +01:00
|
|
|
}
|
|
|
|
|
|
1999-12-01 07:06:16 +01:00
|
|
|
|
1999-11-01 03:07:40 +01:00
|
|
|
void synth(Design*des)
|
|
|
|
|
{
|
|
|
|
|
synth_f synth_obj;
|
|
|
|
|
des->functor(&synth_obj);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* $Log: synth.cc,v $
|
2000-04-12 22:02:52 +02:00
|
|
|
* Revision 1.8 2000/04/12 20:02:53 steve
|
|
|
|
|
* Finally remove the NetNEvent and NetPEvent classes,
|
|
|
|
|
* Get synthesis working with the NetEvWait class,
|
|
|
|
|
* and get started supporting multiple events in a
|
|
|
|
|
* wait in vvm.
|
|
|
|
|
*
|
2000-04-02 06:26:06 +02:00
|
|
|
* Revision 1.7 2000/04/02 04:26:07 steve
|
|
|
|
|
* Remove the useless sref template.
|
|
|
|
|
*
|
2000-02-23 03:56:53 +01:00
|
|
|
* Revision 1.6 2000/02/23 02:56:55 steve
|
|
|
|
|
* Macintosh compilers do not support ident.
|
|
|
|
|
*
|
2000-02-13 05:35:43 +01:00
|
|
|
* Revision 1.5 2000/02/13 04:35:43 steve
|
|
|
|
|
* Include some block matching from Larry.
|
|
|
|
|
*
|
1999-12-05 03:24:08 +01:00
|
|
|
* Revision 1.4 1999/12/05 02:24:09 steve
|
|
|
|
|
* Synthesize LPM_RAM_DQ for writes into memories.
|
|
|
|
|
*
|
1999-12-01 07:06:16 +01:00
|
|
|
* Revision 1.3 1999/12/01 06:06:16 steve
|
|
|
|
|
* Redo synth to use match_proc_t scanner.
|
|
|
|
|
*
|
1999-11-02 05:55:34 +01:00
|
|
|
* Revision 1.2 1999/11/02 04:55:34 steve
|
|
|
|
|
* Add the synthesize method to NetExpr to handle
|
|
|
|
|
* synthesis of expressions, and use that method
|
|
|
|
|
* to improve r-value handling of LPM_FF synthesis.
|
|
|
|
|
*
|
|
|
|
|
* Modify the XNF target to handle LPM_FF objects.
|
|
|
|
|
*
|
1999-11-01 03:07:40 +01:00
|
|
|
* Revision 1.1 1999/11/01 02:07:41 steve
|
|
|
|
|
* Add the synth functor to do generic synthesis
|
|
|
|
|
* and add the LPM_FF device to handle rows of
|
|
|
|
|
* flip-flops.
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
|