Capture the carry out of carry-chain addition.

This commit is contained in:
steve 1999-12-16 18:54:32 +00:00
parent ced7cc6d60
commit d355270c2d
2 changed files with 174 additions and 47 deletions

102
examples/xnf_add.vl Normal file
View File

@ -0,0 +1,102 @@
/*
* Copyright (c) 1999 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
* 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
*/
/*
* This example demonstrates Icarus Verilog's ability to synthesize
* efficient adders for Xilinx 4000 series FPGAs. The synthesis and
* code generation makes an adder and translates that into XOR gates
* and fast carry chain hardware.
*
* To compile this for XNF, try a command like this:
*
* verilog -X -fpart=XC4010XLPQ160 -fncf=xnf_add.ncf xnf_add.v
*
* That command causes an xnf_add.xnf and xnf_add.ncf file to be created.
* Next, make the xnf_add.ngd file with the command:
*
* xnf2ngd -l xilinxun -u xnf_add.xnf xnf_add.ngo
* ngdbuild xnf_add.ngo xnf_add.ngd
*
* Finally, map the file to fully render it in the target part. The
* par command is the step that actually optimizes the design and tries
* to meet timing constraints.
*
* map -o map.ncd xnf_add.ngd
* par -w map.ncd xnf_add.ncd
*
* At this point, you can use the FPGA Editor to edit the xnf_add.ncd
* file to see the carry chains made up to support the adder.
*/
module main;
wire [3:0] a, b;
wire [3:0] out;
wire carry;
wire a0, a1, a2, a3, b0, b1, b2, b3;
wire out0, out1, out2, out3;
// This creates the actual adder. Note that we also create a link
// to the carry output. The principle adder is 4 bits wide, so two
// IOBs are used to to the actual addition. PAR will place them in
// order along carry lines. An extra carry cell from below a[0] is
// used in FORCE-0 mode to load the bottom carry node.
//
// The carry signal from the top CY device is used to drive the
// main.carry wire shown here. It is managed in this case by using
// an ADD-FG-CI CY device for the top pair and using the CLB above
// the carry chain, with its CY in EXAMINE-CI mode, to put the carry
// out through its G function unit.
assign {carry, out} = a + b;
// These attribute commands assign pins to the listed wires.
// This can be done to wires and registers, as internally both
// are treated as named signals. It doesn't work (yet) on vectors,
// though, so break out the vectors with scaler assignments.
assign a[0] = a0;
assign a[1] = a1;
assign a[2] = a2;
assign a[3] = a3;
$attribute(a0, "PAD", "i150");
$attribute(a1, "PAD", "i152");
$attribute(a2, "PAD", "i153");
$attribute(a3, "PAD", "i154");
assign b[0] = b0;
assign b[1] = b1;
assign b[2] = b2;
assign b[3] = b3;
$attribute(b0, "PAD", "i155");
$attribute(b1, "PAD", "i156");
$attribute(b2, "PAD", "i157");
$attribute(b3, "PAD", "i158");
assign out0 = out[0];
assign out1 = out[1];
assign out2 = out[2];
assign out3 = out[3];
$attribute(out0, "PAD", "o71");
$attribute(out1, "PAD", "o72");
$attribute(out2, "PAD", "o73");
$attribute(out3, "PAD", "o74");
$attribute(carry, "PAD", "o75");
endmodule /* main */

119
t-xnf.cc
View File

