Support negedge flip-flops in synthesis and in vvp.

Also extend the support for FF asynchronous set values to vvp and
fix the dff functor in vvp to correctly model asynchronous set/clr
behaviour.
This commit is contained in:
Martin Whitaker 2015-06-13 15:56:12 +01:00
parent d39c284055
commit b242663cae
19 changed files with 356 additions and 214 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1998-2014 Stephen Williams (steve@icarus.com)
* Copyright (c) 1998-2015 Stephen Williams (steve@icarus.com)
*
* This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU
@ -677,8 +677,12 @@ void NetConst::dump_node(ostream&o, unsigned ind) const
void NetFF::dump_node(ostream&o, unsigned ind) const
{
o << setw(ind) << "" << "LPM_FF: " << name()
<< " scope=" << scope_path(scope())
<< " aset_value=" << aset_value_ << endl;
<< " scope=" << scope_path(scope());
if (negedge_)
o << " negedge";
else
o << " posedge";
o << " aset_value=" << aset_value_ << endl;
dump_node_pins(o, ind+4);
dump_obj_attr(o, ind+4);

View File

@ -125,6 +125,7 @@ ivl_lpm_enable
ivl_lpm_file
ivl_lpm_lineno
ivl_lpm_name
ivl_lpm_negedge
ivl_lpm_q
ivl_lpm_scope
ivl_lpm_select

View File

@ -1,7 +1,7 @@
#ifndef IVL_ivl_target_H
#define IVL_ivl_target_H
/*
* Copyright (c) 2000-2014 Stephen Williams (steve@icarus.com)
* Copyright (c) 2000-2015 Stephen Williams (steve@icarus.com)
*
* This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU
@ -1418,6 +1418,7 @@ extern ivl_signal_t ivl_lpm_array(ivl_lpm_t net);
/* IVL_LPM_PART IVL_LPM_SUBSTITUTE */
extern unsigned ivl_lpm_base(ivl_lpm_t net);
/* IVL_LPM_FF */
extern unsigned ivl_lpm_negedge(ivl_lpm_t net);
extern ivl_nexus_t ivl_lpm_clk(ivl_lpm_t net);
/* IVL_LPM_UFUNC */
extern ivl_scope_t ivl_lpm_define(ivl_lpm_t net);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1998-2013 Stephen Williams (steve@icarus.com)
* Copyright (c) 1998-2015 Stephen Williams (steve@icarus.com)
*
* This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU
@ -1153,8 +1153,8 @@ unsigned NetReplicate::repeat() const
* ...
*/
NetFF::NetFF(NetScope*s, perm_string n, unsigned width__)
: NetNode(s, n, 8), width_(width__)
NetFF::NetFF(NetScope*s, perm_string n, bool negedge__, unsigned width__)
: NetNode(s, n, 8), negedge_(negedge__), width_(width__)
{
pin_Clock().set_dir(Link::INPUT);
pin_Enable().set_dir(Link::INPUT);
@ -1170,6 +1170,11 @@ NetFF::~NetFF()
{
}
bool NetFF::is_negedge() const
{
return negedge_;
}
unsigned NetFF::width() const
{
return width_;

View File

@ -1,7 +1,7 @@
#ifndef IVL_netlist_H
#define IVL_netlist_H
/*
* Copyright (c) 1998-2014 Stephen Williams (steve@icarus.com)
* Copyright (c) 1998-2015 Stephen Williams (steve@icarus.com)
* Copyright CERN 2013 / Stephen Williams (steve@icarus.com)
*
* This source code is free software; you can redistribute it
@ -1622,9 +1622,10 @@ class NetModulo : public NetNode {
class NetFF : public NetNode {
public:
NetFF(NetScope*s, perm_string n, unsigned vector_width);
NetFF(NetScope*s, perm_string n, bool negedge, unsigned vector_width);
~NetFF();
bool is_negedge() const;
unsigned width() const;
Link& pin_Clock();
@ -1656,6 +1657,7 @@ class NetFF : public NetNode {
virtual void functor_node(Design*des, functor_t*fun);
private:
bool negedge_;
unsigned width_;
verinum aset_value_;
verinum sset_value_;
@ -2618,9 +2620,10 @@ class NetProc : public virtual LineInfo {
// picked off by e.g. condit statements as set/reset inputs to
// the flipflop being generated.
virtual bool synth_sync(Design*des, NetScope*scope,
bool&ff_negedge,
NetNet*ff_clock, NetBus&ff_ce,
NetBus&ff_aclr, NetBus&ff_aset,
vector<verinum>&aset_value,
vector<verinum>&ff_aset_value,
NexusSet&nex_map, NetBus&nex_out,
const std::vector<NetEvProbe*>&events);
@ -2900,9 +2903,10 @@ class NetBlock : public NetProc {
NetBus&accumulated_nex_out);
bool synth_sync(Design*des, NetScope*scope,
bool&ff_negedge,
NetNet*ff_clk, NetBus&ff_ce,
NetBus&ff_aclr,NetBus&ff_aset,
vector<verinum>&aset_value,
vector<verinum>&ff_aset_value,
NexusSet&nex_map, NetBus&nex_out,
const std::vector<NetEvProbe*>&events);
@ -3042,9 +3046,10 @@ class NetCondit : public NetProc {
NetBus&accumulated_nex_out);
bool synth_sync(Design*des, NetScope*scope,
bool&ff_negedge,
NetNet*ff_clk, NetBus&ff_ce,
NetBus&ff_aclr,NetBus&ff_aset,
vector<verinum>&aset_value,
vector<verinum>&ff_aset_value,
NexusSet&nex_map, NetBus&nex_out,
const std::vector<NetEvProbe*>&events);
@ -3320,9 +3325,10 @@ class NetEvWait : public NetProc {
NetBus&accumulated_nex_out);
virtual bool synth_sync(Design*des, NetScope*scope,
bool&ff_negedge,
NetNet*ff_clk, NetBus&ff_ce,
NetBus&ff_aclr,NetBus&ff_aset,
vector<verinum>&aset_value,
vector<verinum>&ff_aset_value,
NexusSet&nex_map, NetBus&nex_out,
const std::vector<NetEvProbe*>&events);

View File

@ -1,7 +1,7 @@
%{
/*
* Copyright (c) 2000-2014 Stephen Williams (steve@icarus.com)
* Copyright (c) 2000-2015 Stephen Williams (steve@icarus.com)
*
* This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU
@ -121,11 +121,6 @@ static void hookup_DFF_CE(NetFF*ff, NetESignal*d, NetEvProbe*pclk,
connect(ff->pin_Clock(), pclk->pin(0));
if (ce) connect(ff->pin_Enable(), ce->pin(0));
ff->attribute(perm_string::literal("LPM_FFType"), verinum("DFF"));
if (pclk->edge() == NetEvProbe::NEGEDGE)
ff->attribute(perm_string::literal("Clock:LPM_Polarity"), verinum("INVERT"));
/* This lval_ represents a reg that is a WIRE in the
synthesized results. This function signals the destructor
to change the REG that this l-value refers to into a
@ -165,7 +160,8 @@ static void make_DFF_CE(Design*des, NetProcTop*top,
if (a->sig()) {
// cerr << "new NetFF named " << a->name() << endl;
NetFF*ff = new NetFF(top->scope(), a->name(),
bool negedge = pclk->edge() == NetEvProbe::NEGEDGE;
NetFF*ff = new NetFF(top->scope(), a->name(), negedge,
a->sig()->vector_width());
hookup_DFF_CE(ff, d, pclk, ce, a, rval_pinoffset);
des->add_node(ff);

View File

@ -34,9 +34,10 @@ bool NetProc::synth_async(Design*, NetScope*, NexusSet&, NetBus&, NetBus&)
}
bool NetProc::synth_sync(Design*des, NetScope*scope,
bool& /* ff_negedge */,
NetNet* /* ff_clk */, NetBus& /* ff_ce */,
NetBus& /* ff_aclr*/, NetBus& /* ff_aset*/,
vector<verinum>& /*aset_value*/,
vector<verinum>& /*ff_aset_value*/,
NexusSet&nex_map, NetBus&nex_out,
const vector<NetEvProbe*>&events)
{
@ -1383,9 +1384,10 @@ bool NetProcTop::synth_async(Design*des)
* the statements may each infer different reset and enable signals.
*/
bool NetBlock::synth_sync(Design*des, NetScope*scope,
bool&ff_negedge,
NetNet*ff_clk, NetBus&ff_ce,
NetBus&ff_aclr,NetBus&ff_aset,
vector<verinum>&aset_value,
vector<verinum>&ff_aset_value,
NexusSet&nex_map, NetBus&nex_out,
const vector<NetEvProbe*>&events_in)
{
@ -1428,8 +1430,9 @@ bool NetBlock::synth_sync(Design*des, NetScope*scope,
subset of the statement. The tmp_map is the output
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, tmp_ce,
ff_aclr, ff_aset, aset_value,
bool ok_flag = cur->synth_sync(des, scope,
ff_negedge, ff_clk, tmp_ce,
ff_aclr, ff_aset, ff_aset_value,
tmp_set, tmp_out, events_in);
flag = flag && ok_flag;
@ -1465,9 +1468,10 @@ bool NetBlock::synth_sync(Design*des, NetScope*scope,
* expression is connected to an event, or not.
*/
bool NetCondit::synth_sync(Design*des, NetScope*scope,
bool&ff_negedge,
NetNet*ff_clk, NetBus&ff_ce,
NetBus&ff_aclr,NetBus&ff_aset,
vector<verinum>&aset_value,
vector<verinum>&ff_aset_value,
NexusSet&nex_map, NetBus&nex_out,
const vector<NetEvProbe*>&events_in)
{
@ -1514,25 +1518,13 @@ bool NetCondit::synth_sync(Design*des, NetScope*scope,
for (unsigned pin = 0 ; pin < tmp_out.pin_count() ; pin += 1) {
Nexus*rst_nex = tmp_out.pin(pin).nexus();
vector<bool> rst_mask = rst_nex->driven_mask();
if (debug_synth2) {
cerr << get_fileline() << ": NetCondit::synth_sync: "
<< "nex_out pin=" << pin
<< ", rst_mask.size()==" << rst_mask.size()
<< ", rst_nex->vector_width()=" << rst_nex->vector_width()
<< endl;
}
for (size_t bit = 0 ; bit < rst_mask.size() ; bit += 1) {
if (rst_mask[bit]==false) {
cerr << get_fileline() << ": sorry: "
<< "Asynchronous LOAD not implemented." << endl;
return false;
}
if (! rst_nex->drivers_constant()) {
cerr << get_fileline() << ": sorry: "
<< "Asynchronous LOAD not implemented." << endl;
return false;
}
verinum rst_drv = rst_nex->driven_vector();
ivl_assert(*this, rst_drv.len()==rst_mask.size());
verinum zero (verinum::V0, rst_drv.len());
verinum ones (verinum::V1, rst_drv.len());
@ -1551,12 +1543,13 @@ bool NetCondit::synth_sync(Design*des, NetScope*scope,
ivl_assert(*this, rst->pin_count()==1);
connect(ff_aset.pin(pin), rst->pin(0));
if (rst_drv!=ones)
aset_value[pin] = rst_drv;
ff_aset_value[pin] = rst_drv;
}
}
return else_->synth_sync(des, scope, ff_clk, ff_ce,
ff_aclr, ff_aset, aset_value,
return else_->synth_sync(des, scope,
ff_negedge, ff_clk, ff_ce,
ff_aclr, ff_aset, ff_aset_value,
nex_map, nex_out, vector<NetEvProbe*>(0));
}
@ -1711,15 +1704,19 @@ bool NetCondit::synth_sync(Design*des, NetScope*scope,
}
}
bool flag = if_->synth_sync(des, scope, ff_clk, ff_ce, ff_aclr, ff_aset, aset_value, nex_map, nex_out, events_in);
bool flag = if_->synth_sync(des, scope,
ff_negedge, ff_clk, ff_ce,
ff_aclr, ff_aset, ff_aset_value,
nex_map, nex_out, events_in);
return flag;
}
bool NetEvWait::synth_sync(Design*des, NetScope*scope,
bool&ff_negedge,
NetNet*ff_clk, NetBus&ff_ce,
NetBus&ff_aclr,NetBus&ff_aset,
vector<verinum>&aset_value,
vector<verinum>&ff_aset_value,
NexusSet&nex_map, NetBus&nex_out,
const vector<NetEvProbe*>&events_in)
{
@ -1789,8 +1786,7 @@ bool NetEvWait::synth_sync(Design*des, NetScope*scope,
connect(ff_clk->pin(0), pclk->pin(0));
if (pclk->edge() == NetEvProbe::NEGEDGE) {
perm_string polarity = perm_string::literal("Clock:LPM_Polarity");
ff_clk->attribute(polarity, verinum("INVERT"));
ff_negedge = true;
if (debug_synth2) {
cerr << get_fileline() << ": debug: "
@ -1800,8 +1796,9 @@ 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, aset_value,
bool flag = statement_->synth_sync(des, scope,
ff_negedge, ff_clk, ff_ce,
ff_aclr, ff_aset, ff_aset_value,
nex_map, nex_out, events);
return flag;
@ -1857,7 +1854,10 @@ 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, aclr, aset, aset_value,
bool negedge = false;
bool flag = statement_->synth_sync(des, scope(),
negedge, clock, ce,
aclr, aset, aset_value,
nex_set, nex_d,
vector<NetEvProbe*>());
if (! flag) {
@ -1876,7 +1876,7 @@ bool NetProcTop::synth_sync(Design*des)
}
NetFF*ff2 = new NetFF(scope(), scope()->local_symbol(),
nex_set[idx].wid);
negedge, nex_set[idx].wid);
des->add_node(ff2);
ff2->set_line(*this);
ff2->aset_value(aset_value[idx]);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2000-2014 Stephen Williams (steve@icarus.com)
* Copyright (c) 2000-2015 Stephen Williams (steve@icarus.com)
* Copyright CERN 2013 / Stephen Williams (steve@icarus.com)
*
* This source code is free software; you can redistribute it
@ -1090,6 +1090,18 @@ extern "C" unsigned ivl_lpm_base(ivl_lpm_t net)
}
}
extern "C" unsigned ivl_lpm_negedge(ivl_lpm_t net)
{
assert(net);
switch (net->type) {
case IVL_LPM_FF:
return net->u_.ff.negedge_flag;
default:
assert(0);
return 0;
}
}
extern "C" ivl_nexus_t ivl_lpm_clk(ivl_lpm_t net)
{
assert(net);

View File

@ -1938,6 +1938,9 @@ void dll_target::lpm_ff(const NetFF*net)
const Nexus*nex;
/* Set the clock polarity. */
obj->u_.ff.negedge_flag = net->is_negedge();
/* Set the clk signal to point to the nexus, and the nexus to
point back to this device. */
nex = net->pin_Clock().nexus();
@ -1976,7 +1979,10 @@ void dll_target::lpm_ff(const NetFF*net)
nexus_lpm_add(obj->u_.ff.aset, obj, 0, IVL_DR_HiZ, IVL_DR_HiZ);
verinum tmp = net->aset_value();
obj->u_.ff.aset_value = expr_from_value_(tmp);
if (tmp.len() > 0)
obj->u_.ff.aset_value = expr_from_value_(tmp);
else
obj->u_.ff.aset_value = 0;
} else {
obj->u_.ff.aset = 0;
@ -2001,7 +2007,10 @@ void dll_target::lpm_ff(const NetFF*net)
nexus_lpm_add(obj->u_.ff.sset, obj, 0, IVL_DR_HiZ, IVL_DR_HiZ);
verinum tmp = net->sset_value();
obj->u_.ff.sset_value = expr_from_value_(tmp);
if (tmp.len() > 0)
obj->u_.ff.sset_value = expr_from_value_(tmp);
else
obj->u_.ff.sset_value = 0;
} else {
obj->u_.ff.sset = 0;

View File

@ -1,7 +1,7 @@
#ifndef IVL_t_dll_H
#define IVL_t_dll_H
/*
* Copyright (c) 2000-2014 Stephen Williams (steve@icarus.com)
* Copyright (c) 2000-2015 Stephen Williams (steve@icarus.com)
*
* This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU
@ -370,6 +370,7 @@ struct ivl_lpm_s {
union {
struct ivl_lpm_ff_s {
unsigned negedge_flag :1;
ivl_nexus_t clk;
ivl_nexus_t we;
ivl_nexus_t aclr;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2000-2014 Stephen Williams (steve@icarus.com)
* Copyright (c) 2000-2015 Stephen Williams (steve@icarus.com)
*
* This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU
@ -497,10 +497,11 @@ static void show_lpm_concat(ivl_lpm_t net)
static void show_lpm_ff(ivl_lpm_t net)
{
ivl_nexus_t nex;
char*edge = ivl_lpm_negedge(net) ? "negedge" : "posedge";
unsigned width = ivl_lpm_width(net);
fprintf(out, " LPM_FF %s: <width=%u>\n",
ivl_lpm_basename(net), width);
fprintf(out, " LPM_FF %s: <polarity=%s> <width=%u>\n",
ivl_lpm_basename(net), edge, width);
nex = ivl_lpm_clk(net);
fprintf(out, " clk: %p\n", nex);

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2011-2014 Cary R. (cygcary@yahoo.com)
* Copyright (C) 2011-2015 Cary R. (cygcary@yahoo.com)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -1245,7 +1245,43 @@ static void emit_posedge_dff_prim(void)
fprintf(vlog_out, "endprimitive\n");
}
static void emit_negedge_dff_prim(void)
{
fprintf(vlog_out, "\n");
fprintf(vlog_out, "/* Icarus generated UDP to represent a synthesized "
"negative edge D-FF. */\n");
fprintf(vlog_out, "primitive IVL_negedge_DFF "
"(q, clk, en, d, clr, set);\n");
fprintf(vlog_out, "%*coutput q;\n", indent_incr, ' ');
fprintf(vlog_out, "%*cinput clk, en, d, clr, set;\n", indent_incr, ' ');
fprintf(vlog_out, "%*creg q;\n", indent_incr, ' ');
fprintf(vlog_out, "%*ctable\n", indent_incr, ' ');
fprintf(vlog_out, "%*cf 1 0 0 0 : ? : 0 ;\n", 2*indent_incr, ' ');
fprintf(vlog_out, "%*cf 1 1 0 0 : ? : 1 ;\n", 2*indent_incr, ' ');
fprintf(vlog_out, "%*cn 1 0 0 0 : 0 : - ;\n", 2*indent_incr, ' ');
fprintf(vlog_out, "%*cn 1 1 0 0 : 1 : - ;\n", 2*indent_incr, ' ');
fprintf(vlog_out, "%*cn x 0 0 0 : 0 : - ;\n", 2*indent_incr, ' ');
fprintf(vlog_out, "%*cn x 1 0 0 : 1 : - ;\n", 2*indent_incr, ' ');
fprintf(vlog_out, "%*cp ? ? 0 0 : ? : - ;\n", 2*indent_incr, ' ');
fprintf(vlog_out, "%*c* 0 ? 0 0 : ? : - ;\n", 2*indent_incr, ' ');
fprintf(vlog_out, "%*c? * ? ? ? : ? : - ;\n", 2*indent_incr, ' ');
fprintf(vlog_out, "%*c? ? * ? ? : ? : - ;\n", 2*indent_incr, ' ');
fprintf(vlog_out, "%*c? ? ? * ? : ? : - ;\n", 2*indent_incr, ' ');
fprintf(vlog_out, "%*c? ? ? ? * : ? : - ;\n", 2*indent_incr, ' ');
fprintf(vlog_out, "%*c? ? ? 0 1 : ? : 1 ;\n", 2*indent_incr, ' ');
fprintf(vlog_out, "%*c? ? ? 0 x : 1 : - ;\n", 2*indent_incr, ' ');
fprintf(vlog_out, "%*c? ? ? 0 x : 0 : x ;\n", 2*indent_incr, ' ');
fprintf(vlog_out, "%*c? ? ? 1 ? : ? : 0 ;\n", 2*indent_incr, ' ');
fprintf(vlog_out, "%*c? ? ? x 0 : 0 : - ;\n", 2*indent_incr, ' ');
fprintf(vlog_out, "%*c? ? ? x 0 : 1 : x ;\n", 2*indent_incr, ' ');
fprintf(vlog_out, "%*c? ? ? x x : ? : x ;\n", 2*indent_incr, ' ');
fprintf(vlog_out, "%*c? ? ? x 1 : ? : x ;\n", 2*indent_incr, ' ');
fprintf(vlog_out, "%*cendtable\n", indent_incr, ' ');
fprintf(vlog_out, "endprimitive\n");
}
static unsigned need_posedge_dff_prim = 0;
static unsigned need_negedge_dff_prim = 0;
/*
* Synthesis creates a D-FF LPM object. To allow this to be simulated as
@ -1263,14 +1299,14 @@ void emit_icarus_generated_udps()
{
/* Emit the copyright information and LGPL note and then emit any
* needed primitives. */
if (need_posedge_dff_prim) {
if (need_posedge_dff_prim || need_negedge_dff_prim) {
fprintf(vlog_out,
"\n"
"/*\n"
" * This is the copyright information for the following primitive(s)\n"
" * (library elements).\n"
" *\n"
" * Copyright (C) 2011 Cary R. (cygcary@yahoo.com)\n"
" * Copyright (C) 2011-2015 Cary R. (cygcary@yahoo.com)\n"
" *\n"
" * This library is free software; you can redistribute it and/or\n"
" * modify it under the terms of the GNU Lesser General Public\n"
@ -1290,13 +1326,12 @@ void emit_icarus_generated_udps()
"NOTE: vlog95: Adding LGPL 2.1 primitive(s) at the end of the output file.\n");
}
if (need_posedge_dff_prim) emit_posedge_dff_prim();
if (need_negedge_dff_prim) emit_negedge_dff_prim();
}
static void emit_lpm_ff(ivl_scope_t scope, ivl_lpm_t lpm)
{
// HERE: No support for lpm attributes and hence polarity information.
// ivl_attribute_t clock_pol = find_lpm_attr(lpm, "Clock:LPM_Polarity");
ivl_attribute_t clock_pol = 0;
unsigned negedge = ivl_lpm_negedge(lpm);
ivl_expr_t aset_expr = ivl_lpm_aset_value(lpm);
ivl_expr_t sset_expr = ivl_lpm_sset_value(lpm);
ivl_nexus_t nex;
@ -1305,7 +1340,13 @@ static void emit_lpm_ff(ivl_scope_t scope, ivl_lpm_t lpm)
const char *sset_bits = 0;
/* For now we only support a width of 1 for these bits. */
if (aset_expr) {
assert(ivl_expr_width(aset_expr) == 1);
if (ivl_expr_width(aset_expr) != 1) {
fprintf(stderr, "%s:%u: vlog95 sorry: FF LPMs with "
"multi-bit asynchronous set values are not "
"currently translated.\n",
ivl_lpm_file(lpm), ivl_lpm_lineno(lpm));
vlog_errors += 1;
}
aset_bits = ivl_expr_bits(aset_expr);
}
if (sset_expr) {
@ -1314,9 +1355,7 @@ static void emit_lpm_ff(ivl_scope_t scope, ivl_lpm_t lpm)
}
fprintf(vlog_out, "%*c", indent, ' ');
/* If there is a clock polarity attribute then we have a negative
* edge D-FF. */
if (clock_pol) {
if (negedge) {
fprintf(vlog_out, "IVL_negedge_DFF");
} else {
fprintf(vlog_out, "IVL_posedge_DFF");
@ -1404,7 +1443,10 @@ static void emit_lpm_ff(ivl_scope_t scope, ivl_lpm_t lpm)
else fprintf(vlog_out, "1'b0");
fprintf(vlog_out, ");\n");
/* We need to emit a primitive for this instance. */
need_posedge_dff_prim = 1;
if (negedge)
need_negedge_dff_prim = 1;
else
need_posedge_dff_prim = 1;
}
static ivl_signal_t get_output_from_nexus(ivl_scope_t scope, ivl_nexus_t nex,

View File

@ -1716,44 +1716,45 @@ static void draw_lpm_concat(ivl_lpm_t net)
/*
* Emit a DFF primitive. This uses the following syntax:
*
* .dff <data>, <clock>, <enable>, <async>;
*
* The async pin currently sets the stored data value and propagates it
* to the output (not very useful). This routine always sets the async
* value to high-Z which is ignored in the VVP code. This is all OK
* since synthesis is not currently functional.
* .dff<variant> <width> <data>, <clock>, <enable>[, <async>[, <async-value>]];
*/
static void draw_lpm_ff(ivl_lpm_t net)
{
ivl_nexus_t nex;
/* Sync/Async set/clear control is currently only supported in V0.8
* which has working synthesis. If/when this is added see that code
* for clues about how this should be implemented. The dff primitive
* used here (from vvp) needs to be improved to support both an
* async set and clear. See the UDP generated by the tgt-vlog95 code
* generator in V0.10 and later for how this might be done. */
/* Sync set/clear control is not currently supported. This is not
* a problem, as synthesis can incorporate this in the D input
* expression. All modern synthesis tools do this as a matter of
* course, as most cell libraries don't contain flip-flops with
* sync set/clear.
*/
assert(ivl_lpm_sync_clr(net) == 0);
assert(ivl_lpm_sync_set(net) == 0);
unsigned width = ivl_lpm_width(net);
char*edge = ivl_lpm_negedge(net) ? "n" : "p";
if (ivl_lpm_async_clr(net)) {
/* Synthesis doesn't currently support both set and clear.
If it ever does, it might be better to implement the
flip-flop as a UDP. See tgt-vlog95 for an example of
how to do this. */
if (ivl_lpm_async_set(net)) {
fprintf(stderr, "%s:%u:vvp.tgt: sorry: No support for a D-ff "
fprintf(stderr, "%s:%u:vvp.tgt: sorry: No support for a DFF "
"with both an async. set and clear.\n",
ivl_lpm_file(net), ivl_lpm_lineno(net));
vvp_errors += 1;
}
fprintf(vvp_out, "L_%p .dff/aclr ", net);
fprintf(vvp_out, "L_%p .dff/%s/aclr %u ", net, edge, width);
} else if (ivl_lpm_async_set(net)) {
fprintf(vvp_out, "L_%p .dff/aset ", net);
fprintf(vvp_out, "L_%p .dff/%s/aset %u ", net, edge, width);
} else {
fprintf(vvp_out, "L_%p .dff ", net);
fprintf(vvp_out, "L_%p .dff/%s %u ", net, edge, width);
}
nex = ivl_lpm_data(net,0);
assert(nex);
fprintf(vvp_out, "%s", draw_net_input(nex));
assert(width_of_nexus(nex) == ivl_lpm_width(net));;
assert(width_of_nexus(nex) == width);;
nex = ivl_lpm_clk(net);
assert(nex);
@ -1769,11 +1770,24 @@ static void draw_lpm_ff(ivl_lpm_t net)
}
if ( (nex = ivl_lpm_async_clr(net)) ) {
assert(width_of_nexus(nex) == 1);;
fprintf(vvp_out, ", %s", draw_net_input(nex));
}
if ( (nex = ivl_lpm_async_set(net)) ) {
ivl_expr_t val = ivl_lpm_aset_value(net);
assert(width_of_nexus(nex) == 1);;
fprintf(vvp_out, ", %s", draw_net_input(nex));
if (val) {
unsigned nbits = ivl_expr_width(val);
const char*bits = ivl_expr_bits(val);
unsigned bb;
assert(nbits == width);
fprintf(vvp_out, ", C4<");
for (bb = 0 ; bb < nbits; bb += 1)
fprintf(vvp_out, "%c", bits[nbits-bb-1]);
fprintf(vvp_out, ">");
}
}
fprintf(vvp_out, ";\n");

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2001-2010 Stephen Williams (steve@icarus.com)
* Copyright (c) 2001-2015 Stephen Williams (steve@icarus.com)
*
*/
@ -192,18 +192,24 @@ 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>;
<label> .dff/aclr <d>, <clk>, <ce>, <async-input>;
<label> .dff/aset <d>, <clk>, <ce>, <async-input>;
<label> .dff/p <width> <d>, <clk>, <ce>;
<label> .dff/n <width> <d>, <clk>, <ce>;
<label> .dff/p/aclr <width> <d>, <clk>, <ce>, <async-input>;
<label> .dff/n/aclr <width> <d>, <clk>, <ce>, <async-input>;
<label> .dff/p/aset <width> <d>, <clk>, <ce>, <async-input>[, <set-value>];
<label> .dff/n/aset <width> <d>, <clk>, <ce>, <async-input>[, <set-value>];
The /p variants simulate positive-edge triggered flip-flops and the
/n variants simulate negative-edge triggered flip-flops. The generated
functor is generally synchronous on the specified 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 on the
rising edge causes the device to clear/set, forces the output to
propagate, and disables the clock until the aynchronous input is
deasserted. Thus, they implement DFF with asynchronous clr or set.
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 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

@ -208,21 +208,22 @@ extern void compile_cmp_ne_r(char*label, unsigned argc, struct symb_s*argv);
extern void compile_cmp_ge_r(char*label, unsigned argc, struct symb_s*argv);
extern void compile_cmp_gt_r(char*label, unsigned argc, struct symb_s*argv);
extern void compile_dff(char*label,
extern void compile_dff(char*label, unsigned width, bool negedge,
struct symb_s arg_d,
struct symb_s arg_c,
struct symb_s arg_e);
extern void compile_dff_aclr(char*label,
extern void compile_dff_aclr(char*label, unsigned width, bool negedge,
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,
extern void compile_dff_aset(char*label, unsigned width, bool negedge,
struct symb_s arg_d,
struct symb_s arg_c,
struct symb_s arg_e,
struct symb_s arg_a);
struct symb_s arg_a,
char*asc_value);
extern void compile_enum2_type(char*label, long width, bool signed_flag,
std::list<struct enum_name_s>*names);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2005-2010 Stephen Williams (steve@icarus.com)
* Copyright (c) 2005-2015 Stephen Williams (steve@icarus.com)
*
* This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU
@ -26,17 +26,43 @@
# include <cstdlib>
# include <iostream>
vvp_dff::vvp_dff(bool invert_clk, bool invert_ce)
: iclk_(invert_clk), ice_(invert_ce)
/* We need to ensure an initial output value is propagated. This is
achieved by setting asc_ to BIT4_Z to flag that we haven't yet
propagated an output value. This will also disable clocked output.
For flip-flops without an asynchronous set/clear, we schedule an
initial value of BIT4_0 to be sent to port 3. For flip-flops with
an asynchronous set/clear, we rely on the network propagating an
initial value to port 3. The first value received on port 3 will
either propagate the set/clear value (if the received value is
BIT4_1) or will propagate an initial value of 'bx. From then on
the flip-flop operates normally. */
vvp_dff::vvp_dff(unsigned width, bool negedge)
: clk_(BIT4_X), ena_(BIT4_X), asc_(BIT4_Z), d_(width, BIT4_X)
{
clk_cur_ = BIT4_X;
enable_ = BIT4_X;
clk_active_ = negedge ? BIT4_0 : BIT4_1;
}
vvp_dff::~vvp_dff()
{
}
vvp_dff_aclr::vvp_dff_aclr(unsigned width, bool negedge)
: vvp_dff(width, negedge)
{
}
vvp_dff_aset::vvp_dff_aset(unsigned width, bool negedge)
: vvp_dff(width, negedge)
{
}
vvp_dff_asc::vvp_dff_asc(unsigned width, bool negedge, char*asc_value)
: vvp_dff(width, negedge)
{
asc_value_ = c4string_to_vector4(asc_value);
}
void vvp_dff::recv_vec4(vvp_net_ptr_t port, const vvp_vector4_t&bit,
vvp_context_t)
{
@ -48,105 +74,70 @@ void vvp_dff::recv_vec4(vvp_net_ptr_t port, const vvp_vector4_t&bit,
d_ = bit;
break;
/* This is a clock input */
case 1: // CLK
assert(bit.size() == 1);
if (enable_ != BIT4_1)
if (asc_ != BIT4_0)
break;
tmp = clk_cur_;
clk_cur_ = bit.value(0);
if (clk_cur_ == BIT4_1 && tmp != BIT4_1)
if (ena_ != BIT4_1)
break;
tmp = clk_;
clk_ = bit.value(0);
if (clk_ == clk_active_ && tmp != clk_active_)
port.ptr()->send_vec4(d_, 0);
break;
case 2: // CE
assert(bit.size() == 1);
enable_ = bit.value(0);
ena_ = bit.value(0);
break;
case 3: // Asynch-D
assert(0);
case 3: // asynch SET/CLR
assert(bit.size() == 1);
tmp = asc_;
asc_ = bit.value(0);
if (asc_ == BIT4_1 && tmp != BIT4_1)
recv_async(port);
else if (tmp == BIT4_Z)
port.ptr()->send_vec4(vvp_vector4_t(d_.size(), BIT4_X), 0);
break;
}
}
/*
* The recv_clear and recv_set function respond to asynchronous
* clear/set input by propagating the desired output.
* The recv_async functions respond to the asynchronous
* set/clear 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)
void vvp_dff::recv_async(vvp_net_ptr_t)
{
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);
// The base dff does not have an asynchronous set/clr input.
assert(0);
}
void vvp_dff::recv_set(vvp_net_ptr_t port)
void vvp_dff_aclr::recv_async(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);
port.ptr()->send_vec4(vvp_vector4_t(d_.size(), BIT4_0), 0);
}
vvp_dff_aclr::vvp_dff_aclr(bool invert_clk, bool invert_ce)
: vvp_dff(invert_clk, invert_ce)
void vvp_dff_aset::recv_async(vvp_net_ptr_t port)
{
a_ = BIT4_X;
port.ptr()->send_vec4(vvp_vector4_t(d_.size(), BIT4_1), 0);
}
void vvp_dff_aclr::recv_vec4(vvp_net_ptr_t port, const vvp_vector4_t&bit,
vvp_context_t ctx)
void vvp_dff_asc::recv_async(vvp_net_ptr_t port)
{
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);
}
port.ptr()->send_vec4(asc_value_, 0);
}
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,
void compile_dff(char*label, unsigned width, bool negedge,
struct symb_s arg_d,
struct symb_s arg_c,
struct symb_s arg_e)
{
vvp_net_t*ptr = new vvp_net_t;
vvp_dff*fun = new vvp_dff(false, false);
vvp_dff*fun = new vvp_dff(width, negedge);
ptr->fun = fun;
define_functor_symbol(label, ptr);
@ -154,15 +145,19 @@ void compile_dff(char*label, struct symb_s arg_d,
input_connect(ptr, 0, arg_d.text);
input_connect(ptr, 1, arg_c.text);
input_connect(ptr, 2, arg_e.text);
vvp_vector4_t init_val = vvp_vector4_t(1, BIT4_0);
schedule_init_vector(vvp_net_ptr_t(ptr,3), init_val);
}
void compile_dff_aclr(char*label, struct symb_s arg_d,
void compile_dff_aclr(char*label, unsigned width, bool negedge,
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);
vvp_dff*fun = new vvp_dff_aclr(width, negedge);
ptr->fun = fun;
define_functor_symbol(label, ptr);
@ -173,13 +168,22 @@ void compile_dff_aclr(char*label, struct symb_s arg_d,
input_connect(ptr, 3, arg_a.text);
}
void compile_dff_aset(char*label, struct symb_s arg_d,
void compile_dff_aset(char*label, unsigned width, bool negedge,
struct symb_s arg_d,
struct symb_s arg_c,
struct symb_s arg_e,
struct symb_s arg_a)
struct symb_s arg_a,
char*asc_value)
{
vvp_net_t*ptr = new vvp_net_t;
vvp_dff*fun = new vvp_dff_aset(false, false);
vvp_dff*fun;
if (asc_value) {
assert(c4string_test(asc_value));
fun = new vvp_dff_asc(width, negedge, asc_value);
free(asc_value);
} else {
fun = new vvp_dff_aset(width, negedge);
}
ptr->fun = fun;
define_functor_symbol(label, ptr);

View File

@ -1,7 +1,7 @@
#ifndef IVL_dff_H
#define IVL_dff_H
/*
* Copyright (c) 2005-2014 Stephen Williams (steve@icarus.com)
* Copyright (c) 2005-2015 Stephen Williams (steve@icarus.com)
*
* This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU
@ -22,57 +22,77 @@
# include "vvp_net.h"
/*
* The vvp_dff implements a D-type FF that is agnostic to the data
* type that is holds. The clock and clock-enable inputs are single
* bits and may be invertible. An output is propagated on the logical
* rising edge of the clock input, or whenever an asynchronous input
* is received. Ports are:
* The vvp_dff implements an arbitrary width D-type FF. The clock,
* clock-enable, and asynchronous set/clear inputs are single bits.
* Both positive and negative edge triggered flip-flops are supported.
* An output is propagated on the chosen edge of the clock input, or
* on the rising edge of the asynchronous set/clear input. Ports are:
*
* port-0: D input
* port-1: Clock input
* port-2: Clock Enable input
* port-3: Asynchronous D input.
* port-3: Asynchronous Set/Clear input.
*
* The base vvp_dff does not implement an asychronous set/clear.
*/
class vvp_dff : public vvp_net_fun_t {
class vvp_dff : public vvp_net_fun_t {
public:
explicit vvp_dff(bool invert_clk =false, bool invert_ce =false);
explicit vvp_dff(unsigned width, bool negedge);
~vvp_dff();
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_;
vvp_bit4_t enable_;
virtual void recv_async(vvp_net_ptr_t port);
vvp_bit4_t clk_active_ : 8;
vvp_bit4_t clk_ : 8;
vvp_bit4_t ena_ : 8;
vvp_bit4_t asc_ : 8;
protected:
vvp_vector4_t d_;
};
class vvp_dff_aclr : public vvp_dff {
/*
* This variant implements an asynchronous clear to all zeros.
*/
class vvp_dff_aclr : public vvp_dff {
public:
explicit vvp_dff_aclr(bool invert_clk =false, bool invert_ce =false);
explicit vvp_dff_aclr(unsigned width, bool negedge);
void recv_vec4(vvp_net_ptr_t port, const vvp_vector4_t&bit,
vvp_context_t);
private:
vvp_bit4_t a_;
void recv_async(vvp_net_ptr_t port);
};
class vvp_dff_aset : public vvp_dff {
/*
* This variant implements an asynchronous set to all ones.
*/
class vvp_dff_aset : public vvp_dff {
public:
explicit vvp_dff_aset(bool invert_clk =false, bool invert_ce =false);
explicit vvp_dff_aset(unsigned width, bool negedge);
void recv_vec4(vvp_net_ptr_t port, const vvp_vector4_t&bit,
vvp_context_t);
private:
vvp_bit4_t a_;
void recv_async(vvp_net_ptr_t port);
};
/*
* This variant implements an asynchronous set to a specified constant
* vector value.
*/
class vvp_dff_asc : public vvp_dff {
public:
explicit vvp_dff_asc(unsigned width, bool negedge, char*asc_value);
private:
void recv_async(vvp_net_ptr_t port);
vvp_vector4_t asc_value_;
};
#endif /* IVL_dff_H */

View File

@ -151,9 +151,12 @@ static char* strdupnew(char const *str)
".concat" { return K_CONCAT; }
".concat8" { return K_CONCAT8; }
".delay" { return K_DELAY; }
".dff" { return K_DFF; }
".dff/aclr" { return K_DFF_ACLR; }
".dff/aset" { return K_DFF_ASET; }
".dff/n" { return K_DFF_N; }
".dff/n/aclr" { return K_DFF_N_ACLR; }
".dff/n/aset" { return K_DFF_N_ASET; }
".dff/p" { return K_DFF_P; }
".dff/p/aclr" { return K_DFF_P_ACLR; }
".dff/p/aset" { return K_DFF_P_ASET; }
".enum2" { return K_ENUM2; }
".enum2/s" { return K_ENUM2_S; }
".enum4" { return K_ENUM4; }

View File

@ -1,7 +1,7 @@
%{
/*
* Copyright (c) 2001-2013 Stephen Williams (steve@icarus.com)
* Copyright (c) 2001-2015 Stephen Williams (steve@icarus.com)
*
* This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU
@ -83,7 +83,8 @@ 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 K_DFF_ACLR K_DFF_ASET
%token K_CONCAT K_CONCAT8 K_DEBUG K_DELAY K_DFF_N K_DFF_N_ACLR
%token K_DFF_N_ASET K_DFF_P K_DFF_P_ACLR K_DFF_P_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
@ -503,14 +504,29 @@ statement
/* DFF nodes have an output and take up to 4 inputs. */
| T_LABEL K_DFF symbol ',' symbol ',' symbol ';'
{ compile_dff($1, $3, $5, $7); }
| T_LABEL K_DFF_N T_NUMBER symbol ',' symbol ',' symbol ';'
{ compile_dff($1, $3, true, $4, $6, $8); }
| T_LABEL K_DFF_ACLR symbol ',' symbol ',' symbol ',' symbol ';'
{ compile_dff_aclr($1, $3, $5, $7, $9); }
| T_LABEL K_DFF_P T_NUMBER symbol ',' symbol ',' symbol ';'
{ compile_dff($1, $3, false, $4, $6, $8); }
| T_LABEL K_DFF_ASET symbol ',' symbol ',' symbol ',' symbol ';'
{ compile_dff_aset($1, $3, $5, $7, $9); }
| T_LABEL K_DFF_N_ACLR T_NUMBER symbol ',' symbol ',' symbol ',' symbol ';'
{ compile_dff_aclr($1, $3, true, $4, $6, $8, $10); }
| T_LABEL K_DFF_P_ACLR T_NUMBER symbol ',' symbol ',' symbol ',' symbol ';'
{ compile_dff_aclr($1, $3, false, $4, $6, $8, $10); }
| T_LABEL K_DFF_N_ASET T_NUMBER symbol ',' symbol ',' symbol ',' symbol ';'
{ compile_dff_aset($1, $3, true, $4, $6, $8, $10, 0); }
| T_LABEL K_DFF_P_ASET T_NUMBER symbol ',' symbol ',' symbol ',' symbol ';'
{ compile_dff_aset($1, $3, false, $4, $6, $8, $10, 0); }
| T_LABEL K_DFF_N_ASET T_NUMBER symbol ',' symbol ',' symbol ',' symbol ',' T_SYMBOL ';'
{ compile_dff_aset($1, $3, true, $4, $6, $8, $10, $12); }
| T_LABEL K_DFF_P_ASET T_NUMBER symbol ',' symbol ',' symbol ',' symbol ',' T_SYMBOL ';'
{ compile_dff_aset($1, $3, false, $4, $6, $8, $10, $12); }
/* The various reduction operator nodes take a single input. */