From b242663cae4500b4c68edffb34b1dff5762b2b62 Mon Sep 17 00:00:00 2001 From: Martin Whitaker Date: Sat, 13 Jun 2015 15:56:12 +0100 Subject: [PATCH] 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. --- design_dump.cc | 10 ++- ivl.def | 1 + ivl_target.h | 3 +- netlist.cc | 11 ++- netlist.h | 18 +++-- syn-rules.y | 10 +-- synth2.cc | 64 ++++++++--------- t-dll-api.cc | 14 +++- t-dll.cc | 13 +++- t-dll.h | 3 +- tgt-stub/stub.c | 7 +- tgt-vlog95/logic_lpm.c | 64 ++++++++++++++--- tgt-vvp/vvp_scope.c | 48 ++++++++----- vvp/README.txt | 30 ++++---- vvp/compile.h | 9 +-- vvp/dff.cc | 152 +++++++++++++++++++++-------------------- vvp/dff.h | 72 ++++++++++++------- vvp/lexor.lex | 9 ++- vvp/parse.y | 32 ++++++--- 19 files changed, 356 insertions(+), 214 deletions(-) diff --git a/design_dump.cc b/design_dump.cc index 123917d16..b92bd2c4e 100644 --- a/design_dump.cc +++ b/design_dump.cc @@ -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); diff --git a/ivl.def b/ivl.def index a49849ba7..562211831 100644 --- a/ivl.def +++ b/ivl.def @@ -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 diff --git a/ivl_target.h b/ivl_target.h index 4cb84d786..959a6fb65 100644 --- a/ivl_target.h +++ b/ivl_target.h @@ -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); diff --git a/netlist.cc b/netlist.cc index 44f142246..ae4f95349 100644 --- a/netlist.cc +++ b/netlist.cc @@ -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_; diff --git a/netlist.h b/netlist.h index a9cea0ecc..8a3e3f98f 100644 --- a/netlist.h +++ b/netlist.h @@ -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&aset_value, + vector&ff_aset_value, NexusSet&nex_map, NetBus&nex_out, const std::vector&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&aset_value, + vector&ff_aset_value, NexusSet&nex_map, NetBus&nex_out, const std::vector&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&aset_value, + vector&ff_aset_value, NexusSet&nex_map, NetBus&nex_out, const std::vector&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&aset_value, + vector&ff_aset_value, NexusSet&nex_map, NetBus&nex_out, const std::vector&events); diff --git a/syn-rules.y b/syn-rules.y index 8a86fe6e5..ac90c186a 100644 --- a/syn-rules.y +++ b/syn-rules.y @@ -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); diff --git a/synth2.cc b/synth2.cc index 86d1a9d43..44f7e07f6 100644 --- a/synth2.cc +++ b/synth2.cc @@ -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& /*aset_value*/, + vector& /*ff_aset_value*/, NexusSet&nex_map, NetBus&nex_out, const vector&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&aset_value, + vector&ff_aset_value, NexusSet&nex_map, NetBus&nex_out, const vector&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&aset_value, + vector&ff_aset_value, NexusSet&nex_map, NetBus&nex_out, const vector&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 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(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&aset_value, + vector&ff_aset_value, NexusSet&nex_map, NetBus&nex_out, const vector&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()); 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]); diff --git a/t-dll-api.cc b/t-dll-api.cc index 5a77f26eb..a4e33852e 100644 --- a/t-dll-api.cc +++ b/t-dll-api.cc @@ -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); diff --git a/t-dll.cc b/t-dll.cc index 3c0b9fa2d..23be1f7ce 100644 --- a/t-dll.cc +++ b/t-dll.cc @@ -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; diff --git a/t-dll.h b/t-dll.h index 918a95bde..97ed7607f 100644 --- a/t-dll.h +++ b/t-dll.h @@ -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; diff --git a/tgt-stub/stub.c b/tgt-stub/stub.c index 451da0441..f71b70d0a 100644 --- a/tgt-stub/stub.c +++ b/tgt-stub/stub.c @@ -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: \n", - ivl_lpm_basename(net), width); + fprintf(out, " LPM_FF %s: \n", + ivl_lpm_basename(net), edge, width); nex = ivl_lpm_clk(net); fprintf(out, " clk: %p\n", nex); diff --git a/tgt-vlog95/logic_lpm.c b/tgt-vlog95/logic_lpm.c index 1cffbd1e0..5a7bfcdd6 100644 --- a/tgt-vlog95/logic_lpm.c +++ b/tgt-vlog95/logic_lpm.c @@ -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, diff --git a/tgt-vvp/vvp_scope.c b/tgt-vvp/vvp_scope.c index 44d5b1e53..6a333d926 100644 --- a/tgt-vvp/vvp_scope.c +++ b/tgt-vvp/vvp_scope.c @@ -1716,44 +1716,45 @@ static void draw_lpm_concat(ivl_lpm_t net) /* * Emit a DFF primitive. This uses the following syntax: * - * .dff , , , ; - * - * 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 , , [, [, ]]; */ 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"); diff --git a/vvp/README.txt b/vvp/README.txt index b1a98768d..e151543b8 100644 --- a/vvp/README.txt +++ b/vvp/README.txt @@ -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: -