Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] use FX2Crossbar for communication with FX2 chip in HDMI2USB designs #248

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
2 changes: 2 additions & 0 deletions gateware/fx2_crossbar/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from gateware.fx2_crossbar.core import FX2Crossbar
from gateware.fx2_crossbar.uart import FX2PHY
561 changes: 561 additions & 0 deletions gateware/fx2_crossbar/core.py

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions gateware/fx2_crossbar/uart.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from migen import *

class FX2PHY(Module):
def __init__(self, fx2_sink, fx2_source):
self.sink, self.source = fx2_sink, fx2_source
46 changes: 46 additions & 0 deletions gateware/fx2_crossbar/verilog/ddr_iobuf.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
module ddr_iobuf(input clk, input d, input oe, output q, inout io);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the ddr_iobuf used for?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is a replacement for SB_IO iCE40 primitive that was used in the original crossbar design https://github.com/GlasgowEmbedded/glasgow/blob/367ecc4e83cf644c994cd25ab204e019f7568b9c/software/glasgow/gateware/fx2_crossbar.py#L289
It is used as a IO buffer for all signals connected to the FX2 chip

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be done in migen, not in verilog?

Copy link
Member

@mithro mithro Jan 25, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you also add timing diagrams like @smunaut did at GlasgowEmbedded/glasgow#89 (comment) which verify things will work correctly with the Spartan 6? You should reference https://www.xilinx.com/support/documentation/user_guides/ug381.pdf

Is there a way to also replicate the testing that @whitequark did to prove that everything is working correctly here?

wire clk;
wire d;
wire q;
wire io;

wire ioddr_d;
wire ioddr_q;
wire ioddr_t;

