Skip to content

Commit

Permalink
vendor.altera: prototype differential DDR
Browse files Browse the repository at this point in the history
  • Loading branch information
Ravenslofty committed Sep 30, 2019
1 parent 95d5ca5 commit ba3dc3c
Showing 1 changed file with 97 additions and 23 deletions.
120 changes: 97 additions & 23 deletions nmigen/vendor/altera.py
Expand Up @@ -142,7 +142,7 @@ def _add_ff(self, m, xdr, src, dest, clk, kind):
i_prn=1,
o_q=dest
)

# Despite the altiobuf manual saying ENABLE_BUS_HOLD is optional, Quartus requires it to be specified.

def get_input(self, pin, port, attrs, invert):
Expand Down Expand Up @@ -318,19 +318,56 @@ def get_diff_input(self, pin, p_port, n_port, attrs, invert):
pin.i.attrs["useioff"] = "1"

for bit in range(pin.width):
clk = pin.i_clk if pin.xdr != 0 else None
if pin.xdr <= 1:
clk = pin.i_clk if pin.xdr != 0 else None

self._add_ff(m, pin.xdr, self._invert_if(invert, ff_i[bit]), pin.i[bit], clk, "i")

m.submodules["{}_buf_{}".format(pin.name, bit)] = Instance("altiobuf_in",
p_NUMBER_OF_CHANNELS=1,
p_ENABLE_BUS_HOLD="FALSE",
p_USE_DIFFERENTIAL_MODE="TRUE",
i_datain=p_port[bit],
i_datain_b=n_port[bit],
o_dataout=ff_i[bit]
)

self._add_ff(m, pin.xdr, self._invert_if(invert, ff_i[bit]), pin.i[bit], clk, "i")

m.submodules["{}_buf_{}".format(pin.name, bit)] = Instance("altiobuf_in",
p_NUMBER_OF_CHANNELS=1,
p_ENABLE_BUS_HOLD="FALSE",
p_USE_DIFFERENTIAL_MODE="TRUE",
i_datain=p_port[bit],
i_datain_b=n_port[bit],
o_dataout=ff_i[bit]
)
else:
p_posedge = Signal()
p_negedge = Signal()
n_posedge = Signal()
n_negedge = Signal()

m.submodules["{}_ddr_p_{}".format(pin.name, bit)] = Instance("altddio_in",
p_width=1,
i_datain=p_port[bit],
i_inclock=pin.i_clk,
o_dataout_h=p_posedge,
o_dataout_l=p_negedge,
)
m.submodules["{}_ddr_n_{}".format(pin.name, bit)] = Instance("altddio_in",
p_width=1,
i_datain=n_port[bit],
i_inclock=pin.i_clk,
o_dataout_h=n_posedge,
o_dataout_l=n_negedge,
)
m.submodules["{}_buf_p_{}".format(pin.name, bit)] = Instance("altiobuf_in",
p_NUMBER_OF_CHANNELS=1,
p_ENABLE_BUS_HOLD="FALSE",
p_USE_DIFFERENTIAL_MODE="TRUE",
i_datain=p_posedge,
i_datain_b=n_posedge,
o_dataout=self._invert_if(pin.i0[bit])
)
m.submodules["{}_buf_n_{}".format(pin.name, bit)] = Instance("altiobuf_in",
p_NUMBER_OF_CHANNELS=1,
p_ENABLE_BUS_HOLD="FALSE",
p_USE_DIFFERENTIAL_MODE="TRUE",
i_datain=p_negedge,
i_datain_b=n_negedge,
o_dataout=self._invert_if(pin.i1[bit])
)

return m

def get_diff_output(self, pin, p_port, n_port, attrs, invert):
Expand All @@ -344,18 +381,55 @@ def get_diff_output(self, pin, p_port, n_port, attrs, invert):
pin.o.attrs["useioff"] = "1"

for bit in range(pin.width):
clk = pin.o_clk if pin.xdr != 0 else None
if pin.xdr <= 1:
clk = pin.o_clk if pin.xdr != 0 else None

self._add_ff(m, pin.xdr, self._invert_if(invert, pin.o[bit]), ff_o[bit], pin.o_clk, "o")
self._add_ff(m, pin.xdr, self._invert_if(invert, pin.o[bit]), ff_o[bit], pin.o_clk, "o")