@ -17,7 +17,7 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#if !defined(WINNT)
#ident "$Id: t-xnf.cc,v 1.20 1999/12/16 02:42:15 steve Exp $"
#ident "$Id: t-xnf.cc,v 1.21 1999/12/16 18:54:32 steve Exp $"
#endif
/* XNF BACKEND
@ -98,7 +98,7 @@ class target_xnf : public target_t {
static void draw_sym_with_lcaname(ostream&os, string lca,
const NetNode*net);
static void draw_xor(ostream&os, const NetAddSub*, unsigned idx);
enum adder_type {FORCE0, LOWER, UPPER, DOUBLE };
enum adder_type {FORCE0, LOWER, DOUBLE, LOWER_W_CO, EXAMINE_CI };
static void draw_carry(ostream&os, const NetAddSub*, unsigned idx,
enum adder_type);
@ -374,44 +374,43 @@ void target_xnf::draw_carry(ostream &os, const NetAddSub*gate, unsigned idx,
"LIBVER=2.0.0" << endl;
// Less significant bit addends, if any
if ( type == LOWER || type == DOUBLE ) {
if ( type == LOWER || type == DOUBLE || type == LOWER_W_CO ) {
draw_pin(os, "A0", gate->pin_DataA(idx));
draw_pin(os, "B0", gate->pin_DataB(idx));
}
// More significant bit addends, if any
if ( type == UPPER || type == DOUBLE ) {
unsigned int i = (type==UPPER)?idx:(idx+1);
draw_pin(os, "A1", gate->pin_DataA(i));
draw_pin(os, "B1", gate->pin_DataB(i));
if ( type == DOUBLE ) {
draw_pin(os, "A1", gate->pin_DataA(idx+1));
draw_pin(os, "B1", gate->pin_DataB(idx+1));
}
// Carry input
if ( type != FORCE0 && type != UPPER ) {
os << " PIN, CIN, I, " << name_cout << "<" <<
idx << ">" << endl;
// All but FORCE0 cells have carry input
if ( type != FORCE0 ) {
os << " PIN, CIN, I, " << name_cout << "<" << idx << ">" << endl;
}
// Connect the Cout0 to a signal so that I can connect
// it to the adder.
if ( type == LOWER || type == DOUBLE ) {
switch (type) {
case LOWER:
case DOUBLE:
os << " PIN, COUT0, O, " << name_cout << "<" << (idx+1) <<
">" << endl;
break;
case EXAMINE_CI:
case LOWER_W_CO:
draw_pin(os, "COUT0", gate->pin_Cout());
break;
}
// Connect the Cout, this will connect to the next Cin
if ( type == FORCE0 || type == UPPER || type == DOUBLE ) {
if ( type == FORCE0 || type == DOUBLE ) {
unsigned int to = (type==FORCE0)?(0):(idx+2);
if (type==UPPER) to=idx+1;
os << " PIN, COUT, O, " << name_cout << "<" << to <<
">" << endl;
}
// Carry In for mode UPPER comes from a strange place
if ( type == UPPER ) {
os << " PIN, A0, I, " << name_cout << "<dummy>" << endl;
}
// These are the mode inputs from the CY_xx pseudo-device
for (unsigned cn = 0 ; cn < 8 ; cn += 1) {
os << " PIN, C" << cn << ", I, " << name << "/C"
@ -419,26 +418,24 @@ void target_xnf::draw_carry(ostream &os, const NetAddSub*gate, unsigned idx,
}
os << "END" << endl;
// Complete the dummy force used above
if ( type == UPPER ) {
os << "PWR, 0, " << name_cout << "<dummy>" << endl;
}
// On to the CY_xx pseudo-device itself
os << "SYM, " << name_cym << "<" << (idx) << ">, ";
switch (type) {
case FORCE0:
case FORCE0:
os << "CY4_37, CYMODE=FORCE-0" << endl;
break;
case LOWER:
case LOWER:
os << "CY4_01, CYMODE=ADD-F-CI" << endl;
break;
case UPPER:
os << "CY4_03, CYMODE=ADD-G-F1" << endl;
case LOWER_W_CO:
os << "CY4_01, CYMODE=ADD-F-CI" << endl;
break;
case DOUBLE:
case DOUBLE:
os << "CY4_02, CYMODE=ADD-FG-CI" << endl;
break;
case EXAMINE_CI:
os << "CY4_42, CYMODE-EXAMINE-CI" << endl;
break;
}
for (unsigned cn = 0 ; cn < 8 ; cn += 1) {
os << " PIN, C" << cn << ", O, " << name << "/C"
@ -464,32 +461,57 @@ void target_xnf::lpm_add_sub(ostream&os, const NetAddSub*gate)
{
unsigned width = gate->width();
// Don't handle carry output yet
assert (! gate->pin_Cout().is_linked());
unsigned carry_width = width-1;
/* Make the force-0 cary mode object to initialize the bottom
/* Make the force-0 carry mode object to initialize the bottom
bits of the carry chain. Label this with the width instead
of the bit position so that symbols don't clash. */
if (carry_width%2) {
draw_carry(os, gate, width, FORCE0);
} else {
draw_carry(os, gate, 0, UPPER);
}
draw_carry(os, gate, width+1, FORCE0);
/* Now make the 2 bit adders that chain from the cin
initializer and up. Save the tail bit (if there is one) for
later. */
for (unsigned idx = 1-(carry_width%2) ; idx < carry_width-1 ; idx += 2) {
initializer and up. Save the tail bits for later. */
for (unsigned idx = 0 ; idx < width-2 ; idx += 2)
draw_carry(os, gate, idx, DOUBLE);
/* Always have one or two tail bits. The situation gets a
little tricky if we want the carry output, so handle that
here.
If the carry-out is connected, and there are an even number
of data bits, we need to see the cout from the CLB. This is
done by configuring the top CLB CY device as ADD-FG-CI (to
activate cout) and create an extra CLB CY device on top of
the carry chain configured EXAMINE-CI to put the carry into
the G function block.
IF the carry-out is connected and there are an odd number
of data bits, then the top CLB can be configured to carry
the top bit in the F unit and deliver the carry out through
the G unit.
If the carry-out is not connected, then configure this top
CLB as ADD-F-CI. The draw_xor for the top bit will include
the F carry if needed. */
if (gate->pin_Cout().is_linked()) {
if (width%2 == 0) {
draw_carry(os, gate, width-2, DOUBLE);
draw_carry(os, gate, width, EXAMINE_CI);
} else {
draw_carry(os, gate, width-1, LOWER_W_CO);
}
} else {
if (width%2 == 0)
draw_carry(os, gate, width-2, LOWER);
else
draw_carry(os, gate, width-1, LOWER);
}
/* Always have a tail bit */
draw_carry(os, gate, carry_width-1, LOWER);
for (unsigned idx = 0 ; idx < width ; ++idx) {
/* Now draw all the single bit (plus carry in) adders from XOR
gates. This puts the F and G units to use. */
for (unsigned idx = 0 ; idx < width ; idx += 1)
draw_xor(os, gate, idx);
}
}
@ -687,6 +709,9 @@ extern const struct target tgt_xnf = { "xnf", &target_xnf_obj };
/*
* $Log: t-xnf.cc,v $
* Revision 1.21 1999/12/16 18:54:32 steve
* Capture the carry out of carry-chain addition.
*
* Revision 1.20 1999/12/16 02:42:15 steve
* Simulate carry output on adders.
*