IDDR2 id (
.C0(clk),
.C1(~clk),
.CE(1'b1),
.R(1'b0),
.S(1'b0),
.D(ioddr_d),
.Q1(q)
);
ODDR2 od (
.C0(clk),
.C1(~clk),
.CE(1'b1),
.R(1'b0),
.S(1'b0),
.D0(d),
.D1(d),
.Q(ioddr_q)
);
ODDR2 ot (
.C0(clk),
.C1(~clk),
.CE(1'b1),
.R(1'b0),
.S(1'b0),
.D0(~oe),
.D1(~oe),
.Q(ioddr_t)
);
IOBUF iob (
.I(ioddr_q),
.O(ioddr_d),
.T(ioddr_t),
.IO(io)
);
endmodule
9 changes: 3 additions & 6 deletions platforms/atlys.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,17 +74,14 @@ def __radd__(self, o):
# NET "EppDB<7>" LOC = "C5"; # Bank = 0, Pin name = IO_L6P, Sch name = U1-FD7

# HDMI2USB configuration....
("clk_ifclk", 0, Pins("C10"), IOStandard(LVCMOS_BANK0)),
("fx2", 0,
Subsignal("ifclk", Pins("C10")),
Subsignal("data", Pins("A2 D6 C6 B3 A3 B4 A4 C5")),
Subsignal("addr", Pins("A14 B14"), Misc("DRIVE=12")),
Subsignal("flaga", Pins("B9"), Misc("DRIVE=12")),
Subsignal("flagb", Pins("A9"), Misc("DRIVE=12")),
Subsignal("flagc", Pins("C15"), Misc("DRIVE=12")),
Subsignal("flag", Pins("B9 A9 C15 B2"), Misc("DRIVE=12")),
Subsignal("rd_n", Pins("F13"), Misc("DRIVE=12")),
Subsignal("wr_n", Pins("E13")),
Subsignal("oe_n", Pins("A15"), Misc("DRIVE=12")),
Subsignal("cs_n", Pins("B2")),
Subsignal("pktend_n", Pins("C4"), Misc("DRIVE=12")),
IOStandard(LVCMOS_BANK0)
),
Expand Down Expand Up @@ -697,7 +694,7 @@ def do_finalize(self, fragment):

# USB input clock pins.
try:
self.add_period_constraint(self.lookup_request("fx2").ifclk, 10)
self.add_period_constraint(self.lookup_request("clk_ifclk"), 20.833)
except ConstraintError:
pass

Expand Down
11 changes: 4 additions & 7 deletions platforms/opsis.py
Original file line number Diff line number Diff line change
Expand Up @@ -278,9 +278,9 @@ def tofe_pin(tofe_netname):

# FX2 USB Interface
# CY7C68013A_100AC - component U2
("fx2", 0,
#NET "fx2_ifclk" LOC = "P20" |IOSTANDARD = LVCMOS33 |SLEW=SLOW |DRIVE=12 ; # (/FPGA_Bank_1_2/CY-IFCLK)
Subsignal("ifclk", Pins("P20"), IOStandard("LVCMOS33")),
("clk_ifclk", 0, Pins("P20"), IOStandard("LVCMOS33")),
("fx2", 0,
#NET "fx2_fd<0>" LOC = "C20" |IOSTANDARD = LVCMOS33 |SLEW=SLOW |DRIVE=12 ; # (/FPGA_Bank_1_2/CY_FD0)
#NET "fx2_fd<1>" LOC = "C22" |IOSTANDARD = LVCMOS33 |SLEW=SLOW |DRIVE=12 ; # (/FPGA_Bank_1_2/CY_FD1)
#NET "fx2_fd<2>" LOC = "L15" |IOSTANDARD = LVCMOS33 |SLEW=SLOW |DRIVE=12 ; # (/FPGA_Bank_1_2/CY_FD2)
Expand All @@ -304,11 +304,8 @@ def tofe_pin(tofe_netname):
#NET "fx2_flaga" LOC = "N16" |IOSTANDARD = LVCMOS33 |SLEW=SLOW |DRIVE=12 ; # (/FPGA_Bank_1_2/CY_CTL0)
#NET "fx2_flagb" LOC = "P16" |IOSTANDARD = LVCMOS33 |SLEW=SLOW |DRIVE=12 ; # (/FPGA_Bank_1_2/CY_CTL1)
#NET "fx2_flagc" LOC = "R15" |IOSTANDARD = LVCMOS33 |SLEW=SLOW |DRIVE=12 ; # (/FPGA_Bank_1_2/CY_CTL2)
Subsignal("flaga", Pins("N16"), IOStandard("LVCMOS33"), Misc("DRIVE=12")),
Subsignal("flagb", Pins("P16"), IOStandard("LVCMOS33"), Misc("DRIVE=12")),
Subsignal("flagc", Pins("R15"), IOStandard("LVCMOS33"), Misc("DRIVE=12")),
#NET "fx2_flagd/slcs_n" LOC = "J17" |IOSTANDARD = LVCMOS33 |SLEW=SLOW |DRIVE=12 ; # (/FPGA_Bank_1_2/CY_PA7)
Subsignal("cs_n", Pins("J17"), IOStandard("LVCMOS33"), Misc("DRIVE=12")),
Subsignal("flag", Pins("N16 P16 R15 J17"), IOStandard("LVCMOS33"), Misc("DRIVE=12")),
#NET "fx2_slrd" LOC = "P19" |IOSTANDARD = LVCMOS33 |SLEW=SLOW |DRIVE=12 ; # (/FPGA_Bank_1_2/CY_RD0)
Subsignal("rd_n", Pins("P19"), IOStandard("LVCMOS33"), Misc("DRIVE=12")),
#NET "fx2_slwr" LOC = "R19" |IOSTANDARD = LVCMOS33 |SLEW=SLOW |DRIVE=12 ; # (/FPGA_Bank_1_2/CY_RD1)
Expand Down Expand Up @@ -583,6 +580,6 @@ def do_finalize(self, fragment):

# USB input clock pins.
try:
self.add_period_constraint(self.lookup_request("fx2").ifclk, 10)
self.add_period_constraint(self.lookup_request("clk_ifclk"), 20.833)
except ConstraintError:
pass
24 changes: 24 additions & 0 deletions targets/atlys/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,17 @@ def __init__(self, platform, clk_freq):
# Clock domain for peripherals (such as HDMI output).
self.clock_domains.cd_base50 = ClockDomain()
self.clock_domains.cd_encoder = ClockDomain()
# FX2 Clock domain
self.clock_domains.cd_fx2 = ClockDomain()

self.reset = Signal()

# Input FX2 clock
clk_ifclk = platform.request("clk_ifclk")
# Input FX2 clock (buffered)
clk_ifclka = Signal()
self.specials += Instance("IBUFG", i_I=clk_ifclk, o_O=clk_ifclka)

# Input 100MHz clock
f0 = 100*1000000
clk100 = platform.request("clk100")
Expand Down Expand Up @@ -170,6 +178,22 @@ def __init__(self, platform, clk_freq):
]
platform.add_period_constraint(self.cd_base50.clk, 20)

# FX2 clock
dcm_fx2_locked = Signal()
self.specials += [
Instance("DCM_SP", name="crg_fx2_dcm_sp",
p_CLKIN_PERIOD=20.0,
i_PSEN=C(0),
i_CLKIN=clk_ifclka,
i_CLKFB=self.cd_fx2.clk,
o_CLK0=self.cd_fx2.clk,
o_LOCKED=dcm_fx2_locked,
i_RST=ResetSignal(),
),
AsyncResetSynchronizer(self.cd_fx2,
self.cd_sys.rst | ~dcm_fx2_locked)
]

# Encoder clock - 66 MHz
# ------------------------------------------------------------------------------
self.specials += Instance("BUFG", name="encoder_bufg", i_I=unbuf_encoder, o_O=self.cd_encoder.clk)
Expand Down
33 changes: 21 additions & 12 deletions targets/atlys/hdmi2usb.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
from migen.fhdl.decorators import ClockDomainsRenamer
from migen.fhdl.decorators import ClockDomainsRenamer, ResetInserter
from litex.soc.integration.soc_core import mem_decoder
from litex.soc.interconnect import stream
from litex.soc.cores.uart import UART

from gateware.encoder import EncoderDMAReader, EncoderBuffer, Encoder
from gateware.streamer import USBStreamer
from gateware.fx2_crossbar import FX2Crossbar, FX2PHY

from targets.utils import csr_map_update
from targets.atlys.video import SoC as BaseSoC
Expand All @@ -21,7 +23,7 @@ class HDMI2USBSoC(BaseSoC):
mem_map.update(BaseSoC.mem_map)

def __init__(self, platform, *args, **kwargs):
BaseSoC.__init__(self, platform, *args, **kwargs)
BaseSoC.__init__(self, platform, with_uart=False, *args, **kwargs)

encoder_port = self.sdram.crossbar.get_port(
mode="read",
Expand All @@ -34,27 +36,34 @@ def __init__(self, platform, *args, **kwargs):
"read": "encoder"})(encoder_cdc)
encoder_buffer = ClockDomainsRenamer("encoder")(EncoderBuffer())
encoder = Encoder(platform)
encoder_streamer = USBStreamer(platform, platform.request("fx2"))
self.submodules += encoder_cdc, encoder_buffer, encoder, encoder_streamer

xbar = ClockDomainsRenamer("fx2")(FX2Crossbar(platform))

self.submodules += encoder_cdc, encoder_buffer, encoder, xbar

self.comb += [
self.encoder_reader.source.connect(encoder_cdc.sink),
encoder_cdc.source.connect(encoder_buffer.sink),
encoder_buffer.source.connect(encoder.sink),
encoder.source.connect(encoder_streamer.sink)
encoder.source.connect(xbar.get_in_fifo(0, clock_domain=self.crg.cd_encoder))
]
self.add_wb_slave(mem_decoder(self.mem_map["encoder"]), encoder.bus)
self.add_memory_region("encoder",
self.mem_map["encoder"] + self.shadow_base, 0x2000)

self.platform.add_period_constraint(encoder_streamer.cd_usb.clk, 10.0)

encoder_streamer.cd_usb.clk.attr.add("keep")
self.crg.cd_encoder.clk.attr.add("keep")
self.platform.add_false_path_constraints(
self.crg.cd_sys.clk,
self.crg.cd_encoder.clk,
encoder_streamer.cd_usb.clk)

fx2_uart_sink = xbar.get_in_fifo(1, clock_domain=self.crg.cd_sys)
fx2_uart_source = xbar.get_out_fifo(0, clock_domain=self.crg.cd_sys)

self.submodules.uart_phy = uart_phy = FX2PHY(fx2_uart_sink, fx2_uart_source)
self.submodules.uart = uart = ResetInserter()(UART(uart_phy))

self.add_csr("uart", allow_user_defined=True)
self.add_interrupt("uart", allow_user_defined=True)


self.platform.add_false_path_constraints(self.crg.cd_sys.clk, self.crg.cd_fx2.clk)


SoC = HDMI2USBSoC
24 changes: 24 additions & 0 deletions targets/opsis/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,17 @@ def __init__(self, platform, clk_freq):
# Clock domain for peripherals (such as HDMI output).
self.clock_domains.cd_base50 = ClockDomain()
self.clock_domains.cd_encoder = ClockDomain()
# FX2 Clock domain
self.clock_domains.cd_fx2 = ClockDomain()

self.reset = Signal()

# Input FX2 clock
clk_ifclk = platform.request("clk_ifclk")
# Input FX2 clock (buffered)
clk_ifclka = Signal()
self.specials += Instance("IBUFG", i_I=clk_ifclk, o_O=clk_ifclka)

# Input 100MHz clock
f0 = 100*1000000
clk100 = platform.request("clk100")
Expand Down Expand Up @@ -205,6 +213,22 @@ def __init__(self, platform, clk_freq):
]
platform.add_period_constraint(self.cd_base50.clk, 20)

# FX2 clock
dcm_fx2_locked = Signal()
self.specials += [
Instance("DCM_SP", name="crg_fx2_dcm_sp",
p_CLKIN_PERIOD=20.0,
i_PSEN=C(0),
i_CLKIN=clk_ifclka,
i_CLKFB=self.cd_fx2.clk,
o_CLK0=self.cd_fx2.clk,
o_LOCKED=dcm_fx2_locked,
i_RST=ResetSignal(),
),
AsyncResetSynchronizer(self.cd_fx2,
self.cd_sys.rst | ~dcm_fx2_locked)
]

# Encoder clock - 66 MHz
# ------------------------------------------------------------------------------
self.specials += Instance("BUFG", name="encoder_bufg", i_I=unbuf_encoder, o_O=self.cd_encoder.clk)
Expand Down
30 changes: 19 additions & 11 deletions targets/opsis/hdmi2usb.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
from migen.fhdl.decorators import ClockDomainsRenamer
from migen.fhdl.decorators import ClockDomainsRenamer, ResetInserter
from litex.soc.integration.soc_core import mem_decoder
from litex.soc.interconnect import stream
from litex.soc.cores.uart import UART

from gateware.encoder import EncoderDMAReader, EncoderBuffer, Encoder
from gateware.streamer import USBStreamer
from gateware.fx2_crossbar import FX2Crossbar, FX2PHY

from targets.utils import csr_map_update
from targets.opsis.video import SoC as BaseSoC
Expand All @@ -30,27 +32,33 @@ def __init__(self, platform, *args, **kwargs):
"read": "encoder"})(encoder_cdc)
encoder_buffer = ClockDomainsRenamer("encoder")(EncoderBuffer())
encoder = Encoder(platform)
encoder_streamer = USBStreamer(platform, platform.request("fx2"))
self.submodules += encoder_cdc, encoder_buffer, encoder, encoder_streamer

xbar = ClockDomainsRenamer("fx2")(FX2Crossbar(platform))

self.submodules += encoder_cdc, encoder_buffer, encoder, xbar

self.comb += [
self.encoder_reader.source.connect(encoder_cdc.sink),
encoder_cdc.source.connect(encoder_buffer.sink),
encoder_buffer.source.connect(encoder.sink),
encoder.source.connect(encoder_streamer.sink)
encoder.source.connect(xbar.get_in_fifo(0, clock_domain=self.crg.cd_encoder))
]
self.add_wb_slave(mem_decoder(self.mem_map["encoder"]), encoder.bus)
self.add_memory_region("encoder",
self.mem_map["encoder"] + self.shadow_base, 0x2000)

self.platform.add_period_constraint(encoder_streamer.cd_usb.clk, 10.0)

encoder_streamer.cd_usb.clk.attr.add("keep")
self.crg.cd_encoder.clk.attr.add("keep")
self.platform.add_false_path_constraints(
self.crg.cd_sys.clk,
self.crg.cd_encoder.clk,
encoder_streamer.cd_usb.clk)

fx2_uart_sink = xbar.get_in_fifo(1, clock_domain=self.crg.cd_sys)
fx2_uart_source = xbar.get_out_fifo(0, clock_domain=self.crg.cd_sys)

self.submodules.uart_phy = uart_phy = FX2PHY(fx2_uart_sink, fx2_uart_source)
self.submodules.uart = uart = ResetInserter()(UART(uart_phy))

self.add_csr("uart", allow_user_defined=True)
self.add_interrupt("uart", allow_user_defined=True)

self.platform.add_false_path_constraints(self.crg.cd_sys.clk, self.crg.cd_fx2.clk)


SoC = HDMI2USBSoC