m.submodules["{}_buf_{}".format(pin.name, bit)] = Instance("altiobuf_out",
p_NUMBER_OF_CHANNELS=1,
p_ENABLE_BUS_HOLD="FALSE",
p_USE_DIFFERENTIAL_MODE="TRUE",
i_datain=ff_o[bit],
o_dataout=p_port[bit],
o_dataout_b=n_port[bit]
)
m.submodules["{}_buf_{}".format(pin.name, bit)] = Instance("altiobuf_out",
p_NUMBER_OF_CHANNELS=1,
p_ENABLE_BUS_HOLD="FALSE",
p_USE_DIFFERENTIAL_MODE="TRUE",
i_datain=ff_o[bit],
o_dataout=p_port[bit],
o_dataout_b=n_port[bit]
)
else:
p_posedge = Signal()
p_negedge = Signal()
n_posedge = Signal()
n_negedge = Signal()

m.submodules["{}_ddr_p_{}".format(pin.name, bit)] = Instance("altddio_out",
p_width=1,
i_datain_h=p_posedge,
i_datain_l=p_negedge,
i_outclock=pin.o_clk,
o_dataout=p_port[bit],
)
m.submodules["{}_ddr_n_{}".format(pin.name, bit)] = Instance("altddio_out",
p_width=1,
i_datain_h=n_posedge,
i_datain_l=n_negedge,
i_outclock=pin.o_clk,
o_dataout=n_port[bit],
)
m.submodules["{}_buf_p_{}".format(pin.name, bit)] = Instance("altiobuf_out",
p_NUMBER_OF_CHANNELS=1,
p_ENABLE_BUS_HOLD="FALSE",
p_USE_DIFFERENTIAL_MODE="TRUE",
i_datain=self._invert_if(invert, pin.o0[bit]),
o_dataout=p_posedge,
o_dataout_b=n_posedge,
)
m.submodules["{}_buf_n_{}".format(pin.name, bit)] = Instance("altiobuf_out",
p_NUMBER_OF_CHANNELS=1,
p_ENABLE_BUS_HOLD="FALSE",
p_USE_DIFFERENTIAL_MODE="TRUE",
i_datain=self._invert_if(invert, pin.o1[bit]),
o_dataout=p_negedge,
o_dataout_b=n_negedge,
)

This comment has been minimized.

Copy link
@whitequark

whitequark Sep 30, 2019

Contributor

This seems wrong; on most FPGA families, the IO buffer follows the DDR buffer, not precedes it.

This comment has been minimized.

Copy link
@Ravenslofty

Ravenslofty Sep 30, 2019

Author Contributor

firefox_2019-09-30_12-28-10
firefox_2019-09-30_12-29-41

(similar for other buffers)

My reading of this is that the altddio cell must be directly connected to the pin, while altiobuf doesn't care.

This comment has been minimized.

Copy link
@whitequark

whitequark Sep 30, 2019

Contributor

My reading of this is that the altddio cell must be directly connected to the pin, while altiobuf doesn't care.

OK but that makes no sense. The purpose of the altiobuf primitive is to configure the input buffer, which is what is physically connected to the input pin in the chip. If you connect altiobuf to something through the fabric, what would it do?

This comment has been minimized.

Copy link
@Ravenslofty

Ravenslofty Sep 30, 2019

Author Contributor

But the purpose of altddio is also to configure the input buffer. So based on this, the alternative is that the hardware physically does not support differential DDR pins.

This comment has been minimized.

Copy link
@whitequark

whitequark Sep 30, 2019

Contributor

That's possible, although it seems like a severe limitation. Does Altera claim to support them anywhere?

This comment has been minimized.

Copy link
@Ravenslofty

Ravenslofty Sep 30, 2019

Author Contributor

I found this in the altddio guide, which suggests differential DDR I/O is implemented using SERDES instead of the altddio cell.
d8007cc2a8

This comment has been minimized.

Copy link
@Ravenslofty

Ravenslofty Sep 30, 2019

Author Contributor

...However the reference simulation library has the following:

//START_MODULE_NAME----------------------------------------------------
//
// Module Name     :    altlvds_rx
//
// Description     :   Low Voltage Differential Signaling (LVDS) receiver
//                     megafunction. The altlvds_rx megafunction implements a
//                     deserialization receiver. LVDS is a high speed IO interface
//                     that uses inputs without a reference voltage. LVDS uses
//                     two wires carrying differential values to create a single
//                     channel. These wires are connected to two pins on
//                     supported device to create a single LVDS channel
//
// Limitation      :   Only available for STRATIX,
//                     STRATIX GX, Stratix II, Cyclone and Cyclone II families.
//
// Results expected:   output clock, deserialized output data and pll locked
//                     signal.
//
//END_MODULE_NAME----------------------------------------------------

which is very confidence-inspiring.

This comment has been minimized.

Copy link
@Ravenslofty

Ravenslofty Sep 30, 2019

Author Contributor

Ah, this does support all the chips, despite the entirely unhelpful comment.


return m

Expand Down

0 comments on commit ba3dc3c

Please sign in to comment.