From 4c18c991bca5482a2650889db9c9a756666a1274 Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Fri, 5 Jul 2019 18:30:34 +0200 Subject: [PATCH] cores: add ICAP core (tested with reconfiguration commands) --- litex/soc/cores/icap.py | 76 +++++++++++++++++++++++++++++++++++++++++ test/test_icap.py | 27 +++++++++++++++ 2 files changed, 103 insertions(+) create mode 100644 litex/soc/cores/icap.py create mode 100644 test/test_icap.py diff --git a/litex/soc/cores/icap.py b/litex/soc/cores/icap.py new file mode 100644 index 000000000..d607e3566 --- /dev/null +++ b/litex/soc/cores/icap.py @@ -0,0 +1,76 @@ +# This file is Copyright (c) 2019 Florent Kermarrec +# License: BSD + +from migen import * + +from migen.genlib.misc import timeline +from migen.genlib.cdc import PulseSynchronizer + +from litex.soc.interconnect.csr import * + +# Xilinx 7-series ---------------------------------------------------------------------------------- + +class ICAP(Module, AutoCSR): + """ICAP + + Allow sending commands to ICAPE2 of Xilinx 7-Series FPGAs, the bistream can for example be + reloaded from SPI Flash by writing 0x00000000 at address @0x4. + """ + def __init__(self, simulation=False): + self.addr = CSRStorage(5) + self.data = CSRStorage(32) + self.send = CSR() + self.done = CSRStatus(reset=1) + + # # # + + # Create slow icap clk (sys_clk/2) --------------------------------------------------------- + self.clock_domains.cd_icap = ClockDomain() + icap_clk_counter = Signal(4) + self.sync += icap_clk_counter.eq(icap_clk_counter + 1) + self.sync += self.cd_icap.clk.eq(icap_clk_counter[3]) + + # Resychronize send pulse to icap domain --------------------------------------------------- + ps_send = PulseSynchronizer("sys", "icap") + self.submodules += ps_send + self.comb += [ps_send.i.eq(self.send.re)] + + # generate icap bitstream write sequence + _csib = Signal(reset=1) + _i = Signal(32) + _addr = self.addr.storage << 13 + _data = self.data.storage + self.sync.icap += [ + _i.eq(0xffffffff), # dummy + timeline(ps_send.o, [ + (1, [_csib.eq(1), self.done.status.eq(0)]), + (2, [_csib.eq(0), _i.eq(0x20000000)]), # noop + (3, [_csib.eq(0), _i.eq(0xaa995566)]), # sync word + (4, [_csib.eq(0), _i.eq(0x20000000)]), # noop + (5, [_csib.eq(0), _i.eq(0x20000000)]), # noop + (6, [_csib.eq(0), _i.eq(0x30000001 | _addr)]), # write command + (7, [_csib.eq(0), _i.eq(_data)]), # write value + (8, [_csib.eq(0), _i.eq(0x20000000)]), # noop + (9, [_csib.eq(0), _i.eq(0x20000000)]), # noop + (10, [_csib.eq(0), _i.eq(0x30008001)]), # write to cmd register + (11, [_csib.eq(0), _i.eq(0x0000000d)]), # desync command + (12, [_csib.eq(0), _i.eq(0x20000000)]), # noop + (13, [_csib.eq(0), _i.eq(0x20000000)]), # noop + (14, [_csib.eq(1), self.done.status.eq(1)]), + ]) + ] + + self._csib = _csib + self._i = _i + + # icap instance + if not simulation: + self.specials += [ + Instance("ICAPE2", + p_ICAP_WIDTH="X32", + i_CLK=ClockSignal("icap"), + i_CSIB=_csib, + i_RDWRB=0, + i_I=Cat(*[_i[8*i:8*(i+1)][::-1] for i in range(4)]), + ) + ] diff --git a/test/test_icap.py b/test/test_icap.py new file mode 100644 index 000000000..444451764 --- /dev/null +++ b/test/test_icap.py @@ -0,0 +1,27 @@ +# This file is Copyright (c) 2019 Florent Kermarrec +# License: BSD + +import unittest + +from migen import * + +from litex.soc.cores.icap import ICAP + + +class TestICAP(unittest.TestCase): + def test_reload(self): + def generator(dut): + yield dut.addr.storage.eq(0x4) + yield dut.data.storage.eq(0xf) + for i in range(16): + yield + yield dut.send.re.eq(1) + yield + yield dut.send.re.eq(0) + for i in range(256): + yield + + dut = ICAP(simulation=True) + clocks = {"sys": 10, + "icap":20} + run_simulation(dut, generator(dut), clocks, vcd_name="icap.vcd")