Basic DFF asynchronous set/reset synthesis support.

This commit is contained in:
Stephen Williams 2014-06-15 18:22:02 -07:00
parent ccce9d9271
commit 0f85bf0b9a
10 changed files with 274 additions and 80 deletions

View File

@ -2557,6 +2557,7 @@ class NetProc : public virtual LineInfo {
// the flipflop being generated.
virtual bool synth_sync(Design*des, NetScope*scope,
NetNet*ff_clock, NetNet*ff_ce,
NetNet*ff_aclr, NetNet*ff_aset,
NexusSet&nex_map, NetBus&nex_out,
const std::vector<NetEvProbe*>&events);
@ -2837,6 +2838,7 @@ class NetBlock : public NetProc {
bool synth_sync(Design*des, NetScope*scope,
NetNet*ff_clk, NetNet*ff_ce,
NetNet*ff_aclr,NetNet*ff_aset,
NexusSet&nex_map, NetBus&nex_out,
const std::vector<NetEvProbe*>&events);
@ -2975,6 +2977,7 @@ class NetCondit : public NetProc {
bool synth_sync(Design*des, NetScope*scope,
NetNet*ff_clk, NetNet*ff_ce,
NetNet*ff_aclr,NetNet*ff_aset,
NexusSet&nex_map, NetBus&nex_out,
const std::vector<NetEvProbe*>&events);
@ -3251,6 +3254,7 @@ class NetEvWait : public NetProc {
virtual bool synth_sync(Design*des, NetScope*scope,
NetNet*ff_clk, NetNet*ff_ce,
NetNet*ff_aclr,NetNet*ff_aset,
NexusSet&nex_map, NetBus&nex_out,
const std::vector<NetEvProbe*>&events);

120
synth2.cc
View File

@ -35,6 +35,7 @@ bool NetProc::synth_async(Design*, NetScope*, NexusSet&, NetBus&, NetBus&)
bool NetProc::synth_sync(Design*des, NetScope*scope,
NetNet* /* ff_clk */, NetNet* /* ff_ce */,
NetNet* /* ff_aclr*/, NetNet* /* ff_aset*/,
NexusSet&nex_map, NetBus&nex_out,
const vector<NetEvProbe*>&events)
{
@ -1035,6 +1036,7 @@ bool NetProcTop::synth_async(Design*des)
*/
bool NetBlock::synth_sync(Design*des, NetScope*scope,
NetNet*ff_clk, NetNet*ff_ce,
NetNet*ff_aclr,NetNet*ff_aset,
NexusSet&nex_map, NetBus&nex_out,
const vector<NetEvProbe*>&events_in)
{
@ -1064,6 +1066,7 @@ bool NetBlock::synth_sync(Design*des, NetScope*scope,
nexa that we expect, and the tmp_out is where we want
those outputs connected. */
bool ok_flag = cur->synth_sync(des, scope, ff_clk, ff_ce,
ff_aclr, ff_aset,
tmp_set, tmp_out, events_in);
flag = flag && ok_flag;
@ -1099,6 +1102,7 @@ bool NetBlock::synth_sync(Design*des, NetScope*scope,
*/
bool NetCondit::synth_sync(Design*des, NetScope*scope,
NetNet*ff_clk, NetNet*ff_ce,
NetNet*ff_aclr,NetNet*ff_aset,
NexusSet&nex_map, NetBus&nex_out,
const vector<NetEvProbe*>&events_in)
{
@ -1118,65 +1122,71 @@ bool NetCondit::synth_sync(Design*des, NetScope*scope,
if (! expr_input->contains(pin_set))
continue;
cerr << get_fileline() << ": sorry: "
<< "Forgot how to implement asynchronous set/reset." << endl;
return false;
#if 0
/* Ah, this edge is in the sensitivity list for the
expression, so we have an asynchronous
input. Synthesize the set/reset input expression. */
NetNet*rst = expr_->synthesize(des);
assert(rst->pin_count() == 1);
// Synthesize the set/reset input expression.
NetNet*rst = expr_->synthesize(des, scope, expr_);
ivl_assert(*this, rst->pin_count() == 1);
/* XXXX I really should find a way to check that the
edge used on the reset input is correct. This would
involve interpreting the expression that is fed by the
reset expression. */
//assert(ev->edge() == NetEvProbe::POSEDGE);
ivl_assert(*this, ev->edge() == NetEvProbe::POSEDGE);
/* Synthesize the true clause to figure out what
kind of set/reset we have. */
NetNet*asig = new NetNet(scope, scope->local_symbol(),
NetNet::WIRE, nex_map->pin_count());
asig->local_flag(true);
// Synthesize the true clause to figure out what kind of
// set/reset we have. This should synthesize down to a
// constant. If not, we have an asynchronous LOAD, a
// very different beast.
ivl_assert(*this, if_);
bool flag;
NetBus tmp_out(scope, nex_out.pin_count());
NetBus accumulated_tmp_out(scope, nex_out.pin_count());
flag = if_->synth_async(des, scope, nex_map, tmp_out, accumulated_tmp_out);
ivl_assert(*this, flag);
assert(if_ != 0);
bool flag = if_->synth_async(des, scope, nex_map, asig);
ivl_assert(*this, tmp_out.pin_count()==1);
Nexus*rst_nex = tmp_out.pin(0).nexus();
assert(asig->pin_count() == ff->width());
vector<bool> rst_mask = rst_nex->driven_mask();
cerr << get_fileline() << ": NetCondit::synth_sync: "
<< "rst_mask.size()==" << rst_mask.size()
<< ", rst_nex->vector_width()=" << rst_nex->vector_width()
<< endl;
/* Collect the set/reset value into a verinum. If
this turns out to be entirely 0 values, then
use the Aclr input. Otherwise, use the Aset
input and save the set value. */
verinum tmp (verinum::V0, ff->width());
for (unsigned bit = 0 ; bit < ff->width() ; bit += 1) {
assert(asig->pin(bit).nexus()->drivers_constant());
tmp.set(bit, asig->pin(bit).nexus()->driven_value());
ivl_assert(*this, rst_mask.size()==1);
if (rst_mask[0]==false) {
cerr << get_fileline() << ": sorry: "
<< "Asynchronous LOAD not implemented." << endl;
return false;
}
assert(tmp.is_defined());
if (tmp.is_zero()) {
connect(ff->pin_Aclr(), rst->pin(0));
verinum rst_drv = rst_nex->driven_vector();
ivl_assert(*this, rst_drv.len()==1);
if (rst_drv[0]==verinum::V0) {
ivl_assert(*this, ff_aclr && ff_aclr->pin_count()==1);
// Don't yet support multiple asynchronous reset inputs.
ivl_assert(*this, ! ff_aclr->pin(0).is_linked());
ivl_assert(*this, rst->pin_count()==1);
connect(ff_aclr->pin(0), rst->pin(0));
} else if (rst_drv[0]==verinum::V1) {
ivl_assert(*this, ff_aset && ff_aset->pin_count()==1);
// Don't yet support multiple asynchronous set inputs.
ivl_assert(*this, ! ff_aset->pin(0).is_linked());
ivl_assert(*this, rst->pin_count()==1);
connect(ff_aset->pin(0), rst->pin(0));
} else {
connect(ff->pin_Aset(), rst->pin(0));
ff->aset_value(tmp);
cerr << get_fileline() << ": sorry: "
<< "Forgot how to implement asynchronous scramble (set to x/z)." << endl;
return false;
}
delete asig;
delete expr_input;
assert(events_in.count() == 1);
assert(else_ != 0);
flag = else_->synth_sync(des, scope, ff, nex_map,
nex_out, svector<NetEvProbe*>(0))
&& flag;
DEBUG_SYNTH2_EXIT("NetCondit",flag)
return flag;
#endif
return else_->synth_sync(des, scope, ff_clk, ff_ce,
ff_aclr, ff_aset,
nex_map, nex_out, vector<NetEvProbe*>(0));
}
delete expr_input;
@ -1302,13 +1312,14 @@ bool NetCondit::synth_sync(Design*des, NetScope*scope,
connect(ff_ce->pin(0), ce->pin(0));
}
bool flag = if_->synth_sync(des, scope, ff_clk, ff_ce, nex_map, nex_out, events_in);
bool flag = if_->synth_sync(des, scope, ff_clk, ff_ce, ff_aclr, ff_aset, nex_map, nex_out, events_in);
return flag;
}
bool NetEvWait::synth_sync(Design*des, NetScope*scope,
NetNet*ff_clk, NetNet*ff_ce,
NetNet*ff_aclr,NetNet*ff_aset,
NexusSet&nex_map, NetBus&nex_out,
const vector<NetEvProbe*>&events_in)
{
@ -1390,6 +1401,7 @@ bool NetEvWait::synth_sync(Design*des, NetScope*scope,
/* Synthesize the input to the DFF. */
bool flag = statement_->synth_sync(des, scope, ff_clk, ff_ce,
ff_aclr, ff_aset,
nex_map, nex_out, events);
return flag;
@ -1424,6 +1436,14 @@ bool NetProcTop::synth_sync(Design*des)
NetNet::TRI, &netvector_t::scalar_logic);
ce->local_flag(true);
NetNet*aclr = new NetNet(scope(), scope()->local_symbol(),
NetNet::TRI, &netvector_t::scalar_logic);
aclr->local_flag(true);
NetNet*aset = new NetNet(scope(), scope()->local_symbol(),
NetNet::TRI, &netvector_t::scalar_logic);
aset->local_flag(true);
NetBus nex_d (scope(), nex_set.size());
NetBus nex_q (scope(), nex_set.size());
@ -1438,7 +1458,7 @@ bool NetProcTop::synth_sync(Design*des)
// Connect the input later.
/* Synthesize the input to the DFF. */
bool flag = statement_->synth_sync(des, scope(), clock, ce,
bool flag = statement_->synth_sync(des, scope(), clock, ce, aclr, aset,
nex_set, nex_d,
vector<NetEvProbe*>());
if (! flag) {
@ -1473,11 +1493,11 @@ bool NetProcTop::synth_sync(Design*des)
connect(clock->pin(0), ff2->pin_Clock());
if (ce->is_linked())
connect(ce->pin(0), ff2->pin_Enable());
if (aclr->is_linked())
connect(aclr->pin(0), ff2->pin_Aclr());
if (aset->is_linked())
connect(aset->pin(0), ff2->pin_Aset());
#if 0
if (ff->pin_Aset().is_linked())
connect(ff->pin_Aset(), ff2->pin_Aset());
if (ff->pin_Aclr().is_linked())
connect(ff->pin_Aclr(), ff2->pin_Aclr());
if (ff->pin_Sset().is_linked())
connect(ff->pin_Sset(), ff2->pin_Sset());
if (ff->pin_Sclr().is_linked())

View File

@ -517,8 +517,28 @@ static void show_lpm_ff(ivl_lpm_t net)
}
}
if (ivl_lpm_async_clr(net)) {
nex = ivl_lpm_async_clr(net);
fprintf(out, " Aclr: %p\n", nex);
if (width_of_nexus(nex) != 1) {
fprintf(out, " Aclr: ERROR: Nexus width is %u\n",
width_of_nexus(nex));
stub_errors += 1;
}
}
if (ivl_lpm_async_set(net)) {
nex = ivl_lpm_async_set(net);
fprintf(out, " Aset: %p\n", nex);
if (width_of_nexus(nex) != 1) {
fprintf(out, " Aset: ERROR: Nexus width is %u\n",
width_of_nexus(nex));
stub_errors += 1;
}
}
nex = ivl_lpm_data(net,0);
fprintf(out, " D: %p\n", nex);
fprintf(out, " D: %p\n", nex);
if (width_of_nexus(nex) != width) {
fprintf(out, " D: ERROR: Nexus width is %u\n",
width_of_nexus(nex));
@ -526,7 +546,7 @@ static void show_lpm_ff(ivl_lpm_t net)
}
nex = ivl_lpm_q(net);
fprintf(out, " Q: %p\n", nex);
fprintf(out, " Q: %p\n", nex);
if (width_of_nexus(nex) != width) {
fprintf(out, " Q: ERROR: Nexus width is %u\n",
width_of_nexus(nex));

View File

@ -1724,10 +1724,16 @@ static void draw_lpm_ff(ivl_lpm_t net)
* generator in V0.10 and later for how this might be done. */
assert(ivl_lpm_sync_clr(net) == 0);
assert(ivl_lpm_sync_set(net) == 0);
assert(ivl_lpm_async_clr(net) == 0);
assert(ivl_lpm_async_set(net) == 0);
fprintf(vvp_out, "L_%p .dff ", net);
if (ivl_lpm_async_clr(net)) {
assert(! ivl_lpm_async_set(net));
fprintf(vvp_out, "L_%p .dff/aclr ", net);
} else if (ivl_lpm_async_set(net)) {
assert(! ivl_lpm_async_clr(net));
fprintf(vvp_out, "L_%p .dff/aset ", net);
} else {
fprintf(vvp_out, "L_%p .dff ", net);
}
nex = ivl_lpm_data(net,0);
assert(nex);
@ -1747,8 +1753,13 @@ static void draw_lpm_ff(ivl_lpm_t net)
fprintf(vvp_out, ", C4<1>");
}
/* Stub asynchronous input for now. */
fprintf(vvp_out, ", C4<z>");
if ( (nex = ivl_lpm_async_clr(net)) ) {
fprintf(vvp_out, ", %s", draw_net_input(nex));
}
if ( (nex = ivl_lpm_async_set(net)) ) {
fprintf(vvp_out, ", %s", draw_net_input(nex));
}
fprintf(vvp_out, ";\n");
}

View File

@ -192,16 +192,18 @@ The Verilog language itself does not have a DFF primitive, but post
synthesis readily creates DFF devices that are best simulated with a
common device. Thus, there is the DFF statement to create DFF devices:
<label> .dff <d>, <clk>, <ce>, <async-input>;
<label> .dff <d>, <clk>, <ce>;
<label> .dff/aclr <d>, <clk>, <ce>, <async-input>;
<label> .dff/aset <d>, <clk>, <ce>, <async-input>;
The generated functor is generally synchronous on the <clk> rising
edge of <clk>, with the <ce> enable active high. The <clk> and <ce>
are single bit vectors (or scalars) on ports 1 and 2. Port-0 is any
type of datum at all. The device will transfer the input to the output
when it is loaded by a clock. The <async-input> is a special
asynchronous input that is immediately stored and transferred to the
output when data arrives here. This is useful for implementing
asynchronous set/clear functions.
asynchronous input that on the rising edge causes the device to
clear/set, and force the output to propagate. Thus, they implement DFF
with asynchronous clr or set.
UDP STATEMENTS:

View File

@ -207,8 +207,18 @@ extern void compile_cmp_gt_r(char*label, unsigned argc, struct symb_s*argv);
extern void compile_dff(char*label,
struct symb_s arg_d,
struct symb_s arg_c,
struct symb_s arg_e,
struct symb_s arg_a);
struct symb_s arg_e);
extern void compile_dff_aclr(char*label,
struct symb_s arg_d,
struct symb_s arg_c,
struct symb_s arg_e,
struct symb_s arg_a);
extern void compile_dff_aset(char*label,
struct symb_s arg_d,
struct symb_s arg_c,
struct symb_s arg_e,
struct symb_s arg_a);
extern void compile_enum2_type(char*label, long width, bool signed_flag,
std::list<struct enum_name_s>*names);

View File

@ -65,29 +65,122 @@ void vvp_dff::recv_vec4(vvp_net_ptr_t port, const vvp_vector4_t&bit,
break;
case 3: // Asynch-D
// FIXME! The code generator is writing an input C4<z>
// no matter what the intent of this device. This is
// almost certainly NOT correct, nor do we want to
// propagate that. But that needs to be fixed later.
if (bit.size() == 1 && bit.value(0)==BIT4_Z)
break;
if (d_.size() > bit.size())
d_ .copy_bits(bit);
else
d_ = bit;
port.ptr()->send_vec4(d_, 0);
assert(0);
break;
}
}
/*
* The recv_clear and recv_set function respond to asynchronout
* clear/set input by propagating the desired output.
*
* NOTE: Don't touch the d_ value, because that tracks the D input,
* which may be needed when the device is clocked afterwards.
*/
void vvp_dff::recv_clear(vvp_net_ptr_t port)
{
vvp_vector4_t tmp = d_;
for (unsigned idx = 0 ; idx < d_.size() ; idx += 1)
tmp.set_bit(idx, BIT4_0);
port.ptr()->send_vec4(tmp, 0);
}
void vvp_dff::recv_set(vvp_net_ptr_t port)
{
vvp_vector4_t tmp = d_;
for (unsigned idx = 0 ; idx < d_.size() ; idx += 1)
tmp.set_bit(idx, BIT4_1);
port.ptr()->send_vec4(tmp, 0);
}
vvp_dff_aclr::vvp_dff_aclr(bool invert_clk, bool invert_ce)
: vvp_dff(invert_clk, invert_ce)
{
a_ = BIT4_X;
}
void vvp_dff_aclr::recv_vec4(vvp_net_ptr_t port, const vvp_vector4_t&bit,
vvp_context_t ctx)
{
if (port.port() == 3) {
assert(bit.size()==1);
if (a_ == bit.value(0))
return;
a_ = bit.value(0);
recv_clear(port);
} else {
vvp_dff::recv_vec4(port, bit, ctx);
}
}
vvp_dff_aset::vvp_dff_aset(bool invert_clk, bool invert_ce)
: vvp_dff(invert_clk, invert_ce)
{
a_ = BIT4_X;
}
void vvp_dff_aset::recv_vec4(vvp_net_ptr_t port, const vvp_vector4_t&bit,
vvp_context_t ctx)
{
if (port.port() == 3) {
assert(bit.size()==1);
if (a_ == bit.value(0))
return;
a_ = bit.value(0);
recv_set(port);
} else {
vvp_dff::recv_vec4(port, bit, ctx);
}
}
void compile_dff(char*label, struct symb_s arg_d,
struct symb_s arg_c,
struct symb_s arg_e,
struct symb_s arg_a)
struct symb_s arg_e)
{
vvp_net_t*ptr = new vvp_net_t;
vvp_dff*fun = new vvp_dff(false, false);
ptr->fun = fun;
define_functor_symbol(label, ptr);
free(label);
input_connect(ptr, 0, arg_d.text);
input_connect(ptr, 1, arg_c.text);
input_connect(ptr, 2, arg_e.text);
}
void compile_dff_aclr(char*label, struct symb_s arg_d,
struct symb_s arg_c,
struct symb_s arg_e,
struct symb_s arg_a)
{
vvp_net_t*ptr = new vvp_net_t;
vvp_dff*fun = new vvp_dff_aclr(false, false);
ptr->fun = fun;
define_functor_symbol(label, ptr);
free(label);
input_connect(ptr, 0, arg_d.text);
input_connect(ptr, 1, arg_c.text);
input_connect(ptr, 2, arg_e.text);
input_connect(ptr, 3, arg_a.text);
}
void compile_dff_aset(char*label, struct symb_s arg_d,
struct symb_s arg_c,
struct symb_s arg_e,
struct symb_s arg_a)
{
vvp_net_t*ptr = new vvp_net_t;
vvp_dff*fun = new vvp_dff_aset(false, false);
ptr->fun = fun;
define_functor_symbol(label, ptr);
free(label);

View File

@ -42,6 +42,10 @@ class vvp_dff : public vvp_net_fun_t {
void recv_vec4(vvp_net_ptr_t port, const vvp_vector4_t&bit,
vvp_context_t);
protected:
void recv_clear(vvp_net_ptr_t port);
void recv_set(vvp_net_ptr_t port);
private:
bool iclk_, ice_;
vvp_bit4_t clk_cur_;
@ -49,4 +53,26 @@ class vvp_dff : public vvp_net_fun_t {
vvp_vector4_t d_;
};
class vvp_dff_aclr : public vvp_dff {
public:
explicit vvp_dff_aclr(bool invert_clk =false, bool invert_ce =false);
void recv_vec4(vvp_net_ptr_t port, const vvp_vector4_t&bit,
vvp_context_t);
private:
vvp_bit4_t a_;
};
class vvp_dff_aset : public vvp_dff {
public:
explicit vvp_dff_aset(bool invert_clk =false, bool invert_ce =false);
void recv_vec4(vvp_net_ptr_t port, const vvp_vector4_t&bit,
vvp_context_t);
private:
vvp_bit4_t a_;
};
#endif

View File

@ -151,6 +151,8 @@ static char* strdupnew(char const *str)
".concat8" { return K_CONCAT8; }
".delay" { return K_DELAY; }
".dff" { return K_DFF; }
".dff/aclr" { return K_DFF_ACLR; }
".dff/aset" { return K_DFF_ASET; }
".enum2" { return K_ENUM2; }
".enum2/s" { return K_ENUM2_S; }
".enum4" { return K_ENUM4; }

View File

@ -83,7 +83,7 @@ static struct __vpiModPath*modpath_dst = 0;
%token K_CMP_EEQ K_CMP_EQ K_CMP_EQX K_CMP_EQZ
%token K_CMP_EQ_R K_CMP_NEE K_CMP_NE K_CMP_NE_R
%token K_CMP_GE K_CMP_GE_R K_CMP_GE_S K_CMP_GT K_CMP_GT_R K_CMP_GT_S
%token K_CONCAT K_CONCAT8 K_DEBUG K_DELAY K_DFF
%token K_CONCAT K_CONCAT8 K_DEBUG K_DELAY K_DFF K_DFF_ACLR K_DFF_ASET
%token K_ENUM2 K_ENUM2_S K_ENUM4 K_ENUM4_S K_EVENT K_EVENT_OR
%token K_EXPORT K_EXTEND_S K_FUNCTOR K_IMPORT K_ISLAND K_MODPATH
%token K_NET K_NET_S K_NET_R K_NET_2S K_NET_2U
@ -491,10 +491,16 @@ statement
modpath_src_list ';'
{ modpath_dst = 0; }
/* DFF nodes have an output and take exactly 4 inputs. */
/* DFF nodes have an output and take up to 4 inputs. */
| T_LABEL K_DFF symbol ',' symbol ',' symbol ',' symbol ';'
{ compile_dff($1, $3, $5, $7, $9); }
| T_LABEL K_DFF symbol ',' symbol ',' symbol ';'
{ compile_dff($1, $3, $5, $7); }
| T_LABEL K_DFF_ACLR symbol ',' symbol ',' symbol ',' symbol ';'
{ compile_dff_aclr($1, $3, $5, $7, $9); }
| T_LABEL K_DFF_ASET symbol ',' symbol ',' symbol ',' symbol ';'
{ compile_dff_aset($1, $3, $5, $7, $9); }
/* The various reduction operator nodes take a single input. */