From 7bc8a0d5eff65fc6c9e69e33bb53c8eff63c06cc Mon Sep 17 00:00:00 2001 From: whitequark Date: Mon, 3 Jun 2019 07:43:02 +0000 Subject: [PATCH] vendor.fpga.lattice_ice40: implement SDR and DDR I/O buffers. --- nmigen/vendor/fpga/lattice_ice40.py | 111 ++++++++++++++++++++-------- 1 file changed, 80 insertions(+), 31 deletions(-) diff --git a/nmigen/vendor/fpga/lattice_ice40.py b/nmigen/vendor/fpga/lattice_ice40.py index 0583451..778e9c7 100644 --- a/nmigen/vendor/fpga/lattice_ice40.py +++ b/nmigen/vendor/fpga/lattice_ice40.py @@ -108,53 +108,102 @@ class LatticeICE40Platform(TemplatedPlatform): """ ] - def _get_io_buffer(self, port, extras, fn): + def _get_dff(self, clk, d, q): + return Instance("$dff", + p_CLK_POLARITY=0, + p_WIDTH=len(d), + i_CLK=clk, + i_D=d, + o_Q=q) + + def _get_io_buffer(self, pin, port, extras): m = Module() + + if "i" in pin.dir and pin.xdr == 2: + i0_ff = Signal.like(pin.i0, name="{}_ff".format(pin.i0.name)) + i1_ff = Signal.like(pin.i1, name="{}_ff".format(pin.i1.name)) + m.submodules += self._get_dff(pin.i_clk, i0_ff, pin.i0) + m.submodules += self._get_dff(pin.i_clk, i1_ff, pin.i1) + if "o" in pin.dir and pin.xdr == 2: + o1_ff = Signal.like(pin.o1, name="{}_ff".format(pin.o1.name)) + m.submodules += self._get_dff(pin.o_clk, pin.o1, o1_ff) + for bit in range(len(port)): - m.submodules += Instance("SB_IO", + io_args = [ ("io", "PACKAGE_PIN", port[bit]), - *fn(bit), - *(("p", key, value) for key, value in extras.items())) + *(("p", key, value) for key, value in extras.items()), + ] + + if "i" not in pin.dir: + i_type = 0b00 # PIN_NO_INPUT aka PIN_INPUT_REGISTERED + elif pin.xdr == 0: + i_type = 0b01 # PIN_INPUT + elif pin.xdr > 0: + i_type = 0b00 # PIN_INPUT_REGISTERED + if "o" not in pin.dir: + o_type = 0b0000 # PIN_NO_OUTPUT + elif pin.xdr == 0 and pin.dir == "o": + o_type = 0b0110 # PIN_OUTPUT + elif pin.xdr == 0: + o_type = 0b1010 # PIN_OUTPUT_TRISTATE + elif pin.xdr == 1 and pin.dir == "o": + o_type = 0b0101 # PIN_OUTPUT_REGISTERED + elif pin.xdr == 1: + o_type = 0b1101 # PIN_OUTPUT_REGISTERED_ENABLE_REGISTERED + elif pin.xdr == 2 and pin.dir == "o": + o_type = 0b0100 # PIN_OUTPUT_DDR + elif pin.xdr == 2: + o_type = 0b1100 # PIN_OUTPUT_DDR_ENABLE_REGISTERED + io_args.append(("p", "PIN_TYPE", (o_type << 2) | i_type)) + + if hasattr(pin, "i_clk"): + io_args.append(("i", "INPUT_CLK", pin.i_clk)) + if hasattr(pin, "o_clk"): + io_args.append(("i", "OUTPUT_CLK", pin.o_clk)) + + if "i" in pin.dir: + if pin.xdr < 2: + io_args.append(("o", "D_IN_0", pin.i[bit])) + if pin.xdr == 2: + # Re-register both inputs before they enter fabric. This increases hold time + # to an entire cycle, and adds one cycle of latency. + io_args.append(("o", "D_IN_0", i0_ff)) + io_args.append(("o", "D_IN_1", i1_ff)) + if "o" in pin.dir: + if pin.xdr < 2: + io_args.append(("i", "D_OUT_0", pin.o[bit])) + if pin.xdr == 2: + # Re-register negedge output after it leaves fabric. This increases setup time + # to an entire cycle, and doesn't add latency. + io_args.append(("i", "D_OUT_0", pin.o0[bit])) + io_args.append(("i", "D_OUT_1", o1_ff)) + + if pin.dir in ("oe", "io"): + io_args.append(("i", "OUTPUT_ENABLE", pin.oe)) + + m.submodules += Instance("SB_IO", *io_args) + return m def get_input(self, pin, port, extras): self._check_feature("single-ended input", pin, extras, - valid_xdrs=(0,), valid_extras=True) - return self._get_io_buffer(port, extras, lambda bit: [ - # PIN_NO_OUTPUT|PIN_INPUT - ("p", "PIN_TYPE", 0b0000_01), - ("o", "D_IN_0", pin.i[bit]), - ]) + valid_xdrs=(0, 1, 2), valid_extras=True) + return self._get_io_buffer(pin, port, extras) def get_output(self, pin, port, extras): self._check_feature("single-ended output", pin, extras, - valid_xdrs=(0,), valid_extras=True) - return self._get_io_buffer(port, extras, lambda bit: [ - # PIN_OUTPUT|PIN_INPUT_REGISTERED - ("p", "PIN_TYPE", 0b0110_00), - ("i", "D_OUT_0", pin.o[bit]), - ]) + valid_xdrs=(0, 1, 2), valid_extras=True) + return self._get_io_buffer(pin, port, extras) def get_tristate(self, pin, port, extras): self._check_feature("single-ended tristate", pin, extras, - valid_xdrs=(0,), valid_extras=True) - return self._get_io_buffer(port, extras, lambda bit: [ - # PIN_OUTPUT_TRISTATE|PIN_INPUT_REGISTERED - ("p", "PIN_TYPE", 0b1010_00), - ("i", "D_OUT_0", pin.o[bit]), - ("i", "OUTPUT_ENABLE", pin.oe), - ]) + valid_xdrs=(0, 1, 2), valid_extras=True) + return self._get_io_buffer(pin, port, extras) def get_input_output(self, pin, port, extras): self._check_feature("single-ended input/output", pin, extras, - valid_xdrs=(0,), valid_extras=True) - return self._get_io_buffer(port, extras, lambda bit: [ - # PIN_OUTPUT_TRISTATE|PIN_INPUT - ("p", "PIN_TYPE", 0b1010_01), - ("o", "D_IN_0", pin.i[bit]), - ("i", "D_OUT_0", pin.o[bit]), - ("i", "OUTPUT_ENABLE", pin.oe), - ]) + valid_xdrs=(0, 1, 2), valid_extras=True) + return self._get_io_buffer(pin, port, extras) class IceStormProgrammerMixin: -- 2.30.2