iverilog/tgt-edif/xilinx.c

969 lines
28 KiB
C
Raw Normal View History

/*
* Copyright (c) 2005-2010 Stephen Williams (steve at icarus.com)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* This source file contains common functions used by Xilinx
* devices. The functions here do not support any specific Xilinx part
2007-02-26 20:51:38 +01:00
* family, but instead provide some core functions that are used by
* specific devices.
*
* For example, some xilinx_* functions are suitable for placing
* directly in a device_s table, but there is no "xilinx"
* device. These functions can be placed in the tables for devices
* that have no better way to handle the device_s function, or these
* functions can be called by device specific device_s functions that
* fall back on the generic handling in certain cases. For an example
* of both cases, see d-virtex.c.
*/
# include "edif.h"
# include "generic.h"
# include "xilinx.h"
# include <stdlib.h>
# include <string.h>
#ifdef HAVE_MALLOC_H
# include <malloc.h>
#endif
# include <assert.h>
edif_cell_t xilinx_cell_buf(edif_xlibrary_t xlib)
{
static edif_cell_t cell = 0;
if (cell) return cell;
cell = edif_xcell_create(xlib, "BUF", 2);
edif_cell_portconfig(cell, BUF_O, "O", IVL_SIP_OUTPUT);
edif_cell_portconfig(cell, BUF_I, "I", IVL_SIP_INPUT);
return cell;
}
edif_cell_t xilinx_cell_bufe(edif_xlibrary_t xlib)
{
static edif_cell_t cell = 0;
if (cell) return cell;
cell = edif_xcell_create(xlib, "BUFE", 3);
edif_cell_portconfig(cell, BUF_O, "O", IVL_SIP_OUTPUT);
edif_cell_portconfig(cell, BUF_I, "I", IVL_SIP_INPUT);
edif_cell_portconfig(cell, BUF_T, "E", IVL_SIP_INPUT);
return cell;
}
edif_cell_t xilinx_cell_bufg(edif_xlibrary_t xlib)
{
static edif_cell_t cell = 0;
if (cell) return cell;
cell = edif_xcell_create(xlib, "BUFG", 2);
edif_cell_portconfig(cell, BUF_O, "O", IVL_SIP_OUTPUT);
edif_cell_portconfig(cell, BUF_I, "I", IVL_SIP_INPUT);
return cell;
}
edif_cell_t xilinx_cell_buft(edif_xlibrary_t xlib)
{
static edif_cell_t cell = 0;
if (cell) return cell;
cell = edif_xcell_create(xlib, "BUFT", 3);
edif_cell_portconfig(cell, BUF_O, "O", IVL_SIP_OUTPUT);
edif_cell_portconfig(cell, BUF_I, "I", IVL_SIP_INPUT);
edif_cell_portconfig(cell, BUF_T, "T", IVL_SIP_INPUT);
return cell;
}
edif_cell_t xilinx_cell_ibuf(edif_xlibrary_t xlib)
{
static edif_cell_t cell = 0;
if (cell) return cell;
cell = edif_xcell_create(xlib, "IBUF", 2);
edif_cell_portconfig(cell, BUF_O, "O", IVL_SIP_OUTPUT);
edif_cell_portconfig(cell, BUF_I, "I", IVL_SIP_INPUT);
return cell;
}
edif_cell_t xilinx_cell_inv(edif_xlibrary_t xlib)
{
static edif_cell_t cell = 0;
if (cell) return cell;
cell = edif_xcell_create(xlib, "INV", 2);
edif_cell_portconfig(cell, BUF_O, "O", IVL_SIP_OUTPUT);
edif_cell_portconfig(cell, BUF_I, "I", IVL_SIP_INPUT);
return cell;
}
edif_cell_t xilinx_cell_muxf5(edif_xlibrary_t xlib)
{
static edif_cell_t cell = 0;
if (cell) return cell;
cell = edif_xcell_create(xlib, "MUXF5", 4);
edif_cell_portconfig(cell, MUXF_O, "O", IVL_SIP_OUTPUT);
edif_cell_portconfig(cell, MUXF_I0, "I0", IVL_SIP_INPUT);
edif_cell_portconfig(cell, MUXF_I1, "I1", IVL_SIP_INPUT);
edif_cell_portconfig(cell, MUXF_S, "S", IVL_SIP_INPUT);
return cell;
}
edif_cell_t xilinx_cell_muxf6(edif_xlibrary_t xlib)
{
static edif_cell_t cell = 0;
if (cell) return cell;
cell = edif_xcell_create(xlib, "MUXF6", 4);
edif_cell_portconfig(cell, MUXF_O, "O", IVL_SIP_OUTPUT);
edif_cell_portconfig(cell, MUXF_I0, "I0", IVL_SIP_INPUT);
edif_cell_portconfig(cell, MUXF_I1, "I1", IVL_SIP_INPUT);
edif_cell_portconfig(cell, MUXF_S, "S", IVL_SIP_INPUT);
return cell;
}
edif_cell_t xilinx_cell_obuf(edif_xlibrary_t xlib)
{
static edif_cell_t cell = 0;
if (cell) return cell;
cell = edif_xcell_create(xlib, "OBUF", 2);
edif_cell_portconfig(cell, BUF_O, "O", IVL_SIP_OUTPUT);
edif_cell_portconfig(cell, BUF_I, "I", IVL_SIP_INPUT);
return cell;
}
edif_cell_t xilinx_cell_lut2(edif_xlibrary_t xlib)
{
static edif_cell_t cell = 0;
if (cell != 0) return cell;
cell = edif_xcell_create(xlib, "LUT2", 3);
edif_cell_portconfig(cell, LUT_O, "O", IVL_SIP_OUTPUT);
edif_cell_portconfig(cell, LUT_I0, "I0", IVL_SIP_INPUT);
edif_cell_portconfig(cell, LUT_I1, "I1", IVL_SIP_INPUT);
return cell;
}
edif_cell_t xilinx_cell_lut3(edif_xlibrary_t xlib)
{
static edif_cell_t cell = 0;
if (cell != 0) return cell;
cell = edif_xcell_create(xlib, "LUT3", 4);
edif_cell_portconfig(cell, LUT_O, "O", IVL_SIP_OUTPUT);
edif_cell_portconfig(cell, LUT_I0, "I0", IVL_SIP_INPUT);
edif_cell_portconfig(cell, LUT_I1, "I1", IVL_SIP_INPUT);
edif_cell_portconfig(cell, LUT_I2, "I2", IVL_SIP_INPUT);
return cell;
}
edif_cell_t xilinx_cell_lut4(edif_xlibrary_t xlib)
{
static edif_cell_t cell = 0;
if (cell != 0) return cell;
cell = edif_xcell_create(xlib, "LUT4", 5);
edif_cell_portconfig(cell, LUT_O, "O", IVL_SIP_OUTPUT);
edif_cell_portconfig(cell, LUT_I0, "I0", IVL_SIP_INPUT);
edif_cell_portconfig(cell, LUT_I1, "I1", IVL_SIP_INPUT);
edif_cell_portconfig(cell, LUT_I2, "I2", IVL_SIP_INPUT);
edif_cell_portconfig(cell, LUT_I3, "I3", IVL_SIP_INPUT);
return cell;
}
edif_cell_t xilinx_cell_fdce(edif_xlibrary_t xlib)
{
static edif_cell_t cell = 0;
if (cell != 0) return cell;
cell = edif_xcell_create(xlib, "FDCE", 5);
edif_cell_portconfig(cell, FDCE_Q, "Q", IVL_SIP_OUTPUT);
edif_cell_portconfig(cell, FDCE_D, "D", IVL_SIP_INPUT);
edif_cell_portconfig(cell, FDCE_C, "C", IVL_SIP_INPUT);
edif_cell_portconfig(cell, FDCE_CE, "CE", IVL_SIP_INPUT);
edif_cell_portconfig(cell, FDCE_CLR,"CLR", IVL_SIP_INPUT);
return cell;
}
edif_cell_t xilinx_cell_fdcpe(edif_xlibrary_t xlib)
{
static edif_cell_t cell = 0;
if (cell != 0) return cell;
cell = edif_xcell_create(xlib, "FDCPE", 6);
edif_cell_portconfig(cell, FDCE_Q, "Q", IVL_SIP_OUTPUT);
edif_cell_portconfig(cell, FDCE_D, "D", IVL_SIP_INPUT);
edif_cell_portconfig(cell, FDCE_C, "C", IVL_SIP_INPUT);
edif_cell_portconfig(cell, FDCE_CE, "CE", IVL_SIP_INPUT);
edif_cell_portconfig(cell, FDCE_CLR,"CLR", IVL_SIP_INPUT);
edif_cell_portconfig(cell, FDCE_PRE,"PRE", IVL_SIP_INPUT);
return cell;
}
edif_cell_t xilinx_cell_fdre(edif_xlibrary_t xlib)
{
static edif_cell_t cell = 0;
if (cell != 0) return cell;
cell = edif_xcell_create(xlib, "FDRE", 5);
edif_cell_portconfig(cell, FDCE_Q, "Q", IVL_SIP_OUTPUT);
edif_cell_portconfig(cell, FDCE_D, "D", IVL_SIP_INPUT);
edif_cell_portconfig(cell, FDCE_C, "C", IVL_SIP_INPUT);
edif_cell_portconfig(cell, FDCE_CE, "CE", IVL_SIP_INPUT);
edif_cell_portconfig(cell, FDCE_CLR,"R", IVL_SIP_INPUT);
return cell;
}
edif_cell_t xilinx_cell_mult_and(edif_xlibrary_t xlib)
{
static edif_cell_t cell = 0;
if (cell != 0) return cell;
cell = edif_xcell_create(xlib, "MULT_AND", 3);
edif_cell_portconfig(cell, MULT_AND_LO, "LO", IVL_SIP_OUTPUT);
edif_cell_portconfig(cell, MULT_AND_I0, "I0", IVL_SIP_INPUT);
edif_cell_portconfig(cell, MULT_AND_I1, "I1", IVL_SIP_INPUT);
return cell;
}
edif_cell_t xilinx_cell_muxcy(edif_xlibrary_t xlib)
{
static edif_cell_t cell = 0;
if (cell != 0) return cell;
cell = edif_xcell_create(xlib, "MUXCY", 4);
edif_cell_portconfig(cell, MUXCY_O, "O", IVL_SIP_OUTPUT);
edif_cell_portconfig(cell, MUXCY_DI, "DI", IVL_SIP_INPUT);
edif_cell_portconfig(cell, MUXCY_CI, "CI", IVL_SIP_INPUT);
edif_cell_portconfig(cell, MUXCY_S, "S", IVL_SIP_INPUT);
return cell;
}
edif_cell_t xilinx_cell_muxcy_l(edif_xlibrary_t xlib)
{
static edif_cell_t cell = 0;
if (cell != 0) return cell;
cell = edif_xcell_create(xlib, "MUXCY_L", 4);
edif_cell_portconfig(cell, MUXCY_O, "LO", IVL_SIP_OUTPUT);
edif_cell_portconfig(cell, MUXCY_DI, "DI", IVL_SIP_INPUT);
edif_cell_portconfig(cell, MUXCY_CI, "CI", IVL_SIP_INPUT);
edif_cell_portconfig(cell, MUXCY_S, "S", IVL_SIP_INPUT);
return cell;
}
edif_cell_t xilinx_cell_xorcy(edif_xlibrary_t xlib)
{
static edif_cell_t cell = 0;
if (cell != 0) return cell;
cell = edif_xcell_create(xlib, "XORCY", 3);
edif_cell_portconfig(cell, XORCY_O, "O", IVL_SIP_OUTPUT);
edif_cell_portconfig(cell, XORCY_CI, "CI", IVL_SIP_INPUT);
edif_cell_portconfig(cell, XORCY_LI, "LI", IVL_SIP_INPUT);
return cell;
}
/*
* This function does a lot of the stuff common to the header
2007-02-26 20:51:38 +01:00
* functions of various Xilinx families. This includes creating the edf
* object that holds the netlist.
*/
void xilinx_common_header(ivl_design_t des)
{
unsigned idx;
ivl_scope_t root = ivl_design_root(des);
unsigned sig_cnt = ivl_scope_sigs(root);
unsigned nports = 0, pidx;
/* Count the ports I'm going to use. */
for (idx = 0 ; idx < sig_cnt ; idx += 1) {
ivl_signal_t sig = ivl_scope_sig(root, idx);
if (ivl_signal_port(sig) == IVL_SIP_NONE)
continue;
if (ivl_signal_attr(sig, "PAD") != 0)
continue;
nports += ivl_signal_pins(sig);
}
edf = edif_create(ivl_scope_basename(root), nports);
pidx = 0;
for (idx = 0 ; idx < sig_cnt ; idx += 1) {
edif_joint_t jnt;
ivl_signal_t sig = ivl_scope_sig(root, idx);
if (ivl_signal_port(sig) == IVL_SIP_NONE)
continue;
if (ivl_signal_attr(sig, "PAD") != 0)
continue;
if (ivl_signal_pins(sig) == 1) {
edif_portconfig(edf, pidx, ivl_signal_basename(sig),
ivl_signal_port(sig));
assert(ivl_signal_pins(sig) == 1);
jnt = edif_joint_of_nexus(edf, ivl_signal_pin(sig, 0));
edif_port_to_joint(jnt, edf, pidx);
} else {
const char*name = ivl_signal_basename(sig);
ivl_signal_port_t dir = ivl_signal_port(sig);
char buf[128];
unsigned bit;
for (bit = 0 ; bit < ivl_signal_pins(sig) ; bit += 1) {
const char*tmp;
sprintf(buf, "%s[%u]", name, bit);
tmp = strdup(buf);
edif_portconfig(edf, pidx+bit, tmp, dir);
jnt = edif_joint_of_nexus(edf,ivl_signal_pin(sig,bit));
edif_port_to_joint(jnt, edf, pidx+bit);
}
}
pidx += ivl_signal_pins(sig);
}
assert(pidx == nports);
}
void xilinx_show_footer(ivl_design_t des)
{
unsigned idx;
for (idx = 0 ; idx < ivl_design_consts(des) ; idx += 1) {
unsigned pin;
ivl_net_const_t net = ivl_design_const(des, idx);
const char*val = ivl_const_bits(net);
for (pin = 0 ; pin < ivl_const_pins(net) ; pin += 1) {
edif_joint_t jnt;
edif_cellref_t pad;
jnt = edif_joint_of_nexus(edf, ivl_const_pin(net, pin));
switch (val[pin]) {
case '0':
pad = edif_cellref_create(edf, cell_0);
break;
case '1':
pad = edif_cellref_create(edf, cell_1);
break;
default:
assert(0);
break;
}
edif_add_to_joint(jnt, pad, 0);
}
}
edif_print(xnf, edf);
}
/*
* Make (or retrieve) a cell in the external library that reflects the
* scope with its ports.
*/
void xilinx_show_scope(ivl_scope_t scope)
{
edif_cell_t cell;
edif_cellref_t ref;
unsigned port, idx;
cell = edif_xlibrary_scope_cell(xlib, scope);
ref = edif_cellref_create(edf, cell);
for (idx = 0 ; idx < ivl_scope_sigs(scope) ; idx += 1) {
edif_joint_t jnt;
ivl_signal_t sig = ivl_scope_sig(scope, idx);
if (ivl_signal_port(sig) == IVL_SIP_NONE)
continue;
port = edif_cell_port_byname(cell, ivl_signal_basename(sig));
jnt = edif_joint_of_nexus(edf, ivl_signal_pin(sig, 0));
edif_add_to_joint(jnt, ref, port);
}
}
void xilinx_pad(ivl_signal_t sig, const char*str)
{
unsigned idx;
char**pins;
if (cell_ipad == 0) {
cell_ipad = edif_xcell_create(xlib, "IPAD", 1);
edif_cell_portconfig(cell_ipad, 0, "IPAD", IVL_SIP_OUTPUT);
}
if (cell_opad == 0) {
cell_opad = edif_xcell_create(xlib, "OPAD", 1);
edif_cell_portconfig(cell_opad, 0, "OPAD", IVL_SIP_INPUT);
}
if (cell_iopad == 0) {
cell_iopad = edif_xcell_create(xlib, "IOPAD", 1);
edif_cell_portconfig(cell_iopad, 0, "IOPAD", IVL_SIP_INOUT);
}
/* Collect an array of pin assignments from the attribute
string passed in as str. The format is a comma separated
list of location names. */
pins = calloc(ivl_signal_pins(sig), sizeof(char*));
for (idx = 0 ; idx < ivl_signal_pins(sig) ; idx += 1) {
const char*tmp = strchr(str, ',');
if (tmp == 0) tmp = str+strlen(str);
pins[idx] = malloc(tmp-str+1);
strncpy(pins[idx], str, tmp-str);
pins[idx][tmp-str] = 0;
if (*tmp != 0)
tmp += 1;
str = tmp;
}
/* Now go through the pins of the signal, creating pads and
bufs and joining them to the signal nexus. */
for (idx = 0 ; idx < ivl_signal_pins(sig) ; idx += 1) {
edif_joint_t jnt;
edif_cellref_t pad, buf;
const char*name_str = ivl_signal_basename(sig);
if (ivl_signal_pins(sig) > 1) {
char name_buf[128];
sprintf(name_buf, "%s[%u]", name_str, idx);
name_str = strdup(name_buf);
}
switch (ivl_signal_port(sig)) {
case IVL_SIP_INPUT:
pad = edif_cellref_create(edf, cell_ipad);
buf = edif_cellref_create(edf, xilinx_cell_ibuf(xlib));
jnt = edif_joint_create(edf);
edif_joint_rename(jnt, name_str);
edif_add_to_joint(jnt, pad, 0);
edif_add_to_joint(jnt, buf, BUF_I);
jnt = edif_joint_of_nexus(edf, ivl_signal_pin(sig, idx));
edif_add_to_joint(jnt, buf, BUF_O);
break;
case IVL_SIP_OUTPUT:
pad = edif_cellref_create(edf, cell_opad);
buf = edif_cellref_create(edf, xilinx_cell_obuf(xlib));
jnt = edif_joint_create(edf);
edif_joint_rename(jnt, name_str);
edif_add_to_joint(jnt, pad, 0);
edif_add_to_joint(jnt, buf, BUF_O);
jnt = edif_joint_of_nexus(edf, ivl_signal_pin(sig, idx));
edif_add_to_joint(jnt, buf, BUF_I);
break;
case IVL_SIP_INOUT:
pad = edif_cellref_create(edf, cell_iopad);
jnt = edif_joint_of_nexus(edf, ivl_signal_pin(sig, idx));
edif_add_to_joint(jnt, pad, 0);
break;
default:
assert(0);
}
if (pins[idx])
edif_cellref_pstring(pad, "LOC", pins[idx]);
}
/* Don't free the allocated pad name strings. The
edif_cellref_pstring function attached the string to the
LOC attribute, so the reference is permanent. */
free(pins);
}
/*
* This function handles the case where the user specifies the cell to
* use by attribute.
*/
static void edif_cellref_logic(ivl_net_logic_t net, const char*def)
{
char*str = strdup(def);
char*pins;
edif_cell_t cell;
edif_cellref_t ref;
edif_joint_t jnt;
unsigned idx, port;
pins = strchr(str, ':');
assert(pins);
*pins++ = 0;
/* Locate the cell in the library, lookup by name. */
cell = edif_xlibrary_findcell(xlib, str);
assert(cell);
ref = edif_cellref_create(edf, cell);
for (idx = 0 ; idx < ivl_logic_pins(net) ; idx += 1) {
char*tmp;
assert(pins);
tmp = strchr(pins,',');
if (tmp != 0)
*tmp++ = 0;
else
tmp = 0;
port = edif_cell_port_byname(cell, pins);
jnt = edif_joint_of_nexus(edf, ivl_logic_pin(net, idx));
edif_add_to_joint(jnt, ref, port);
pins = tmp;
}
free(str);
}
static void lut_logic(ivl_net_logic_t net, const char*init3,
const char*init4, const char*init5)
{
edif_cellref_t lut = NULL; /* initialization shuts up gcc -Wall */
edif_joint_t jnt;
const char* init = NULL; /* ditto */
assert(ivl_logic_pins(net) <= 5);
assert(ivl_logic_pins(net) >= 3);
switch (ivl_logic_pins(net)) {
case 3:
lut = edif_cellref_create(edf, xilinx_cell_lut2(xlib));
init = init3;
break;
case 4:
lut = edif_cellref_create(edf, xilinx_cell_lut3(xlib));
init = init4;
break;
case 5:
lut = edif_cellref_create(edf, xilinx_cell_lut4(xlib));
init = init5;
break;
}
edif_cellref_pstring(lut, "INIT", init);
switch (ivl_logic_pins(net)) {
case 5:
jnt = edif_joint_of_nexus(edf, ivl_logic_pin(net, 4));
edif_add_to_joint(jnt, lut, LUT_I3);
case 4:
jnt = edif_joint_of_nexus(edf, ivl_logic_pin(net, 3));
edif_add_to_joint(jnt, lut, LUT_I2);
case 3:
jnt = edif_joint_of_nexus(edf, ivl_logic_pin(net, 2));
edif_add_to_joint(jnt, lut, LUT_I1);
}
jnt = edif_joint_of_nexus(edf, ivl_logic_pin(net, 1));
edif_add_to_joint(jnt, lut, LUT_I0);
jnt = edif_joint_of_nexus(edf, ivl_logic_pin(net, 0));
edif_add_to_joint(jnt, lut, LUT_O);
}
void xilinx_logic(ivl_net_logic_t net)
{
edif_cellref_t obj;
edif_joint_t jnt;
{ const char*cellref_attribute = ivl_logic_attr(net, "cellref");
if (cellref_attribute != 0) {
edif_cellref_logic(net, cellref_attribute);
return;
}
}
switch (ivl_logic_type(net)) {
case IVL_LO_BUF:
case IVL_LO_BUFZ:
assert(ivl_logic_pins(net) == 2);
obj = edif_cellref_create(edf, xilinx_cell_buf(xlib));
jnt = edif_joint_of_nexus(edf, ivl_logic_pin(net, 0));
edif_add_to_joint(jnt, obj, BUF_O);
jnt = edif_joint_of_nexus(edf, ivl_logic_pin(net, 1));
edif_add_to_joint(jnt, obj, BUF_I);
break;
case IVL_LO_BUFIF0:
/* The Xilinx BUFT devices is a BUF that adds a T
input. The output is tri-stated if the T input is
1. In other words, it acts just like bufif0. */
assert(ivl_logic_pins(net) == 3);
obj = edif_cellref_create(edf, xilinx_cell_buft(xlib));
jnt = edif_joint_of_nexus(edf, ivl_logic_pin(net, 0));
edif_add_to_joint(jnt, obj, BUF_O);
jnt = edif_joint_of_nexus(edf, ivl_logic_pin(net, 1));
edif_add_to_joint(jnt, obj, BUF_I);
jnt = edif_joint_of_nexus(edf, ivl_logic_pin(net, 2));
edif_add_to_joint(jnt, obj, BUF_T);
break;
case IVL_LO_BUFIF1:
/* The Xilinx BUFE devices is a BUF that adds an enable
input. The output is tri-stated if the E input is 0.
In other words, it acts just like bufif1. */
assert(ivl_logic_pins(net) == 3);
obj = edif_cellref_create(edf, xilinx_cell_bufe(xlib));
jnt = edif_joint_of_nexus(edf, ivl_logic_pin(net, 0));
edif_add_to_joint(jnt, obj, BUF_O);
jnt = edif_joint_of_nexus(edf, ivl_logic_pin(net, 1));
edif_add_to_joint(jnt, obj, BUF_I);
jnt = edif_joint_of_nexus(edf, ivl_logic_pin(net, 2));
edif_add_to_joint(jnt, obj, BUF_T);
break;
case IVL_LO_NOT:
assert(ivl_logic_pins(net) == 2);
obj = edif_cellref_create(edf, xilinx_cell_inv(xlib));
jnt = edif_joint_of_nexus(edf, ivl_logic_pin(net, 0));
edif_add_to_joint(jnt, obj, BUF_O);
jnt = edif_joint_of_nexus(edf, ivl_logic_pin(net, 1));
edif_add_to_joint(jnt, obj, BUF_I);
break;
case IVL_LO_AND:
assert(ivl_logic_pins(net) <= 5);
assert(ivl_logic_pins(net) >= 3);
lut_logic(net, "8", "80", "8000");
break;
case IVL_LO_NOR:
assert(ivl_logic_pins(net) <= 5);
assert(ivl_logic_pins(net) >= 3);
lut_logic(net, "1", "01", "0001");
break;
case IVL_LO_OR:
assert(ivl_logic_pins(net) <= 5);
assert(ivl_logic_pins(net) >= 3);
lut_logic(net, "E", "FE", "FFFE");
break;
case IVL_LO_XNOR:
assert(ivl_logic_pins(net) <= 5);
assert(ivl_logic_pins(net) >= 3);
lut_logic(net, "9", "69", "9669");
break;
case IVL_LO_XOR:
assert(ivl_logic_pins(net) <= 5);
assert(ivl_logic_pins(net) >= 3);
lut_logic(net, "6", "96", "6996");
break;
default:
fprintf(stderr, "UNSUPPORTED LOGIC TYPE: %u\n",
ivl_logic_type(net));
break;
}
}
/*
* A fully generic Xilinx MUX is implemented entirely from LUT
* devices.
*/
void xilinx_mux(ivl_lpm_t net)
{
unsigned idx;
edif_cellref_t lut;
edif_joint_t jnt;
assert(ivl_lpm_selects(net) == 1);
/* A/B Mux devices are made from LUT3 devices. I0 is connected
to A, I1 to B, and I2 to the Select input. Create as many
as are needed to implement the requested width.
S B A | Q
------+--
0 0 0 | 0
0 0 1 | 1
0 1 0 | 0
0 1 1 | 1
1 0 0 | 0
1 0 1 | 0
1 1 0 | 1
1 1 1 | 1
INIT = "CA" */
for (idx = 0 ; idx < ivl_lpm_width(net) ; idx += 1) {
lut = edif_cellref_create(edf, xilinx_cell_lut3(xlib));
jnt = edif_joint_of_nexus(edf, ivl_lpm_q(net, idx));
edif_add_to_joint(jnt, lut, LUT_O);
jnt = edif_joint_of_nexus(edf, ivl_lpm_data2(net, 0, idx));
edif_add_to_joint(jnt, lut, LUT_I0);
jnt = edif_joint_of_nexus(edf, ivl_lpm_data2(net, 1, idx));
edif_add_to_joint(jnt, lut, LUT_I1);
jnt = edif_joint_of_nexus(edf, ivl_lpm_select(net, 0));
edif_add_to_joint(jnt, lut, LUT_I2);
edif_cellref_pstring(lut, "INIT", "CA");
}
}
/*
* Any Xilinx device works with this adder.
* Generic Xilinx add only works for single bit slices.
*/
void xilinx_add(ivl_lpm_t net)
{
const char*ha_init = 0;
edif_cellref_t lut;
edif_joint_t jnt;
switch (ivl_lpm_type(net)) {
case IVL_LPM_ADD:
ha_init = "6";
break;
case IVL_LPM_SUB:
ha_init = "9";
break;
default:
assert(0);
}
/* If this is a single bit wide, then generate only a
half-adder. Normally this is an XOR, but if this is a SUB
then it is an XNOR. */
if (ivl_lpm_width(net) == 1) {
lut = edif_cellref_create(edf, xilinx_cell_lut2(xlib));
jnt = edif_joint_of_nexus(edf, ivl_lpm_q(net, 0));
edif_add_to_joint(jnt, lut, LUT_O);
jnt = edif_joint_of_nexus(edf, ivl_lpm_data(net, 0));
edif_add_to_joint(jnt, lut, LUT_I0);
jnt = edif_joint_of_nexus(edf, ivl_lpm_datab(net, 0));
edif_add_to_joint(jnt, lut, LUT_I1);
edif_cellref_pstring(lut, "INIT", ha_init);
return;
}
assert(0);
}
/*
* The left shift is implemented as a matrix of MUX2_1 devices. The
* matrix has as many rows as the device width, and a column for each
* select.
*/
void xilinx_shiftl(ivl_lpm_t net)
{
unsigned width = ivl_lpm_width(net);
unsigned nsel = 0, swid = 0;
unsigned sdx, qdx;
edif_cellref_t* cells;
edif_cellref_t**table;
edif_cellref_t pad0_cell;
edif_joint_t pad0;
/* First, find out how many select inputs we really need. We
can only use the selects that are enough to shift out the
entire width of the device. The excess can be used as an
enable for the last column. When disabled, the last column
emits zeros. */
while (nsel < ivl_lpm_selects(net)) {
nsel += 1;
swid = 1 << nsel;
if (swid >= width)
break;
}
assert(nsel > 0);
/* Allocate a matrix of edif_cellref_t variables. A devices
will be addressed by the expression table[sdx][qdx];
2007-02-26 20:51:38 +01:00
This should make the algorithm code easier to read. */
cells = calloc(nsel * width, sizeof(edif_cellref_t));
table = calloc(nsel, sizeof(edif_cellref_t*));
for (sdx = 0 ; sdx < nsel ; sdx += 1)
table[sdx] = cells + sdx*width;
2007-02-26 20:51:38 +01:00
/* Make a 0 valued pad bit. I will use this for all the shift in
values that are beyond the input. */
pad0_cell = edif_cellref_create(edf, cell_0);
pad0 = edif_joint_create(edf);
edif_add_to_joint(pad0, pad0_cell, 0);
/* The LUT matrix is <nsel> columns of <width> devices, with
the last column a LUT4 devices. The extra input of the
LUT4s in the last column are used as an enable to collect
all the excess select inputs. */
/* Allocate the LUT devices of the matrix, and connect the
select inputs to I2 of all the devices of the column. */
for (sdx = 0 ; sdx < nsel ; sdx += 1) {
const char*init_string = 0;
ivl_nexus_t nex = ivl_lpm_select(net,sdx);
edif_joint_t sdx_jnt = edif_joint_of_nexus(edf, nex);
edif_cell_t lut;
if (((sdx+1) == nsel) && (nsel < ivl_lpm_selects(net))) {
lut = xilinx_cell_lut4(xlib);
init_string = "00CA";
} else {
lut = xilinx_cell_lut3(xlib);
init_string = "CA";
}
for (qdx = 0 ; qdx < width ; qdx += 1) {
table[sdx][qdx] = edif_cellref_create(edf, lut);
edif_add_to_joint(sdx_jnt, table[sdx][qdx], LUT_I2);
edif_cellref_pstring(table[sdx][qdx], "INIT", init_string);
}
}
/* Connect the inputs of the SHIFTL device to the column 0 LUT
inputs. The slice on the low end shifts in a 0 for a select
input. */
for (qdx = 0 ; qdx < width ; qdx += 1) {
ivl_nexus_t nex0, nex1;
edif_joint_t jnt0;
edif_joint_t jnt1;
nex0 = ivl_lpm_data(net,qdx);
jnt0 = edif_joint_of_nexus(edf, nex0);
if (qdx > 0) {
nex1 = ivl_lpm_data(net,qdx-1);
jnt1 = edif_joint_of_nexus(edf, nex1);
} else {
jnt1 = pad0;
}
edif_add_to_joint(jnt0, table[0][qdx], LUT_I0);
edif_add_to_joint(jnt1, table[0][qdx], LUT_I1);
}
/* Make the inner connections between LUT devices. Each column
connects to the previous column, shifted by the power of
the column value. If the shifted input falls off the end,
then pad with zero. */
for (sdx = 1 ; sdx < nsel ; sdx += 1) {
for (qdx = 0 ; qdx < width ; qdx += 1) {
unsigned shift = 1 << sdx;
edif_joint_t jnt0 = edif_joint_create(edf);
edif_joint_t jnt1 = (qdx >= shift)
? edif_joint_create(edf)
: pad0;
edif_add_to_joint(jnt0, table[sdx][qdx], LUT_I0);
edif_add_to_joint(jnt1, table[sdx][qdx], LUT_I1);
edif_add_to_joint(jnt0, table[sdx-1][qdx], LUT_O);
if (qdx >= shift)
edif_add_to_joint(jnt1, table[sdx-1][qdx-shift], LUT_O);
}
}
/* Connect the output of the last column to the output of the
SHIFTL device. */
for (qdx = 0 ; qdx < width ; qdx += 1) {
ivl_nexus_t nex = ivl_lpm_q(net,qdx);
edif_joint_t jnt = edif_joint_of_nexus(edf, nex);
edif_add_to_joint(jnt, table[nsel-1][qdx], LUT_O);
}
/* Connect the excess select inputs to the enable inputs of
the LUT4 devices in the last column. */
if (nsel < ivl_lpm_selects(net)) {
edif_joint_t jnt;
/* XXXX Only support 1 excess bit for now. */
assert((nsel + 1) == ivl_lpm_selects(net));
jnt = edif_joint_of_nexus(edf, ivl_lpm_select(net,nsel));
for (qdx = 0 ; qdx < width ; qdx += 1)
edif_add_to_joint(jnt, table[nsel-1][qdx], LUT_I3);
}
free(cells);
free(table);
}