From: Sebastien Bourdeauducq Date: Wed, 23 Sep 2015 16:18:27 +0000 (+0800) Subject: reorganization WIP: flatten core structure (SDRAM still needs to be done) X-Git-Tag: 24jan2021_ls180~2106^2~49 X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=83509163df9fab091e9579483e62d6cf329253ce;p=litex.git reorganization WIP: flatten core structure (SDRAM still needs to be done) --- diff --git a/.gitmodules b/.gitmodules index 2f2413b1..0dc417a3 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,8 +1,8 @@ -[submodule "extcores/lm32/submodule"] - path = extcores/lm32/submodule +[submodule "misoc/lm32/verilog/submodule"] + path = misoc/lm32/verilog/submodule url = https://github.com/m-labs/lm32.git -[submodule "extcores/mor1kx/submodule"] - path = extcores/mor1kx/submodule +[submodule "misoc/mor1kx/verilog"] + path = misoc/mor1kx/verilog url = https://github.com/openrisc/mor1kx.git [submodule "software/compiler-rt"] path = software/compiler-rt diff --git a/extcores/__init__.py b/extcores/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/extcores/lm32/lm32_config.v b/extcores/lm32/lm32_config.v deleted file mode 100644 index 41c2daad..00000000 --- a/extcores/lm32/lm32_config.v +++ /dev/null @@ -1,199 +0,0 @@ -`ifdef LM32_CONFIG_V -`else -`define LM32_CONFIG_V - -// -// EXCEPTION VECTORS BASE ADDRESS -// - -// Base address for exception vectors -`define CFG_EBA_RESET 32'h00000000 - -// Base address for the debug exception vectors. If the DC_RE flag is -// set or the at_debug signal is asserted (see CFG_ALTERNATE_EBA) this -// will also be used for normal exception vectors. -`define CFG_DEBA_RESET 32'h10000000 - -// Enable exception vector remapping by external signal -//`define CFG_ALTERNATE_EBA - - -// -// ALU OPTIONS -// - -// Enable sign-extension instructions -`define CFG_SIGN_EXTEND_ENABLED - -// Shifter -// You may either enable the piplined or the multi-cycle barrel -// shifter. The multi-cycle shifter will stall the pipeline until -// the result is available after 32 cycles. -// If both options are disabled, only "right shift by one bit" is -// available. -//`define CFG_MC_BARREL_SHIFT_ENABLED -`define CFG_PL_BARREL_SHIFT_ENABLED - -// Multiplier -// The multiplier is available either in a multi-cycle version or -// in a pipelined one. The multi-cycle multiplier stalls the pipe -// for 32 cycles. If both options are disabled, multiply operations -// are not supported. -//`define CFG_MC_MULTIPLY_ENABLED -`define CFG_PL_MULTIPLY_ENABLED - -// Enable the multi-cycle divider. Stalls the pipe until the result -// is ready after 32 cycles. If disabled, the divide operation is not -// supported. -`define CFG_MC_DIVIDE_ENABLED - - -// -// INTERRUPTS -// - -// Enable support for 32 hardware interrupts -`define CFG_INTERRUPTS_ENABLED - -// Enable level-sensitive interrupts. The interrupt line status is -// reflected in the IP register, which is then read-only. -`define CFG_LEVEL_SENSITIVE_INTERRUPTS - - -// -// USER INSTRUCTION -// - -// Enable support for the user opcode. -//`define CFG_USER_ENABLED - - -// -// MEMORY MANAGEMENT UNIT -// - -// Enable instruction and data translation lookaside buffers and -// restricted user mode. -//`define CFG_MMU_ENABLED - - -// -// CACHE -// - -// Instruction cache -`define CFG_ICACHE_ENABLED -`define CFG_ICACHE_ASSOCIATIVITY 1 -`define CFG_ICACHE_SETS 256 -`define CFG_ICACHE_BYTES_PER_LINE 16 -`define CFG_ICACHE_BASE_ADDRESS 32'h00000000 -`define CFG_ICACHE_LIMIT 32'h7fffffff - -// Data cache -`define CFG_DCACHE_ENABLED -`define CFG_DCACHE_ASSOCIATIVITY 1 -`define CFG_DCACHE_SETS 256 -`define CFG_DCACHE_BYTES_PER_LINE 16 -`define CFG_DCACHE_BASE_ADDRESS 32'h00000000 -`define CFG_DCACHE_LIMIT 32'h7fffffff - - -// -// DEBUG OPTION -// - -// Globally enable debugging -//`define CFG_DEBUG_ENABLED - -// Enable the hardware JTAG debugging interface. -// Note: to use this, there must be a special JTAG module for your -// device. At the moment, there is only support for the -// Spartan-6. -//`define CFG_JTAG_ENABLED - -// JTAG UART is a communication channel which uses JTAG to transmit -// and receive bytes to and from the host computer. -//`define CFG_JTAG_UART_ENABLED - -// Enable reading and writing to the memory and writing CSRs using -// the JTAG interface. -//`define CFG_HW_DEBUG_ENABLED - -// Number of hardware watchpoints, max. 4 -//`define CFG_WATCHPOINTS 32'h4 - -// Enable hardware breakpoints -//`define CFG_ROM_DEBUG_ENABLED - -// Number of hardware breakpoints, max. 4 -//`define CFG_BREAKPOINTS 32'h4 - -// Put the processor into debug mode by an external signal. That is, -// raise a breakpoint exception. This is useful if you have a debug -// monitor and a serial line and you want to trap into the monitor on a -// BREAK symbol on the serial line. -//`define CFG_EXTERNAL_BREAK_ENABLED - - -// -// REGISTER FILE -// - -// The following option explicitly infers block RAM for the register -// file. There is extra logic to avoid parallel writes and reads. -// Normally, if your synthesizer is smart enough, this should not be -// necessary because it will automatically infer block RAM for you. -//`define CFG_EBR_POSEDGE_REGISTER_FILE - -// Explicitly infers block RAM, too. But it uses two different clocks, -// one being shifted by 180deg, for the read and write port. Therefore, -// no additional logic to avoid the parallel write/reads. -//`define CFG_EBR_NEGEDGE_REGISTER_FILE - - -// -// MISCELLANEOUS -// - -// Exceptions on wishbone bus errors -//`define CFG_BUS_ERRORS_ENABLED - -// Enable the cycle counter -`define CFG_CYCLE_COUNTER_ENABLED - -// Embedded instruction ROM using on-chip block RAM -//`define CFG_IROM_ENABLED -//`define CFG_IROM_INIT_FILE "NONE" -//`define CFG_IROM_BASE_ADDRESS 32'h10000000 -//`define CFG_IROM_LIMIT 32'h10000fff - -// Embedded data RAM using on-chip block RAM -//`define CFG_DRAM_ENABLED -//`define CFG_DRAM_INIT_FILE "NONE" -//`define CFG_DRAM_BASE_ADDRESS 32'h20000000 -//`define CFG_DRAM_LIMIT 32'h20000fff - -// Trace unit -//`define CFG_TRACE_ENABLED - -// Resolve unconditional branches already in the X stage (UNTESTED!) -//`define CFG_FAST_UNCONDITIONAL_BRANCH - -// log2 function -// If your simulator/synthesizer does not support the $clog2 system -// function you can use a constant function instead. - -function integer clog2; - input integer value; - begin - value = value - 1; - for (clog2 = 0; value > 0; clog2 = clog2 + 1) - value = value >> 1; - end -endfunction - -`define CLOG2 clog2 - -//`define CLOG2 $clog2 - -`endif diff --git a/extcores/lm32/submodule b/extcores/lm32/submodule deleted file mode 160000 index 84b3e3ca..00000000 --- a/extcores/lm32/submodule +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 84b3e3ca0ad9535acaef201c1482342871358b08 diff --git a/extcores/mor1kx/submodule b/extcores/mor1kx/submodule deleted file mode 160000 index 95fc8e43..00000000 --- a/extcores/mor1kx/submodule +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 95fc8e432d762e48b42991663cf9d0cdb918e27e diff --git a/misoc/com/__init__.py b/misoc/com/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/misoc/com/gpio.py b/misoc/com/gpio.py deleted file mode 100644 index 6db225fb..00000000 --- a/misoc/com/gpio.py +++ /dev/null @@ -1,31 +0,0 @@ -from migen import * -from migen.genlib.cdc import MultiReg -from migen.bank.description import * - - -class GPIOIn(Module, AutoCSR): - def __init__(self, signal): - self._in = CSRStatus(flen(signal)) - self.specials += MultiReg(signal, self._in.status) - - -class GPIOOut(Module, AutoCSR): - def __init__(self, signal): - self._out = CSRStorage(flen(signal)) - self.comb += signal.eq(self._out.storage) - - -class GPIOInOut(Module): - def __init__(self, in_signal, out_signal): - self.submodules.gpio_in = GPIOIn(in_signal) - self.submodules.gpio_out = GPIOOut(out_signal) - - def get_csrs(self): - return self.gpio_in.get_csrs() + self.gpio_out.get_csrs() - - -class Blinker(Module): - def __init__(self, signal, divbits=26): - counter = Signal(divbits) - self.comb += signal.eq(counter[divbits-1]) - self.sync += counter.eq(counter + 1) diff --git a/misoc/com/liteethmini/LICENSE b/misoc/com/liteethmini/LICENSE deleted file mode 100644 index cbbfe8be..00000000 --- a/misoc/com/liteethmini/LICENSE +++ /dev/null @@ -1,28 +0,0 @@ -Unless otherwise noted, LiteEth is copyright (C) 2015 Florent Kermarrec. - -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. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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. - - -Other authors retain ownership of their contributions. If a submission can -reasonably be considered independently copyrightable, it's yours and we -encourage you to claim it with appropriate copyright notices. This submission -then falls under the "otherwise noted" category. All submissions are strongly -encouraged to use the two-clause BSD license reproduced above. diff --git a/misoc/com/liteethmini/README b/misoc/com/liteethmini/README deleted file mode 100644 index 595d2fc9..00000000 --- a/misoc/com/liteethmini/README +++ /dev/null @@ -1,58 +0,0 @@ - __ _ __ ______ __ __ ____ _ - / / (_) /____ / __/ /_/ / / |/ (_)__ (_) - / /__/ / __/ -_) _// __/ _ \/ /|_/ / / _ \/ / - /____/_/\__/\__/___/\__/_//_/_/ /_/_/_//_/_/ - - Copyright 2012-2015 / EnjoyDigital / M-Labs Ltd - - A small footprint and configurable minimal Ethernet core - powered by Migen - -[> Intro ---------- -LiteEthMini is a subset of LiteEth (https://github.com/enjoy-digital/liteeth) -intended to be used with a CPU and a software stack. - -[> Features ------------ -- Ethernet MAC with various various PHYs (GMII, MII, RGMII, Loopback) -- SRAM storage and wishbone interface - -[> Possible improvements -------------------------- -- add DMA interface to MAC -- add SGMII PHY -- ... See below Support and consulting :) - -If you want to support these features, please contact us at florent [AT] -enjoy-digital.fr. You can also contact our partner on the public mailing list -devel [AT] lists.m-labs.hk. - -[> License ------------ -LiteEthMini is released under the very permissive two-clause BSD license. Under -the terms of this license, you are authorized to use LiteEthMini for closed-source -proprietary designs. -Even though we do not require you to do so, those things are awesome, so please -do them if possible: - - tell us that you are using LiteEthMini - - cite LiteEthMini in publications related to research it has helped - - send us feedback and suggestions for improvements - - send us bug reports when something goes wrong - - send us the modifications and improvements you have done to LiteEthMini. - -[> Support and consulting --------------------------- -We love open-source hardware and like sharing our designs with others. - -LiteEthMini is mainly developed and maintained by EnjoyDigital. - -If you would like to know more about LiteEthMini or if you are already a happy -user and would like to extend it for your needs, EnjoyDigital can provide standard -commercial support as well as consulting services. - -So feel free to contact us, we'd love to work with you! (and eventually shorten -the list of the possible improvements :) - -[> Contact -E-mail: florent [AT] enjoy-digital.fr \ No newline at end of file diff --git a/misoc/com/liteethmini/__init__.py b/misoc/com/liteethmini/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/misoc/com/liteethmini/common.py b/misoc/com/liteethmini/common.py deleted file mode 100644 index 21d0da08..00000000 --- a/misoc/com/liteethmini/common.py +++ /dev/null @@ -1,44 +0,0 @@ -import math -from collections import OrderedDict - -from migen import * -from migen.genlib.resetsync import AsyncResetSynchronizer -from migen.genlib.record import * -from migen.genlib.fsm import FSM, NextState -from migen.genlib.misc import chooser, reverse_bytes, FlipFlop, Counter, WaitTimer -from migen.flow.actor import * -from migen.actorlib.structuring import Converter, Pipeline -from migen.actorlib.fifo import SyncFIFO, AsyncFIFO -from migen.actorlib.packet import * -from migen.bank.description import * - -class Port: - def connect(self, port): - r = [ - Record.connect(self.source, port.sink), - Record.connect(port.source, self.sink) - ] - return r - -eth_mtu = 1532 -eth_min_len = 46 -eth_interpacket_gap = 12 -eth_preamble = 0xD555555555555555 -buffer_depth = 2**log2_int(eth_mtu, need_pow2=False) - -def eth_phy_description(dw): - payload_layout = [ - ("data", dw), - ("last_be", dw//8), - ("error", dw//8) - ] - return EndpointDescription(payload_layout, packetized=True) - - -def eth_mac_description(dw): - payload_layout = mac_header.get_layout() + [ - ("data", dw), - ("last_be", dw//8), - ("error", dw//8) - ] - return EndpointDescription(payload_layout, packetized=True) diff --git a/misoc/com/liteethmini/mac/__init__.py b/misoc/com/liteethmini/mac/__init__.py deleted file mode 100644 index 6844c420..00000000 --- a/misoc/com/liteethmini/mac/__init__.py +++ /dev/null @@ -1,22 +0,0 @@ -from misoc.com.liteethmini.common import * -from misoc.com.liteethmini.mac.core import LiteEthMACCore -from misoc.com.liteethmini.mac.frontend.wishbone import LiteEthMACWishboneInterface - - -class LiteEthMAC(Module, AutoCSR): - def __init__(self, phy, dw, - interface="wishbone", - endianness="big", - with_preamble_crc=True): - self.submodules.core = LiteEthMACCore(phy, dw, endianness, with_preamble_crc) - self.csrs = [] - if interface == "wishbone": - self.submodules.interface = LiteEthMACWishboneInterface(dw, 2, 2) - self.comb += Port.connect(self.interface, self.core) - self.ev, self.bus = self.interface.sram.ev, self.interface.bus - self.csrs = self.interface.get_csrs() + self.core.get_csrs() - else: - raise NotImplementedError - - def get_csrs(self): - return self.csrs diff --git a/misoc/com/liteethmini/mac/core/__init__.py b/misoc/com/liteethmini/mac/core/__init__.py deleted file mode 100644 index 2551f5eb..00000000 --- a/misoc/com/liteethmini/mac/core/__init__.py +++ /dev/null @@ -1,100 +0,0 @@ -from misoc.com.liteethmini.common import * -from misoc.com.liteethmini.mac.core import gap, preamble, crc, padding, last_be -from misoc.com.liteethmini.phy.sim import LiteEthPHYSim -from misoc.com.liteethmini.phy.mii import LiteEthPHYMII - - -class LiteEthMACCore(Module, AutoCSR): - def __init__(self, phy, dw, endianness="big", - with_preamble_crc=True, - with_padding=True): - if dw < phy.dw: - raise ValueError("Core data width({}) must be larger than PHY data width({})".format(dw, phy.dw)) - - rx_pipeline = [phy] - tx_pipeline = [phy] - - # Interpacket gap - tx_gap_inserter = gap.LiteEthMACGap(phy.dw) - rx_gap_checker = gap.LiteEthMACGap(phy.dw, ack_on_gap=True) - self.submodules += RenameClockDomains(tx_gap_inserter, "eth_tx") - self.submodules += RenameClockDomains(rx_gap_checker, "eth_rx") - - tx_pipeline += [tx_gap_inserter] - rx_pipeline += [rx_gap_checker] - - # Preamble / CRC - if isinstance(phy, LiteEthPHYSim): - # In simulation, avoid CRC/Preamble to enable direct connection - # to the Ethernet tap. - self._preamble_crc = CSRStatus(reset=1) - elif with_preamble_crc: - self._preamble_crc = CSRStatus(reset=1) - # Preamble insert/check - preamble_inserter = preamble.LiteEthMACPreambleInserter(phy.dw) - preamble_checker = preamble.LiteEthMACPreambleChecker(phy.dw) - self.submodules += RenameClockDomains(preamble_inserter, "eth_tx") - self.submodules += RenameClockDomains(preamble_checker, "eth_rx") - - # CRC insert/check - crc32_inserter = crc.LiteEthMACCRC32Inserter(eth_phy_description(phy.dw)) - crc32_checker = crc.LiteEthMACCRC32Checker(eth_phy_description(phy.dw)) - self.submodules += RenameClockDomains(crc32_inserter, "eth_tx") - self.submodules += RenameClockDomains(crc32_checker, "eth_rx") - - tx_pipeline += [preamble_inserter, crc32_inserter] - rx_pipeline += [preamble_checker, crc32_checker] - - # Padding - if with_padding: - padding_inserter = padding.LiteEthMACPaddingInserter(phy.dw, 60) - padding_checker = padding.LiteEthMACPaddingChecker(phy.dw, 60) - self.submodules += RenameClockDomains(padding_inserter, "eth_tx") - self.submodules += RenameClockDomains(padding_checker, "eth_rx") - - tx_pipeline += [padding_inserter] - rx_pipeline += [padding_checker] - - # Delimiters - if dw != 8: - tx_last_be = last_be.LiteEthMACTXLastBE(phy.dw) - rx_last_be = last_be.LiteEthMACRXLastBE(phy.dw) - self.submodules += RenameClockDomains(tx_last_be, "eth_tx") - self.submodules += RenameClockDomains(rx_last_be, "eth_rx") - - tx_pipeline += [tx_last_be] - rx_pipeline += [rx_last_be] - - # Converters - if dw != phy.dw: - reverse = endianness == "big" - tx_converter = Converter(eth_phy_description(dw), - eth_phy_description(phy.dw), - reverse=reverse) - rx_converter = Converter(eth_phy_description(phy.dw), - eth_phy_description(dw), - reverse=reverse) - self.submodules += RenameClockDomains(tx_converter, "eth_tx") - self.submodules += RenameClockDomains(rx_converter, "eth_rx") - - tx_pipeline += [tx_converter] - rx_pipeline += [rx_converter] - - # Cross Domain Crossing - if isinstance(phy, LiteEthPHYMII): - fifo_depth = 8 - else: - fifo_depth = 64 - tx_cdc = AsyncFIFO(eth_phy_description(dw), fifo_depth) - rx_cdc = AsyncFIFO(eth_phy_description(dw), fifo_depth) - self.submodules += RenameClockDomains(tx_cdc, {"write": "sys", "read": "eth_tx"}) - self.submodules += RenameClockDomains(rx_cdc, {"write": "eth_rx", "read": "sys"}) - - tx_pipeline += [tx_cdc] - rx_pipeline += [rx_cdc] - - # Graph - self.submodules.tx_pipeline = Pipeline(*reversed(tx_pipeline)) - self.submodules.rx_pipeline = Pipeline(*rx_pipeline) - - self.sink, self.source = self.tx_pipeline.sink, self.rx_pipeline.source diff --git a/misoc/com/liteethmini/mac/core/crc.py b/misoc/com/liteethmini/mac/core/crc.py deleted file mode 100644 index 01b88f7e..00000000 --- a/misoc/com/liteethmini/mac/core/crc.py +++ /dev/null @@ -1,281 +0,0 @@ -from misoc.com.liteethmini.common import * - - -class LiteEthMACCRCEngine(Module): - """Cyclic Redundancy Check Engine - - Compute next CRC value from last CRC value and data input using - an optimized asynchronous LFSR. - - Parameters - ---------- - data_width : int - Width of the data bus. - width : int - Width of the CRC. - polynom : int - Polynom of the CRC (ex: 0x04C11DB7 for IEEE 802.3 CRC) - - Attributes - ---------- - data : in - Data input. - last : in - last CRC value. - next : - next CRC value. - """ - def __init__(self, data_width, width, polynom): - self.data = Signal(data_width) - self.last = Signal(width) - self.next = Signal(width) - - # # # - - def _optimize_eq(l): - """ - Replace even numbers of XORs in the equation - with an equivalent XOR - """ - d = OrderedDict() - for e in l: - if e in d: - d[e] += 1 - else: - d[e] = 1 - r = [] - for key, value in d.items(): - if value%2 != 0: - r.append(key) - return r - - # compute and optimize CRC's LFSR - curval = [[("state", i)] for i in range(width)] - for i in range(data_width): - feedback = curval.pop() + [("din", i)] - for j in range(width-1): - if (polynom & (1<<(j+1))): - curval[j] += feedback - curval[j] = _optimize_eq(curval[j]) - curval.insert(0, feedback) - - # implement logic - for i in range(width): - xors = [] - for t, n in curval[i]: - if t == "state": - xors += [self.last[n]] - elif t == "din": - xors += [self.data[n]] - self.comb += self.next[i].eq(optree("^", xors)) - - -@DecorateModule(InsertReset) -@DecorateModule(InsertCE) -class LiteEthMACCRC32(Module): - """IEEE 802.3 CRC - - Implement an IEEE 802.3 CRC generator/checker. - - Parameters - ---------- - data_width : int - Width of the data bus. - - Attributes - ---------- - d : in - Data input. - value : out - CRC value (used for generator). - error : out - CRC error (used for checker). - """ - width = 32 - polynom = 0x04C11DB7 - init = 2**width-1 - check = 0xC704DD7B - def __init__(self, data_width): - self.data = Signal(data_width) - self.value = Signal(self.width) - self.error = Signal() - - # # # - - self.submodules.engine = LiteEthMACCRCEngine(data_width, self.width, self.polynom) - reg = Signal(self.width, reset=self.init) - self.sync += reg.eq(self.engine.next) - self.comb += [ - self.engine.data.eq(self.data), - self.engine.last.eq(reg), - - self.value.eq(~reg[::-1]), - self.error.eq(self.engine.next != self.check) - ] - - -class LiteEthMACCRCInserter(Module): - """CRC Inserter - - Append a CRC at the end of each packet. - - Parameters - ---------- - description : description - description of the dataflow. - - Attributes - ---------- - sink : in - Packets input without CRC. - source : out - Packets output with CRC. - """ - def __init__(self, crc_class, description): - self.sink = sink = Sink(description) - self.source = source = Source(description) - self.busy = Signal() - - # # # - - dw = flen(sink.data) - crc = crc_class(dw) - fsm = FSM(reset_state="IDLE") - self.submodules += crc, fsm - - fsm.act("IDLE", - crc.reset.eq(1), - sink.ack.eq(1), - If(sink.stb & sink.sop, - sink.ack.eq(0), - NextState("COPY"), - ) - ) - fsm.act("COPY", - crc.ce.eq(sink.stb & source.ack), - crc.data.eq(sink.data), - Record.connect(sink, source), - source.eop.eq(0), - If(sink.stb & sink.eop & source.ack, - NextState("INSERT"), - ) - ) - ratio = crc.width//dw - if ratio > 1: - cnt = Signal(max=ratio, reset=ratio-1) - cnt_done = Signal() - fsm.act("INSERT", - source.stb.eq(1), - chooser(crc.value, cnt, source.data, reverse=True), - If(cnt_done, - source.eop.eq(1), - If(source.ack, NextState("IDLE")) - ) - ) - self.comb += cnt_done.eq(cnt == 0) - self.sync += \ - If(fsm.ongoing("IDLE"), - cnt.eq(cnt.reset) - ).Elif(fsm.ongoing("INSERT") & ~cnt_done, - cnt.eq(cnt - source.ack) - ) - else: - fsm.act("INSERT", - source.stb.eq(1), - source.eop.eq(1), - source.data.eq(crc.value), - If(source.ack, NextState("IDLE")) - ) - self.comb += self.busy.eq(~fsm.ongoing("IDLE")) - - -class LiteEthMACCRC32Inserter(LiteEthMACCRCInserter): - def __init__(self, description): - LiteEthMACCRCInserter.__init__(self, LiteEthMACCRC32, description) - - -class LiteEthMACCRCChecker(Module): - """CRC Checker - - Check CRC at the end of each packet. - - Parameters - ---------- - description : description - description of the dataflow. - - Attributes - ---------- - sink : in - Packets input with CRC. - source : out - Packets output without CRC and "error" set to 0 - on eop when CRC OK / set to 1 when CRC KO. - """ - def __init__(self, crc_class, description): - self.sink = sink = Sink(description) - self.source = source = Source(description) - self.busy = Signal() - - # # # - - dw = flen(sink.data) - crc = crc_class(dw) - self.submodules += crc - ratio = crc.width//dw - - error = Signal() - fifo = InsertReset(SyncFIFO(description, ratio + 1)) - self.submodules += fifo - - fsm = FSM(reset_state="RESET") - self.submodules += fsm - - fifo_in = Signal() - fifo_out = Signal() - fifo_full = Signal() - - self.comb += [ - fifo_full.eq(fifo.fifo.level == ratio), - fifo_in.eq(sink.stb & (~fifo_full | fifo_out)), - fifo_out.eq(source.stb & source.ack), - - Record.connect(sink, fifo.sink), - fifo.sink.stb.eq(fifo_in), - self.sink.ack.eq(fifo_in), - - source.stb.eq(sink.stb & fifo_full), - source.sop.eq(fifo.source.sop), - source.eop.eq(sink.eop), - fifo.source.ack.eq(fifo_out), - source.payload.eq(fifo.source.payload), - - source.error.eq(sink.error | crc.error), - ] - - fsm.act("RESET", - crc.reset.eq(1), - fifo.reset.eq(1), - NextState("IDLE"), - ) - self.comb += crc.data.eq(sink.data) - fsm.act("IDLE", - If(sink.stb & sink.sop & sink.ack, - crc.ce.eq(1), - NextState("COPY") - ) - ) - fsm.act("COPY", - If(sink.stb & sink.ack, - crc.ce.eq(1), - If(sink.eop, - NextState("RESET") - ) - ) - ) - self.comb += self.busy.eq(~fsm.ongoing("IDLE")) - - -class LiteEthMACCRC32Checker(LiteEthMACCRCChecker): - def __init__(self, description): - LiteEthMACCRCChecker.__init__(self, LiteEthMACCRC32, description) diff --git a/misoc/com/liteethmini/mac/core/gap.py b/misoc/com/liteethmini/mac/core/gap.py deleted file mode 100644 index ce4ad1ba..00000000 --- a/misoc/com/liteethmini/mac/core/gap.py +++ /dev/null @@ -1,27 +0,0 @@ -from misoc.com.liteethmini.common import * - -class LiteEthMACGap(Module): - def __init__(self, dw, ack_on_gap=False): - self.sink = sink = Sink(eth_phy_description(dw)) - self.source = source = Source(eth_phy_description(dw)) - - # # # - - gap = math.ceil(eth_interpacket_gap/(dw//8)) - self.submodules.counter = counter = Counter(max=gap) - - self.submodules.fsm = fsm = FSM(reset_state="COPY") - fsm.act("COPY", - counter.reset.eq(1), - Record.connect(sink, source), - If(sink.stb & sink.eop & sink.ack, - NextState("GAP") - ) - ) - fsm.act("GAP", - counter.ce.eq(1), - sink.ack.eq(int(ack_on_gap)), - If(counter.value == (gap-1), - NextState("COPY") - ) - ) diff --git a/misoc/com/liteethmini/mac/core/last_be.py b/misoc/com/liteethmini/mac/core/last_be.py deleted file mode 100644 index 14d8a308..00000000 --- a/misoc/com/liteethmini/mac/core/last_be.py +++ /dev/null @@ -1,43 +0,0 @@ -from misoc.com.liteethmini.common import * - - -class LiteEthMACTXLastBE(Module): - def __init__(self, dw): - self.sink = sink = Sink(eth_phy_description(dw)) - self.source = source = Source(eth_phy_description(dw)) - - # # # - - ongoing = Signal() - self.sync += \ - If(sink.stb & sink.ack, - If(sink.sop, - ongoing.eq(1) - ).Elif(sink.last_be, - ongoing.eq(0) - ) - ) - self.comb += [ - source.stb.eq(sink.stb & (sink.sop | ongoing)), - source.sop.eq(sink.sop), - source.eop.eq(sink.last_be), - source.data.eq(sink.data), - sink.ack.eq(source.ack) - ] - - -class LiteEthMACRXLastBE(Module): - def __init__(self, dw): - self.sink = sink = Sink(eth_phy_description(dw)) - self.source = source = Source(eth_phy_description(dw)) - - # # # - - self.comb += [ - source.stb.eq(sink.stb), - source.sop.eq(sink.sop), - source.eop.eq(sink.eop), - source.data.eq(sink.data), - source.last_be.eq(sink.eop), - sink.ack.eq(source.ack) - ] diff --git a/misoc/com/liteethmini/mac/core/padding.py b/misoc/com/liteethmini/mac/core/padding.py deleted file mode 100644 index fa4aa927..00000000 --- a/misoc/com/liteethmini/mac/core/padding.py +++ /dev/null @@ -1,56 +0,0 @@ -from misoc.com.liteethmini.common import * - - -class LiteEthMACPaddingInserter(Module): - def __init__(self, dw, padding): - self.sink = sink = Sink(eth_phy_description(dw)) - self.source = source = Source(eth_phy_description(dw)) - - # # # - - padding_limit = math.ceil(padding/(dw/8))-1 - - self.submodules.counter = counter = Counter(16, reset=1) - counter_done = Signal() - self.comb += [ - counter.reset.eq(sink.stb & sink.sop & sink.ack), - counter.ce.eq(source.stb & source.ack), - counter_done.eq(counter.value >= padding_limit), - ] - - self.submodules.fsm = fsm = FSM(reset_state="IDLE") - fsm.act("IDLE", - Record.connect(sink, source), - If(source.stb & source.ack, - counter.ce.eq(1), - If(sink.eop, - If(~counter_done, - source.eop.eq(0), - NextState("PADDING") - ) - ) - ) - ) - fsm.act("PADDING", - source.stb.eq(1), - source.eop.eq(counter_done), - source.data.eq(0), - If(source.ack, - If(counter_done, - NextState("IDLE") - ) - ) - ) - - -class LiteEthMACPaddingChecker(Module): - def __init__(self, dw, packet_min_length): - self.sink = sink = Sink(eth_phy_description(dw)) - self.source = source = Source(eth_phy_description(dw)) - - # # # - - # XXX see if we should drop the packet when - # payload size < minimum ethernet payload size - self.comb += Record.connect(sink, source) - diff --git a/misoc/com/liteethmini/mac/core/preamble.py b/misoc/com/liteethmini/mac/core/preamble.py deleted file mode 100644 index 52078f90..00000000 --- a/misoc/com/liteethmini/mac/core/preamble.py +++ /dev/null @@ -1,150 +0,0 @@ -from misoc.com.liteethmini.common import * - - -class LiteEthMACPreambleInserter(Module): - def __init__(self, dw): - self.sink = Sink(eth_phy_description(dw)) - self.source = Source(eth_phy_description(dw)) - - # # # - - preamble = Signal(64, reset=eth_preamble) - cnt_max = (64//dw)-1 - cnt = Signal(max=cnt_max+1) - clr_cnt = Signal() - inc_cnt = Signal() - - self.sync += \ - If(clr_cnt, - cnt.eq(0) - ).Elif(inc_cnt, - cnt.eq(cnt+1) - ) - - fsm = FSM(reset_state="IDLE") - self.submodules += fsm - fsm.act("IDLE", - self.sink.ack.eq(1), - clr_cnt.eq(1), - If(self.sink.stb & self.sink.sop, - self.sink.ack.eq(0), - NextState("INSERT"), - ) - ) - fsm.act("INSERT", - self.source.stb.eq(1), - self.source.sop.eq(cnt == 0), - chooser(preamble, cnt, self.source.data), - If(cnt == cnt_max, - If(self.source.ack, NextState("COPY")) - ).Else( - inc_cnt.eq(self.source.ack) - ) - ) - - self.comb += [ - self.source.data.eq(self.sink.data), - self.source.last_be.eq(self.sink.last_be) - ] - fsm.act("COPY", - Record.connect(self.sink, self.source, leave_out=set(["data", "last_be"])), - self.source.sop.eq(0), - - If(self.sink.stb & self.sink.eop & self.source.ack, - NextState("IDLE"), - ) - ) - - -class LiteEthMACPreambleChecker(Module): - def __init__(self, dw): - self.sink = Sink(eth_phy_description(dw)) - self.source = Source(eth_phy_description(dw)) - - # # # - - preamble = Signal(64, reset=eth_preamble) - cnt_max = (64//dw) - 1 - cnt = Signal(max=cnt_max+1) - clr_cnt = Signal() - inc_cnt = Signal() - - self.sync += \ - If(clr_cnt, - cnt.eq(0) - ).Elif(inc_cnt, - cnt.eq(cnt+1) - ) - - discard = Signal() - clr_discard = Signal() - set_discard = Signal() - - self.sync += \ - If(clr_discard, - discard.eq(0) - ).Elif(set_discard, - discard.eq(1) - ) - - sop = Signal() - clr_sop = Signal() - set_sop = Signal() - self.sync += \ - If(clr_sop, - sop.eq(0) - ).Elif(set_sop, - sop.eq(1) - ) - - ref = Signal(dw) - match = Signal() - self.comb += [ - chooser(preamble, cnt, ref), - match.eq(self.sink.data == ref) - ] - - fsm = FSM(reset_state="IDLE") - self.submodules += fsm - - fsm.act("IDLE", - self.sink.ack.eq(1), - clr_cnt.eq(1), - clr_discard.eq(1), - If(self.sink.stb & self.sink.sop, - clr_cnt.eq(0), - inc_cnt.eq(1), - clr_discard.eq(0), - set_discard.eq(~match), - NextState("CHECK"), - ) - ) - fsm.act("CHECK", - self.sink.ack.eq(1), - If(self.sink.stb, - set_discard.eq(~match), - If(cnt == cnt_max, - If(discard | (~match), - NextState("IDLE") - ).Else( - set_sop.eq(1), - NextState("COPY") - ) - ).Else( - inc_cnt.eq(1) - ) - ) - ) - self.comb += [ - self.source.data.eq(self.sink.data), - self.source.last_be.eq(self.sink.last_be) - ] - fsm.act("COPY", - Record.connect(self.sink, self.source, leave_out=set(["data", "last_be"])), - self.source.sop.eq(sop), - clr_sop.eq(self.source.stb & self.source.ack), - - If(self.source.stb & self.source.eop & self.source.ack, - NextState("IDLE"), - ) - ) diff --git a/misoc/com/liteethmini/mac/frontend/__init__.py b/misoc/com/liteethmini/mac/frontend/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/misoc/com/liteethmini/mac/frontend/sram.py b/misoc/com/liteethmini/mac/frontend/sram.py deleted file mode 100644 index 1495e08e..00000000 --- a/misoc/com/liteethmini/mac/frontend/sram.py +++ /dev/null @@ -1,234 +0,0 @@ -from misoc.com.liteethmini.common import * - -from migen.bank.description import * -from migen.bank.eventmanager import * - - -class LiteEthMACSRAMWriter(Module, AutoCSR): - def __init__(self, dw, depth, nslots=2): - self.sink = sink = Sink(eth_phy_description(dw)) - self.crc_error = Signal() - - slotbits = max(log2_int(nslots), 1) - lengthbits = log2_int(depth*4) # length in bytes - - self._slot = CSRStatus(slotbits) - self._length = CSRStatus(lengthbits) - - self.submodules.ev = EventManager() - self.ev.available = EventSourceLevel() - self.ev.finalize() - - # # # - - # packet dropped if no slot available - sink.ack.reset = 1 - - # length computation - increment = Signal(3) - self.comb += \ - If(sink.last_be[3], - increment.eq(1) - ).Elif(sink.last_be[2], - increment.eq(2) - ).Elif(sink.last_be[1], - increment.eq(3) - ).Else( - increment.eq(4) - ) - counter = Counter(lengthbits, increment=increment) - self.submodules += counter - - # slot computation - slot = Counter(slotbits) - self.submodules += slot - - ongoing = Signal() - - # status fifo - fifo = SyncFIFO([("slot", slotbits), ("length", lengthbits)], nslots) - self.submodules += fifo - - # fsm - fsm = FSM(reset_state="IDLE") - self.submodules += fsm - - fsm.act("IDLE", - If(sink.stb & sink.sop, - If(fifo.sink.ack, - ongoing.eq(1), - counter.ce.eq(1), - NextState("WRITE") - ) - ) - ) - fsm.act("WRITE", - counter.ce.eq(sink.stb), - ongoing.eq(1), - If(sink.stb & sink.eop, - If((sink.error & sink.last_be) != 0, - NextState("DISCARD") - ).Else( - NextState("TERMINATE") - ) - ) - ) - fsm.act("DISCARD", - counter.reset.eq(1), - NextState("IDLE") - ) - self.comb += [ - fifo.sink.slot.eq(slot.value), - fifo.sink.length.eq(counter.value) - ] - fsm.act("TERMINATE", - counter.reset.eq(1), - slot.ce.eq(1), - fifo.sink.stb.eq(1), - NextState("IDLE") - ) - self.comb += [ - fifo.source.ack.eq(self.ev.available.clear), - self.ev.available.trigger.eq(fifo.source.stb), - self._slot.status.eq(fifo.source.slot), - self._length.status.eq(fifo.source.length), - ] - - # memory - mems = [None]*nslots - ports = [None]*nslots - for n in range(nslots): - mems[n] = Memory(dw, depth) - ports[n] = mems[n].get_port(write_capable=True) - self.specials += ports[n] - self.mems = mems - - cases = {} - for n, port in enumerate(ports): - cases[n] = [ - ports[n].adr.eq(counter.value[2:]), - ports[n].dat_w.eq(sink.data), - If(sink.stb & ongoing, - ports[n].we.eq(0xf) - ) - ] - self.comb += Case(slot.value, cases) - - -class LiteEthMACSRAMReader(Module, AutoCSR): - def __init__(self, dw, depth, nslots=2): - self.source = source = Source(eth_phy_description(dw)) - - slotbits = max(log2_int(nslots), 1) - lengthbits = log2_int(depth*4) # length in bytes - self.lengthbits = lengthbits - - self._start = CSR() - self._ready = CSRStatus() - self._slot = CSRStorage(slotbits) - self._length = CSRStorage(lengthbits) - - self.submodules.ev = EventManager() - self.ev.done = EventSourcePulse() - self.ev.finalize() - - # # # - - # command fifo - fifo = SyncFIFO([("slot", slotbits), ("length", lengthbits)], nslots) - self.submodules += fifo - self.comb += [ - fifo.sink.stb.eq(self._start.re), - fifo.sink.slot.eq(self._slot.storage), - fifo.sink.length.eq(self._length.storage), - self._ready.status.eq(fifo.sink.ack) - ] - - # length computation - self.submodules.counter = counter = Counter(lengthbits, increment=4) - - # fsm - first = Signal() - last = Signal() - last_d = Signal() - - fsm = FSM(reset_state="IDLE") - self.submodules += fsm - - fsm.act("IDLE", - counter.reset.eq(1), - If(fifo.source.stb, - NextState("CHECK") - ) - ) - fsm.act("CHECK", - If(~last_d, - NextState("SEND"), - ).Else( - NextState("END"), - ) - ) - length_lsb = fifo.source.length[0:2] - self.comb += [ - If(last, - If(length_lsb == 3, - source.last_be.eq(0b0010) - ).Elif(length_lsb == 2, - source.last_be.eq(0b0100) - ).Elif(length_lsb == 1, - source.last_be.eq(0b1000) - ).Else( - source.last_be.eq(0b0001) - ) - ) - ] - fsm.act("SEND", - source.stb.eq(1), - source.sop.eq(first), - source.eop.eq(last), - If(source.ack, - counter.ce.eq(~last), - NextState("CHECK") - ) - ) - fsm.act("END", - fifo.source.ack.eq(1), - self.ev.done.trigger.eq(1), - NextState("IDLE") - ) - - # first/last computation - self.sync += [ - If(fsm.ongoing("IDLE"), - first.eq(1) - ).Elif(source.stb & source.ack, - first.eq(0) - ) - ] - self.comb += last.eq((counter.value + 4) >= fifo.source.length) - self.sync += last_d.eq(last) - - # memory - rd_slot = fifo.source.slot - - mems = [None]*nslots - ports = [None]*nslots - for n in range(nslots): - mems[n] = Memory(dw, depth) - ports[n] = mems[n].get_port() - self.specials += ports[n] - self.mems = mems - - cases = {} - for n, port in enumerate(ports): - self.comb += ports[n].adr.eq(counter.value[2:]) - cases[n] = [source.data.eq(port.dat_r)] - self.comb += Case(rd_slot, cases) - - -class LiteEthMACSRAM(Module, AutoCSR): - def __init__(self, dw, depth, nrxslots, ntxslots): - self.submodules.writer = LiteEthMACSRAMWriter(dw, depth, nrxslots) - self.submodules.reader = LiteEthMACSRAMReader(dw, depth, ntxslots) - self.submodules.ev = SharedIRQ(self.writer.ev, self.reader.ev) - self.sink, self.source = self.writer.sink, self.reader.source diff --git a/misoc/com/liteethmini/mac/frontend/wishbone.py b/misoc/com/liteethmini/mac/frontend/wishbone.py deleted file mode 100644 index e45f744e..00000000 --- a/misoc/com/liteethmini/mac/frontend/wishbone.py +++ /dev/null @@ -1,41 +0,0 @@ -from misoc.com.liteethmini.common import * -from misoc.com.liteethmini.mac.frontend import sram - -from migen.bus import wishbone -from migen.fhdl.simplify import FullMemoryWE - - -class LiteEthMACWishboneInterface(Module, AutoCSR): - def __init__(self, dw, nrxslots=2, ntxslots=2): - self.sink = Sink(eth_phy_description(dw)) - self.source = Source(eth_phy_description(dw)) - self.bus = wishbone.Interface() - - # # # - - # storage in SRAM - sram_depth = buffer_depth//(dw//8) - self.submodules.sram = sram.LiteEthMACSRAM(dw, sram_depth, nrxslots, ntxslots) - self.comb += [ - Record.connect(self.sink, self.sram.sink), - Record.connect(self.sram.source, self.source) - ] - - # Wishbone interface - wb_rx_sram_ifs = [wishbone.SRAM(self.sram.writer.mems[n], read_only=True) - for n in range(nrxslots)] - # TODO: FullMemoryWE should move to Mibuild - wb_tx_sram_ifs = [FullMemoryWE()(wishbone.SRAM(self.sram.reader.mems[n], read_only=False)) - for n in range(ntxslots)] - wb_sram_ifs = wb_rx_sram_ifs + wb_tx_sram_ifs - - wb_slaves = [] - decoderoffset = log2_int(sram_depth) - decoderbits = log2_int(len(wb_sram_ifs)) - for n, wb_sram_if in enumerate(wb_sram_ifs): - def slave_filter(a, v=n): - return a[decoderoffset:decoderoffset+decoderbits] == v - wb_slaves.append((slave_filter, wb_sram_if.bus)) - self.submodules += wb_sram_if - wb_con = wishbone.Decoder(self.bus, wb_slaves, register=True) - self.submodules += wb_con diff --git a/misoc/com/liteethmini/phy/__init__.py b/misoc/com/liteethmini/phy/__init__.py deleted file mode 100644 index c17124d1..00000000 --- a/misoc/com/liteethmini/phy/__init__.py +++ /dev/null @@ -1,27 +0,0 @@ -from misoc.com.liteethmini.common import * - - -def LiteEthPHY(clock_pads, pads, clk_freq=None, **kwargs): - # Autodetect PHY - if hasattr(pads, "source_stb"): - # This is a simulation PHY - from misoc.com.liteethmini.phy.sim import LiteEthPHYSim - return LiteEthPHYSim(pads) - elif hasattr(clock_pads, "gtx") and flen(pads.tx_data) == 8: - if hasattr(clock_pads, "tx"): - # This is a 10/100/1G PHY - from misoc.com.liteethmini.phy.gmii_mii import LiteEthPHYGMIIMII - return LiteEthPHYGMIIMII(clock_pads, pads, clk_freq=clk_freq, **kwargs) - else: - # This is a pure 1G PHY - from misoc.com.liteethmini.phy.gmii import LiteEthPHYGMII - return LiteEthPHYGMII(clock_pads, pads, **kwargs) - elif hasattr(pads, "rx_ctl"): - # This is a 10/100/1G RGMII PHY - raise ValueError("RGMII PHYs are specific to vendors (for now), use direct instantiation") - elif flen(pads.tx_data) == 4: - # This is a MII PHY - from misoc.com.liteethmini.phy.mii import LiteEthPHYMII - return LiteEthPHYMII(clock_pads, pads, **kwargs) - else: - raise ValueError("Unable to autodetect PHY from platform file, use direct instantiation") diff --git a/misoc/com/liteethmini/phy/gmii.py b/misoc/com/liteethmini/phy/gmii.py deleted file mode 100644 index 24f4dc8f..00000000 --- a/misoc/com/liteethmini/phy/gmii.py +++ /dev/null @@ -1,98 +0,0 @@ -from migen.genlib.io import DDROutput - -from misoc.com.liteethmini.common import * - - -class LiteEthPHYGMIITX(Module): - def __init__(self, pads, pads_register=True): - self.sink = sink = Sink(eth_phy_description(8)) - - # # # - - if hasattr(pads, "tx_er"): - self.sync += pads.tx_er.eq(0) - pads_eq = [ - pads.tx_en.eq(sink.stb), - pads.tx_data.eq(sink.data) - ] - if pads_register: - self.sync += pads_eq - else: - self.comb += pads_eq - self.comb += sink.ack.eq(1) - - -class LiteEthPHYGMIIRX(Module): - def __init__(self, pads): - self.source = source = Source(eth_phy_description(8)) - - # # # - - dv_d = Signal() - self.sync += dv_d.eq(pads.dv) - - sop = Signal() - eop = Signal() - self.comb += [ - sop.eq(pads.dv & ~dv_d), - eop.eq(~pads.dv & dv_d) - ] - self.sync += [ - source.stb.eq(pads.dv), - source.sop.eq(sop), - source.data.eq(pads.rx_data) - ] - self.comb += source.eop.eq(eop) - - -class LiteEthPHYGMIICRG(Module, AutoCSR): - def __init__(self, clock_pads, pads, with_hw_init_reset, mii_mode=0): - self._reset = CSRStorage() - - # # # - - self.clock_domains.cd_eth_rx = ClockDomain() - self.clock_domains.cd_eth_tx = ClockDomain() - - # RX : Let the synthesis tool insert the appropriate clock buffer - self.comb += self.cd_eth_rx.clk.eq(clock_pads.rx) - - # TX : GMII: Drive clock_pads.gtx, clock_pads.tx unused - # MII: Use PHY clock_pads.tx as eth_tx_clk, do not drive clock_pads.gtx - self.specials += DDROutput(1, mii_mode, clock_pads.gtx, ClockSignal("eth_tx")) - # XXX Xilinx specific, replace BUFGMUX with a generic clock buffer? - self.specials += Instance("BUFGMUX", - i_I0=self.cd_eth_rx.clk, - i_I1=clock_pads.tx, - i_S=mii_mode, - o_O=self.cd_eth_tx.clk) - - if with_hw_init_reset: - reset = Signal() - counter_done = Signal() - self.submodules.counter = counter = Counter(max=512) - self.comb += [ - counter_done.eq(counter.value == 256), - counter.ce.eq(~counter_done), - reset.eq(~counter_done | self._reset.storage) - ] - else: - reset = self._reset.storage - self.comb += pads.rst_n.eq(~reset) - self.specials += [ - AsyncResetSynchronizer(self.cd_eth_tx, reset), - AsyncResetSynchronizer(self.cd_eth_rx, reset), - ] - - -class LiteEthPHYGMII(Module, AutoCSR): - def __init__(self, clock_pads, pads, with_hw_init_reset=True): - self.dw = 8 - self.submodules.crg = LiteEthPHYGMIICRG(clock_pads, - pads, - with_hw_init_reset) - self.submodules.tx = RenameClockDomains(LiteEthPHYGMIITX(pads), - "eth_tx") - self.submodules.rx = RenameClockDomains(LiteEthPHYGMIIRX(pads), - "eth_rx") - self.sink, self.source = self.tx.sink, self.rx.source diff --git a/misoc/com/liteethmini/phy/gmii_mii.py b/misoc/com/liteethmini/phy/gmii_mii.py deleted file mode 100644 index 8ed4741e..00000000 --- a/misoc/com/liteethmini/phy/gmii_mii.py +++ /dev/null @@ -1,161 +0,0 @@ -from migen.genlib.io import DDROutput -from migen.flow.plumbing import Multiplexer, Demultiplexer -from migen.genlib.cdc import PulseSynchronizer - -from misoc.com.liteethmini.common import * - -from misoc.com.liteethmini.phy.gmii import LiteEthPHYGMIICRG -from misoc.com.liteethmini.phy.mii import LiteEthPHYMIITX, LiteEthPHYMIIRX -from misoc.com.liteethmini.phy.gmii import LiteEthPHYGMIITX, LiteEthPHYGMIIRX - -modes = { - "GMII": 0, - "MII": 1 -} - -tx_pads_layout = [("tx_er", 1), ("tx_en", 1), ("tx_data", 8)] -rx_pads_layout = [("rx_er", 1), ("dv", 1), ("rx_data", 8)] - - -class LiteEthPHYGMIIMIITX(Module): - def __init__(self, pads, mode): - self.sink = sink = Sink(eth_phy_description(8)) - - # # # - - gmii_tx_pads = Record(tx_pads_layout) - gmii_tx = LiteEthPHYGMIITX(gmii_tx_pads, pads_register=False) - self.submodules += gmii_tx - - mii_tx_pads = Record(tx_pads_layout) - mii_tx = LiteEthPHYMIITX(mii_tx_pads, pads_register=False) - self.submodules += mii_tx - - demux = Demultiplexer(eth_phy_description(8), 2) - self.submodules += demux - self.comb += [ - demux.sel.eq(mode == modes["MII"]), - Record.connect(sink, demux.sink), - Record.connect(demux.source0, gmii_tx.sink), - Record.connect(demux.source1, mii_tx.sink), - ] - - if hasattr(pads, "tx_er"): - self.comb += pads.tx_er.eq(0) - self.sync += [ - If(mode == modes["MII"], - pads.tx_en.eq(mii_tx_pads.tx_en), - pads.tx_data.eq(mii_tx_pads.tx_data), - ).Else( - pads.tx_en.eq(gmii_tx_pads.tx_en), - pads.tx_data.eq(gmii_tx_pads.tx_data), - ) - ] - - -class LiteEthPHYGMIIMIIRX(Module): - def __init__(self, pads, mode): - self.source = source = Source(eth_phy_description(8)) - - # # # - - pads_d = Record(rx_pads_layout) - self.sync += [ - pads_d.dv.eq(pads.dv), - pads_d.rx_data.eq(pads.rx_data) - ] - - gmii_rx = LiteEthPHYGMIIRX(pads_d) - self.submodules += gmii_rx - - mii_rx = LiteEthPHYMIIRX(pads_d) - self.submodules += mii_rx - - mux = Multiplexer(eth_phy_description(8), 2) - self.submodules += mux - self.comb += [ - mux.sel.eq(mode == modes["MII"]), - Record.connect(gmii_rx.source, mux.sink0), - Record.connect(mii_rx.source, mux.sink1), - Record.connect(mux.source, source) - ] - - -class LiteEthGMIIMIIModeDetection(Module, AutoCSR): - def __init__(self, clk_freq): - self.mode = Signal() - self._mode = CSRStatus() - - # # # - - mode = Signal() - update_mode = Signal() - self.sync += \ - If(update_mode, - self.mode.eq(mode) - ) - self.comb += self._mode.status.eq(self.mode) - - # Principle: - # sys_clk >= 125MHz - # eth_rx <= 125Mhz - # We generate ticks every 1024 clock cycles in eth_rx domain - # and measure ticks period in sys_clk domain. - - # Generate a tick every 1024 clock cycles (eth_rx clock domain) - eth_tick = Signal() - eth_counter = Signal(10) - self.sync.eth_rx += eth_counter.eq(eth_counter + 1) - self.comb += eth_tick.eq(eth_counter == 0) - - # Synchronize tick (sys clock domain) - sys_tick = Signal() - eth_ps = PulseSynchronizer("eth_rx", "sys") - self.comb += [ - eth_ps.i.eq(eth_tick), - sys_tick.eq(eth_ps.o) - ] - self.submodules += eth_ps - - # sys_clk domain counter - sys_counter = Counter(24) - self.submodules += sys_counter - - fsm = FSM(reset_state="IDLE") - self.submodules += fsm - - fsm.act("IDLE", - sys_counter.reset.eq(1), - If(sys_tick, - NextState("COUNT") - ) - ) - fsm.act("COUNT", - sys_counter.ce.eq(1), - If(sys_tick, - NextState("DETECTION") - ) - ) - fsm.act("DETECTION", - update_mode.eq(1), - # if freq < 125MHz-5% use MII mode - If(sys_counter.value > int((clk_freq/125000000)*1024*1.05), - mode.eq(1) - # if freq >= 125MHz-5% use GMII mode - ).Else( - mode.eq(0) - ), - NextState("IDLE") - ) - - -class LiteEthPHYGMIIMII(Module, AutoCSR): - def __init__(self, clock_pads, pads, clk_freq, with_hw_init_reset=True): - self.dw = 8 - # Note: we can use GMII CRG since it also handles tx clock pad used for MII - self.submodules.mode_detection = LiteEthGMIIMIIModeDetection(clk_freq) - mode = self.mode_detection.mode - self.submodules.crg = LiteEthPHYGMIICRG(clock_pads, pads, with_hw_init_reset, mode == modes["MII"]) - self.submodules.tx = RenameClockDomains(LiteEthPHYGMIIMIITX(pads, mode), "eth_tx") - self.submodules.rx = RenameClockDomains(LiteEthPHYGMIIMIIRX(pads, mode), "eth_rx") - self.sink, self.source = self.tx.sink, self.rx.source diff --git a/misoc/com/liteethmini/phy/loopback.py b/misoc/com/liteethmini/phy/loopback.py deleted file mode 100644 index c9e5cf1c..00000000 --- a/misoc/com/liteethmini/phy/loopback.py +++ /dev/null @@ -1,31 +0,0 @@ -from misoc.com.liteethmini.common import * -from misoc.com.liteethmini.generic import * - - -class LiteEthPHYLoopbackCRG(Module, AutoCSR): - def __init__(self): - self._reset = CSRStorage() - - # # # - - self.clock_domains.cd_eth_rx = ClockDomain() - self.clock_domains.cd_eth_tx = ClockDomain() - self.comb += [ - self.cd_eth_rx.clk.eq(ClockSignal()), - self.cd_eth_tx.clk.eq(ClockSignal()) - ] - - reset = self._reset.storage - self.comb += [ - self.cd_eth_rx.rst.eq(reset), - self.cd_eth_tx.rst.eq(reset) - ] - - -class LiteEthPHYLoopback(Module, AutoCSR): - def __init__(self): - self.dw = 8 - self.submodules.crg = LiteEthLoopbackPHYCRG() - self.sink = sink = Sink(eth_phy_description(8)) - self.source = source = Source(eth_phy_description(8)) - self.comb += Record.connect(self.sink, self.source) diff --git a/misoc/com/liteethmini/phy/mii.py b/misoc/com/liteethmini/phy/mii.py deleted file mode 100644 index 69565246..00000000 --- a/misoc/com/liteethmini/phy/mii.py +++ /dev/null @@ -1,102 +0,0 @@ -from misoc.com.liteethmini.common import * - - -def converter_description(dw): - payload_layout = [("data", dw)] - return EndpointDescription(payload_layout, packetized=True) - - -class LiteEthPHYMIITX(Module): - def __init__(self, pads, pads_register=True): - self.sink = sink = Sink(eth_phy_description(8)) - - # # # - - if hasattr(pads, "tx_er"): - self.sync += pads.tx_er.eq(0) - converter = Converter(converter_description(8), - converter_description(4)) - self.submodules += converter - self.comb += [ - converter.sink.stb.eq(sink.stb), - converter.sink.data.eq(sink.data), - sink.ack.eq(converter.sink.ack), - converter.source.ack.eq(1) - ] - pads_eq = [ - pads.tx_en.eq(converter.source.stb), - pads.tx_data.eq(converter.source.data) - ] - if pads_register: - self.sync += pads_eq - else: - self.comb += pads_eq - - -class LiteEthPHYMIIRX(Module): - def __init__(self, pads): - self.source = source = Source(eth_phy_description(8)) - - # # # - - sop = FlipFlop(reset=1) - self.submodules += sop - - converter = Converter(converter_description(4), - converter_description(8)) - converter = InsertReset(converter) - self.submodules += converter - - self.sync += [ - converter.reset.eq(~pads.dv), - converter.sink.stb.eq(1), - converter.sink.data.eq(pads.rx_data) - ] - self.comb += [ - sop.reset.eq(~pads.dv), - sop.ce.eq(pads.dv), - converter.sink.sop.eq(sop.q), - converter.sink.eop.eq(~pads.dv) - ] - self.comb += Record.connect(converter.source, source) - - -class LiteEthPHYMIICRG(Module, AutoCSR): - def __init__(self, clock_pads, pads, with_hw_init_reset): - self._reset = CSRStorage() - - # # # - - if hasattr(clock_pads, "phy"): - self.sync.base50 += clock_pads.phy.eq(~clock_pads.phy) - - self.clock_domains.cd_eth_rx = ClockDomain() - self.clock_domains.cd_eth_tx = ClockDomain() - self.comb += self.cd_eth_rx.clk.eq(clock_pads.rx) - self.comb += self.cd_eth_tx.clk.eq(clock_pads.tx) - - if with_hw_init_reset: - reset = Signal() - counter_done = Signal() - self.submodules.counter = counter = Counter(max=512) - self.comb += [ - counter_done.eq(counter.value == 256), - counter.ce.eq(~counter_done), - reset.eq(~counter_done | self._reset.storage) - ] - else: - reset = self._reset.storage - self.comb += pads.rst_n.eq(~reset) - self.specials += [ - AsyncResetSynchronizer(self.cd_eth_tx, reset), - AsyncResetSynchronizer(self.cd_eth_rx, reset), - ] - - -class LiteEthPHYMII(Module, AutoCSR): - def __init__(self, clock_pads, pads, with_hw_init_reset=True): - self.dw = 8 - self.submodules.crg = LiteEthPHYMIICRG(clock_pads, pads, with_hw_init_reset) - self.submodules.tx = RenameClockDomains(LiteEthPHYMIITX(pads), "eth_tx") - self.submodules.rx = RenameClockDomains(LiteEthPHYMIIRX(pads), "eth_rx") - self.sink, self.source = self.tx.sink, self.rx.source diff --git a/misoc/com/liteethmini/phy/s6rgmii.py b/misoc/com/liteethmini/phy/s6rgmii.py deleted file mode 100644 index 8f7da75f..00000000 --- a/misoc/com/liteethmini/phy/s6rgmii.py +++ /dev/null @@ -1,162 +0,0 @@ -# RGMII PHY for Spartan-6 - -from migen.genlib.io import DDROutput -from migen.genlib.misc import WaitTimer -from migen.genlib.fsm import FSM, NextState - -from misoc.com.liteethmini.common import * - - -class LiteEthPHYRGMIITX(Module): - def __init__(self, pads, pads_register=True): - self.sink = sink = Sink(eth_phy_description(8)) - - # # # - - self.specials += Instance("ODDR2", - p_DDR_ALIGNMENT="C0", p_INIT=0, p_SRTYPE="ASYNC", - i_C0=ClockSignal("eth_tx"), i_C1=~ClockSignal("eth_tx"), - i_CE=1, i_S=0, i_R=0, - i_D0=sink.stb, i_D1=sink.stb, o_Q=pads.tx_ctl, - ) - for i in range(4): - self.specials += Instance("ODDR2", - p_DDR_ALIGNMENT="C0", p_INIT=0, p_SRTYPE="ASYNC", - i_C0=ClockSignal("eth_tx"), i_C1=~ClockSignal("eth_tx"), - i_CE=1, i_S=0, i_R=0, - i_D0=sink.data[i], i_D1=sink.data[4+i], o_Q=pads.tx_data[i], - ) - self.comb += sink.ack.eq(1) - - -class LiteEthPHYRGMIIRX(Module): - def __init__(self, pads): - self.source = source = Source(eth_phy_description(8)) - - # # # - - rx_ctl = Signal() - rx_data = Signal(8) - - self.specials += Instance("IDDR2", - p_DDR_ALIGNMENT="C0", p_INIT_Q0=0, p_INIT_Q1=0, p_SRTYPE="ASYNC", - i_C0=ClockSignal("eth_rx"), i_C1=~ClockSignal("eth_rx"), - i_CE=1, i_S=0, i_R=0, - i_D=pads.rx_ctl, o_Q1=rx_ctl, - ) - for i in range(4): - self.specials += Instance("IDDR2", - p_DDR_ALIGNMENT="C0", p_INIT_Q0=0, p_INIT_Q1=0, p_SRTYPE="ASYNC", - i_C0=ClockSignal("eth_rx"), i_C1=~ClockSignal("eth_rx"), - i_CE=1, i_S=0, i_R=0, - i_D=pads.rx_data[i], o_Q0=rx_data[4+i], o_Q1=rx_data[i], - ) - - - rx_ctl_d = Signal() - self.sync += rx_ctl_d.eq(rx_ctl) - - sop = Signal() - eop = Signal() - self.comb += [ - sop.eq(rx_ctl & ~rx_ctl_d), - eop.eq(~rx_ctl & rx_ctl_d) - ] - self.sync += [ - source.stb.eq(rx_ctl), - source.sop.eq(sop), - source.data.eq(rx_data) - ] - self.comb += source.eop.eq(eop) - - -class LiteEthPHYRGMIICRG(Module, AutoCSR): - def __init__(self, clock_pads, pads, with_hw_init_reset): - self._reset = CSRStorage() - - # # # - - self.clock_domains.cd_eth_rx = ClockDomain() - self.clock_domains.cd_eth_tx = ClockDomain() - - - # RX - dcm_reset = Signal() - dcm_locked = Signal() - - timer = WaitTimer(1024) - fsm = FSM(reset_state="DCM_RESET") - self.submodules += timer, fsm - - fsm.act("DCM_RESET", - dcm_reset.eq(1), - timer.wait.eq(1), - If(timer.done, - timer.wait.eq(0), - NextState("DCM_WAIT") - ) - ) - fsm.act("DCM_WAIT", - timer.wait.eq(1), - If(timer.done, - NextState("DCM_CHECK_LOCK") - ) - ) - fsm.act("DCM_CHECK_LOCK", - If(~dcm_locked, - NextState("DCM_RESET") - ) - ) - - clk90_rx = Signal() - clk0_rx = Signal() - clk0_rx_bufg = Signal() - self.specials += Instance("DCM", - i_CLKIN=clock_pads.rx, - i_CLKFB=clk0_rx_bufg, - o_CLK0=clk0_rx, - o_CLK90=clk90_rx, - o_LOCKED=dcm_locked, - i_PSEN=0, - i_PSCLK=0, - i_PSINCDEC=0, - i_RST=dcm_reset - ) - - self.specials += Instance("BUFG", i_I=clk0_rx, o_O=clk0_rx_bufg) - self.specials += Instance("BUFG", i_I=clk90_rx, o_O=self.cd_eth_rx.clk) - - # TX - self.specials += DDROutput(1, 0, clock_pads.tx, ClockSignal("eth_tx")) - self.specials += Instance("BUFG", i_I=self.cd_eth_rx.clk, o_O=self.cd_eth_tx.clk) - - # Reset - if with_hw_init_reset: - reset = Signal() - counter_done = Signal() - self.submodules.counter = counter = Counter(max=512) - self.comb += [ - counter_done.eq(counter.value == 256), - counter.ce.eq(~counter_done), - reset.eq(~counter_done | self._reset.storage) - ] - else: - reset = self._reset.storage - self.comb += pads.rst_n.eq(~reset) - self.specials += [ - AsyncResetSynchronizer(self.cd_eth_tx, reset), - AsyncResetSynchronizer(self.cd_eth_rx, reset), - ] - - -class LiteEthPHYRGMII(Module, AutoCSR): - def __init__(self, clock_pads, pads, with_hw_init_reset=True): - self.dw = 8 - self.submodules.crg = LiteEthPHYRGMIICRG(clock_pads, - pads, - with_hw_init_reset) - self.submodules.tx = RenameClockDomains(LiteEthPHYRGMIITX(pads), - "eth_tx") - self.submodules.rx = RenameClockDomains(LiteEthPHYRGMIIRX(pads), - "eth_rx") - self.sink, self.source = self.tx.sink, self.rx.source diff --git a/misoc/com/liteethmini/phy/sim.py b/misoc/com/liteethmini/phy/sim.py deleted file mode 100644 index d6d1778d..00000000 --- a/misoc/com/liteethmini/phy/sim.py +++ /dev/null @@ -1,58 +0,0 @@ -import os - -from misoc.com.liteethmini.common import * - - -class LiteEthPHYSimCRG(Module, AutoCSR): - def __init__(self): - self._reset = CSRStorage() - - # # # - - self.clock_domains.cd_eth_rx = ClockDomain() - self.clock_domains.cd_eth_tx = ClockDomain() - self.comb += [ - self.cd_eth_rx.clk.eq(ClockSignal()), - self.cd_eth_tx.clk.eq(ClockSignal()) - ] - - reset = self._reset.storage - self.comb += [ - self.cd_eth_rx.rst.eq(reset), - self.cd_eth_tx.rst.eq(reset) - ] - - -class LiteEthPHYSim(Module, AutoCSR): - def __init__(self, pads, tap="tap0", ip_address="192.168.0.14"): - self.dw = 8 - self.submodules.crg = LiteEthPHYSimCRG() - self.sink = sink = Sink(eth_phy_description(8)) - self.source = source = Source(eth_phy_description(8)) - self.tap = tap - self.ip_address = ip_address - - self.comb += [ - pads.source_stb.eq(self.sink.stb), - pads.source_data.eq(self.sink.data), - self.sink.ack.eq(1) - ] - - self.sync += [ - self.source.stb.eq(pads.sink_stb), - self.source.sop.eq(pads.sink_stb & ~self.source.stb), - self.source.data.eq(pads.sink_data), - ] - self.comb += [ - self.source.eop.eq(~pads.sink_stb & self.source.stb), - ] - - # XXX avoid use of os.system - os.system("openvpn --mktun --dev {}".format(self.tap)) - os.system("ifconfig {} {} up".format(self.tap, self.ip_address)) - os.system("mknod /dev/net/{} c 10 200".format(self.tap)) - - def do_exit(self, *args, **kwargs): - # XXX avoid use of os.system - os.system("rm -f /dev/net/{}".format(self.tap)) - os.system("openvpn --rmtun --dev {}".format(self.tap)) diff --git a/misoc/com/spi/__init__.py b/misoc/com/spi/__init__.py deleted file mode 100644 index 029a0bec..00000000 --- a/misoc/com/spi/__init__.py +++ /dev/null @@ -1,154 +0,0 @@ -from migen import * -from migen.bank.description import * -from migen.genlib.fsm import FSM, NextState - - -class SPIMaster(Module, AutoCSR): - def __init__(self, pads, width=24, div=2, cpha=1): - self.pads = pads - - self._ctrl = CSR() - self._length = CSRStorage(8) - self._status = CSRStatus() - if hasattr(pads, "mosi"): - self._mosi = CSRStorage(width) - if hasattr(pads, "miso"): - self._miso = CSRStatus(width) - - self.irq = Signal() - - ### - - # ctrl - start = Signal() - length = self._length.storage - enable_cs = Signal() - enable_shift = Signal() - done = Signal() - - self.comb += [ - start.eq(self._ctrl.re & self._ctrl.r[0]), - self._status.status.eq(done) - ] - - # clk - i = Signal(max=div) - clk_en = Signal() - set_clk = Signal() - clr_clk = Signal() - self.sync += [ - If(set_clk, - pads.clk.eq(enable_cs) - ), - If(clr_clk, - pads.clk.eq(0), - i.eq(0) - ).Else( - i.eq(i + 1), - ) - ] - - self.comb += [ - set_clk.eq(i == (div//2-1)), - clr_clk.eq(i == (div-1)) - ] - - # fsm - cnt = Signal(8) - clr_cnt = Signal() - inc_cnt = Signal() - self.sync += \ - If(clr_cnt, - cnt.eq(0) - ).Elif(inc_cnt, - cnt.eq(cnt+1) - ) - - fsm = FSM(reset_state="IDLE") - self.submodules += fsm - fsm.act("IDLE", - If(start, - NextState("WAIT_CLK") - ), - done.eq(1), - clr_cnt.eq(1) - ) - fsm.act("WAIT_CLK", - If(clr_clk, - NextState("SHIFT") - ), - ) - fsm.act("SHIFT", - If(cnt == length, - NextState("END") - ).Else( - inc_cnt.eq(clr_clk), - ), - enable_cs.eq(1), - enable_shift.eq(1), - ) - fsm.act("END", - If(set_clk, - NextState("IDLE") - ), - enable_shift.eq(1), - self.irq.eq(1) - ) - - # miso - if hasattr(pads, "miso"): - miso = Signal() - sr_miso = Signal(width) - - # (cpha = 1: capture on clk falling edge) - if cpha: - self.sync += \ - If(enable_shift, - If(clr_clk, - miso.eq(pads.miso), - ).Elif(set_clk, - sr_miso.eq(Cat(miso, sr_miso[:-1])) - ) - ) - # (cpha = 0: capture on clk rising edge) - else: - self.sync += \ - If(enable_shift, - If(set_clk, - miso.eq(pads.miso), - ).Elif(clr_clk, - sr_miso.eq(Cat(miso, sr_miso[:-1])) - ) - ) - self.comb += self._miso.status.eq(sr_miso) - - # mosi - if hasattr(pads, "mosi"): - mosi = Signal() - sr_mosi = Signal(width) - - # (cpha = 1: propagated on clk rising edge) - if cpha: - self.sync += \ - If(start, - sr_mosi.eq(self._mosi.storage) - ).Elif(clr_clk & enable_shift, - sr_mosi.eq(Cat(Signal(), sr_mosi[:-1])) - ).Elif(set_clk, - pads.mosi.eq(sr_mosi[-1]) - ) - - # (cpha = 0: propagated on clk falling edge) - else: - self.sync += [ - If(start, - sr_mosi.eq(self._mosi.storage) - ).Elif(set_clk & enable_shift, - sr_mosi.eq(Cat(Signal(), sr_mosi[:-1])) - ).Elif(clr_clk, - pads.mosi.eq(sr_mosi[-1]) - ) - ] - - # cs_n - self.comb += pads.cs_n.eq(~enable_cs) diff --git a/misoc/com/spi/test/spi_master_tb.py b/misoc/com/spi/test/spi_master_tb.py deleted file mode 100644 index e0febf53..00000000 --- a/misoc/com/spi/test/spi_master_tb.py +++ /dev/null @@ -1,86 +0,0 @@ -from migen import * -from migen.genlib.record import * -from migen.sim.generic import run_simulation - -from misoc.com.spi import SPIMaster - - -class SPISlave(Module): - def __init__(self, pads, width): - self.pads = pads - self.width = width - - ### - - self.mosi = 0 - self.miso = 0 - - self.last_cs_n = 1 - self.last_clk = 0 - - - def get_mosi(self): - return self.mosi - - def set_miso(self, value): - self.miso = value - - def do_simulation(self, selfp): - # detect edges - cs_n_rising = 0 - cs_n_falling = 0 - clk_rising = 0 - clk_falling = 0 - if selfp.pads.cs_n and not self.last_cs_n: - cs_n_rising = 1 - if not selfp.pads.cs_n and self.last_cs_n: - cs_n_falling = 1 - if selfp.pads.clk and not self.last_clk: - clk_rising = 1 - if not selfp.pads.clk and self.last_clk: - clk_falling = 1 - - # input mosi - if clk_falling and not selfp.pads.cs_n: - self.mosi = self.mosi << 1 - self.mosi |= selfp.pads.mosi - - # output miso - if (clk_rising and not selfp.pads.cs_n): - selfp.pads.miso = (self.miso >> (self.width-1)) & 0x1 - self.miso = self.miso << 1 - - # save signal states - self.last_cs_n = selfp.pads.cs_n - self.last_clk = selfp.pads.clk - - -def spi_access(selfp, length, mosi): - selfp.spi_master._mosi.storage = mosi - yield - selfp.spi_master._ctrl.r = (length << 8) | 1 - selfp.spi_master._ctrl.re = 1 - yield - selfp.spi_master._ctrl.r = 0 - selfp.spi_master._ctrl.re = 0 - yield - while not (selfp.spi_master._status.status & 0x1): - yield - - -class TB(Module): - def __init__(self): - pads = Record([("cs_n", 1), ("clk", 1), ("mosi", 1), ("miso", 1)]) - self.submodules.spi_master = SPIMaster(pads, 24, 4) - self.submodules.spi_slave = SPISlave(pads, 24) - - def gen_simulation(self, selfp): - for i in range(16): - yield - self.spi_slave.set_miso(0x123457) - yield from spi_access(selfp, 8, 0x123457) - print("{:08x}".format(self.spi_slave.get_mosi())) - print("{:08x}".format(selfp.spi_master._miso.status)) - -if __name__ == "__main__": - run_simulation(TB(), ncycles=1000, vcd_name="my.vcd", keep_files=True) diff --git a/misoc/com/uart/__init__.py b/misoc/com/uart/__init__.py deleted file mode 100644 index bc00e506..00000000 --- a/misoc/com/uart/__init__.py +++ /dev/null @@ -1,58 +0,0 @@ -from migen import * -from migen.bank.description import * -from migen.bank.eventmanager import * -from migen.genlib.record import Record -from migen.actorlib.fifo import SyncFIFO, AsyncFIFO - - -def _get_uart_fifo(depth, sink_cd="sys", source_cd="sys"): - if sink_cd != source_cd: - fifo = AsyncFIFO([("data", 8)], depth) - return ClockDomainsRenamer({"write": sink_cd, "read": source_cd})(fifo) - else: - return SyncFIFO([("data", 8)], depth) - - -class UART(Module, AutoCSR): - def __init__(self, phy, - tx_fifo_depth=16, - rx_fifo_depth=16, - phy_cd="sys"): - self._rxtx = CSR(8) - self._txfull = CSRStatus() - self._rxempty = CSRStatus() - - self.submodules.ev = EventManager() - self.ev.tx = EventSourceProcess() - self.ev.rx = EventSourceProcess() - self.ev.finalize() - - # # # - - # TX - tx_fifo = _get_uart_fifo(tx_fifo_depth, source_cd=phy_cd) - self.submodules += tx_fifo - - self.comb += [ - tx_fifo.sink.stb.eq(self._rxtx.re), - tx_fifo.sink.data.eq(self._rxtx.r), - self._txfull.status.eq(~tx_fifo.sink.ack), - Record.connect(tx_fifo.source, phy.sink), - # Generate TX IRQ when tx_fifo becomes non-full - self.ev.tx.trigger.eq(~tx_fifo.sink.ack) - ] - - - # RX - rx_fifo = _get_uart_fifo(rx_fifo_depth, sink_cd=phy_cd) - self.submodules += rx_fifo - - - self.comb += [ - Record.connect(phy.source, rx_fifo.sink), - self._rxempty.status.eq(~rx_fifo.source.stb), - self._rxtx.w.eq(rx_fifo.source.data), - rx_fifo.source.ack.eq(self.ev.rx.clear), - # Generate RX IRQ when tx_fifo becomes non-empty - self.ev.rx.trigger.eq(~rx_fifo.source.stb) - ] diff --git a/misoc/com/uart/bridge.py b/misoc/com/uart/bridge.py deleted file mode 100644 index 61cc098b..00000000 --- a/misoc/com/uart/bridge.py +++ /dev/null @@ -1,9 +0,0 @@ -from migen import * - -from misoc.tools.wishbone import WishboneStreamingBridge -from misoc.com.uart.phy.serial import UARTPHYSerial - -class UARTWishboneBridge(WishboneStreamingBridge): - def __init__(self, pads, clk_freq, baudrate=115200): - self.submodules.phy = UARTPHYSerial(pads, clk_freq, baudrate) - WishboneStreamingBridge.__init__(self, self.phy, clk_freq) diff --git a/misoc/com/uart/phy/__init__.py b/misoc/com/uart/phy/__init__.py deleted file mode 100644 index 76ebd80b..00000000 --- a/misoc/com/uart/phy/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -def UARTPHY(pads, *args, **kwargs): - # Autodetect PHY - if hasattr(pads, "source_stb"): - from misoc.com.uart.phy.sim import UARTPHYSim - return UARTPHYSim(pads, *args, **kwargs) - else: - from misoc.com.uart.phy.serial import UARTPHYSerial - return UARTPHYSerial(pads, *args, **kwargs) diff --git a/misoc/com/uart/phy/serial.py b/misoc/com/uart/phy/serial.py deleted file mode 100644 index 6458d4e3..00000000 --- a/misoc/com/uart/phy/serial.py +++ /dev/null @@ -1,104 +0,0 @@ -from migen import * -from migen.genlib.cdc import MultiReg -from migen.bank.description import * -from migen.flow.actor import Sink, Source - - -class UARTPHYSerialRX(Module): - def __init__(self, pads, tuning_word): - self.source = Source([("data", 8)]) - ### - uart_clk_rxen = Signal() - phase_accumulator_rx = Signal(32) - - rx = Signal() - self.specials += MultiReg(pads.rx, rx) - rx_r = Signal() - rx_reg = Signal(8) - rx_bitcount = Signal(4) - rx_busy = Signal() - rx_done = self.source.stb - rx_data = self.source.data - self.sync += [ - rx_done.eq(0), - rx_r.eq(rx), - If(~rx_busy, - If(~rx & rx_r, # look for start bit - rx_busy.eq(1), - rx_bitcount.eq(0), - ) - ).Else( - If(uart_clk_rxen, - rx_bitcount.eq(rx_bitcount + 1), - If(rx_bitcount == 0, - If(rx, # verify start bit - rx_busy.eq(0) - ) - ).Elif(rx_bitcount == 9, - rx_busy.eq(0), - If(rx, # verify stop bit - rx_data.eq(rx_reg), - rx_done.eq(1) - ) - ).Else( - rx_reg.eq(Cat(rx_reg[1:], rx)) - ) - ) - ) - ] - self.sync += \ - If(rx_busy, - Cat(phase_accumulator_rx, uart_clk_rxen).eq(phase_accumulator_rx + tuning_word) - ).Else( - Cat(phase_accumulator_rx, uart_clk_rxen).eq(2**31) - ) - - -class UARTPHYSerialTX(Module): - def __init__(self, pads, tuning_word): - self.sink = Sink([("data", 8)]) - ### - uart_clk_txen = Signal() - phase_accumulator_tx = Signal(32) - - pads.tx.reset = 1 - - tx_reg = Signal(8) - tx_bitcount = Signal(4) - tx_busy = Signal() - self.sync += [ - self.sink.ack.eq(0), - If(self.sink.stb & ~tx_busy & ~self.sink.ack, - tx_reg.eq(self.sink.data), - tx_bitcount.eq(0), - tx_busy.eq(1), - pads.tx.eq(0) - ).Elif(uart_clk_txen & tx_busy, - tx_bitcount.eq(tx_bitcount + 1), - If(tx_bitcount == 8, - pads.tx.eq(1) - ).Elif(tx_bitcount == 9, - pads.tx.eq(1), - tx_busy.eq(0), - self.sink.ack.eq(1), - ).Else( - pads.tx.eq(tx_reg[0]), - tx_reg.eq(Cat(tx_reg[1:], 0)) - ) - ) - ] - self.sync += [ - If(tx_busy, - Cat(phase_accumulator_tx, uart_clk_txen).eq(phase_accumulator_tx + tuning_word) - ).Else( - Cat(phase_accumulator_tx, uart_clk_txen).eq(0) - ) - ] - - -class UARTPHYSerial(Module, AutoCSR): - def __init__(self, pads, clk_freq, baudrate=115200): - self._tuning_word = CSRStorage(32, reset=int((baudrate/clk_freq)*2**32)) - self.submodules.tx = UARTPHYSerialTX(pads, self._tuning_word.storage) - self.submodules.rx = UARTPHYSerialRX(pads, self._tuning_word.storage) - self.sink, self.source = self.tx.sink, self.rx.source diff --git a/misoc/com/uart/phy/sim.py b/misoc/com/uart/phy/sim.py deleted file mode 100644 index 85a215d5..00000000 --- a/misoc/com/uart/phy/sim.py +++ /dev/null @@ -1,33 +0,0 @@ -import os -import pty -import time - -from migen import * -from migen.flow.actor import Sink, Source - - -class UARTPHYSim(Module): - def __init__(self, pads, *args, **kwargs): - self.sink = Sink([("data", 8)]) - self.source = Source([("data", 8)]) - - self.comb += [ - pads.source_stb.eq(self.sink.stb), - pads.source_data.eq(self.sink.data), - self.sink.ack.eq(pads.source_ack), - - self.source.stb.eq(pads.sink_stb), - self.source.data.eq(pads.sink_data), - pads.sink_ack.eq(self.source.ack) - ] - - m, s = pty.openpty() - name = os.ttyname(s) - print("UART tty: "+name) - time.sleep(0.5) # pause for user - f = open("/tmp/simserial", "w") - f.write(os.ttyname(s)) - f.close() - - def do_exit(self, *args, **kwargs): - os.remove("/tmp/simserial") diff --git a/misoc/com/uart/software/__init__.py b/misoc/com/uart/software/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/misoc/com/uart/software/reg.py b/misoc/com/uart/software/reg.py deleted file mode 100644 index 204a6262..00000000 --- a/misoc/com/uart/software/reg.py +++ /dev/null @@ -1,55 +0,0 @@ -import csv - - -class MappedReg: - def __init__(self, readfn, writefn, name, addr, length, busword, mode): - self.readfn = readfn - self.writefn = writefn - self.addr = addr - self.length = length - self.busword = busword - self.mode = mode - - def read(self): - if self.mode not in ["rw", "ro"]: - raise KeyError(name + "register not readable") - datas = self.readfn(self.addr, burst_length=self.length) - if isinstance(datas, int): - return datas - else: - data = 0 - for i in range(self.length): - data = data << self.busword - data |= datas[i] - return data - - def write(self, value): - if self.mode not in ["rw", "wo"]: - raise KeyError(name + "register not writable") - datas = [] - for i in range(self.length): - datas.append((value >> ((self.length-1-i)*self.busword)) & (2**self.busword-1)) - self.writefn(self.addr, datas) - - -class MappedRegs: - def __init__(self, d): - self.d = d - - def __getattr__(self, attr): - try: - return self.__dict__['d'][attr] - except KeyError: - pass - raise KeyError("No such register " + attr) - - -def build_map(addrmap, busword, readfn, writefn): - csv_reader = csv.reader(open(addrmap), delimiter=',', quotechar='#') - d = {} - for item in csv_reader: - name, addr, length, mode = item - addr = int(addr.replace("0x", ""), 16) - length = int(length) - d[name] = MappedReg(readfn, writefn, name, addr, length, busword, mode) - return MappedRegs(d) diff --git a/misoc/com/uart/software/wishbone.py b/misoc/com/uart/software/wishbone.py deleted file mode 100644 index 608ccf2c..00000000 --- a/misoc/com/uart/software/wishbone.py +++ /dev/null @@ -1,75 +0,0 @@ -import serial -from struct import * -from misoc.com.uart.software.reg import * - - -def write_b(uart, data): - uart.write(pack('B', data)) - - -class UARTWishboneBridgeDriver: - cmds = { - "write": 0x01, - "read": 0x02 - } - def __init__(self, port, baudrate=115200, addrmap=None, busword=8, debug=False): - self.port = port - self.baudrate = str(baudrate) - self.debug = debug - self.uart = serial.Serial(port, baudrate, timeout=0.25) - if addrmap is not None: - self.regs = build_map(addrmap, busword, self.read, self.write) - - def open(self): - self.uart.flushOutput() - self.uart.close() - self.uart.open() - self.uart.flushInput() - - def close(self): - self.uart.flushOutput() - self.uart.close() - - def read(self, addr, burst_length=1): - datas = [] - self.uart.flushInput() - write_b(self.uart, self.cmds["read"]) - write_b(self.uart, burst_length) - word_addr = addr//4 - write_b(self.uart, (word_addr >> 24) & 0xff) - write_b(self.uart, (word_addr >> 16) & 0xff) - write_b(self.uart, (word_addr >> 8) & 0xff) - write_b(self.uart, (word_addr >> 0) & 0xff) - for i in range(burst_length): - data = 0 - for k in range(4): - data = data << 8 - data |= ord(self.uart.read()) - if self.debug: - print("RD {:08X} @ {:08X}".format(data, addr + 4*i)) - datas.append(data) - if burst_length == 1: - return datas[0] - else: - return datas - - def write(self, addr, data): - if isinstance(data, list): - burst_length = len(data) - else: - burst_length = 1 - data = [data] - write_b(self.uart, self.cmds["write"]) - write_b(self.uart, burst_length) - word_addr = addr//4 - write_b(self.uart, (word_addr >> 24) & 0xff) - write_b(self.uart, (word_addr >> 16) & 0xff) - write_b(self.uart, (word_addr >> 8) & 0xff) - write_b(self.uart, (word_addr >> 0) & 0xff) - for i in range(len(data)): - dat = data[i] - for j in range(4): - write_b(self.uart, (dat >> 24) & 0xff) - dat = dat << 8 - if self.debug: - print("WR {:08X} @ {:08X}".format(data[i], addr + 4*i)) diff --git a/misoc/com/uart/test/test_serial_phy.py b/misoc/com/uart/test/test_serial_phy.py deleted file mode 100644 index 71318c7a..00000000 --- a/misoc/com/uart/test/test_serial_phy.py +++ /dev/null @@ -1,97 +0,0 @@ -# XXX Adapt test to new architecture -class UARTTB(Module): - def __init__(self): - self.clk_freq = 83333333 - self.baud = 3000000 - self.pads = Record([("rx", 1), ("tx", 1)]) - self.submodules.slave = UART(self.pads, self.clk_freq, self.baud) - - def wait_for(self, ns_time): - freq_in_ghz = self.clk_freq/(10**9) - period = 1/freq_in_ghz - num_loops = int(ns_time/period) - for i in range(num_loops+1): - yield - - def gen_simulation(self, selfp): - baud_in_ghz = self.baud/(10**9) - uart_period = int(1/baud_in_ghz) - half_uart_period = int(1/(2*baud_in_ghz)) - - # Set TX an RX lines idle - selfp.pads.tx = 1 - selfp.pads.rx = 1 - yield - - # First send a few characters - - tx_string = "01234" - print("Sending string: " + tx_string) - for c in tx_string: - selfp.slave._r_rxtx.r = ord(c) - selfp.slave._r_rxtx.re = 1 - yield - selfp.slave._r_rxtx.re = 0 - - yield from self.wait_for(half_uart_period) - - if selfp.pads.tx: - print("FAILURE: no start bit sent") - - val = 0 - for i in range(8): - yield from self.wait_for(uart_period) - val >>= 1 - if selfp.pads.tx: - val |= 0x80 - - yield from self.wait_for(uart_period) - - if selfp.pads.tx == 0: - print("FAILURE: no stop bit sent") - - if ord(c) != val: - print("FAILURE: sent decimal value "+str(val)+" (char "+chr(val)+") instead of "+c) - else: - print("SUCCESS: sent "+c) - while selfp.slave.ev.tx.trigger != 1: - yield - - # Then receive a character - - rx_string = '5' - print("Receiving character "+rx_string) - rx_value = ord(rx_string) - for i in range(11): - if (i == 0): - # start bit - selfp.pads.rx = 0 - elif (i == 9): - # stop bit - selfp.pads.rx = 1 - elif (i == 10): - selfp.pads.rx = 1 - break - else: - selfp.pads.rx = 1 if (rx_value & 1) else 0 - rx_value >>= 1 - yield from self.wait_for(uart_period) - - rx_value = ord(rx_string) - received_value = selfp.slave._r_rxtx.w - if (received_value == rx_value): - print("RX SUCCESS: ") - else: - print("RX FAILURE: ") - - print("received "+chr(received_value)) - - while True: - yield - -if __name__ == "__main__": - from migen.sim.generic import Simulator, TopLevel - from migen.sim import icarus - with Simulator(UARTTB(), TopLevel("top.vcd", clk_period=int(1/0.08333333)), - icarus.Runner(keep_files=False)) as s: - s.run(20000) diff --git a/misoc/cpu/__init__.py b/misoc/cpu/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/misoc/cpu/identifier.py b/misoc/cpu/identifier.py deleted file mode 100644 index a70f44b0..00000000 --- a/misoc/cpu/identifier.py +++ /dev/null @@ -1,26 +0,0 @@ -import subprocess - -from migen import * -from migen.bank.description import * - -def get_id(): - output = subprocess.check_output(["git", "rev-parse", "HEAD"]).decode("ascii") - return int(output[:8], 16) - - -class Identifier(Module, AutoCSR): - def __init__(self, sysid, frequency, revision=None): - self._sysid = CSRStatus(16) - self._revision = CSRStatus(32) - self._frequency = CSRStatus(32) - - ### - - if revision is None: - revision = get_id() - - self.comb += [ - self._sysid.status.eq(sysid), - self._revision.status.eq(revision), - self._frequency.status.eq(frequency) - ] diff --git a/misoc/cpu/lm32.py b/misoc/cpu/lm32.py deleted file mode 100644 index ca9b001c..00000000 --- a/misoc/cpu/lm32.py +++ /dev/null @@ -1,63 +0,0 @@ -import os - -from migen import * -from migen.bus import wishbone - - -class LM32(Module): - def __init__(self, platform, eba_reset): - self.ibus = i = wishbone.Interface() - self.dbus = d = wishbone.Interface() - self.interrupt = Signal(32) - - ### - - i_adr_o = Signal(32) - d_adr_o = Signal(32) - self.specials += Instance("lm32_cpu", - p_eba_reset=Instance.PreformattedParam("32'h{:08x}".format(eba_reset)), - - i_clk_i=ClockSignal(), - i_rst_i=ResetSignal(), - - i_interrupt=self.interrupt, - - o_I_ADR_O=i_adr_o, - o_I_DAT_O=i.dat_w, - o_I_SEL_O=i.sel, - o_I_CYC_O=i.cyc, - o_I_STB_O=i.stb, - o_I_WE_O=i.we, - o_I_CTI_O=i.cti, - o_I_BTE_O=i.bte, - i_I_DAT_I=i.dat_r, - i_I_ACK_I=i.ack, - i_I_ERR_I=i.err, - i_I_RTY_I=0, - - o_D_ADR_O=d_adr_o, - o_D_DAT_O=d.dat_w, - o_D_SEL_O=d.sel, - o_D_CYC_O=d.cyc, - o_D_STB_O=d.stb, - o_D_WE_O=d.we, - o_D_CTI_O=d.cti, - o_D_BTE_O=d.bte, - i_D_DAT_I=d.dat_r, - i_D_ACK_I=d.ack, - i_D_ERR_I=d.err, - i_D_RTY_I=0) - - self.comb += [ - self.ibus.adr.eq(i_adr_o[2:]), - self.dbus.adr.eq(d_adr_o[2:]) - ] - - # add Verilog sources - platform.add_sources(os.path.join("extcores", "lm32", "submodule", "rtl"), - "lm32_cpu.v", "lm32_instruction_unit.v", "lm32_decoder.v", - "lm32_load_store_unit.v", "lm32_adder.v", "lm32_addsub.v", "lm32_logic_op.v", - "lm32_shifter.v", "lm32_multiplier.v", "lm32_mc_arithmetic.v", - "lm32_interrupt.v", "lm32_ram.v", "lm32_dp_ram.v", "lm32_icache.v", - "lm32_dcache.v", "lm32_debug.v", "lm32_itlb.v", "lm32_dtlb.v") - platform.add_verilog_include_path(os.path.join("extcores", "lm32")) diff --git a/misoc/cpu/mor1kx.py b/misoc/cpu/mor1kx.py deleted file mode 100644 index f9c69642..00000000 --- a/misoc/cpu/mor1kx.py +++ /dev/null @@ -1,80 +0,0 @@ -import os - -from migen import * -from migen.bus import wishbone - - -class MOR1KX(Module): - def __init__(self, platform, reset_pc): - self.ibus = i = wishbone.Interface() - self.dbus = d = wishbone.Interface() - self.interrupt = Signal(32) - - ### - - i_adr_o = Signal(32) - d_adr_o = Signal(32) - self.specials += Instance("mor1kx", - p_FEATURE_INSTRUCTIONCACHE="ENABLED", - p_OPTION_ICACHE_BLOCK_WIDTH=4, - p_OPTION_ICACHE_SET_WIDTH=8, - p_OPTION_ICACHE_WAYS=1, - p_OPTION_ICACHE_LIMIT_WIDTH=31, - p_FEATURE_DATACACHE="ENABLED", - p_OPTION_DCACHE_BLOCK_WIDTH=4, - p_OPTION_DCACHE_SET_WIDTH=8, - p_OPTION_DCACHE_WAYS=1, - p_OPTION_DCACHE_LIMIT_WIDTH=31, - p_FEATURE_TIMER="NONE", - p_OPTION_PIC_TRIGGER="LEVEL", - p_FEATURE_SYSCALL="NONE", - p_FEATURE_TRAP="NONE", - p_FEATURE_RANGE="NONE", - p_FEATURE_OVERFLOW="NONE", - p_FEATURE_ADDC="ENABLED", - p_FEATURE_CMOV="ENABLED", - p_FEATURE_FFL1="ENABLED", - p_OPTION_CPU0="CAPPUCCINO", - p_OPTION_RESET_PC=reset_pc, - p_IBUS_WB_TYPE="B3_REGISTERED_FEEDBACK", - p_DBUS_WB_TYPE="B3_REGISTERED_FEEDBACK", - - i_clk=ClockSignal(), - i_rst=ResetSignal(), - - i_irq_i=self.interrupt, - - o_iwbm_adr_o=i_adr_o, - o_iwbm_dat_o=i.dat_w, - o_iwbm_sel_o=i.sel, - o_iwbm_cyc_o=i.cyc, - o_iwbm_stb_o=i.stb, - o_iwbm_we_o=i.we, - o_iwbm_cti_o=i.cti, - o_iwbm_bte_o=i.bte, - i_iwbm_dat_i=i.dat_r, - i_iwbm_ack_i=i.ack, - i_iwbm_err_i=i.err, - i_iwbm_rty_i=0, - - o_dwbm_adr_o=d_adr_o, - o_dwbm_dat_o=d.dat_w, - o_dwbm_sel_o=d.sel, - o_dwbm_cyc_o=d.cyc, - o_dwbm_stb_o=d.stb, - o_dwbm_we_o=d.we, - o_dwbm_cti_o=d.cti, - o_dwbm_bte_o=d.bte, - i_dwbm_dat_i=d.dat_r, - i_dwbm_ack_i=d.ack, - i_dwbm_err_i=d.err, - i_dwbm_rty_i=0) - - self.comb += [ - self.ibus.adr.eq(i_adr_o[2:]), - self.dbus.adr.eq(d_adr_o[2:]) - ] - - # add Verilog sources - platform.add_source_dir(os.path.join("extcores", "mor1kx", "submodule", - "rtl", "verilog")) diff --git a/misoc/cpu/timer.py b/misoc/cpu/timer.py deleted file mode 100644 index 62a88acf..00000000 --- a/misoc/cpu/timer.py +++ /dev/null @@ -1,34 +0,0 @@ -from migen import * -from migen.bank.description import * -from migen.bank.eventmanager import * - - -class Timer(Module, AutoCSR): - def __init__(self, width=32): - self._load = CSRStorage(width) - self._reload = CSRStorage(width) - self._en = CSRStorage() - self._update_value = CSR() - self._value = CSRStatus(width) - - self.submodules.ev = EventManager() - self.ev.zero = EventSourceProcess() - self.ev.finalize() - - ### - - value = Signal(width) - self.sync += [ - If(self._en.storage, - If(value == 0, - # set reload to 0 to disable reloading - value.eq(self._reload.storage) - ).Else( - value.eq(value - 1) - ) - ).Else( - value.eq(self._load.storage) - ), - If(self._update_value.re, self._value.status.eq(value)) - ] - self.comb += self.ev.zero.trigger.eq(value != 0) diff --git a/misoc/dvisampler/__init__.py b/misoc/dvisampler/__init__.py new file mode 100644 index 00000000..073f8312 --- /dev/null +++ b/misoc/dvisampler/__init__.py @@ -0,0 +1 @@ +from misoc.dvisampler.core import DVISampler diff --git a/misoc/dvisampler/analysis.py b/misoc/dvisampler/analysis.py new file mode 100644 index 00000000..5f31a66f --- /dev/null +++ b/misoc/dvisampler/analysis.py @@ -0,0 +1,208 @@ +from migen import * +from migen.genlib.cdc import MultiReg, PulseSynchronizer +from migen.genlib.fifo import AsyncFIFO +from migen.genlib.record import Record +from migen.bank.description import * +from migen.flow.actor import * + +from misoc.dvisampler.common import channel_layout + + +class SyncPolarity(Module): + def __init__(self): + self.valid_i = Signal() + self.data_in0 = Record(channel_layout) + self.data_in1 = Record(channel_layout) + self.data_in2 = Record(channel_layout) + + self.valid_o = Signal() + self.de = Signal() + self.hsync = Signal() + self.vsync = Signal() + self.r = Signal(8) + self.g = Signal(8) + self.b = Signal(8) + + ### + + de = self.data_in0.de + de_r = Signal() + c = self.data_in0.c + c_polarity = Signal(2) + c_out = Signal(2) + + self.comb += [ + self.de.eq(de_r), + self.hsync.eq(c_out[0]), + self.vsync.eq(c_out[1]) + ] + + self.sync.pix += [ + self.valid_o.eq(self.valid_i), + self.r.eq(self.data_in2.d), + self.g.eq(self.data_in1.d), + self.b.eq(self.data_in0.d), + + de_r.eq(de), + If(de_r & ~de, + c_polarity.eq(c), + c_out.eq(0) + ).Else( + c_out.eq(c ^ c_polarity) + ) + ] + + +class ResolutionDetection(Module, AutoCSR): + def __init__(self, nbits=11): + self.valid_i = Signal() + self.vsync = Signal() + self.de = Signal() + + self._hres = CSRStatus(nbits) + self._vres = CSRStatus(nbits) + + ### + + # Detect DE transitions + de_r = Signal() + pn_de = Signal() + self.sync.pix += de_r.eq(self.de) + self.comb += pn_de.eq(~self.de & de_r) + + # HRES + hcounter = Signal(nbits) + self.sync.pix += If(self.valid_i & self.de, + hcounter.eq(hcounter + 1) + ).Else( + hcounter.eq(0) + ) + + hcounter_st = Signal(nbits) + self.sync.pix += If(self.valid_i, + If(pn_de, hcounter_st.eq(hcounter)) + ).Else( + hcounter_st.eq(0) + ) + self.specials += MultiReg(hcounter_st, self._hres.status) + + # VRES + vsync_r = Signal() + p_vsync = Signal() + self.sync.pix += vsync_r.eq(self.vsync), + self.comb += p_vsync.eq(self.vsync & ~vsync_r) + + vcounter = Signal(nbits) + self.sync.pix += If(self.valid_i & p_vsync, + vcounter.eq(0) + ).Elif(pn_de, + vcounter.eq(vcounter + 1) + ) + + vcounter_st = Signal(nbits) + self.sync.pix += If(self.valid_i, + If(p_vsync, vcounter_st.eq(vcounter)) + ).Else( + vcounter_st.eq(0) + ) + self.specials += MultiReg(vcounter_st, self._vres.status) + + +class FrameExtraction(Module, AutoCSR): + def __init__(self, word_width, fifo_depth): + # in pix clock domain + self.valid_i = Signal() + self.vsync = Signal() + self.de = Signal() + self.r = Signal(8) + self.g = Signal(8) + self.b = Signal(8) + + # in sys clock domain + word_layout = [("sof", 1), ("pixels", word_width)] + self.frame = Source(word_layout) + self.busy = Signal() + + self._overflow = CSR() + + ### + + # start of frame detection + vsync_r = Signal() + new_frame = Signal() + self.comb += new_frame.eq(self.vsync & ~vsync_r) + self.sync.pix += vsync_r.eq(self.vsync) + + # pack pixels into words + cur_word = Signal(word_width) + cur_word_valid = Signal() + encoded_pixel = Signal(24) + self.comb += encoded_pixel.eq(Cat(self.b, self.g, self.r)) + pack_factor = word_width//24 + assert(pack_factor & (pack_factor - 1) == 0) # only support powers of 2 + pack_counter = Signal(max=pack_factor) + self.sync.pix += [ + cur_word_valid.eq(0), + If(new_frame, + cur_word_valid.eq(pack_counter == (pack_factor - 1)), + pack_counter.eq(0), + ).Elif(self.valid_i & self.de, + [If(pack_counter == (pack_factor-i-1), + cur_word[24*i:24*(i+1)].eq(encoded_pixel)) for i in range(pack_factor)], + cur_word_valid.eq(pack_counter == (pack_factor - 1)), + pack_counter.eq(pack_counter + 1) + ) + ] + + # FIFO + fifo = RenameClockDomains(AsyncFIFO(word_layout, fifo_depth), + {"write": "pix", "read": "sys"}) + self.submodules += fifo + self.comb += [ + fifo.din.pixels.eq(cur_word), + fifo.we.eq(cur_word_valid) + ] + self.sync.pix += \ + If(new_frame, + fifo.din.sof.eq(1) + ).Elif(cur_word_valid, + fifo.din.sof.eq(0) + ) + self.comb += [ + self.frame.stb.eq(fifo.readable), + self.frame.payload.eq(fifo.dout), + fifo.re.eq(self.frame.ack), + self.busy.eq(0) + ] + + # overflow detection + pix_overflow = Signal() + pix_overflow_reset = Signal() + self.sync.pix += [ + If(fifo.we & ~fifo.writable, + pix_overflow.eq(1) + ).Elif(pix_overflow_reset, + pix_overflow.eq(0) + ) + ] + + sys_overflow = Signal() + self.specials += MultiReg(pix_overflow, sys_overflow) + self.submodules.overflow_reset = PulseSynchronizer("sys", "pix") + self.submodules.overflow_reset_ack = PulseSynchronizer("pix", "sys") + self.comb += [ + pix_overflow_reset.eq(self.overflow_reset.o), + self.overflow_reset_ack.i.eq(pix_overflow_reset) + ] + + overflow_mask = Signal() + self.comb += [ + self._overflow.w.eq(sys_overflow & ~overflow_mask), + self.overflow_reset.i.eq(self._overflow.re) + ] + self.sync += \ + If(self._overflow.re, + overflow_mask.eq(1) + ).Elif(self.overflow_reset_ack.o, + overflow_mask.eq(0) + ) diff --git a/misoc/dvisampler/chansync.py b/misoc/dvisampler/chansync.py new file mode 100644 index 00000000..ee416c39 --- /dev/null +++ b/misoc/dvisampler/chansync.py @@ -0,0 +1,132 @@ +from migen import * +from migen.genlib.cdc import MultiReg +from migen.genlib.fifo import _inc +from migen.genlib.record import Record, layout_len +from migen.genlib.misc import optree +from migen.bank.description import * + +from misoc.dvisampler.common import channel_layout + + +class _SyncBuffer(Module): + def __init__(self, width, depth): + self.din = Signal(width) + self.dout = Signal(width) + self.re = Signal() + + ### + + produce = Signal(max=depth) + consume = Signal(max=depth) + storage = Memory(width, depth) + self.specials += storage + + wrport = storage.get_port(write_capable=True) + self.specials += wrport + self.comb += [ + wrport.adr.eq(produce), + wrport.dat_w.eq(self.din), + wrport.we.eq(1) + ] + self.sync += _inc(produce, depth) + + rdport = storage.get_port(async_read=True) + self.specials += rdport + self.comb += [ + rdport.adr.eq(consume), + self.dout.eq(rdport.dat_r) + ] + self.sync += If(self.re, _inc(consume, depth)) + + +class ChanSync(Module, AutoCSR): + def __init__(self, nchan=3, depth=8): + self.valid_i = Signal() + self.chan_synced = Signal() + + self._channels_synced = CSRStatus() + + lst_control = [] + all_control = Signal() + for i in range(nchan): + name = "data_in" + str(i) + data_in = Record(channel_layout, name=name) + setattr(self, name, data_in) + name = "data_out" + str(i) + data_out = Record(channel_layout, name=name) + setattr(self, name, data_out) + + ### + + syncbuffer = RenameClockDomains(_SyncBuffer(layout_len(channel_layout), depth), "pix") + self.submodules += syncbuffer + self.comb += [ + syncbuffer.din.eq(data_in.raw_bits()), + data_out.raw_bits().eq(syncbuffer.dout) + ] + is_control = Signal() + self.comb += [ + is_control.eq(~data_out.de), + syncbuffer.re.eq(~is_control | all_control) + ] + lst_control.append(is_control) + + some_control = Signal() + self.comb += [ + all_control.eq(optree("&", lst_control)), + some_control.eq(optree("|", lst_control)) + ] + self.sync.pix += If(~self.valid_i, + self.chan_synced.eq(0) + ).Else( + If(some_control, + If(all_control, + self.chan_synced.eq(1) + ).Else( + self.chan_synced.eq(0) + ) + ) + ) + self.specials += MultiReg(self.chan_synced, self._channels_synced.status) + + +class _TB(Module): + def __init__(self, test_seq_it): + self.test_seq_it = test_seq_it + + self.submodules.chansync = RenameClockDomains(ChanSync(), {"pix": "sys"}) + self.comb += self.chansync.valid_i.eq(1) + + def do_simulation(self, selfp): + try: + de0, de1, de2 = next(self.test_seq_it) + except StopIteration: + raise StopSimulation + + selfp.chansync.data_in0.de = de0 + selfp.chansync.data_in1.de = de1 + selfp.chansync.data_in2.de = de2 + selfp.chansync.data_in0.d = selfp.simulator.cycle_counter + selfp.chansync.data_in1.d = selfp.simulator.cycle_counter + selfp.chansync.data_in2.d = selfp.simulator.cycle_counter + + out0 = selfp.chansync.data_out0.d + out1 = selfp.chansync.data_out1.d + out2 = selfp.chansync.data_out2.d + + print("{0:5} {1:5} {2:5}".format(out0, out1, out2)) + +if __name__ == "__main__": + from migen.sim.generic import run_simulation + + test_seq = [ + (1, 1, 1), + (1, 1, 0), + (0, 0, 0), + (0, 0, 0), + (0, 0, 1), + (1, 1, 1), + (1, 1, 1), + ] + tb = _TB(iter(test_seq*2)) + run_simulation(tb) diff --git a/misoc/dvisampler/charsync.py b/misoc/dvisampler/charsync.py new file mode 100644 index 00000000..266bb347 --- /dev/null +++ b/misoc/dvisampler/charsync.py @@ -0,0 +1,54 @@ +from migen import * +from migen.genlib.cdc import MultiReg +from migen.genlib.misc import optree +from migen.bank.description import * + +from misoc.dvisampler.common import control_tokens + + +class CharSync(Module, AutoCSR): + def __init__(self, required_controls=8): + self.raw_data = Signal(10) + self.synced = Signal() + self.data = Signal(10) + + self._char_synced = CSRStatus() + self._ctl_pos = CSRStatus(bits_for(9)) + + ### + + raw_data1 = Signal(10) + self.sync.pix += raw_data1.eq(self.raw_data) + raw = Signal(20) + self.comb += raw.eq(Cat(raw_data1, self.raw_data)) + + found_control = Signal() + control_position = Signal(max=10) + self.sync.pix += found_control.eq(0) + for i in range(10): + self.sync.pix += If(optree("|", [raw[i:i+10] == t for t in control_tokens]), + found_control.eq(1), + control_position.eq(i) + ) + + control_counter = Signal(max=required_controls) + previous_control_position = Signal(max=10) + word_sel = Signal(max=10) + self.sync.pix += [ + If(found_control & (control_position == previous_control_position), + If(control_counter == (required_controls - 1), + control_counter.eq(0), + self.synced.eq(1), + word_sel.eq(control_position) + ).Else( + control_counter.eq(control_counter + 1) + ) + ).Else( + control_counter.eq(0) + ), + previous_control_position.eq(control_position) + ] + self.specials += MultiReg(self.synced, self._char_synced.status) + self.specials += MultiReg(word_sel, self._ctl_pos.status) + + self.sync.pix += self.data.eq(raw >> word_sel) diff --git a/misoc/dvisampler/clocking.py b/misoc/dvisampler/clocking.py new file mode 100644 index 00000000..0fc8969b --- /dev/null +++ b/misoc/dvisampler/clocking.py @@ -0,0 +1,80 @@ +from migen import * +from migen.genlib.cdc import MultiReg +from migen.bank.description import * + + +class Clocking(Module, AutoCSR): + def __init__(self, pads): + self._pll_reset = CSRStorage(reset=1) + self._locked = CSRStatus() + + # DRP + self._pll_adr = CSRStorage(5) + self._pll_dat_r = CSRStatus(16) + self._pll_dat_w = CSRStorage(16) + self._pll_read = CSR() + self._pll_write = CSR() + self._pll_drdy = CSRStatus() + + self.locked = Signal() + self.serdesstrobe = Signal() + self.clock_domains._cd_pix = ClockDomain() + self.clock_domains._cd_pix2x = ClockDomain() + self.clock_domains._cd_pix10x = ClockDomain(reset_less=True) + + ### + + clk_se = Signal() + self.specials += Instance("IBUFDS", i_I=pads.clk_p, i_IB=pads.clk_n, o_O=clk_se) + + clkfbout = Signal() + pll_locked = Signal() + pll_clk0 = Signal() + pll_clk1 = Signal() + pll_clk2 = Signal() + pll_drdy = Signal() + self.sync += If(self._pll_read.re | self._pll_write.re, + self._pll_drdy.status.eq(0) + ).Elif(pll_drdy, + self._pll_drdy.status.eq(1) + ) + self.specials += Instance("PLL_ADV", + p_CLKFBOUT_MULT=10, + p_CLKOUT0_DIVIDE=1, # pix10x + p_CLKOUT1_DIVIDE=5, # pix2x + p_CLKOUT2_DIVIDE=10, # pix + p_COMPENSATION="INTERNAL", + + i_CLKINSEL=1, + i_CLKIN1=clk_se, + o_CLKOUT0=pll_clk0, o_CLKOUT1=pll_clk1, o_CLKOUT2=pll_clk2, + o_CLKFBOUT=clkfbout, i_CLKFBIN=clkfbout, + o_LOCKED=pll_locked, i_RST=self._pll_reset.storage, + + i_DADDR=self._pll_adr.storage, + o_DO=self._pll_dat_r.status, + i_DI=self._pll_dat_w.storage, + i_DEN=self._pll_read.re | self._pll_write.re, + i_DWE=self._pll_write.re, + o_DRDY=pll_drdy, + i_DCLK=ClockSignal()) + + locked_async = Signal() + self.specials += [ + Instance("BUFPLL", p_DIVIDE=5, + i_PLLIN=pll_clk0, i_GCLK=ClockSignal("pix2x"), i_LOCKED=pll_locked, + o_IOCLK=self._cd_pix10x.clk, o_LOCK=locked_async, o_SERDESSTROBE=self.serdesstrobe), + Instance("BUFG", i_I=pll_clk1, o_O=self._cd_pix2x.clk), + Instance("BUFG", i_I=pll_clk2, o_O=self._cd_pix.clk), + MultiReg(locked_async, self.locked, "sys") + ] + self.comb += self._locked.status.eq(self.locked) + + # sychronize pix+pix2x reset + pix_rst_n = 1 + for i in range(2): + new_pix_rst_n = Signal() + self.specials += Instance("FDCE", i_D=pix_rst_n, i_CE=1, i_C=ClockSignal("pix"), + i_CLR=~locked_async, o_Q=new_pix_rst_n) + pix_rst_n = new_pix_rst_n + self.comb += self._cd_pix.rst.eq(~pix_rst_n), self._cd_pix2x.rst.eq(~pix_rst_n) diff --git a/misoc/dvisampler/common.py b/misoc/dvisampler/common.py new file mode 100644 index 00000000..7fb9a420 --- /dev/null +++ b/misoc/dvisampler/common.py @@ -0,0 +1,2 @@ +control_tokens = [0b1101010100, 0b0010101011, 0b0101010100, 0b1010101011] +channel_layout = [("d", 8), ("c", 2), ("de", 1)] diff --git a/misoc/dvisampler/core.py b/misoc/dvisampler/core.py new file mode 100644 index 00000000..545fbe89 --- /dev/null +++ b/misoc/dvisampler/core.py @@ -0,0 +1,80 @@ +from migen import * +from migen.bank.description import AutoCSR + +from misoc.dvisampler.edid import EDID +from misoc.dvisampler.clocking import Clocking +from misoc.dvisampler.datacapture import DataCapture +from misoc.dvisampler.charsync import CharSync +from misoc.dvisampler.wer import WER +from misoc.dvisampler.decoding import Decoding +from misoc.dvisampler.chansync import ChanSync +from misoc.dvisampler.analysis import SyncPolarity, ResolutionDetection, FrameExtraction +from misoc.dvisampler.dma import DMA + + +class DVISampler(Module, AutoCSR): + def __init__(self, pads, lasmim, n_dma_slots=2, fifo_depth=512): + self.submodules.edid = EDID(pads) + self.submodules.clocking = Clocking(pads) + + for datan in range(3): + name = "data" + str(datan) + + cap = DataCapture(getattr(pads, name + "_p"), getattr(pads, name + "_n"), 8) + setattr(self.submodules, name + "_cap", cap) + self.comb += cap.serdesstrobe.eq(self.clocking.serdesstrobe) + + charsync = CharSync() + setattr(self.submodules, name + "_charsync", charsync) + self.comb += charsync.raw_data.eq(cap.d) + + wer = WER() + setattr(self.submodules, name + "_wer", wer) + self.comb += wer.data.eq(charsync.data) + + decoding = Decoding() + setattr(self.submodules, name + "_decod", decoding) + self.comb += [ + decoding.valid_i.eq(charsync.synced), + decoding.input.eq(charsync.data) + ] + + self.submodules.chansync = ChanSync() + self.comb += [ + self.chansync.valid_i.eq(self.data0_decod.valid_o & \ + self.data1_decod.valid_o & self.data2_decod.valid_o), + self.chansync.data_in0.eq(self.data0_decod.output), + self.chansync.data_in1.eq(self.data1_decod.output), + self.chansync.data_in2.eq(self.data2_decod.output), + ] + + self.submodules.syncpol = SyncPolarity() + self.comb += [ + self.syncpol.valid_i.eq(self.chansync.chan_synced), + self.syncpol.data_in0.eq(self.chansync.data_out0), + self.syncpol.data_in1.eq(self.chansync.data_out1), + self.syncpol.data_in2.eq(self.chansync.data_out2) + ] + + self.submodules.resdetection = ResolutionDetection() + self.comb += [ + self.resdetection.valid_i.eq(self.syncpol.valid_o), + self.resdetection.de.eq(self.syncpol.de), + self.resdetection.vsync.eq(self.syncpol.vsync) + ] + + self.submodules.frame = FrameExtraction(24*lasmim.dw//32, fifo_depth) + self.comb += [ + self.frame.valid_i.eq(self.syncpol.valid_o), + self.frame.de.eq(self.syncpol.de), + self.frame.vsync.eq(self.syncpol.vsync), + self.frame.r.eq(self.syncpol.r), + self.frame.g.eq(self.syncpol.g), + self.frame.b.eq(self.syncpol.b) + ] + + self.submodules.dma = DMA(lasmim, n_dma_slots) + self.comb += self.frame.frame.connect(self.dma.frame) + self.ev = self.dma.ev + + autocsr_exclude = {"ev"} diff --git a/misoc/dvisampler/datacapture.py b/misoc/dvisampler/datacapture.py new file mode 100644 index 00000000..49f0ccef --- /dev/null +++ b/misoc/dvisampler/datacapture.py @@ -0,0 +1,187 @@ +from migen import * +from migen.genlib.cdc import MultiReg, PulseSynchronizer +from migen.bank.description import * + + +class DataCapture(Module, AutoCSR): + def __init__(self, pad_p, pad_n, ntbits): + self.serdesstrobe = Signal() + self.d = Signal(10) + + self._dly_ctl = CSR(6) + self._dly_busy = CSRStatus(2) + self._phase = CSRStatus(2) + self._phase_reset = CSR() + + ### + + # IO + pad_se = Signal() + self.specials += Instance("IBUFDS", i_I=pad_p, i_IB=pad_n, o_O=pad_se) + + pad_delayed_master = Signal() + pad_delayed_slave = Signal() + delay_inc = Signal() + delay_ce = Signal() + delay_master_cal = Signal() + delay_master_rst = Signal() + delay_master_busy = Signal() + delay_slave_cal = Signal() + delay_slave_rst = Signal() + delay_slave_busy = Signal() + self.specials += Instance("IODELAY2", + p_SERDES_MODE="MASTER", + p_DELAY_SRC="IDATAIN", p_IDELAY_TYPE="DIFF_PHASE_DETECTOR", + p_COUNTER_WRAPAROUND="STAY_AT_LIMIT", p_DATA_RATE="SDR", + + i_IDATAIN=pad_se, o_DATAOUT=pad_delayed_master, + i_CLK=ClockSignal("pix2x"), i_IOCLK0=ClockSignal("pix10x"), + + i_INC=delay_inc, i_CE=delay_ce, + i_CAL=delay_master_cal, i_RST=delay_master_rst, o_BUSY=delay_master_busy, + i_T=1) + self.specials += Instance("IODELAY2", + p_SERDES_MODE="SLAVE", + p_DELAY_SRC="IDATAIN", p_IDELAY_TYPE="DIFF_PHASE_DETECTOR", + p_COUNTER_WRAPAROUND="WRAPAROUND", p_DATA_RATE="SDR", + + i_IDATAIN=pad_se, o_DATAOUT=pad_delayed_slave, + i_CLK=ClockSignal("pix2x"), i_IOCLK0=ClockSignal("pix10x"), + + i_INC=delay_inc, i_CE=delay_ce, + i_CAL=delay_slave_cal, i_RST=delay_slave_rst, o_BUSY=delay_slave_busy, + i_T=1) + + dsr2 = Signal(5) + pd_valid = Signal() + pd_incdec = Signal() + pd_edge = Signal() + pd_cascade = Signal() + self.specials += Instance("ISERDES2", + p_SERDES_MODE="MASTER", + p_BITSLIP_ENABLE="FALSE", p_DATA_RATE="SDR", p_DATA_WIDTH=5, + p_INTERFACE_TYPE="RETIMED", + + i_D=pad_delayed_master, + o_Q4=dsr2[4], o_Q3=dsr2[3], o_Q2=dsr2[2], o_Q1=dsr2[1], + + i_BITSLIP=0, i_CE0=1, i_RST=0, + i_CLK0=ClockSignal("pix10x"), i_CLKDIV=ClockSignal("pix2x"), + i_IOCE=self.serdesstrobe, + + o_VALID=pd_valid, o_INCDEC=pd_incdec, + i_SHIFTIN=pd_edge, o_SHIFTOUT=pd_cascade) + self.specials += Instance("ISERDES2", + p_SERDES_MODE="SLAVE", + p_BITSLIP_ENABLE="FALSE", p_DATA_RATE="SDR", p_DATA_WIDTH=5, + p_INTERFACE_TYPE="RETIMED", + + i_D=pad_delayed_slave, + o_Q4=dsr2[0], + + i_BITSLIP=0, i_CE0=1, i_RST=0, + i_CLK0=ClockSignal("pix10x"), i_CLKDIV=ClockSignal("pix2x"), + i_IOCE=self.serdesstrobe, + + i_SHIFTIN=pd_cascade, o_SHIFTOUT=pd_edge) + + # Phase error accumulator + lateness = Signal(ntbits, reset=2**(ntbits - 1)) + too_late = Signal() + too_early = Signal() + reset_lateness = Signal() + self.comb += [ + too_late.eq(lateness == (2**ntbits - 1)), + too_early.eq(lateness == 0) + ] + self.sync.pix2x += [ + If(reset_lateness, + lateness.eq(2**(ntbits - 1)) + ).Elif(~delay_master_busy & ~delay_slave_busy & ~too_late & ~too_early, + If(pd_valid & pd_incdec, lateness.eq(lateness - 1)), + If(pd_valid & ~pd_incdec, lateness.eq(lateness + 1)) + ) + ] + + # Delay control + self.submodules.delay_master_done = PulseSynchronizer("pix2x", "sys") + delay_master_pending = Signal() + self.sync.pix2x += [ + self.delay_master_done.i.eq(0), + If(~delay_master_pending, + If(delay_master_cal | delay_ce, delay_master_pending.eq(1)) + ).Else( + If(~delay_master_busy, + self.delay_master_done.i.eq(1), + delay_master_pending.eq(0) + ) + ) + ] + self.submodules.delay_slave_done = PulseSynchronizer("pix2x", "sys") + delay_slave_pending = Signal() + self.sync.pix2x += [ + self.delay_slave_done.i.eq(0), + If(~delay_slave_pending, + If(delay_slave_cal | delay_ce, delay_slave_pending.eq(1)) + ).Else( + If(~delay_slave_busy, + self.delay_slave_done.i.eq(1), + delay_slave_pending.eq(0) + ) + ) + ] + + self.submodules.do_delay_master_cal = PulseSynchronizer("sys", "pix2x") + self.submodules.do_delay_master_rst = PulseSynchronizer("sys", "pix2x") + self.submodules.do_delay_slave_cal = PulseSynchronizer("sys", "pix2x") + self.submodules.do_delay_slave_rst = PulseSynchronizer("sys", "pix2x") + self.submodules.do_delay_inc = PulseSynchronizer("sys", "pix2x") + self.submodules.do_delay_dec = PulseSynchronizer("sys", "pix2x") + self.comb += [ + delay_master_cal.eq(self.do_delay_master_cal.o), + delay_master_rst.eq(self.do_delay_master_rst.o), + delay_slave_cal.eq(self.do_delay_slave_cal.o), + delay_slave_rst.eq(self.do_delay_slave_rst.o), + delay_inc.eq(self.do_delay_inc.o), + delay_ce.eq(self.do_delay_inc.o | self.do_delay_dec.o), + ] + + sys_delay_master_pending = Signal() + self.sync += [ + If(self.do_delay_master_cal.i | self.do_delay_inc.i | self.do_delay_dec.i, + sys_delay_master_pending.eq(1) + ).Elif(self.delay_master_done.o, + sys_delay_master_pending.eq(0) + ) + ] + sys_delay_slave_pending = Signal() + self.sync += [ + If(self.do_delay_slave_cal.i | self.do_delay_inc.i | self.do_delay_dec.i, + sys_delay_slave_pending.eq(1) + ).Elif(self.delay_slave_done.o, + sys_delay_slave_pending.eq(0) + ) + ] + + self.comb += [ + self.do_delay_master_cal.i.eq(self._dly_ctl.re & self._dly_ctl.r[0]), + self.do_delay_master_rst.i.eq(self._dly_ctl.re & self._dly_ctl.r[1]), + self.do_delay_slave_cal.i.eq(self._dly_ctl.re & self._dly_ctl.r[2]), + self.do_delay_slave_rst.i.eq(self._dly_ctl.re & self._dly_ctl.r[3]), + self.do_delay_inc.i.eq(self._dly_ctl.re & self._dly_ctl.r[4]), + self.do_delay_dec.i.eq(self._dly_ctl.re & self._dly_ctl.r[5]), + self._dly_busy.status.eq(Cat(sys_delay_master_pending, sys_delay_slave_pending)) + ] + + # Phase detector control + self.specials += MultiReg(Cat(too_late, too_early), self._phase.status) + self.submodules.do_reset_lateness = PulseSynchronizer("sys", "pix2x") + self.comb += [ + reset_lateness.eq(self.do_reset_lateness.o), + self.do_reset_lateness.i.eq(self._phase_reset.re) + ] + + # 5:10 deserialization + dsr = Signal(10) + self.sync.pix2x += dsr.eq(Cat(dsr[5:], dsr2)) + self.sync.pix += self.d.eq(dsr) diff --git a/misoc/dvisampler/debug.py b/misoc/dvisampler/debug.py new file mode 100644 index 00000000..4121514f --- /dev/null +++ b/misoc/dvisampler/debug.py @@ -0,0 +1,48 @@ +from migen import * +from migen.genlib.fifo import AsyncFIFO +from migen.genlib.record import layout_len +from migen.bank.description import AutoCSR +from migen.actorlib import structuring, spi + +from misoc.mem.sdram.frontend import dma_lasmi +from misoc.dvisampler.edid import EDID +from misoc.dvisampler.clocking import Clocking +from misoc.dvisampler.datacapture import DataCapture + + +class RawDVISampler(Module, AutoCSR): + def __init__(self, pads, asmiport): + self.submodules.edid = EDID(pads) + self.submodules.clocking = Clocking(pads) + + invert = False + try: + s = getattr(pads, "data0") + except AttributeError: + s = getattr(pads, "data0_n") + invert = True + self.submodules.data0_cap = DataCapture(8, invert) + self.comb += [ + self.data0_cap.pad.eq(s), + self.data0_cap.serdesstrobe.eq(self.clocking.serdesstrobe) + ] + + fifo = RenameClockDomains(AsyncFIFO(10, 256), + {"write": "pix", "read": "sys"}) + self.submodules += fifo + self.comb += [ + fifo.din.eq(self.data0_cap.d), + fifo.we.eq(1) + ] + + pack_factor = asmiport.hub.dw//16 + self.submodules.packer = structuring.Pack([("word", 10), ("pad", 6)], pack_factor) + self.submodules.cast = structuring.Cast(self.packer.source.payload.layout, asmiport.hub.dw) + self.submodules.dma = spi.DMAWriteController(dma_lasmi.Writer(lasmim), spi.MODE_SINGLE_SHOT) + self.comb += [ + self.packer.sink.stb.eq(fifo.readable), + fifo.re.eq(self.packer.sink.ack), + self.packer.sink.word.eq(fifo.dout), + self.packer.source.connect_flat(self.cast.sink), + self.cast.source.connect_flat(self.dma.data) + ] diff --git a/misoc/dvisampler/decoding.py b/misoc/dvisampler/decoding.py new file mode 100644 index 00000000..6035c4c7 --- /dev/null +++ b/misoc/dvisampler/decoding.py @@ -0,0 +1,25 @@ +from migen import * +from migen.genlib.record import Record + +from misoc.dvisampler.common import control_tokens, channel_layout + + +class Decoding(Module): + def __init__(self): + self.valid_i = Signal() + self.input = Signal(10) + self.valid_o = Signal() + self.output = Record(channel_layout) + + ### + + self.sync.pix += self.output.de.eq(1) + for i, t in enumerate(control_tokens): + self.sync.pix += If(self.input == t, + self.output.de.eq(0), + self.output.c.eq(i) + ) + self.sync.pix += self.output.d[0].eq(self.input[0] ^ self.input[9]) + for i in range(1, 8): + self.sync.pix += self.output.d[i].eq(self.input[i] ^ self.input[i-1] ^ ~self.input[8]) + self.sync.pix += self.valid_o.eq(self.valid_i) diff --git a/misoc/dvisampler/dma.py b/misoc/dvisampler/dma.py new file mode 100644 index 00000000..7ed4f829 --- /dev/null +++ b/misoc/dvisampler/dma.py @@ -0,0 +1,144 @@ +from migen import * +from migen.genlib.fsm import FSM, NextState +from migen.bank.description import * +from migen.bank.eventmanager import * +from migen.flow.actor import * + +from misoc.mem.sdram.frontend import dma_lasmi + + +# Slot status: EMPTY=0 LOADED=1 PENDING=2 +class _Slot(Module, AutoCSR): + def __init__(self, addr_bits, alignment_bits): + self.ev_source = EventSourceLevel() + self.address = Signal(addr_bits) + self.address_reached = Signal(addr_bits) + self.address_valid = Signal() + self.address_done = Signal() + + self._status = CSRStorage(2, write_from_dev=True) + self._address = CSRStorage(addr_bits + alignment_bits, alignment_bits=alignment_bits, write_from_dev=True) + + ### + + self.comb += [ + self.address.eq(self._address.storage), + self.address_valid.eq(self._status.storage[0]), + self._status.dat_w.eq(2), + self._status.we.eq(self.address_done), + self._address.dat_w.eq(self.address_reached), + self._address.we.eq(self.address_done), + self.ev_source.trigger.eq(self._status.storage[1]) + ] + + +class _SlotArray(Module, AutoCSR): + def __init__(self, nslots, addr_bits, alignment_bits): + self.submodules.ev = EventManager() + self.address = Signal(addr_bits) + self.address_reached = Signal(addr_bits) + self.address_valid = Signal() + self.address_done = Signal() + + ### + + slots = [_Slot(addr_bits, alignment_bits) for i in range(nslots)] + for n, slot in enumerate(slots): + setattr(self.submodules, "slot"+str(n), slot) + setattr(self.ev, "slot"+str(n), slot.ev_source) + self.ev.finalize() + + change_slot = Signal() + current_slot = Signal(max=nslots) + self.sync += If(change_slot, [If(slot.address_valid, current_slot.eq(n)) for n, slot in reversed(list(enumerate(slots)))]) + self.comb += change_slot.eq(~self.address_valid | self.address_done) + + self.comb += [ + self.address.eq(Array(slot.address for slot in slots)[current_slot]), + self.address_valid.eq(Array(slot.address_valid for slot in slots)[current_slot]) + ] + self.comb += [slot.address_reached.eq(self.address_reached) for slot in slots] + self.comb += [slot.address_done.eq(self.address_done & (current_slot == n)) for n, slot in enumerate(slots)] + + +class DMA(Module): + def __init__(self, lasmim, nslots): + bus_aw = lasmim.aw + bus_dw = lasmim.dw + alignment_bits = bits_for(bus_dw//8) - 1 + + fifo_word_width = 24*bus_dw//32 + self.frame = Sink([("sof", 1), ("pixels", fifo_word_width)]) + self._frame_size = CSRStorage(bus_aw + alignment_bits, alignment_bits=alignment_bits) + self.submodules._slot_array = _SlotArray(nslots, bus_aw, alignment_bits) + self.ev = self._slot_array.ev + + ### + + # address generator + maximum memory word count to prevent DMA buffer overrun + reset_words = Signal() + count_word = Signal() + last_word = Signal() + current_address = Signal(bus_aw) + mwords_remaining = Signal(bus_aw) + self.comb += [ + self._slot_array.address_reached.eq(current_address), + last_word.eq(mwords_remaining == 1) + ] + self.sync += [ + If(reset_words, + current_address.eq(self._slot_array.address), + mwords_remaining.eq(self._frame_size.storage) + ).Elif(count_word, + current_address.eq(current_address + 1), + mwords_remaining.eq(mwords_remaining - 1) + ) + ] + + # 24bpp -> 32bpp + memory_word = Signal(bus_dw) + pixbits = [] + for i in range(bus_dw//32): + for j in range(3): + b = (i*3+j)*8 + pixbits.append(self.frame.pixels[b+6:b+8]) + pixbits.append(self.frame.pixels[b:b+8]) + pixbits.append(0) + pixbits.append(0) + self.comb += memory_word.eq(Cat(*pixbits)) + + # bus accessor + self.submodules._bus_accessor = dma_lasmi.Writer(lasmim) + self.comb += [ + self._bus_accessor.address_data.a.eq(current_address), + self._bus_accessor.address_data.d.eq(memory_word) + ] + + # control FSM + fsm = FSM() + self.submodules += fsm + + fsm.act("WAIT_SOF", + reset_words.eq(1), + self.frame.ack.eq(~self._slot_array.address_valid | ~self.frame.sof), + If(self._slot_array.address_valid & self.frame.sof & self.frame.stb, NextState("TRANSFER_PIXELS")) + ) + fsm.act("TRANSFER_PIXELS", + self.frame.ack.eq(self._bus_accessor.address_data.ack), + If(self.frame.stb, + self._bus_accessor.address_data.stb.eq(1), + If(self._bus_accessor.address_data.ack, + count_word.eq(1), + If(last_word, NextState("EOF")) + ) + ) + ) + fsm.act("EOF", + If(~self._bus_accessor.busy, + self._slot_array.address_done.eq(1), + NextState("WAIT_SOF") + ) + ) + + def get_csrs(self): + return [self._frame_size] + self._slot_array.get_csrs() diff --git a/misoc/dvisampler/edid.py b/misoc/dvisampler/edid.py new file mode 100644 index 00000000..eb674b3e --- /dev/null +++ b/misoc/dvisampler/edid.py @@ -0,0 +1,194 @@ +from migen import * +from migen.fhdl.specials import Tristate +from migen.genlib.cdc import MultiReg +from migen.genlib.fsm import FSM, NextState +from migen.genlib.misc import chooser +from migen.bank.description import CSRStorage, CSRStatus, AutoCSR + +_default_edid = [ + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x3D, 0x17, 0x32, 0x12, 0x2A, 0x6A, 0xBF, 0x00, + 0x05, 0x17, 0x01, 0x03, 0x80, 0x28, 0x1E, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0xB2, 0x0C, 0x00, 0x40, 0x41, 0x00, 0x26, 0x30, 0x18, 0x88, + 0x36, 0x00, 0x28, 0x1E, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x4D, 0x31, 0x20, + 0x44, 0x56, 0x49, 0x20, 0x6D, 0x69, 0x78, 0x65, 0x72, 0x0A, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, +] + + +class EDID(Module, AutoCSR): + def __init__(self, pads, default=_default_edid): + self._hpd_notif = CSRStatus() + self._hpd_en = CSRStorage() + self.specials.mem = Memory(8, 128, init=default) + + ### + + # HPD + if hasattr(pads, "hpd_notif"): + self.specials += MultiReg(pads.hpd_notif, self._hpd_notif.status) + else: + self.comb += self._hpd_notif.status.eq(1) + if hasattr(pads, "hpd_en"): + self.comb += pads.hpd_en.eq(self._hpd_en.storage) + + # EDID + scl_raw = Signal() + sda_i = Signal() + sda_raw = Signal() + sda_drv = Signal() + _sda_drv_reg = Signal() + _sda_i_async = Signal() + self.sync += _sda_drv_reg.eq(sda_drv) + self.specials += [ + MultiReg(pads.scl, scl_raw), + Tristate(pads.sda, 0, _sda_drv_reg, _sda_i_async), + MultiReg(_sda_i_async, sda_raw) + ] + + scl_i = Signal() + samp_count = Signal(6) + samp_carry = Signal() + self.sync += [ + Cat(samp_count, samp_carry).eq(samp_count + 1), + If(samp_carry, + scl_i.eq(scl_raw), + sda_i.eq(sda_raw) + ) + ] + + scl_r = Signal() + sda_r = Signal() + scl_rising = Signal() + sda_rising = Signal() + sda_falling = Signal() + self.sync += [ + scl_r.eq(scl_i), + sda_r.eq(sda_i) + ] + self.comb += [ + scl_rising.eq(scl_i & ~scl_r), + sda_rising.eq(sda_i & ~sda_r), + sda_falling.eq(~sda_i & sda_r) + ] + + start = Signal() + self.comb += start.eq(scl_i & sda_falling) + + din = Signal(8) + counter = Signal(max=9) + self.sync += [ + If(start, counter.eq(0)), + If(scl_rising, + If(counter == 8, + counter.eq(0) + ).Else( + counter.eq(counter + 1), + din.eq(Cat(sda_i, din[:7])) + ) + ) + ] + + is_read = Signal() + update_is_read = Signal() + self.sync += If(update_is_read, is_read.eq(din[0])) + + offset_counter = Signal(max=128) + oc_load = Signal() + oc_inc = Signal() + self.sync += [ + If(oc_load, + offset_counter.eq(din) + ).Elif(oc_inc, + offset_counter.eq(offset_counter + 1) + ) + ] + rdport = self.mem.get_port() + self.specials += rdport + self.comb += rdport.adr.eq(offset_counter) + data_bit = Signal() + + zero_drv = Signal() + data_drv = Signal() + self.comb += If(zero_drv, sda_drv.eq(1)).Elif(data_drv, sda_drv.eq(~data_bit)) + + data_drv_en = Signal() + data_drv_stop = Signal() + self.sync += If(data_drv_en, data_drv.eq(1)).Elif(data_drv_stop, data_drv.eq(0)) + self.sync += If(data_drv_en, chooser(rdport.dat_r, counter, data_bit, 8, reverse=True)) + + fsm = FSM() + self.submodules += fsm + + fsm.act("WAIT_START") + fsm.act("RCV_ADDRESS", + If(counter == 8, + If(din[1:] == 0x50, + update_is_read.eq(1), + NextState("ACK_ADDRESS0") + ).Else( + NextState("WAIT_START") + ) + ) + ) + fsm.act("ACK_ADDRESS0", + If(~scl_i, NextState("ACK_ADDRESS1")) + ) + fsm.act("ACK_ADDRESS1", + zero_drv.eq(1), + If(scl_i, NextState("ACK_ADDRESS2")) + ) + fsm.act("ACK_ADDRESS2", + zero_drv.eq(1), + If(~scl_i, + If(is_read, + NextState("READ") + ).Else( + NextState("RCV_OFFSET") + ) + ) + ) + + fsm.act("RCV_OFFSET", + If(counter == 8, + oc_load.eq(1), + NextState("ACK_OFFSET0") + ) + ) + fsm.act("ACK_OFFSET0", + If(~scl_i, NextState("ACK_OFFSET1")) + ) + fsm.act("ACK_OFFSET1", + zero_drv.eq(1), + If(scl_i, NextState("ACK_OFFSET2")) + ) + fsm.act("ACK_OFFSET2", + zero_drv.eq(1), + If(~scl_i, NextState("RCV_ADDRESS")) + ) + + fsm.act("READ", + If(~scl_i, + If(counter == 8, + data_drv_stop.eq(1), + NextState("ACK_READ") + ).Else( + data_drv_en.eq(1) + ) + ) + ) + fsm.act("ACK_READ", + If(scl_rising, + oc_inc.eq(1), + If(sda_i, + NextState("WAIT_START") + ).Else( + NextState("READ") + ) + ) + ) + + for state in fsm.actions.keys(): + fsm.act(state, If(start, NextState("RCV_ADDRESS"))) + fsm.act(state, If(~self._hpd_en.storage, NextState("WAIT_START"))) diff --git a/misoc/dvisampler/wer.py b/misoc/dvisampler/wer.py new file mode 100644 index 00000000..904c225b --- /dev/null +++ b/misoc/dvisampler/wer.py @@ -0,0 +1,60 @@ +from migen import * +from migen.bank.description import * +from migen.genlib.misc import optree +from migen.genlib.cdc import PulseSynchronizer + +from misoc.dvisampler.common import control_tokens + + +class WER(Module, AutoCSR): + def __init__(self, period_bits=24): + self.data = Signal(10) + self._update = CSR() + self._value = CSRStatus(period_bits) + + ### + + # pipeline stage 1 + # we ignore the 10th (inversion) bit, as it is independent of the transition minimization + data_r = Signal(9) + self.sync.pix += data_r.eq(self.data[:9]) + + # pipeline stage 2 + transitions = Signal(8) + self.comb += [transitions[i].eq(data_r[i] ^ data_r[i+1]) for i in range(8)] + transition_count = Signal(max=9) + self.sync.pix += transition_count.eq(optree("+", [transitions[i] for i in range(8)])) + + is_control = Signal() + self.sync.pix += is_control.eq(optree("|", [data_r == ct for ct in control_tokens])) + + # pipeline stage 3 + is_error = Signal() + self.sync.pix += is_error.eq((transition_count > 4) & ~is_control) + + # counter + period_counter = Signal(period_bits) + period_done = Signal() + self.sync.pix += Cat(period_counter, period_done).eq(period_counter + 1) + + wer_counter = Signal(period_bits) + wer_counter_r = Signal(period_bits) + wer_counter_r_updated = Signal() + self.sync.pix += [ + wer_counter_r_updated.eq(period_done), + If(period_done, + wer_counter_r.eq(wer_counter), + wer_counter.eq(0) + ).Elif(is_error, + wer_counter.eq(wer_counter + 1) + ) + ] + + # sync to system clock domain + wer_counter_sys = Signal(period_bits) + self.submodules.ps_counter = PulseSynchronizer("pix", "sys") + self.comb += self.ps_counter.i.eq(wer_counter_r_updated) + self.sync += If(self.ps_counter.o, wer_counter_sys.eq(wer_counter_r)) + + # register interface + self.sync += If(self._update.re, self._value.status.eq(wer_counter_sys)) diff --git a/misoc/framebuffer/__init__.py b/misoc/framebuffer/__init__.py new file mode 100644 index 00000000..28889bac --- /dev/null +++ b/misoc/framebuffer/__init__.py @@ -0,0 +1 @@ +from misoc.framebuffer.core import Framebuffer diff --git a/misoc/framebuffer/core.py b/misoc/framebuffer/core.py new file mode 100644 index 00000000..fc7ce067 --- /dev/null +++ b/misoc/framebuffer/core.py @@ -0,0 +1,33 @@ +from migen import * +from migen.flow.network import * +from migen.flow import plumbing +from migen.bank.description import AutoCSR +from migen.actorlib import structuring, misc + +from misoc.mem.sdram.frontend import dma_lasmi +from misoc.framebuffer.format import bpp, pixel_layout, FrameInitiator, VTG +from misoc.framebuffer.phy import Driver + + +class Framebuffer(Module, AutoCSR): + def __init__(self, pads_vga, pads_dvi, lasmim): + pack_factor = lasmim.dw//bpp + + g = DataFlowGraph() + + self.fi = FrameInitiator(lasmim.aw, pack_factor) + + intseq = misc.IntSequence(lasmim.aw, lasmim.aw) + dma_out = AbstractActor(plumbing.Buffer) + g.add_connection(self.fi, intseq, source_subr=self.fi.dma_subr()) + g.add_pipeline(intseq, AbstractActor(plumbing.Buffer), dma_lasmi.Reader(lasmim), dma_out) + + cast = structuring.Cast(lasmim.dw, pixel_layout(pack_factor), reverse_to=True) + vtg = VTG(pack_factor) + self.driver = Driver(pack_factor, pads_vga, pads_dvi) + + g.add_connection(self.fi, vtg, source_subr=self.fi.timing_subr, sink_ep="timing") + g.add_connection(dma_out, cast) + g.add_connection(cast, vtg, sink_ep="pixels") + g.add_connection(vtg, self.driver) + self.submodules += CompositeActor(g) diff --git a/misoc/framebuffer/dvi.py b/misoc/framebuffer/dvi.py new file mode 100644 index 00000000..03bb6acd --- /dev/null +++ b/misoc/framebuffer/dvi.py @@ -0,0 +1,224 @@ +from migen import * +from migen.genlib.misc import optree + +control_tokens = [0b1101010100, 0b0010101011, 0b0101010100, 0b1010101011] + + +class Encoder(Module): + def __init__(self): + self.d = Signal(8) + self.c = Signal(2) + self.de = Signal() + + self.out = Signal(10) + + ### + + # stage 1 - count number of 1s in data + d = Signal(8) + n1d = Signal(max=9) + self.sync += [ + n1d.eq(optree("+", [self.d[i] for i in range(8)])), + d.eq(self.d) + ] + + # stage 2 - add 9th bit + q_m = Signal(9) + q_m8_n = Signal() + self.comb += q_m8_n.eq((n1d > 4) | ((n1d == 4) & ~d[0])) + for i in range(8): + if i: + curval = curval ^ d[i] ^ q_m8_n + else: + curval = d[0] + self.sync += q_m[i].eq(curval) + self.sync += q_m[8].eq(~q_m8_n) + + # stage 3 - count number of 1s and 0s in q_m[:8] + q_m_r = Signal(9) + n0q_m = Signal(max=9) + n1q_m = Signal(max=9) + self.sync += [ + n0q_m.eq(optree("+", [~q_m[i] for i in range(8)])), + n1q_m.eq(optree("+", [q_m[i] for i in range(8)])), + q_m_r.eq(q_m) + ] + + # stage 4 - final encoding + cnt = Signal((6, True)) + + s_c = self.c + s_de = self.de + for p in range(3): + new_c = Signal(2) + new_de = Signal() + self.sync += new_c.eq(s_c), new_de.eq(s_de) + s_c, s_de = new_c, new_de + + self.sync += If(s_de, + If((cnt == 0) | (n1q_m == n0q_m), + self.out[9].eq(~q_m_r[8]), + self.out[8].eq(q_m_r[8]), + If(q_m_r[8], + self.out[:8].eq(q_m_r[:8]), + cnt.eq(cnt + n1q_m - n0q_m) + ).Else( + self.out[:8].eq(~q_m_r[:8]), + cnt.eq(cnt + n0q_m - n1q_m) + ) + ).Else( + If((~cnt[5] & (n1q_m > n0q_m)) | (cnt[5] & (n0q_m > n1q_m)), + self.out[9].eq(1), + self.out[8].eq(q_m_r[8]), + self.out[:8].eq(~q_m_r[:8]), + cnt.eq(cnt + Cat(0, q_m_r[8]) + n0q_m - n1q_m) + ).Else( + self.out[9].eq(0), + self.out[8].eq(q_m_r[8]), + self.out[:8].eq(q_m_r[:8]), + cnt.eq(cnt - Cat(0, ~q_m_r[8]) + n1q_m - n0q_m) + ) + ) + ).Else( + self.out.eq(Array(control_tokens)[s_c]), + cnt.eq(0) + ) + + +class _EncoderSerializer(Module): + def __init__(self, serdesstrobe, pad_p, pad_n): + self.submodules.encoder = RenameClockDomains(Encoder(), "pix") + self.d, self.c, self.de = self.encoder.d, self.encoder.c, self.encoder.de + + ### + + # 2X soft serialization + ed_2x = Signal(5) + self.sync.pix2x += ed_2x.eq(Mux(ClockSignal("pix"), self.encoder.out[:5], self.encoder.out[5:])) + + # 5X hard serialization + cascade_di = Signal() + cascade_do = Signal() + cascade_ti = Signal() + cascade_to = Signal() + pad_se = Signal() + self.specials += [ + Instance("OSERDES2", + p_DATA_WIDTH=5, p_DATA_RATE_OQ="SDR", p_DATA_RATE_OT="SDR", + p_SERDES_MODE="MASTER", p_OUTPUT_MODE="DIFFERENTIAL", + + o_OQ=pad_se, + i_OCE=1, i_IOCE=serdesstrobe, i_RST=0, + i_CLK0=ClockSignal("pix10x"), i_CLK1=0, i_CLKDIV=ClockSignal("pix2x"), + i_D1=ed_2x[4], i_D2=0, i_D3=0, i_D4=0, + i_T1=0, i_T2=0, i_T3=0, i_T4=0, + i_TRAIN=0, i_TCE=1, + i_SHIFTIN1=1, i_SHIFTIN2=1, + i_SHIFTIN3=cascade_do, i_SHIFTIN4=cascade_to, + o_SHIFTOUT1=cascade_di, o_SHIFTOUT2=cascade_ti), + Instance("OSERDES2", + p_DATA_WIDTH=5, p_DATA_RATE_OQ="SDR", p_DATA_RATE_OT="SDR", + p_SERDES_MODE="SLAVE", p_OUTPUT_MODE="DIFFERENTIAL", + + i_OCE=1, i_IOCE=serdesstrobe, i_RST=0, + i_CLK0=ClockSignal("pix10x"), i_CLK1=0, i_CLKDIV=ClockSignal("pix2x"), + i_D1=ed_2x[0], i_D2=ed_2x[1], i_D3=ed_2x[2], i_D4=ed_2x[3], + i_T1=0, i_T2=0, i_T3=0, i_T4=0, + i_TRAIN=0, i_TCE=1, + i_SHIFTIN1=cascade_di, i_SHIFTIN2=cascade_ti, + i_SHIFTIN3=1, i_SHIFTIN4=1, + o_SHIFTOUT3=cascade_do, o_SHIFTOUT4=cascade_to), + Instance("OBUFDS", i_I=pad_se, o_O=pad_p, o_OB=pad_n) + ] + + +class PHY(Module): + def __init__(self, serdesstrobe, pads): + self.hsync = Signal() + self.vsync = Signal() + self.de = Signal() + self.r = Signal(8) + self.g = Signal(8) + self.b = Signal(8) + + ### + + self.submodules.es0 = _EncoderSerializer(serdesstrobe, pads.data0_p, pads.data0_n) + self.submodules.es1 = _EncoderSerializer(serdesstrobe, pads.data1_p, pads.data1_n) + self.submodules.es2 = _EncoderSerializer(serdesstrobe, pads.data2_p, pads.data2_n) + self.comb += [ + self.es0.d.eq(self.r), + self.es1.d.eq(self.g), + self.es2.d.eq(self.b), + self.es0.c.eq(Cat(self.hsync, self.vsync)), + self.es1.c.eq(0), + self.es2.c.eq(0), + self.es0.de.eq(self.de), + self.es1.de.eq(self.de), + self.es2.de.eq(self.de), + ] + + +class _EncoderTB(Module): + def __init__(self, inputs): + self.outs = [] + self._iter_inputs = iter(inputs) + self._end_cycle = None + self.submodules.dut = Encoder() + self.comb += self.dut.de.eq(1) + + def do_simulation(self, selfp): + if self._end_cycle is None: + try: + nv = next(self._iter_inputs) + except StopIteration: + self._end_cycle = selfp.simulator.cycle_counter + 4 + else: + selfp.dut.d = nv + if selfp.simulator.cycle_counter == self._end_cycle: + raise StopSimulation + if selfp.simulator.cycle_counter > 4: + self.outs.append(selfp.dut.out) + + +def _bit(i, n): + return (i >> n) & 1 + + +def _decode_tmds(b): + try: + c = control_tokens.index(b) + de = False + except ValueError: + c = 0 + de = True + vsync = bool(c & 2) + hsync = bool(c & 1) + + value = _bit(b, 0) ^ _bit(b, 9) + for i in range(1, 8): + value |= (_bit(b, i) ^ _bit(b, i-1) ^ (~_bit(b, 8) & 1)) << i + + return de, hsync, vsync, value + +if __name__ == "__main__": + from migen.sim.generic import run_simulation + from random import Random + + rng = Random(788) + test_list = [rng.randrange(256) for i in range(500)] + tb = _EncoderTB(test_list) + run_simulation(tb) + + check = [_decode_tmds(out)[3] for out in tb.outs] + assert(check == test_list) + + nb0 = 0 + nb1 = 0 + for out in tb.outs: + for i in range(10): + if _bit(out, i): + nb1 += 1 + else: + nb0 += 1 + print("0/1: {}/{} ({:.2f})".format(nb0, nb1, nb0/nb1)) diff --git a/misoc/framebuffer/format.py b/misoc/framebuffer/format.py new file mode 100644 index 00000000..210ec5ce --- /dev/null +++ b/misoc/framebuffer/format.py @@ -0,0 +1,150 @@ +from migen import * +from migen.flow.actor import * +from migen.bank.description import CSRStorage +from migen.genlib.record import Record +from migen.genlib.fsm import FSM, NextState +from migen.actorlib import spi + +_hbits = 12 +_vbits = 12 + +bpp = 32 +bpc = 10 +pixel_layout_s = [ + ("pad", bpp-3*bpc), + ("r", bpc), + ("g", bpc), + ("b", bpc) +] + + +def pixel_layout(pack_factor): + return [("p"+str(i), pixel_layout_s) for i in range(pack_factor)] + +bpc_phy = 8 +phy_layout_s = [ + ("r", bpc_phy), + ("g", bpc_phy), + ("b", bpc_phy) +] + + +def phy_layout(pack_factor): + r = [("hsync", 1), ("vsync", 1), ("de", 1)] + for i in range(pack_factor): + r.append(("p"+str(i), phy_layout_s)) + return r + + +class FrameInitiator(spi.SingleGenerator): + def __init__(self, bus_aw, pack_factor, ndmas=1): + h_alignment_bits = log2_int(pack_factor) + hbits_dyn = _hbits - h_alignment_bits + bus_alignment_bits = h_alignment_bits + log2_int(bpp//8) + layout = [ + ("hres", hbits_dyn, 640, h_alignment_bits), + ("hsync_start", hbits_dyn, 656, h_alignment_bits), + ("hsync_end", hbits_dyn, 752, h_alignment_bits), + ("hscan", hbits_dyn, 800, h_alignment_bits), + + ("vres", _vbits, 480), + ("vsync_start", _vbits, 492), + ("vsync_end", _vbits, 494), + ("vscan", _vbits, 525), + + ("length", bus_aw + bus_alignment_bits, 640*480*bpp//8, bus_alignment_bits) + ] + layout += [("base"+str(i), bus_aw + bus_alignment_bits, 0, bus_alignment_bits) + for i in range(ndmas)] + spi.SingleGenerator.__init__(self, layout, spi.MODE_CONTINUOUS) + + timing_subr = ["hres", "hsync_start", "hsync_end", "hscan", + "vres", "vsync_start", "vsync_end", "vscan"] + + def dma_subr(self, i=0): + return ["length", "base"+str(i)] + + +class VTG(Module): + def __init__(self, pack_factor): + hbits_dyn = _hbits - log2_int(pack_factor) + timing_layout = [ + ("hres", hbits_dyn), + ("hsync_start", hbits_dyn), + ("hsync_end", hbits_dyn), + ("hscan", hbits_dyn), + ("vres", _vbits), + ("vsync_start", _vbits), + ("vsync_end", _vbits), + ("vscan", _vbits)] + self.timing = Sink(timing_layout) + self.pixels = Sink(pixel_layout(pack_factor)) + self.phy = Source(phy_layout(pack_factor)) + self.busy = Signal() + + ### + + hactive = Signal() + vactive = Signal() + active = Signal() + + hcounter = Signal(hbits_dyn) + vcounter = Signal(_vbits) + + skip = bpc - bpc_phy + self.comb += [ + active.eq(hactive & vactive), + If(active, + [getattr(getattr(self.phy.payload, p), c).eq(getattr(getattr(self.pixels.payload, p), c)[skip:]) + for p in ["p"+str(i) for i in range(pack_factor)] for c in ["r", "g", "b"]], + self.phy.de.eq(1) + ), + self.pixels.ack.eq(self.phy.ack & active) + ] + + load_timing = Signal() + tr = Record(timing_layout) + self.sync += If(load_timing, tr.eq(self.timing.payload)) + + generate_en = Signal() + generate_frame_done = Signal() + self.sync += [ + generate_frame_done.eq(0), + If(generate_en, + hcounter.eq(hcounter + 1), + + If(hcounter == 0, hactive.eq(1)), + If(hcounter == tr.hres, hactive.eq(0)), + If(hcounter == tr.hsync_start, self.phy.hsync.eq(1)), + If(hcounter == tr.hsync_end, self.phy.hsync.eq(0)), + If(hcounter == tr.hscan, + hcounter.eq(0), + If(vcounter == tr.vscan, + vcounter.eq(0), + generate_frame_done.eq(1) + ).Else( + vcounter.eq(vcounter + 1) + ) + ), + + If(vcounter == 0, vactive.eq(1)), + If(vcounter == tr.vres, vactive.eq(0)), + If(vcounter == tr.vsync_start, self.phy.vsync.eq(1)), + If(vcounter == tr.vsync_end, self.phy.vsync.eq(0)) + ) + ] + + self.submodules.fsm = FSM() + self.fsm.act("GET_TIMING", + self.timing.ack.eq(1), + load_timing.eq(1), + If(self.timing.stb, NextState("GENERATE")) + ) + self.fsm.act("GENERATE", + self.busy.eq(1), + If(~active | self.pixels.stb, + self.phy.stb.eq(1), + If(self.phy.ack, generate_en.eq(1)) + ), + If(generate_frame_done, NextState("GET_TIMING")) + ) diff --git a/misoc/framebuffer/phy.py b/misoc/framebuffer/phy.py new file mode 100644 index 00000000..497c861e --- /dev/null +++ b/misoc/framebuffer/phy.py @@ -0,0 +1,222 @@ +from migen import * +from migen.genlib.fifo import AsyncFIFO +from migen.genlib.cdc import MultiReg +from migen.bank.description import * +from migen.flow.actor import * + +from misoc.framebuffer.format import bpc_phy, phy_layout +from misoc.framebuffer import dvi + + +class _FIFO(Module): + def __init__(self, pack_factor): + self.phy = Sink(phy_layout(pack_factor)) + self.busy = Signal() + + self.pix_hsync = Signal() + self.pix_vsync = Signal() + self.pix_de = Signal() + self.pix_r = Signal(bpc_phy) + self.pix_g = Signal(bpc_phy) + self.pix_b = Signal(bpc_phy) + + ### + + fifo = RenameClockDomains(AsyncFIFO(phy_layout(pack_factor), 512), + {"write": "sys", "read": "pix"}) + self.submodules += fifo + self.comb += [ + self.phy.ack.eq(fifo.writable), + fifo.we.eq(self.phy.stb), + fifo.din.eq(self.phy.payload), + self.busy.eq(0) + ] + + unpack_counter = Signal(max=pack_factor) + assert(pack_factor & (pack_factor - 1) == 0) # only support powers of 2 + self.sync.pix += [ + unpack_counter.eq(unpack_counter + 1), + self.pix_hsync.eq(fifo.dout.hsync), + self.pix_vsync.eq(fifo.dout.vsync), + self.pix_de.eq(fifo.dout.de) + ] + for i in range(pack_factor): + pixel = getattr(fifo.dout, "p"+str(i)) + self.sync.pix += If(unpack_counter == i, + self.pix_r.eq(pixel.r), + self.pix_g.eq(pixel.g), + self.pix_b.eq(pixel.b) + ) + self.comb += fifo.re.eq(unpack_counter == (pack_factor - 1)) + + +# This assumes a 50MHz base clock +class _Clocking(Module, AutoCSR): + def __init__(self, pads_vga, pads_dvi): + self._cmd_data = CSRStorage(10) + self._send_cmd_data = CSR() + self._send_go = CSR() + self._status = CSRStatus(4) + + self.clock_domains.cd_pix = ClockDomain(reset_less=True) + if pads_dvi is not None: + self._pll_reset = CSRStorage() + self._pll_adr = CSRStorage(5) + self._pll_dat_r = CSRStatus(16) + self._pll_dat_w = CSRStorage(16) + self._pll_read = CSR() + self._pll_write = CSR() + self._pll_drdy = CSRStatus() + + self.clock_domains.cd_pix2x = ClockDomain(reset_less=True) + self.clock_domains.cd_pix10x = ClockDomain(reset_less=True) + self.serdesstrobe = Signal() + + ### + + # Generate 1x pixel clock + clk_pix_unbuffered = Signal() + pix_progdata = Signal() + pix_progen = Signal() + pix_progdone = Signal() + pix_locked = Signal() + self.specials += Instance("DCM_CLKGEN", + p_CLKFXDV_DIVIDE=2, p_CLKFX_DIVIDE=4, p_CLKFX_MD_MAX=1.0, p_CLKFX_MULTIPLY=2, + p_CLKIN_PERIOD=20.0, p_SPREAD_SPECTRUM="NONE", p_STARTUP_WAIT="FALSE", + + i_CLKIN=ClockSignal("base50"), o_CLKFX=clk_pix_unbuffered, + i_PROGCLK=ClockSignal(), i_PROGDATA=pix_progdata, i_PROGEN=pix_progen, + o_PROGDONE=pix_progdone, o_LOCKED=pix_locked, + i_FREEZEDCM=0, i_RST=ResetSignal()) + + remaining_bits = Signal(max=11) + transmitting = Signal() + self.comb += transmitting.eq(remaining_bits != 0) + sr = Signal(10) + self.sync += [ + If(self._send_cmd_data.re, + remaining_bits.eq(10), + sr.eq(self._cmd_data.storage) + ).Elif(transmitting, + remaining_bits.eq(remaining_bits - 1), + sr.eq(sr[1:]) + ) + ] + self.comb += [ + pix_progdata.eq(transmitting & sr[0]), + pix_progen.eq(transmitting | self._send_go.re) + ] + + # enforce gap between commands + busy_counter = Signal(max=14) + busy = Signal() + self.comb += busy.eq(busy_counter != 0) + self.sync += If(self._send_cmd_data.re, + busy_counter.eq(13) + ).Elif(busy, + busy_counter.eq(busy_counter - 1) + ) + + mult_locked = Signal() + self.comb += self._status.status.eq(Cat(busy, pix_progdone, pix_locked, mult_locked)) + + # Clock multiplication and buffering + if pads_dvi is None: + # Just buffer 1x pixel clock + self.specials += Instance("BUFG", i_I=clk_pix_unbuffered, o_O=self.cd_pix.clk) + self.comb += mult_locked.eq(pix_locked) + else: + # Route unbuffered 1x pixel clock to PLL + # Generate 1x, 2x and 10x IO pixel clocks + clkfbout = Signal() + pll_locked = Signal() + pll_clk0 = Signal() + pll_clk1 = Signal() + pll_clk2 = Signal() + locked_async = Signal() + pll_drdy = Signal() + self.sync += If(self._pll_read.re | self._pll_write.re, + self._pll_drdy.status.eq(0) + ).Elif(pll_drdy, + self._pll_drdy.status.eq(1) + ) + self.specials += [ + Instance("PLL_ADV", + p_CLKFBOUT_MULT=10, + p_CLKOUT0_DIVIDE=1, # pix10x + p_CLKOUT1_DIVIDE=5, # pix2x + p_CLKOUT2_DIVIDE=10, # pix + p_COMPENSATION="INTERNAL", + + i_CLKINSEL=1, + i_CLKIN1=clk_pix_unbuffered, + o_CLKOUT0=pll_clk0, o_CLKOUT1=pll_clk1, o_CLKOUT2=pll_clk2, + o_CLKFBOUT=clkfbout, i_CLKFBIN=clkfbout, + o_LOCKED=pll_locked, + i_RST=~pix_locked | self._pll_reset.storage, + + i_DADDR=self._pll_adr.storage, + o_DO=self._pll_dat_r.status, + i_DI=self._pll_dat_w.storage, + i_DEN=self._pll_read.re | self._pll_write.re, + i_DWE=self._pll_write.re, + o_DRDY=pll_drdy, + i_DCLK=ClockSignal()), + Instance("BUFPLL", p_DIVIDE=5, + i_PLLIN=pll_clk0, i_GCLK=ClockSignal("pix2x"), i_LOCKED=pll_locked, + o_IOCLK=self.cd_pix10x.clk, o_LOCK=locked_async, o_SERDESSTROBE=self.serdesstrobe), + Instance("BUFG", i_I=pll_clk1, o_O=self.cd_pix2x.clk), + Instance("BUFG", name="dviout_pix_bufg", i_I=pll_clk2, o_O=self.cd_pix.clk), + MultiReg(locked_async, mult_locked, "sys") + ] + + # Drive VGA/DVI clock pads + if pads_vga is not None: + self.specials += Instance("ODDR2", + p_DDR_ALIGNMENT="NONE", p_INIT=0, p_SRTYPE="SYNC", + o_Q=pads_vga.clk, + i_C0=ClockSignal("pix"), + i_C1=~ClockSignal("pix"), + i_CE=1, i_D0=1, i_D1=0, + i_R=0, i_S=0) + if pads_dvi is not None: + dvi_clk_se = Signal() + self.specials += Instance("ODDR2", + p_DDR_ALIGNMENT="NONE", p_INIT=0, p_SRTYPE="SYNC", + o_Q=dvi_clk_se, + i_C0=ClockSignal("pix"), + i_C1=~ClockSignal("pix"), + i_CE=1, i_D0=1, i_D1=0, + i_R=0, i_S=0) + self.specials += Instance("OBUFDS", i_I=dvi_clk_se, + o_O=pads_dvi.clk_p, o_OB=pads_dvi.clk_n) + + +class Driver(Module, AutoCSR): + def __init__(self, pack_factor, pads_vga, pads_dvi): + fifo = _FIFO(pack_factor) + self.submodules += fifo + self.phy = fifo.phy + self.busy = fifo.busy + + self.submodules.clocking = _Clocking(pads_vga, pads_dvi) + + if pads_vga is not None: + self.comb += [ + pads_vga.hsync_n.eq(~fifo.pix_hsync), + pads_vga.vsync_n.eq(~fifo.pix_vsync), + pads_vga.r.eq(fifo.pix_r), + pads_vga.g.eq(fifo.pix_g), + pads_vga.b.eq(fifo.pix_b), + pads_vga.psave_n.eq(1) + ] + if pads_dvi is not None: + self.submodules.dvi_phy = dvi.PHY(self.clocking.serdesstrobe, pads_dvi) + self.comb += [ + self.dvi_phy.hsync.eq(fifo.pix_hsync), + self.dvi_phy.vsync.eq(fifo.pix_vsync), + self.dvi_phy.de.eq(fifo.pix_de), + self.dvi_phy.r.eq(fifo.pix_r), + self.dvi_phy.g.eq(fifo.pix_g), + self.dvi_phy.b.eq(fifo.pix_b) + ] diff --git a/misoc/gpio.py b/misoc/gpio.py new file mode 100644 index 00000000..6db225fb --- /dev/null +++ b/misoc/gpio.py @@ -0,0 +1,31 @@ +from migen import * +from migen.genlib.cdc import MultiReg +from migen.bank.description import * + + +class GPIOIn(Module, AutoCSR): + def __init__(self, signal): + self._in = CSRStatus(flen(signal)) + self.specials += MultiReg(signal, self._in.status) + + +class GPIOOut(Module, AutoCSR): + def __init__(self, signal): + self._out = CSRStorage(flen(signal)) + self.comb += signal.eq(self._out.storage) + + +class GPIOInOut(Module): + def __init__(self, in_signal, out_signal): + self.submodules.gpio_in = GPIOIn(in_signal) + self.submodules.gpio_out = GPIOOut(out_signal) + + def get_csrs(self): + return self.gpio_in.get_csrs() + self.gpio_out.get_csrs() + + +class Blinker(Module): + def __init__(self, signal, divbits=26): + counter = Signal(divbits) + self.comb += signal.eq(counter[divbits-1]) + self.sync += counter.eq(counter + 1) diff --git a/misoc/identifier.py b/misoc/identifier.py new file mode 100644 index 00000000..a70f44b0 --- /dev/null +++ b/misoc/identifier.py @@ -0,0 +1,26 @@ +import subprocess + +from migen import * +from migen.bank.description import * + +def get_id(): + output = subprocess.check_output(["git", "rev-parse", "HEAD"]).decode("ascii") + return int(output[:8], 16) + + +class Identifier(Module, AutoCSR): + def __init__(self, sysid, frequency, revision=None): + self._sysid = CSRStatus(16) + self._revision = CSRStatus(32) + self._frequency = CSRStatus(32) + + ### + + if revision is None: + revision = get_id() + + self.comb += [ + self._sysid.status.eq(sysid), + self._revision.status.eq(revision), + self._frequency.status.eq(frequency) + ] diff --git a/misoc/integration/__init__.py b/misoc/integration/__init__.py new file mode 100644 index 00000000..2979fa9d --- /dev/null +++ b/misoc/integration/__init__.py @@ -0,0 +1,2 @@ +from misoc.integration.soc_core import SoCCore +from misoc.integration.soc_sdram import SoCSDRAM diff --git a/misoc/integration/cpuif.py b/misoc/integration/cpuif.py new file mode 100644 index 00000000..3ed2dd6d --- /dev/null +++ b/misoc/integration/cpuif.py @@ -0,0 +1,119 @@ +from migen import * +from migen.bank.description import CSRStatus + + +def get_cpu_mak(cpu_type): + if cpu_type == "lm32": + triple = "lm32-elf" + cpuflags = "-mbarrel-shift-enabled -mmultiply-enabled -mdivide-enabled -msign-extend-enabled" + clang = "" + elif cpu_type == "or1k": + triple = "or1k-linux" + cpuflags = "-mhard-mul -mhard-div -mror -mffl1 -maddc" + clang = "1" + else: + raise ValueError("Unsupported CPU type: "+cpu_type) + return "TRIPLE={}\nCPU={}\nCPUFLAGS={}\nCLANG={}".format(triple, cpu_type, cpuflags, clang) + + +def get_linker_output_format(cpu_type): + return "OUTPUT_FORMAT(\"elf32-{}\")\n".format(cpu_type) + + +def get_linker_regions(regions): + r = "MEMORY {\n" + for name, origin, length in regions: + r += "\t{} : ORIGIN = 0x{:08x}, LENGTH = 0x{:08x}\n".format(name, origin, length) + r += "}\n" + return r + + +def get_mem_header(regions, flash_boot_address): + r = "#ifndef __GENERATED_MEM_H\n#define __GENERATED_MEM_H\n\n" + for name, base, size in regions: + r += "#define {name}_BASE 0x{base:08x}\n#define {name}_SIZE 0x{size:08x}\n\n".format(name=name.upper(), base=base, size=size) + if flash_boot_address is not None: + r += "#define FLASH_BOOT_ADDRESS 0x{:08x}\n\n".format(flash_boot_address) + r += "#endif\n" + return r + + +def _get_rw_functions(reg_name, reg_base, nwords, busword, read_only, with_access_functions): + r = "" + + r += "#define CSR_"+reg_name.upper()+"_ADDR "+hex(reg_base)+"\n" + r += "#define CSR_"+reg_name.upper()+"_SIZE "+str(nwords)+"\n" + + size = nwords*busword + if size > 64: + return r + elif size > 32: + ctype = "unsigned long long int" + elif size > 16: + ctype = "unsigned int" + elif size > 8: + ctype = "unsigned short int" + else: + ctype = "unsigned char" + + if with_access_functions: + r += "static inline "+ctype+" "+reg_name+"_read(void) {\n" + if size > 1: + r += "\t"+ctype+" r = MMPTR("+hex(reg_base)+");\n" + for byte in range(1, nwords): + r += "\tr <<= "+str(busword)+";\n\tr |= MMPTR("+hex(reg_base+4*byte)+");\n" + r += "\treturn r;\n}\n" + else: + r += "\treturn MMPTR("+hex(reg_base)+");\n}\n" + + if not read_only: + r += "static inline void "+reg_name+"_write("+ctype+" value) {\n" + for word in range(nwords): + shift = (nwords-word-1)*busword + if shift: + value_shifted = "value >> "+str(shift) + else: + value_shifted = "value" + r += "\tMMPTR("+hex(reg_base+4*word)+") = "+value_shifted+";\n" + r += "}\n" + return r + + +def get_csr_header(regions, constants, with_access_functions=True): + r = "#ifndef __GENERATED_CSR_H\n#define __GENERATED_CSR_H\n" + if with_access_functions: + r += "#include \n" + for name, origin, busword, obj in regions: + if isinstance(obj, Memory): + r += "#define CSR_"+name.upper()+"_BASE "+hex(origin)+"\n" + else: + r += "\n/* "+name+" */\n" + r += "#define CSR_"+name.upper()+"_BASE "+hex(origin)+"\n" + for csr in obj: + nr = (csr.size + busword - 1)//busword + r += _get_rw_functions(name + "_" + csr.name, origin, nr, busword, isinstance(csr, CSRStatus), with_access_functions) + origin += 4*nr + + r += "\n/* constants */\n" + for name, value in constants: + r += "#define " + name + if value is not None: + if isinstance(value, str): + r += " \"" + value + "\"" + else: + r += " " + str(value) + r += "\n" + + r += "\n#endif\n" + return r + + +def get_csr_csv(regions): + r = "" + for name, origin, busword, obj in regions: + if not isinstance(obj, Memory): + for csr in obj: + nr = (csr.size + busword - 1)//busword + r += "{}_{},0x{:08x},{},{}\n".format(name, csr.name, origin, nr, "ro" if isinstance(csr, CSRStatus) else "rw") + origin += 4*nr + return r diff --git a/misoc/integration/soc_core.py b/misoc/integration/soc_core.py new file mode 100644 index 00000000..d0760acf --- /dev/null +++ b/misoc/integration/soc_core.py @@ -0,0 +1,201 @@ +from operator import itemgetter + +from migen import * +from migen.bank import csrgen +from migen.bus import wishbone, csr, wishbone2csr + +from misoc import lm32, mor1kx, identifier, timer, uart + + +def mem_decoder(address, start=26, end=29): + return lambda a: a[start:end] == ((address >> (start+2)) & (2**(end-start))-1) + + +class SoCCore(Module): + csr_map = { + "crg": 0, # user + "uart_phy": 1, # provided by default (optional) + "uart": 2, # provided by default (optional) + "identifier": 3, # provided by default (optional) + "timer0": 4, # provided by default (optional) + "buttons": 5, # user + "leds": 6, # user + } + interrupt_map = { + "uart": 0, + "timer0": 1, + } + mem_map = { + "rom": 0x00000000, # (default shadow @0x80000000) + "sram": 0x10000000, # (default shadow @0x90000000) + "main_ram": 0x40000000, # (default shadow @0xc0000000) + "csr": 0x60000000, # (default shadow @0xe0000000) + } + def __init__(self, platform, clk_freq, + cpu_type="lm32", cpu_reset_address=0x00000000, + integrated_rom_size=0, + integrated_sram_size=4096, + integrated_main_ram_size=0, + shadow_base=0x80000000, + with_csr=True, csr_data_width=8, csr_address_width=14, + with_uart=True, uart_baudrate=115200, + with_identifier=True, + with_timer=True): + self.platform = platform + self.clk_freq = clk_freq + + self.cpu_type = cpu_type + if integrated_rom_size: + cpu_reset_address = 0 + self.cpu_reset_address = cpu_reset_address + + self.integrated_rom_size = integrated_rom_size + self.integrated_sram_size = integrated_sram_size + self.integrated_main_ram_size = integrated_main_ram_size + + self.with_uart = with_uart + self.uart_baudrate = uart_baudrate + + self.with_identifier = with_identifier + + self.shadow_base = shadow_base + + self.with_csr = with_csr + self.csr_data_width = csr_data_width + self.csr_address_width = csr_address_width + + self._memory_regions = [] # list of (name, origin, length) + self._csr_regions = [] # list of (name, origin, busword, csr_list/Memory) + self._constants = [] # list of (name, value) + + self._wb_masters = [] + self._wb_slaves = [] + + if cpu_type != "none": + if cpu_type == "lm32": + self.add_cpu_or_bridge(lm32.LM32(platform, self.cpu_reset_address)) + elif cpu_type == "or1k": + self.add_cpu_or_bridge(mor1kx.MOR1KX(platform, self.cpu_reset_address)) + else: + raise ValueError("Unsupported CPU type: {}".format(cpu_type)) + self.add_wb_master(self.cpu_or_bridge.ibus) + self.add_wb_master(self.cpu_or_bridge.dbus) + + if integrated_rom_size: + self.submodules.rom = wishbone.SRAM(integrated_rom_size, read_only=True) + self.register_rom(self.rom.bus, integrated_rom_size) + + if integrated_sram_size: + self.submodules.sram = wishbone.SRAM(integrated_sram_size) + self.register_mem("sram", self.mem_map["sram"], self.sram.bus, integrated_sram_size) + + # Note: Main Ram can be used when no external SDRAM is available and use SDRAM mapping. + if integrated_main_ram_size: + self.submodules.main_ram = wishbone.SRAM(integrated_main_ram_size) + self.register_mem("main_ram", self.mem_map["main_ram"], self.main_ram.bus, integrated_main_ram_size) + + if with_csr: + self.submodules.wishbone2csr = wishbone2csr.WB2CSR(bus_csr=csr.Interface(csr_data_width, csr_address_width)) + self.register_mem("csr", self.mem_map["csr"], self.wishbone2csr.wishbone) + + if with_uart: + self.submodules.uart_phy = uart.RS232PHY(platform.request("serial"), clk_freq, uart_baudrate) + self.submodules.uart = uart.UART(self.uart_phy) + + if with_identifier: + platform_id = 0x554E if not hasattr(platform, "identifier") else platform.identifier + self.submodules.identifier = identifier.Identifier(platform_id, int(clk_freq)) + + if with_timer: + self.submodules.timer0 = timer.Timer() + + def add_cpu_or_bridge(self, cpu_or_bridge): + if self.finalized: + raise FinalizeError + if hasattr(self, "cpu_or_bridge"): + raise NotImplementedError("More than one CPU is not supported") + self.submodules.cpu_or_bridge = cpu_or_bridge + + def init_rom(self, data): + self.rom.mem.init = data + + def add_wb_master(self, wbm): + if self.finalized: + raise FinalizeError + self._wb_masters.append(wbm) + + def add_wb_slave(self, address_decoder, interface): + if self.finalized: + raise FinalizeError + self._wb_slaves.append((address_decoder, interface)) + + def add_memory_region(self, name, origin, length): + def in_this_region(addr): + return addr >= origin and addr < origin + length + for n, o, l in self._memory_regions: + if n == name or in_this_region(o) or in_this_region(o+l-1): + raise ValueError("Memory region conflict between {} and {}".format(n, name)) + + self._memory_regions.append((name, origin, length)) + + def register_mem(self, name, address, interface, size=None): + self.add_wb_slave(mem_decoder(address), interface) + if size is not None: + self.add_memory_region(name, address, size) + + def register_rom(self, interface, rom_size=0xa000): + self.add_wb_slave(mem_decoder(self.mem_map["rom"]), interface) + self.add_memory_region("rom", self.cpu_reset_address, rom_size) + + def get_memory_regions(self): + return self._memory_regions + + def check_csr_region(self, name, origin): + for n, o, l, obj in self._csr_regions: + if n == name or o == origin: + raise ValueError("CSR region conflict between {} and {}".format(n, name)) + + def add_csr_region(self, name, origin, busword, obj): + self.check_csr_region(name, origin) + self._csr_regions.append((name, origin, busword, obj)) + + def get_csr_regions(self): + return self._csr_regions + + def add_constant(self, name, value=None): + self._constants.append((name, value)) + + def get_constants(self): + r = [] + for name, interrupt in sorted(self.interrupt_map.items(), key=itemgetter(1)): + r.append((name.upper() + "_INTERRUPT", interrupt)) + r += self._constants + return r + + def do_finalize(self): + registered_mems = {regions[0] for regions in self._memory_regions} + if self.cpu_type != "none": + for mem in "rom", "sram": + if mem not in registered_mems: + raise FinalizeError("CPU needs a {} to be registered with SoC.register_mem()".format(mem)) + + # Wishbone + self.submodules.wishbonecon = wishbone.InterconnectShared(self._wb_masters, + self._wb_slaves, register=True) + + # CSR + if self.with_csr: + self.submodules.csrbankarray = csrgen.BankArray(self, + lambda name, memory: self.csr_map[name if memory is None else name + "_" + memory.name_override], + data_width=self.csr_data_width, address_width=self.csr_address_width) + self.submodules.csrcon = csr.Interconnect(self.wishbone2csr.csr, self.csrbankarray.get_buses()) + for name, csrs, mapaddr, rmap in self.csrbankarray.banks: + self.add_csr_region(name, (self.mem_map["csr"] + 0x800*mapaddr) | self.shadow_base, self.csr_data_width, csrs) + for name, memory, mapaddr, mmap in self.csrbankarray.srams: + self.add_csr_region(name + "_" + memory.name_override, (self.mem_map["csr"] + 0x800*mapaddr) | self.shadow_base, self.csr_data_width, memory) + + # Interrupts + if hasattr(self.cpu_or_bridge, "interrupt"): + for k, v in sorted(self.interrupt_map.items(), key=itemgetter(1)): + if hasattr(self, k): + self.comb += self.cpu_or_bridge.interrupt[v].eq(getattr(self, k).ev.irq) diff --git a/misoc/integration/soc_sdram.py b/misoc/integration/soc_sdram.py new file mode 100644 index 00000000..3e048c7f --- /dev/null +++ b/misoc/integration/soc_sdram.py @@ -0,0 +1,111 @@ +from migen import * +from migen.bus import wishbone +from migen.genlib.record import * + +from misoc.mem.sdram.core import SDRAMCore +from misoc.mem.sdram.core.lasmicon import LASMIconSettings +from misoc.mem.sdram.core.minicon import MiniconSettings +from misoc.mem.sdram.frontend import memtest, wishbone2lasmi +from misoc.integration.soc_core import SoCCore + + +class SoCSDRAM(SoCCore): + csr_map = { + "sdram": 8, + "l2_cache": 9, + "memtest_w": 10, + "memtest_r": 11 + } + csr_map.update(SoC.csr_map) + + def __init__(self, platform, clk_freq, sdram_controller_settings, + **kwargs): + SoC.__init__(self, platform, clk_freq, **kwargs) + if isinstance(sdram_controller_settings, str): + self.sdram_controller_settings = eval(sdram_controller_settings) + else: + self.sdram_controller_settings = sdram_controller_settings + self._sdram_phy_registered = False + self._wb_sdram_ifs = [] + self._wb_sdram = wishbone.Interface() + + def add_wb_sdram_if(self, interface): + if self.finalized: + raise FinalizeError + self._wb_sdram_ifs.append(interface) + + def register_sdram_phy(self, phy): + if self._sdram_phy_registered: + raise FinalizeError + self._sdram_phy_registered = True + + # Core + self.submodules.sdram = SDRAMCore(phy, + phy.module.geom_settings, + phy.module.timing_settings, + self.sdram_controller_settings) + + dfi_databits_divisor = 1 if phy.settings.memtype == "SDR" else 2 + sdram_width = phy.settings.dfi_databits//dfi_databits_divisor + main_ram_size = 2**(phy.module.geom_settings.bankbits + + phy.module.geom_settings.rowbits + + phy.module.geom_settings.colbits)*sdram_width//8 + # XXX: Limit main_ram_size to 256MB, we should modify mem_map to allow larger memories. + main_ram_size = min(main_ram_size, 256*1024*1024) + l2_size = self.sdram_controller_settings.l2_size + if l2_size: + self.add_constant("L2_SIZE", l2_size) + + # add a Wishbone interface to the DRAM + wb_sdram = wishbone.Interface() + self.add_wb_sdram_if(wb_sdram) + self.register_mem("main_ram", self.mem_map["main_ram"], wb_sdram, main_ram_size) + + # LASMICON frontend + if isinstance(self.sdram_controller_settings, LASMIconSettings): + if self.sdram_controller_settings.with_bandwidth: + self.sdram.controller.multiplexer.add_bandwidth() + + if self.sdram_controller_settings.with_memtest: + self.submodules.memtest_w = memtest.MemtestWriter(self.sdram.crossbar.get_master()) + self.submodules.memtest_r = memtest.MemtestReader(self.sdram.crossbar.get_master()) + + if l2_size: + lasmim = self.sdram.crossbar.get_master() + l2_cache = wishbone.Cache(l2_size//4, self._wb_sdram, wishbone.Interface(lasmim.dw)) + # XXX Vivado ->2015.1 workaround, Vivado is not able to map correctly our L2 cache. + # Issue is reported to Xilinx and should be fixed in next releases (2015.2?). + # Remove this workaround when fixed by Xilinx. + from mibuild.xilinx.vivado import XilinxVivadoToolchain + if isinstance(self.platform.toolchain, XilinxVivadoToolchain): + from migen.fhdl.simplify import FullMemoryWE + self.submodules.l2_cache = FullMemoryWE()(l2_cache) + else: + self.submodules.l2_cache = l2_cache + self.submodules.wishbone2lasmi = wishbone2lasmi.WB2LASMI(self.l2_cache.slave, lasmim) + + # MINICON frontend + elif isinstance(self.sdram_controller_settings, MiniconSettings): + if l2_size: + l2_cache = wishbone.Cache(l2_size//4, self._wb_sdram, self.sdram.controller.bus) + # XXX Vivado ->2015.1 workaround, Vivado is not able to map correctly our L2 cache. + # Issue is reported to Xilinx and should be fixed in next releases (2015.2?). + # Remove this workaround when fixed by Xilinx. + from mibuild.xilinx.vivado import XilinxVivadoToolchain + if isinstance(self.platform.toolchain, XilinxVivadoToolchain): + from migen.fhdl.simplify import FullMemoryWE + self.submodules.l2_cache = FullMemoryWE()(l2_cache) + else: + self.submodules.l2_cache = l2_cache + else: + self.submodules.converter = wishbone.Converter(self._wb_sdram, self.sdram.controller.bus) + + def do_finalize(self): + if not self.integrated_main_ram_size: + if not self._sdram_phy_registered: + raise FinalizeError("Need to call SDRAMSoC.register_sdram_phy()") + + # arbitrate wishbone interfaces to the DRAM + self.submodules.wb_sdram_con = wishbone.Arbiter(self._wb_sdram_ifs, + self._wb_sdram) + SoC.do_finalize(self) diff --git a/misoc/liteethmini/LICENSE b/misoc/liteethmini/LICENSE new file mode 100644 index 00000000..cbbfe8be --- /dev/null +++ b/misoc/liteethmini/LICENSE @@ -0,0 +1,28 @@ +Unless otherwise noted, LiteEth is copyright (C) 2015 Florent Kermarrec. + +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. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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. + + +Other authors retain ownership of their contributions. If a submission can +reasonably be considered independently copyrightable, it's yours and we +encourage you to claim it with appropriate copyright notices. This submission +then falls under the "otherwise noted" category. All submissions are strongly +encouraged to use the two-clause BSD license reproduced above. diff --git a/misoc/liteethmini/README b/misoc/liteethmini/README new file mode 100644 index 00000000..595d2fc9 --- /dev/null +++ b/misoc/liteethmini/README @@ -0,0 +1,58 @@ + __ _ __ ______ __ __ ____ _ + / / (_) /____ / __/ /_/ / / |/ (_)__ (_) + / /__/ / __/ -_) _// __/ _ \/ /|_/ / / _ \/ / + /____/_/\__/\__/___/\__/_//_/_/ /_/_/_//_/_/ + + Copyright 2012-2015 / EnjoyDigital / M-Labs Ltd + + A small footprint and configurable minimal Ethernet core + powered by Migen + +[> Intro +--------- +LiteEthMini is a subset of LiteEth (https://github.com/enjoy-digital/liteeth) +intended to be used with a CPU and a software stack. + +[> Features +----------- +- Ethernet MAC with various various PHYs (GMII, MII, RGMII, Loopback) +- SRAM storage and wishbone interface + +[> Possible improvements +------------------------- +- add DMA interface to MAC +- add SGMII PHY +- ... See below Support and consulting :) + +If you want to support these features, please contact us at florent [AT] +enjoy-digital.fr. You can also contact our partner on the public mailing list +devel [AT] lists.m-labs.hk. + +[> License +----------- +LiteEthMini is released under the very permissive two-clause BSD license. Under +the terms of this license, you are authorized to use LiteEthMini for closed-source +proprietary designs. +Even though we do not require you to do so, those things are awesome, so please +do them if possible: + - tell us that you are using LiteEthMini + - cite LiteEthMini in publications related to research it has helped + - send us feedback and suggestions for improvements + - send us bug reports when something goes wrong + - send us the modifications and improvements you have done to LiteEthMini. + +[> Support and consulting +-------------------------- +We love open-source hardware and like sharing our designs with others. + +LiteEthMini is mainly developed and maintained by EnjoyDigital. + +If you would like to know more about LiteEthMini or if you are already a happy +user and would like to extend it for your needs, EnjoyDigital can provide standard +commercial support as well as consulting services. + +So feel free to contact us, we'd love to work with you! (and eventually shorten +the list of the possible improvements :) + +[> Contact +E-mail: florent [AT] enjoy-digital.fr \ No newline at end of file diff --git a/misoc/liteethmini/__init__.py b/misoc/liteethmini/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/misoc/liteethmini/common.py b/misoc/liteethmini/common.py new file mode 100644 index 00000000..21d0da08 --- /dev/null +++ b/misoc/liteethmini/common.py @@ -0,0 +1,44 @@ +import math +from collections import OrderedDict + +from migen import * +from migen.genlib.resetsync import AsyncResetSynchronizer +from migen.genlib.record import * +from migen.genlib.fsm import FSM, NextState +from migen.genlib.misc import chooser, reverse_bytes, FlipFlop, Counter, WaitTimer +from migen.flow.actor import * +from migen.actorlib.structuring import Converter, Pipeline +from migen.actorlib.fifo import SyncFIFO, AsyncFIFO +from migen.actorlib.packet import * +from migen.bank.description import * + +class Port: + def connect(self, port): + r = [ + Record.connect(self.source, port.sink), + Record.connect(port.source, self.sink) + ] + return r + +eth_mtu = 1532 +eth_min_len = 46 +eth_interpacket_gap = 12 +eth_preamble = 0xD555555555555555 +buffer_depth = 2**log2_int(eth_mtu, need_pow2=False) + +def eth_phy_description(dw): + payload_layout = [ + ("data", dw), + ("last_be", dw//8), + ("error", dw//8) + ] + return EndpointDescription(payload_layout, packetized=True) + + +def eth_mac_description(dw): + payload_layout = mac_header.get_layout() + [ + ("data", dw), + ("last_be", dw//8), + ("error", dw//8) + ] + return EndpointDescription(payload_layout, packetized=True) diff --git a/misoc/liteethmini/mac/__init__.py b/misoc/liteethmini/mac/__init__.py new file mode 100644 index 00000000..6844c420 --- /dev/null +++ b/misoc/liteethmini/mac/__init__.py @@ -0,0 +1,22 @@ +from misoc.com.liteethmini.common import * +from misoc.com.liteethmini.mac.core import LiteEthMACCore +from misoc.com.liteethmini.mac.frontend.wishbone import LiteEthMACWishboneInterface + + +class LiteEthMAC(Module, AutoCSR): + def __init__(self, phy, dw, + interface="wishbone", + endianness="big", + with_preamble_crc=True): + self.submodules.core = LiteEthMACCore(phy, dw, endianness, with_preamble_crc) + self.csrs = [] + if interface == "wishbone": + self.submodules.interface = LiteEthMACWishboneInterface(dw, 2, 2) + self.comb += Port.connect(self.interface, self.core) + self.ev, self.bus = self.interface.sram.ev, self.interface.bus + self.csrs = self.interface.get_csrs() + self.core.get_csrs() + else: + raise NotImplementedError + + def get_csrs(self): + return self.csrs diff --git a/misoc/liteethmini/mac/core/__init__.py b/misoc/liteethmini/mac/core/__init__.py new file mode 100644 index 00000000..2551f5eb --- /dev/null +++ b/misoc/liteethmini/mac/core/__init__.py @@ -0,0 +1,100 @@ +from misoc.com.liteethmini.common import * +from misoc.com.liteethmini.mac.core import gap, preamble, crc, padding, last_be +from misoc.com.liteethmini.phy.sim import LiteEthPHYSim +from misoc.com.liteethmini.phy.mii import LiteEthPHYMII + + +class LiteEthMACCore(Module, AutoCSR): + def __init__(self, phy, dw, endianness="big", + with_preamble_crc=True, + with_padding=True): + if dw < phy.dw: + raise ValueError("Core data width({}) must be larger than PHY data width({})".format(dw, phy.dw)) + + rx_pipeline = [phy] + tx_pipeline = [phy] + + # Interpacket gap + tx_gap_inserter = gap.LiteEthMACGap(phy.dw) + rx_gap_checker = gap.LiteEthMACGap(phy.dw, ack_on_gap=True) + self.submodules += RenameClockDomains(tx_gap_inserter, "eth_tx") + self.submodules += RenameClockDomains(rx_gap_checker, "eth_rx") + + tx_pipeline += [tx_gap_inserter] + rx_pipeline += [rx_gap_checker] + + # Preamble / CRC + if isinstance(phy, LiteEthPHYSim): + # In simulation, avoid CRC/Preamble to enable direct connection + # to the Ethernet tap. + self._preamble_crc = CSRStatus(reset=1) + elif with_preamble_crc: + self._preamble_crc = CSRStatus(reset=1) + # Preamble insert/check + preamble_inserter = preamble.LiteEthMACPreambleInserter(phy.dw) + preamble_checker = preamble.LiteEthMACPreambleChecker(phy.dw) + self.submodules += RenameClockDomains(preamble_inserter, "eth_tx") + self.submodules += RenameClockDomains(preamble_checker, "eth_rx") + + # CRC insert/check + crc32_inserter = crc.LiteEthMACCRC32Inserter(eth_phy_description(phy.dw)) + crc32_checker = crc.LiteEthMACCRC32Checker(eth_phy_description(phy.dw)) + self.submodules += RenameClockDomains(crc32_inserter, "eth_tx") + self.submodules += RenameClockDomains(crc32_checker, "eth_rx") + + tx_pipeline += [preamble_inserter, crc32_inserter] + rx_pipeline += [preamble_checker, crc32_checker] + + # Padding + if with_padding: + padding_inserter = padding.LiteEthMACPaddingInserter(phy.dw, 60) + padding_checker = padding.LiteEthMACPaddingChecker(phy.dw, 60) + self.submodules += RenameClockDomains(padding_inserter, "eth_tx") + self.submodules += RenameClockDomains(padding_checker, "eth_rx") + + tx_pipeline += [padding_inserter] + rx_pipeline += [padding_checker] + + # Delimiters + if dw != 8: + tx_last_be = last_be.LiteEthMACTXLastBE(phy.dw) + rx_last_be = last_be.LiteEthMACRXLastBE(phy.dw) + self.submodules += RenameClockDomains(tx_last_be, "eth_tx") + self.submodules += RenameClockDomains(rx_last_be, "eth_rx") + + tx_pipeline += [tx_last_be] + rx_pipeline += [rx_last_be] + + # Converters + if dw != phy.dw: + reverse = endianness == "big" + tx_converter = Converter(eth_phy_description(dw), + eth_phy_description(phy.dw), + reverse=reverse) + rx_converter = Converter(eth_phy_description(phy.dw), + eth_phy_description(dw), + reverse=reverse) + self.submodules += RenameClockDomains(tx_converter, "eth_tx") + self.submodules += RenameClockDomains(rx_converter, "eth_rx") + + tx_pipeline += [tx_converter] + rx_pipeline += [rx_converter] + + # Cross Domain Crossing + if isinstance(phy, LiteEthPHYMII): + fifo_depth = 8 + else: + fifo_depth = 64 + tx_cdc = AsyncFIFO(eth_phy_description(dw), fifo_depth) + rx_cdc = AsyncFIFO(eth_phy_description(dw), fifo_depth) + self.submodules += RenameClockDomains(tx_cdc, {"write": "sys", "read": "eth_tx"}) + self.submodules += RenameClockDomains(rx_cdc, {"write": "eth_rx", "read": "sys"}) + + tx_pipeline += [tx_cdc] + rx_pipeline += [rx_cdc] + + # Graph + self.submodules.tx_pipeline = Pipeline(*reversed(tx_pipeline)) + self.submodules.rx_pipeline = Pipeline(*rx_pipeline) + + self.sink, self.source = self.tx_pipeline.sink, self.rx_pipeline.source diff --git a/misoc/liteethmini/mac/core/crc.py b/misoc/liteethmini/mac/core/crc.py new file mode 100644 index 00000000..01b88f7e --- /dev/null +++ b/misoc/liteethmini/mac/core/crc.py @@ -0,0 +1,281 @@ +from misoc.com.liteethmini.common import * + + +class LiteEthMACCRCEngine(Module): + """Cyclic Redundancy Check Engine + + Compute next CRC value from last CRC value and data input using + an optimized asynchronous LFSR. + + Parameters + ---------- + data_width : int + Width of the data bus. + width : int + Width of the CRC. + polynom : int + Polynom of the CRC (ex: 0x04C11DB7 for IEEE 802.3 CRC) + + Attributes + ---------- + data : in + Data input. + last : in + last CRC value. + next : + next CRC value. + """ + def __init__(self, data_width, width, polynom): + self.data = Signal(data_width) + self.last = Signal(width) + self.next = Signal(width) + + # # # + + def _optimize_eq(l): + """ + Replace even numbers of XORs in the equation + with an equivalent XOR + """ + d = OrderedDict() + for e in l: + if e in d: + d[e] += 1 + else: + d[e] = 1 + r = [] + for key, value in d.items(): + if value%2 != 0: + r.append(key) + return r + + # compute and optimize CRC's LFSR + curval = [[("state", i)] for i in range(width)] + for i in range(data_width): + feedback = curval.pop() + [("din", i)] + for j in range(width-1): + if (polynom & (1<<(j+1))): + curval[j] += feedback + curval[j] = _optimize_eq(curval[j]) + curval.insert(0, feedback) + + # implement logic + for i in range(width): + xors = [] + for t, n in curval[i]: + if t == "state": + xors += [self.last[n]] + elif t == "din": + xors += [self.data[n]] + self.comb += self.next[i].eq(optree("^", xors)) + + +@DecorateModule(InsertReset) +@DecorateModule(InsertCE) +class LiteEthMACCRC32(Module): + """IEEE 802.3 CRC + + Implement an IEEE 802.3 CRC generator/checker. + + Parameters + ---------- + data_width : int + Width of the data bus. + + Attributes + ---------- + d : in + Data input. + value : out + CRC value (used for generator). + error : out + CRC error (used for checker). + """ + width = 32 + polynom = 0x04C11DB7 + init = 2**width-1 + check = 0xC704DD7B + def __init__(self, data_width): + self.data = Signal(data_width) + self.value = Signal(self.width) + self.error = Signal() + + # # # + + self.submodules.engine = LiteEthMACCRCEngine(data_width, self.width, self.polynom) + reg = Signal(self.width, reset=self.init) + self.sync += reg.eq(self.engine.next) + self.comb += [ + self.engine.data.eq(self.data), + self.engine.last.eq(reg), + + self.value.eq(~reg[::-1]), + self.error.eq(self.engine.next != self.check) + ] + + +class LiteEthMACCRCInserter(Module): + """CRC Inserter + + Append a CRC at the end of each packet. + + Parameters + ---------- + description : description + description of the dataflow. + + Attributes + ---------- + sink : in + Packets input without CRC. + source : out + Packets output with CRC. + """ + def __init__(self, crc_class, description): + self.sink = sink = Sink(description) + self.source = source = Source(description) + self.busy = Signal() + + # # # + + dw = flen(sink.data) + crc = crc_class(dw) + fsm = FSM(reset_state="IDLE") + self.submodules += crc, fsm + + fsm.act("IDLE", + crc.reset.eq(1), + sink.ack.eq(1), + If(sink.stb & sink.sop, + sink.ack.eq(0), + NextState("COPY"), + ) + ) + fsm.act("COPY", + crc.ce.eq(sink.stb & source.ack), + crc.data.eq(sink.data), + Record.connect(sink, source), + source.eop.eq(0), + If(sink.stb & sink.eop & source.ack, + NextState("INSERT"), + ) + ) + ratio = crc.width//dw + if ratio > 1: + cnt = Signal(max=ratio, reset=ratio-1) + cnt_done = Signal() + fsm.act("INSERT", + source.stb.eq(1), + chooser(crc.value, cnt, source.data, reverse=True), + If(cnt_done, + source.eop.eq(1), + If(source.ack, NextState("IDLE")) + ) + ) + self.comb += cnt_done.eq(cnt == 0) + self.sync += \ + If(fsm.ongoing("IDLE"), + cnt.eq(cnt.reset) + ).Elif(fsm.ongoing("INSERT") & ~cnt_done, + cnt.eq(cnt - source.ack) + ) + else: + fsm.act("INSERT", + source.stb.eq(1), + source.eop.eq(1), + source.data.eq(crc.value), + If(source.ack, NextState("IDLE")) + ) + self.comb += self.busy.eq(~fsm.ongoing("IDLE")) + + +class LiteEthMACCRC32Inserter(LiteEthMACCRCInserter): + def __init__(self, description): + LiteEthMACCRCInserter.__init__(self, LiteEthMACCRC32, description) + + +class LiteEthMACCRCChecker(Module): + """CRC Checker + + Check CRC at the end of each packet. + + Parameters + ---------- + description : description + description of the dataflow. + + Attributes + ---------- + sink : in + Packets input with CRC. + source : out + Packets output without CRC and "error" set to 0 + on eop when CRC OK / set to 1 when CRC KO. + """ + def __init__(self, crc_class, description): + self.sink = sink = Sink(description) + self.source = source = Source(description) + self.busy = Signal() + + # # # + + dw = flen(sink.data) + crc = crc_class(dw) + self.submodules += crc + ratio = crc.width//dw + + error = Signal() + fifo = InsertReset(SyncFIFO(description, ratio + 1)) + self.submodules += fifo + + fsm = FSM(reset_state="RESET") + self.submodules += fsm + + fifo_in = Signal() + fifo_out = Signal() + fifo_full = Signal() + + self.comb += [ + fifo_full.eq(fifo.fifo.level == ratio), + fifo_in.eq(sink.stb & (~fifo_full | fifo_out)), + fifo_out.eq(source.stb & source.ack), + + Record.connect(sink, fifo.sink), + fifo.sink.stb.eq(fifo_in), + self.sink.ack.eq(fifo_in), + + source.stb.eq(sink.stb & fifo_full), + source.sop.eq(fifo.source.sop), + source.eop.eq(sink.eop), + fifo.source.ack.eq(fifo_out), + source.payload.eq(fifo.source.payload), + + source.error.eq(sink.error | crc.error), + ] + + fsm.act("RESET", + crc.reset.eq(1), + fifo.reset.eq(1), + NextState("IDLE"), + ) + self.comb += crc.data.eq(sink.data) + fsm.act("IDLE", + If(sink.stb & sink.sop & sink.ack, + crc.ce.eq(1), + NextState("COPY") + ) + ) + fsm.act("COPY", + If(sink.stb & sink.ack, + crc.ce.eq(1), + If(sink.eop, + NextState("RESET") + ) + ) + ) + self.comb += self.busy.eq(~fsm.ongoing("IDLE")) + + +class LiteEthMACCRC32Checker(LiteEthMACCRCChecker): + def __init__(self, description): + LiteEthMACCRCChecker.__init__(self, LiteEthMACCRC32, description) diff --git a/misoc/liteethmini/mac/core/gap.py b/misoc/liteethmini/mac/core/gap.py new file mode 100644 index 00000000..ce4ad1ba --- /dev/null +++ b/misoc/liteethmini/mac/core/gap.py @@ -0,0 +1,27 @@ +from misoc.com.liteethmini.common import * + +class LiteEthMACGap(Module): + def __init__(self, dw, ack_on_gap=False): + self.sink = sink = Sink(eth_phy_description(dw)) + self.source = source = Source(eth_phy_description(dw)) + + # # # + + gap = math.ceil(eth_interpacket_gap/(dw//8)) + self.submodules.counter = counter = Counter(max=gap) + + self.submodules.fsm = fsm = FSM(reset_state="COPY") + fsm.act("COPY", + counter.reset.eq(1), + Record.connect(sink, source), + If(sink.stb & sink.eop & sink.ack, + NextState("GAP") + ) + ) + fsm.act("GAP", + counter.ce.eq(1), + sink.ack.eq(int(ack_on_gap)), + If(counter.value == (gap-1), + NextState("COPY") + ) + ) diff --git a/misoc/liteethmini/mac/core/last_be.py b/misoc/liteethmini/mac/core/last_be.py new file mode 100644 index 00000000..14d8a308 --- /dev/null +++ b/misoc/liteethmini/mac/core/last_be.py @@ -0,0 +1,43 @@ +from misoc.com.liteethmini.common import * + + +class LiteEthMACTXLastBE(Module): + def __init__(self, dw): + self.sink = sink = Sink(eth_phy_description(dw)) + self.source = source = Source(eth_phy_description(dw)) + + # # # + + ongoing = Signal() + self.sync += \ + If(sink.stb & sink.ack, + If(sink.sop, + ongoing.eq(1) + ).Elif(sink.last_be, + ongoing.eq(0) + ) + ) + self.comb += [ + source.stb.eq(sink.stb & (sink.sop | ongoing)), + source.sop.eq(sink.sop), + source.eop.eq(sink.last_be), + source.data.eq(sink.data), + sink.ack.eq(source.ack) + ] + + +class LiteEthMACRXLastBE(Module): + def __init__(self, dw): + self.sink = sink = Sink(eth_phy_description(dw)) + self.source = source = Source(eth_phy_description(dw)) + + # # # + + self.comb += [ + source.stb.eq(sink.stb), + source.sop.eq(sink.sop), + source.eop.eq(sink.eop), + source.data.eq(sink.data), + source.last_be.eq(sink.eop), + sink.ack.eq(source.ack) + ] diff --git a/misoc/liteethmini/mac/core/padding.py b/misoc/liteethmini/mac/core/padding.py new file mode 100644 index 00000000..fa4aa927 --- /dev/null +++ b/misoc/liteethmini/mac/core/padding.py @@ -0,0 +1,56 @@ +from misoc.com.liteethmini.common import * + + +class LiteEthMACPaddingInserter(Module): + def __init__(self, dw, padding): + self.sink = sink = Sink(eth_phy_description(dw)) + self.source = source = Source(eth_phy_description(dw)) + + # # # + + padding_limit = math.ceil(padding/(dw/8))-1 + + self.submodules.counter = counter = Counter(16, reset=1) + counter_done = Signal() + self.comb += [ + counter.reset.eq(sink.stb & sink.sop & sink.ack), + counter.ce.eq(source.stb & source.ack), + counter_done.eq(counter.value >= padding_limit), + ] + + self.submodules.fsm = fsm = FSM(reset_state="IDLE") + fsm.act("IDLE", + Record.connect(sink, source), + If(source.stb & source.ack, + counter.ce.eq(1), + If(sink.eop, + If(~counter_done, + source.eop.eq(0), + NextState("PADDING") + ) + ) + ) + ) + fsm.act("PADDING", + source.stb.eq(1), + source.eop.eq(counter_done), + source.data.eq(0), + If(source.ack, + If(counter_done, + NextState("IDLE") + ) + ) + ) + + +class LiteEthMACPaddingChecker(Module): + def __init__(self, dw, packet_min_length): + self.sink = sink = Sink(eth_phy_description(dw)) + self.source = source = Source(eth_phy_description(dw)) + + # # # + + # XXX see if we should drop the packet when + # payload size < minimum ethernet payload size + self.comb += Record.connect(sink, source) + diff --git a/misoc/liteethmini/mac/core/preamble.py b/misoc/liteethmini/mac/core/preamble.py new file mode 100644 index 00000000..52078f90 --- /dev/null +++ b/misoc/liteethmini/mac/core/preamble.py @@ -0,0 +1,150 @@ +from misoc.com.liteethmini.common import * + + +class LiteEthMACPreambleInserter(Module): + def __init__(self, dw): + self.sink = Sink(eth_phy_description(dw)) + self.source = Source(eth_phy_description(dw)) + + # # # + + preamble = Signal(64, reset=eth_preamble) + cnt_max = (64//dw)-1 + cnt = Signal(max=cnt_max+1) + clr_cnt = Signal() + inc_cnt = Signal() + + self.sync += \ + If(clr_cnt, + cnt.eq(0) + ).Elif(inc_cnt, + cnt.eq(cnt+1) + ) + + fsm = FSM(reset_state="IDLE") + self.submodules += fsm + fsm.act("IDLE", + self.sink.ack.eq(1), + clr_cnt.eq(1), + If(self.sink.stb & self.sink.sop, + self.sink.ack.eq(0), + NextState("INSERT"), + ) + ) + fsm.act("INSERT", + self.source.stb.eq(1), + self.source.sop.eq(cnt == 0), + chooser(preamble, cnt, self.source.data), + If(cnt == cnt_max, + If(self.source.ack, NextState("COPY")) + ).Else( + inc_cnt.eq(self.source.ack) + ) + ) + + self.comb += [ + self.source.data.eq(self.sink.data), + self.source.last_be.eq(self.sink.last_be) + ] + fsm.act("COPY", + Record.connect(self.sink, self.source, leave_out=set(["data", "last_be"])), + self.source.sop.eq(0), + + If(self.sink.stb & self.sink.eop & self.source.ack, + NextState("IDLE"), + ) + ) + + +class LiteEthMACPreambleChecker(Module): + def __init__(self, dw): + self.sink = Sink(eth_phy_description(dw)) + self.source = Source(eth_phy_description(dw)) + + # # # + + preamble = Signal(64, reset=eth_preamble) + cnt_max = (64//dw) - 1 + cnt = Signal(max=cnt_max+1) + clr_cnt = Signal() + inc_cnt = Signal() + + self.sync += \ + If(clr_cnt, + cnt.eq(0) + ).Elif(inc_cnt, + cnt.eq(cnt+1) + ) + + discard = Signal() + clr_discard = Signal() + set_discard = Signal() + + self.sync += \ + If(clr_discard, + discard.eq(0) + ).Elif(set_discard, + discard.eq(1) + ) + + sop = Signal() + clr_sop = Signal() + set_sop = Signal() + self.sync += \ + If(clr_sop, + sop.eq(0) + ).Elif(set_sop, + sop.eq(1) + ) + + ref = Signal(dw) + match = Signal() + self.comb += [ + chooser(preamble, cnt, ref), + match.eq(self.sink.data == ref) + ] + + fsm = FSM(reset_state="IDLE") + self.submodules += fsm + + fsm.act("IDLE", + self.sink.ack.eq(1), + clr_cnt.eq(1), + clr_discard.eq(1), + If(self.sink.stb & self.sink.sop, + clr_cnt.eq(0), + inc_cnt.eq(1), + clr_discard.eq(0), + set_discard.eq(~match), + NextState("CHECK"), + ) + ) + fsm.act("CHECK", + self.sink.ack.eq(1), + If(self.sink.stb, + set_discard.eq(~match), + If(cnt == cnt_max, + If(discard | (~match), + NextState("IDLE") + ).Else( + set_sop.eq(1), + NextState("COPY") + ) + ).Else( + inc_cnt.eq(1) + ) + ) + ) + self.comb += [ + self.source.data.eq(self.sink.data), + self.source.last_be.eq(self.sink.last_be) + ] + fsm.act("COPY", + Record.connect(self.sink, self.source, leave_out=set(["data", "last_be"])), + self.source.sop.eq(sop), + clr_sop.eq(self.source.stb & self.source.ack), + + If(self.source.stb & self.source.eop & self.source.ack, + NextState("IDLE"), + ) + ) diff --git a/misoc/liteethmini/mac/frontend/__init__.py b/misoc/liteethmini/mac/frontend/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/misoc/liteethmini/mac/frontend/sram.py b/misoc/liteethmini/mac/frontend/sram.py new file mode 100644 index 00000000..1495e08e --- /dev/null +++ b/misoc/liteethmini/mac/frontend/sram.py @@ -0,0 +1,234 @@ +from misoc.com.liteethmini.common import * + +from migen.bank.description import * +from migen.bank.eventmanager import * + + +class LiteEthMACSRAMWriter(Module, AutoCSR): + def __init__(self, dw, depth, nslots=2): + self.sink = sink = Sink(eth_phy_description(dw)) + self.crc_error = Signal() + + slotbits = max(log2_int(nslots), 1) + lengthbits = log2_int(depth*4) # length in bytes + + self._slot = CSRStatus(slotbits) + self._length = CSRStatus(lengthbits) + + self.submodules.ev = EventManager() + self.ev.available = EventSourceLevel() + self.ev.finalize() + + # # # + + # packet dropped if no slot available + sink.ack.reset = 1 + + # length computation + increment = Signal(3) + self.comb += \ + If(sink.last_be[3], + increment.eq(1) + ).Elif(sink.last_be[2], + increment.eq(2) + ).Elif(sink.last_be[1], + increment.eq(3) + ).Else( + increment.eq(4) + ) + counter = Counter(lengthbits, increment=increment) + self.submodules += counter + + # slot computation + slot = Counter(slotbits) + self.submodules += slot + + ongoing = Signal() + + # status fifo + fifo = SyncFIFO([("slot", slotbits), ("length", lengthbits)], nslots) + self.submodules += fifo + + # fsm + fsm = FSM(reset_state="IDLE") + self.submodules += fsm + + fsm.act("IDLE", + If(sink.stb & sink.sop, + If(fifo.sink.ack, + ongoing.eq(1), + counter.ce.eq(1), + NextState("WRITE") + ) + ) + ) + fsm.act("WRITE", + counter.ce.eq(sink.stb), + ongoing.eq(1), + If(sink.stb & sink.eop, + If((sink.error & sink.last_be) != 0, + NextState("DISCARD") + ).Else( + NextState("TERMINATE") + ) + ) + ) + fsm.act("DISCARD", + counter.reset.eq(1), + NextState("IDLE") + ) + self.comb += [ + fifo.sink.slot.eq(slot.value), + fifo.sink.length.eq(counter.value) + ] + fsm.act("TERMINATE", + counter.reset.eq(1), + slot.ce.eq(1), + fifo.sink.stb.eq(1), + NextState("IDLE") + ) + self.comb += [ + fifo.source.ack.eq(self.ev.available.clear), + self.ev.available.trigger.eq(fifo.source.stb), + self._slot.status.eq(fifo.source.slot), + self._length.status.eq(fifo.source.length), + ] + + # memory + mems = [None]*nslots + ports = [None]*nslots + for n in range(nslots): + mems[n] = Memory(dw, depth) + ports[n] = mems[n].get_port(write_capable=True) + self.specials += ports[n] + self.mems = mems + + cases = {} + for n, port in enumerate(ports): + cases[n] = [ + ports[n].adr.eq(counter.value[2:]), + ports[n].dat_w.eq(sink.data), + If(sink.stb & ongoing, + ports[n].we.eq(0xf) + ) + ] + self.comb += Case(slot.value, cases) + + +class LiteEthMACSRAMReader(Module, AutoCSR): + def __init__(self, dw, depth, nslots=2): + self.source = source = Source(eth_phy_description(dw)) + + slotbits = max(log2_int(nslots), 1) + lengthbits = log2_int(depth*4) # length in bytes + self.lengthbits = lengthbits + + self._start = CSR() + self._ready = CSRStatus() + self._slot = CSRStorage(slotbits) + self._length = CSRStorage(lengthbits) + + self.submodules.ev = EventManager() + self.ev.done = EventSourcePulse() + self.ev.finalize() + + # # # + + # command fifo + fifo = SyncFIFO([("slot", slotbits), ("length", lengthbits)], nslots) + self.submodules += fifo + self.comb += [ + fifo.sink.stb.eq(self._start.re), + fifo.sink.slot.eq(self._slot.storage), + fifo.sink.length.eq(self._length.storage), + self._ready.status.eq(fifo.sink.ack) + ] + + # length computation + self.submodules.counter = counter = Counter(lengthbits, increment=4) + + # fsm + first = Signal() + last = Signal() + last_d = Signal() + + fsm = FSM(reset_state="IDLE") + self.submodules += fsm + + fsm.act("IDLE", + counter.reset.eq(1), + If(fifo.source.stb, + NextState("CHECK") + ) + ) + fsm.act("CHECK", + If(~last_d, + NextState("SEND"), + ).Else( + NextState("END"), + ) + ) + length_lsb = fifo.source.length[0:2] + self.comb += [ + If(last, + If(length_lsb == 3, + source.last_be.eq(0b0010) + ).Elif(length_lsb == 2, + source.last_be.eq(0b0100) + ).Elif(length_lsb == 1, + source.last_be.eq(0b1000) + ).Else( + source.last_be.eq(0b0001) + ) + ) + ] + fsm.act("SEND", + source.stb.eq(1), + source.sop.eq(first), + source.eop.eq(last), + If(source.ack, + counter.ce.eq(~last), + NextState("CHECK") + ) + ) + fsm.act("END", + fifo.source.ack.eq(1), + self.ev.done.trigger.eq(1), + NextState("IDLE") + ) + + # first/last computation + self.sync += [ + If(fsm.ongoing("IDLE"), + first.eq(1) + ).Elif(source.stb & source.ack, + first.eq(0) + ) + ] + self.comb += last.eq((counter.value + 4) >= fifo.source.length) + self.sync += last_d.eq(last) + + # memory + rd_slot = fifo.source.slot + + mems = [None]*nslots + ports = [None]*nslots + for n in range(nslots): + mems[n] = Memory(dw, depth) + ports[n] = mems[n].get_port() + self.specials += ports[n] + self.mems = mems + + cases = {} + for n, port in enumerate(ports): + self.comb += ports[n].adr.eq(counter.value[2:]) + cases[n] = [source.data.eq(port.dat_r)] + self.comb += Case(rd_slot, cases) + + +class LiteEthMACSRAM(Module, AutoCSR): + def __init__(self, dw, depth, nrxslots, ntxslots): + self.submodules.writer = LiteEthMACSRAMWriter(dw, depth, nrxslots) + self.submodules.reader = LiteEthMACSRAMReader(dw, depth, ntxslots) + self.submodules.ev = SharedIRQ(self.writer.ev, self.reader.ev) + self.sink, self.source = self.writer.sink, self.reader.source diff --git a/misoc/liteethmini/mac/frontend/wishbone.py b/misoc/liteethmini/mac/frontend/wishbone.py new file mode 100644 index 00000000..e45f744e --- /dev/null +++ b/misoc/liteethmini/mac/frontend/wishbone.py @@ -0,0 +1,41 @@ +from misoc.com.liteethmini.common import * +from misoc.com.liteethmini.mac.frontend import sram + +from migen.bus import wishbone +from migen.fhdl.simplify import FullMemoryWE + + +class LiteEthMACWishboneInterface(Module, AutoCSR): + def __init__(self, dw, nrxslots=2, ntxslots=2): + self.sink = Sink(eth_phy_description(dw)) + self.source = Source(eth_phy_description(dw)) + self.bus = wishbone.Interface() + + # # # + + # storage in SRAM + sram_depth = buffer_depth//(dw//8) + self.submodules.sram = sram.LiteEthMACSRAM(dw, sram_depth, nrxslots, ntxslots) + self.comb += [ + Record.connect(self.sink, self.sram.sink), + Record.connect(self.sram.source, self.source) + ] + + # Wishbone interface + wb_rx_sram_ifs = [wishbone.SRAM(self.sram.writer.mems[n], read_only=True) + for n in range(nrxslots)] + # TODO: FullMemoryWE should move to Mibuild + wb_tx_sram_ifs = [FullMemoryWE()(wishbone.SRAM(self.sram.reader.mems[n], read_only=False)) + for n in range(ntxslots)] + wb_sram_ifs = wb_rx_sram_ifs + wb_tx_sram_ifs + + wb_slaves = [] + decoderoffset = log2_int(sram_depth) + decoderbits = log2_int(len(wb_sram_ifs)) + for n, wb_sram_if in enumerate(wb_sram_ifs): + def slave_filter(a, v=n): + return a[decoderoffset:decoderoffset+decoderbits] == v + wb_slaves.append((slave_filter, wb_sram_if.bus)) + self.submodules += wb_sram_if + wb_con = wishbone.Decoder(self.bus, wb_slaves, register=True) + self.submodules += wb_con diff --git a/misoc/liteethmini/phy/__init__.py b/misoc/liteethmini/phy/__init__.py new file mode 100644 index 00000000..c17124d1 --- /dev/null +++ b/misoc/liteethmini/phy/__init__.py @@ -0,0 +1,27 @@ +from misoc.com.liteethmini.common import * + + +def LiteEthPHY(clock_pads, pads, clk_freq=None, **kwargs): + # Autodetect PHY + if hasattr(pads, "source_stb"): + # This is a simulation PHY + from misoc.com.liteethmini.phy.sim import LiteEthPHYSim + return LiteEthPHYSim(pads) + elif hasattr(clock_pads, "gtx") and flen(pads.tx_data) == 8: + if hasattr(clock_pads, "tx"): + # This is a 10/100/1G PHY + from misoc.com.liteethmini.phy.gmii_mii import LiteEthPHYGMIIMII + return LiteEthPHYGMIIMII(clock_pads, pads, clk_freq=clk_freq, **kwargs) + else: + # This is a pure 1G PHY + from misoc.com.liteethmini.phy.gmii import LiteEthPHYGMII + return LiteEthPHYGMII(clock_pads, pads, **kwargs) + elif hasattr(pads, "rx_ctl"): + # This is a 10/100/1G RGMII PHY + raise ValueError("RGMII PHYs are specific to vendors (for now), use direct instantiation") + elif flen(pads.tx_data) == 4: + # This is a MII PHY + from misoc.com.liteethmini.phy.mii import LiteEthPHYMII + return LiteEthPHYMII(clock_pads, pads, **kwargs) + else: + raise ValueError("Unable to autodetect PHY from platform file, use direct instantiation") diff --git a/misoc/liteethmini/phy/gmii.py b/misoc/liteethmini/phy/gmii.py new file mode 100644 index 00000000..24f4dc8f --- /dev/null +++ b/misoc/liteethmini/phy/gmii.py @@ -0,0 +1,98 @@ +from migen.genlib.io import DDROutput + +from misoc.com.liteethmini.common import * + + +class LiteEthPHYGMIITX(Module): + def __init__(self, pads, pads_register=True): + self.sink = sink = Sink(eth_phy_description(8)) + + # # # + + if hasattr(pads, "tx_er"): + self.sync += pads.tx_er.eq(0) + pads_eq = [ + pads.tx_en.eq(sink.stb), + pads.tx_data.eq(sink.data) + ] + if pads_register: + self.sync += pads_eq + else: + self.comb += pads_eq + self.comb += sink.ack.eq(1) + + +class LiteEthPHYGMIIRX(Module): + def __init__(self, pads): + self.source = source = Source(eth_phy_description(8)) + + # # # + + dv_d = Signal() + self.sync += dv_d.eq(pads.dv) + + sop = Signal() + eop = Signal() + self.comb += [ + sop.eq(pads.dv & ~dv_d), + eop.eq(~pads.dv & dv_d) + ] + self.sync += [ + source.stb.eq(pads.dv), + source.sop.eq(sop), + source.data.eq(pads.rx_data) + ] + self.comb += source.eop.eq(eop) + + +class LiteEthPHYGMIICRG(Module, AutoCSR): + def __init__(self, clock_pads, pads, with_hw_init_reset, mii_mode=0): + self._reset = CSRStorage() + + # # # + + self.clock_domains.cd_eth_rx = ClockDomain() + self.clock_domains.cd_eth_tx = ClockDomain() + + # RX : Let the synthesis tool insert the appropriate clock buffer + self.comb += self.cd_eth_rx.clk.eq(clock_pads.rx) + + # TX : GMII: Drive clock_pads.gtx, clock_pads.tx unused + # MII: Use PHY clock_pads.tx as eth_tx_clk, do not drive clock_pads.gtx + self.specials += DDROutput(1, mii_mode, clock_pads.gtx, ClockSignal("eth_tx")) + # XXX Xilinx specific, replace BUFGMUX with a generic clock buffer? + self.specials += Instance("BUFGMUX", + i_I0=self.cd_eth_rx.clk, + i_I1=clock_pads.tx, + i_S=mii_mode, + o_O=self.cd_eth_tx.clk) + + if with_hw_init_reset: + reset = Signal() + counter_done = Signal() + self.submodules.counter = counter = Counter(max=512) + self.comb += [ + counter_done.eq(counter.value == 256), + counter.ce.eq(~counter_done), + reset.eq(~counter_done | self._reset.storage) + ] + else: + reset = self._reset.storage + self.comb += pads.rst_n.eq(~reset) + self.specials += [ + AsyncResetSynchronizer(self.cd_eth_tx, reset), + AsyncResetSynchronizer(self.cd_eth_rx, reset), + ] + + +class LiteEthPHYGMII(Module, AutoCSR): + def __init__(self, clock_pads, pads, with_hw_init_reset=True): + self.dw = 8 + self.submodules.crg = LiteEthPHYGMIICRG(clock_pads, + pads, + with_hw_init_reset) + self.submodules.tx = RenameClockDomains(LiteEthPHYGMIITX(pads), + "eth_tx") + self.submodules.rx = RenameClockDomains(LiteEthPHYGMIIRX(pads), + "eth_rx") + self.sink, self.source = self.tx.sink, self.rx.source diff --git a/misoc/liteethmini/phy/gmii_mii.py b/misoc/liteethmini/phy/gmii_mii.py new file mode 100644 index 00000000..8ed4741e --- /dev/null +++ b/misoc/liteethmini/phy/gmii_mii.py @@ -0,0 +1,161 @@ +from migen.genlib.io import DDROutput +from migen.flow.plumbing import Multiplexer, Demultiplexer +from migen.genlib.cdc import PulseSynchronizer + +from misoc.com.liteethmini.common import * + +from misoc.com.liteethmini.phy.gmii import LiteEthPHYGMIICRG +from misoc.com.liteethmini.phy.mii import LiteEthPHYMIITX, LiteEthPHYMIIRX +from misoc.com.liteethmini.phy.gmii import LiteEthPHYGMIITX, LiteEthPHYGMIIRX + +modes = { + "GMII": 0, + "MII": 1 +} + +tx_pads_layout = [("tx_er", 1), ("tx_en", 1), ("tx_data", 8)] +rx_pads_layout = [("rx_er", 1), ("dv", 1), ("rx_data", 8)] + + +class LiteEthPHYGMIIMIITX(Module): + def __init__(self, pads, mode): + self.sink = sink = Sink(eth_phy_description(8)) + + # # # + + gmii_tx_pads = Record(tx_pads_layout) + gmii_tx = LiteEthPHYGMIITX(gmii_tx_pads, pads_register=False) + self.submodules += gmii_tx + + mii_tx_pads = Record(tx_pads_layout) + mii_tx = LiteEthPHYMIITX(mii_tx_pads, pads_register=False) + self.submodules += mii_tx + + demux = Demultiplexer(eth_phy_description(8), 2) + self.submodules += demux + self.comb += [ + demux.sel.eq(mode == modes["MII"]), + Record.connect(sink, demux.sink), + Record.connect(demux.source0, gmii_tx.sink), + Record.connect(demux.source1, mii_tx.sink), + ] + + if hasattr(pads, "tx_er"): + self.comb += pads.tx_er.eq(0) + self.sync += [ + If(mode == modes["MII"], + pads.tx_en.eq(mii_tx_pads.tx_en), + pads.tx_data.eq(mii_tx_pads.tx_data), + ).Else( + pads.tx_en.eq(gmii_tx_pads.tx_en), + pads.tx_data.eq(gmii_tx_pads.tx_data), + ) + ] + + +class LiteEthPHYGMIIMIIRX(Module): + def __init__(self, pads, mode): + self.source = source = Source(eth_phy_description(8)) + + # # # + + pads_d = Record(rx_pads_layout) + self.sync += [ + pads_d.dv.eq(pads.dv), + pads_d.rx_data.eq(pads.rx_data) + ] + + gmii_rx = LiteEthPHYGMIIRX(pads_d) + self.submodules += gmii_rx + + mii_rx = LiteEthPHYMIIRX(pads_d) + self.submodules += mii_rx + + mux = Multiplexer(eth_phy_description(8), 2) + self.submodules += mux + self.comb += [ + mux.sel.eq(mode == modes["MII"]), + Record.connect(gmii_rx.source, mux.sink0), + Record.connect(mii_rx.source, mux.sink1), + Record.connect(mux.source, source) + ] + + +class LiteEthGMIIMIIModeDetection(Module, AutoCSR): + def __init__(self, clk_freq): + self.mode = Signal() + self._mode = CSRStatus() + + # # # + + mode = Signal() + update_mode = Signal() + self.sync += \ + If(update_mode, + self.mode.eq(mode) + ) + self.comb += self._mode.status.eq(self.mode) + + # Principle: + # sys_clk >= 125MHz + # eth_rx <= 125Mhz + # We generate ticks every 1024 clock cycles in eth_rx domain + # and measure ticks period in sys_clk domain. + + # Generate a tick every 1024 clock cycles (eth_rx clock domain) + eth_tick = Signal() + eth_counter = Signal(10) + self.sync.eth_rx += eth_counter.eq(eth_counter + 1) + self.comb += eth_tick.eq(eth_counter == 0) + + # Synchronize tick (sys clock domain) + sys_tick = Signal() + eth_ps = PulseSynchronizer("eth_rx", "sys") + self.comb += [ + eth_ps.i.eq(eth_tick), + sys_tick.eq(eth_ps.o) + ] + self.submodules += eth_ps + + # sys_clk domain counter + sys_counter = Counter(24) + self.submodules += sys_counter + + fsm = FSM(reset_state="IDLE") + self.submodules += fsm + + fsm.act("IDLE", + sys_counter.reset.eq(1), + If(sys_tick, + NextState("COUNT") + ) + ) + fsm.act("COUNT", + sys_counter.ce.eq(1), + If(sys_tick, + NextState("DETECTION") + ) + ) + fsm.act("DETECTION", + update_mode.eq(1), + # if freq < 125MHz-5% use MII mode + If(sys_counter.value > int((clk_freq/125000000)*1024*1.05), + mode.eq(1) + # if freq >= 125MHz-5% use GMII mode + ).Else( + mode.eq(0) + ), + NextState("IDLE") + ) + + +class LiteEthPHYGMIIMII(Module, AutoCSR): + def __init__(self, clock_pads, pads, clk_freq, with_hw_init_reset=True): + self.dw = 8 + # Note: we can use GMII CRG since it also handles tx clock pad used for MII + self.submodules.mode_detection = LiteEthGMIIMIIModeDetection(clk_freq) + mode = self.mode_detection.mode + self.submodules.crg = LiteEthPHYGMIICRG(clock_pads, pads, with_hw_init_reset, mode == modes["MII"]) + self.submodules.tx = RenameClockDomains(LiteEthPHYGMIIMIITX(pads, mode), "eth_tx") + self.submodules.rx = RenameClockDomains(LiteEthPHYGMIIMIIRX(pads, mode), "eth_rx") + self.sink, self.source = self.tx.sink, self.rx.source diff --git a/misoc/liteethmini/phy/loopback.py b/misoc/liteethmini/phy/loopback.py new file mode 100644 index 00000000..c9e5cf1c --- /dev/null +++ b/misoc/liteethmini/phy/loopback.py @@ -0,0 +1,31 @@ +from misoc.com.liteethmini.common import * +from misoc.com.liteethmini.generic import * + + +class LiteEthPHYLoopbackCRG(Module, AutoCSR): + def __init__(self): + self._reset = CSRStorage() + + # # # + + self.clock_domains.cd_eth_rx = ClockDomain() + self.clock_domains.cd_eth_tx = ClockDomain() + self.comb += [ + self.cd_eth_rx.clk.eq(ClockSignal()), + self.cd_eth_tx.clk.eq(ClockSignal()) + ] + + reset = self._reset.storage + self.comb += [ + self.cd_eth_rx.rst.eq(reset), + self.cd_eth_tx.rst.eq(reset) + ] + + +class LiteEthPHYLoopback(Module, AutoCSR): + def __init__(self): + self.dw = 8 + self.submodules.crg = LiteEthLoopbackPHYCRG() + self.sink = sink = Sink(eth_phy_description(8)) + self.source = source = Source(eth_phy_description(8)) + self.comb += Record.connect(self.sink, self.source) diff --git a/misoc/liteethmini/phy/mii.py b/misoc/liteethmini/phy/mii.py new file mode 100644 index 00000000..69565246 --- /dev/null +++ b/misoc/liteethmini/phy/mii.py @@ -0,0 +1,102 @@ +from misoc.com.liteethmini.common import * + + +def converter_description(dw): + payload_layout = [("data", dw)] + return EndpointDescription(payload_layout, packetized=True) + + +class LiteEthPHYMIITX(Module): + def __init__(self, pads, pads_register=True): + self.sink = sink = Sink(eth_phy_description(8)) + + # # # + + if hasattr(pads, "tx_er"): + self.sync += pads.tx_er.eq(0) + converter = Converter(converter_description(8), + converter_description(4)) + self.submodules += converter + self.comb += [ + converter.sink.stb.eq(sink.stb), + converter.sink.data.eq(sink.data), + sink.ack.eq(converter.sink.ack), + converter.source.ack.eq(1) + ] + pads_eq = [ + pads.tx_en.eq(converter.source.stb), + pads.tx_data.eq(converter.source.data) + ] + if pads_register: + self.sync += pads_eq + else: + self.comb += pads_eq + + +class LiteEthPHYMIIRX(Module): + def __init__(self, pads): + self.source = source = Source(eth_phy_description(8)) + + # # # + + sop = FlipFlop(reset=1) + self.submodules += sop + + converter = Converter(converter_description(4), + converter_description(8)) + converter = InsertReset(converter) + self.submodules += converter + + self.sync += [ + converter.reset.eq(~pads.dv), + converter.sink.stb.eq(1), + converter.sink.data.eq(pads.rx_data) + ] + self.comb += [ + sop.reset.eq(~pads.dv), + sop.ce.eq(pads.dv), + converter.sink.sop.eq(sop.q), + converter.sink.eop.eq(~pads.dv) + ] + self.comb += Record.connect(converter.source, source) + + +class LiteEthPHYMIICRG(Module, AutoCSR): + def __init__(self, clock_pads, pads, with_hw_init_reset): + self._reset = CSRStorage() + + # # # + + if hasattr(clock_pads, "phy"): + self.sync.base50 += clock_pads.phy.eq(~clock_pads.phy) + + self.clock_domains.cd_eth_rx = ClockDomain() + self.clock_domains.cd_eth_tx = ClockDomain() + self.comb += self.cd_eth_rx.clk.eq(clock_pads.rx) + self.comb += self.cd_eth_tx.clk.eq(clock_pads.tx) + + if with_hw_init_reset: + reset = Signal() + counter_done = Signal() + self.submodules.counter = counter = Counter(max=512) + self.comb += [ + counter_done.eq(counter.value == 256), + counter.ce.eq(~counter_done), + reset.eq(~counter_done | self._reset.storage) + ] + else: + reset = self._reset.storage + self.comb += pads.rst_n.eq(~reset) + self.specials += [ + AsyncResetSynchronizer(self.cd_eth_tx, reset), + AsyncResetSynchronizer(self.cd_eth_rx, reset), + ] + + +class LiteEthPHYMII(Module, AutoCSR): + def __init__(self, clock_pads, pads, with_hw_init_reset=True): + self.dw = 8 + self.submodules.crg = LiteEthPHYMIICRG(clock_pads, pads, with_hw_init_reset) + self.submodules.tx = RenameClockDomains(LiteEthPHYMIITX(pads), "eth_tx") + self.submodules.rx = RenameClockDomains(LiteEthPHYMIIRX(pads), "eth_rx") + self.sink, self.source = self.tx.sink, self.rx.source diff --git a/misoc/liteethmini/phy/s6rgmii.py b/misoc/liteethmini/phy/s6rgmii.py new file mode 100644 index 00000000..8f7da75f --- /dev/null +++ b/misoc/liteethmini/phy/s6rgmii.py @@ -0,0 +1,162 @@ +# RGMII PHY for Spartan-6 + +from migen.genlib.io import DDROutput +from migen.genlib.misc import WaitTimer +from migen.genlib.fsm import FSM, NextState + +from misoc.com.liteethmini.common import * + + +class LiteEthPHYRGMIITX(Module): + def __init__(self, pads, pads_register=True): + self.sink = sink = Sink(eth_phy_description(8)) + + # # # + + self.specials += Instance("ODDR2", + p_DDR_ALIGNMENT="C0", p_INIT=0, p_SRTYPE="ASYNC", + i_C0=ClockSignal("eth_tx"), i_C1=~ClockSignal("eth_tx"), + i_CE=1, i_S=0, i_R=0, + i_D0=sink.stb, i_D1=sink.stb, o_Q=pads.tx_ctl, + ) + for i in range(4): + self.specials += Instance("ODDR2", + p_DDR_ALIGNMENT="C0", p_INIT=0, p_SRTYPE="ASYNC", + i_C0=ClockSignal("eth_tx"), i_C1=~ClockSignal("eth_tx"), + i_CE=1, i_S=0, i_R=0, + i_D0=sink.data[i], i_D1=sink.data[4+i], o_Q=pads.tx_data[i], + ) + self.comb += sink.ack.eq(1) + + +class LiteEthPHYRGMIIRX(Module): + def __init__(self, pads): + self.source = source = Source(eth_phy_description(8)) + + # # # + + rx_ctl = Signal() + rx_data = Signal(8) + + self.specials += Instance("IDDR2", + p_DDR_ALIGNMENT="C0", p_INIT_Q0=0, p_INIT_Q1=0, p_SRTYPE="ASYNC", + i_C0=ClockSignal("eth_rx"), i_C1=~ClockSignal("eth_rx"), + i_CE=1, i_S=0, i_R=0, + i_D=pads.rx_ctl, o_Q1=rx_ctl, + ) + for i in range(4): + self.specials += Instance("IDDR2", + p_DDR_ALIGNMENT="C0", p_INIT_Q0=0, p_INIT_Q1=0, p_SRTYPE="ASYNC", + i_C0=ClockSignal("eth_rx"), i_C1=~ClockSignal("eth_rx"), + i_CE=1, i_S=0, i_R=0, + i_D=pads.rx_data[i], o_Q0=rx_data[4+i], o_Q1=rx_data[i], + ) + + + rx_ctl_d = Signal() + self.sync += rx_ctl_d.eq(rx_ctl) + + sop = Signal() + eop = Signal() + self.comb += [ + sop.eq(rx_ctl & ~rx_ctl_d), + eop.eq(~rx_ctl & rx_ctl_d) + ] + self.sync += [ + source.stb.eq(rx_ctl), + source.sop.eq(sop), + source.data.eq(rx_data) + ] + self.comb += source.eop.eq(eop) + + +class LiteEthPHYRGMIICRG(Module, AutoCSR): + def __init__(self, clock_pads, pads, with_hw_init_reset): + self._reset = CSRStorage() + + # # # + + self.clock_domains.cd_eth_rx = ClockDomain() + self.clock_domains.cd_eth_tx = ClockDomain() + + + # RX + dcm_reset = Signal() + dcm_locked = Signal() + + timer = WaitTimer(1024) + fsm = FSM(reset_state="DCM_RESET") + self.submodules += timer, fsm + + fsm.act("DCM_RESET", + dcm_reset.eq(1), + timer.wait.eq(1), + If(timer.done, + timer.wait.eq(0), + NextState("DCM_WAIT") + ) + ) + fsm.act("DCM_WAIT", + timer.wait.eq(1), + If(timer.done, + NextState("DCM_CHECK_LOCK") + ) + ) + fsm.act("DCM_CHECK_LOCK", + If(~dcm_locked, + NextState("DCM_RESET") + ) + ) + + clk90_rx = Signal() + clk0_rx = Signal() + clk0_rx_bufg = Signal() + self.specials += Instance("DCM", + i_CLKIN=clock_pads.rx, + i_CLKFB=clk0_rx_bufg, + o_CLK0=clk0_rx, + o_CLK90=clk90_rx, + o_LOCKED=dcm_locked, + i_PSEN=0, + i_PSCLK=0, + i_PSINCDEC=0, + i_RST=dcm_reset + ) + + self.specials += Instance("BUFG", i_I=clk0_rx, o_O=clk0_rx_bufg) + self.specials += Instance("BUFG", i_I=clk90_rx, o_O=self.cd_eth_rx.clk) + + # TX + self.specials += DDROutput(1, 0, clock_pads.tx, ClockSignal("eth_tx")) + self.specials += Instance("BUFG", i_I=self.cd_eth_rx.clk, o_O=self.cd_eth_tx.clk) + + # Reset + if with_hw_init_reset: + reset = Signal() + counter_done = Signal() + self.submodules.counter = counter = Counter(max=512) + self.comb += [ + counter_done.eq(counter.value == 256), + counter.ce.eq(~counter_done), + reset.eq(~counter_done | self._reset.storage) + ] + else: + reset = self._reset.storage + self.comb += pads.rst_n.eq(~reset) + self.specials += [ + AsyncResetSynchronizer(self.cd_eth_tx, reset), + AsyncResetSynchronizer(self.cd_eth_rx, reset), + ] + + +class LiteEthPHYRGMII(Module, AutoCSR): + def __init__(self, clock_pads, pads, with_hw_init_reset=True): + self.dw = 8 + self.submodules.crg = LiteEthPHYRGMIICRG(clock_pads, + pads, + with_hw_init_reset) + self.submodules.tx = RenameClockDomains(LiteEthPHYRGMIITX(pads), + "eth_tx") + self.submodules.rx = RenameClockDomains(LiteEthPHYRGMIIRX(pads), + "eth_rx") + self.sink, self.source = self.tx.sink, self.rx.source diff --git a/misoc/liteethmini/phy/sim.py b/misoc/liteethmini/phy/sim.py new file mode 100644 index 00000000..d6d1778d --- /dev/null +++ b/misoc/liteethmini/phy/sim.py @@ -0,0 +1,58 @@ +import os + +from misoc.com.liteethmini.common import * + + +class LiteEthPHYSimCRG(Module, AutoCSR): + def __init__(self): + self._reset = CSRStorage() + + # # # + + self.clock_domains.cd_eth_rx = ClockDomain() + self.clock_domains.cd_eth_tx = ClockDomain() + self.comb += [ + self.cd_eth_rx.clk.eq(ClockSignal()), + self.cd_eth_tx.clk.eq(ClockSignal()) + ] + + reset = self._reset.storage + self.comb += [ + self.cd_eth_rx.rst.eq(reset), + self.cd_eth_tx.rst.eq(reset) + ] + + +class LiteEthPHYSim(Module, AutoCSR): + def __init__(self, pads, tap="tap0", ip_address="192.168.0.14"): + self.dw = 8 + self.submodules.crg = LiteEthPHYSimCRG() + self.sink = sink = Sink(eth_phy_description(8)) + self.source = source = Source(eth_phy_description(8)) + self.tap = tap + self.ip_address = ip_address + + self.comb += [ + pads.source_stb.eq(self.sink.stb), + pads.source_data.eq(self.sink.data), + self.sink.ack.eq(1) + ] + + self.sync += [ + self.source.stb.eq(pads.sink_stb), + self.source.sop.eq(pads.sink_stb & ~self.source.stb), + self.source.data.eq(pads.sink_data), + ] + self.comb += [ + self.source.eop.eq(~pads.sink_stb & self.source.stb), + ] + + # XXX avoid use of os.system + os.system("openvpn --mktun --dev {}".format(self.tap)) + os.system("ifconfig {} {} up".format(self.tap, self.ip_address)) + os.system("mknod /dev/net/{} c 10 200".format(self.tap)) + + def do_exit(self, *args, **kwargs): + # XXX avoid use of os.system + os.system("rm -f /dev/net/{}".format(self.tap)) + os.system("openvpn --rmtun --dev {}".format(self.tap)) diff --git a/misoc/lm32/core.py b/misoc/lm32/core.py new file mode 100644 index 00000000..ca9b001c --- /dev/null +++ b/misoc/lm32/core.py @@ -0,0 +1,63 @@ +import os + +from migen import * +from migen.bus import wishbone + + +class LM32(Module): + def __init__(self, platform, eba_reset): + self.ibus = i = wishbone.Interface() + self.dbus = d = wishbone.Interface() + self.interrupt = Signal(32) + + ### + + i_adr_o = Signal(32) + d_adr_o = Signal(32) + self.specials += Instance("lm32_cpu", + p_eba_reset=Instance.PreformattedParam("32'h{:08x}".format(eba_reset)), + + i_clk_i=ClockSignal(), + i_rst_i=ResetSignal(), + + i_interrupt=self.interrupt, + + o_I_ADR_O=i_adr_o, + o_I_DAT_O=i.dat_w, + o_I_SEL_O=i.sel, + o_I_CYC_O=i.cyc, + o_I_STB_O=i.stb, + o_I_WE_O=i.we, + o_I_CTI_O=i.cti, + o_I_BTE_O=i.bte, + i_I_DAT_I=i.dat_r, + i_I_ACK_I=i.ack, + i_I_ERR_I=i.err, + i_I_RTY_I=0, + + o_D_ADR_O=d_adr_o, + o_D_DAT_O=d.dat_w, + o_D_SEL_O=d.sel, + o_D_CYC_O=d.cyc, + o_D_STB_O=d.stb, + o_D_WE_O=d.we, + o_D_CTI_O=d.cti, + o_D_BTE_O=d.bte, + i_D_DAT_I=d.dat_r, + i_D_ACK_I=d.ack, + i_D_ERR_I=d.err, + i_D_RTY_I=0) + + self.comb += [ + self.ibus.adr.eq(i_adr_o[2:]), + self.dbus.adr.eq(d_adr_o[2:]) + ] + + # add Verilog sources + platform.add_sources(os.path.join("extcores", "lm32", "submodule", "rtl"), + "lm32_cpu.v", "lm32_instruction_unit.v", "lm32_decoder.v", + "lm32_load_store_unit.v", "lm32_adder.v", "lm32_addsub.v", "lm32_logic_op.v", + "lm32_shifter.v", "lm32_multiplier.v", "lm32_mc_arithmetic.v", + "lm32_interrupt.v", "lm32_ram.v", "lm32_dp_ram.v", "lm32_icache.v", + "lm32_dcache.v", "lm32_debug.v", "lm32_itlb.v", "lm32_dtlb.v") + platform.add_verilog_include_path(os.path.join("extcores", "lm32")) diff --git a/misoc/lm32/verilog/lm32_config.v b/misoc/lm32/verilog/lm32_config.v new file mode 100644 index 00000000..41c2daad --- /dev/null +++ b/misoc/lm32/verilog/lm32_config.v @@ -0,0 +1,199 @@ +`ifdef LM32_CONFIG_V +`else +`define LM32_CONFIG_V + +// +// EXCEPTION VECTORS BASE ADDRESS +// + +// Base address for exception vectors +`define CFG_EBA_RESET 32'h00000000 + +// Base address for the debug exception vectors. If the DC_RE flag is +// set or the at_debug signal is asserted (see CFG_ALTERNATE_EBA) this +// will also be used for normal exception vectors. +`define CFG_DEBA_RESET 32'h10000000 + +// Enable exception vector remapping by external signal +//`define CFG_ALTERNATE_EBA + + +// +// ALU OPTIONS +// + +// Enable sign-extension instructions +`define CFG_SIGN_EXTEND_ENABLED + +// Shifter +// You may either enable the piplined or the multi-cycle barrel +// shifter. The multi-cycle shifter will stall the pipeline until +// the result is available after 32 cycles. +// If both options are disabled, only "right shift by one bit" is +// available. +//`define CFG_MC_BARREL_SHIFT_ENABLED +`define CFG_PL_BARREL_SHIFT_ENABLED + +// Multiplier +// The multiplier is available either in a multi-cycle version or +// in a pipelined one. The multi-cycle multiplier stalls the pipe +// for 32 cycles. If both options are disabled, multiply operations +// are not supported. +//`define CFG_MC_MULTIPLY_ENABLED +`define CFG_PL_MULTIPLY_ENABLED + +// Enable the multi-cycle divider. Stalls the pipe until the result +// is ready after 32 cycles. If disabled, the divide operation is not +// supported. +`define CFG_MC_DIVIDE_ENABLED + + +// +// INTERRUPTS +// + +// Enable support for 32 hardware interrupts +`define CFG_INTERRUPTS_ENABLED + +// Enable level-sensitive interrupts. The interrupt line status is +// reflected in the IP register, which is then read-only. +`define CFG_LEVEL_SENSITIVE_INTERRUPTS + + +// +// USER INSTRUCTION +// + +// Enable support for the user opcode. +//`define CFG_USER_ENABLED + + +// +// MEMORY MANAGEMENT UNIT +// + +// Enable instruction and data translation lookaside buffers and +// restricted user mode. +//`define CFG_MMU_ENABLED + + +// +// CACHE +// + +// Instruction cache +`define CFG_ICACHE_ENABLED +`define CFG_ICACHE_ASSOCIATIVITY 1 +`define CFG_ICACHE_SETS 256 +`define CFG_ICACHE_BYTES_PER_LINE 16 +`define CFG_ICACHE_BASE_ADDRESS 32'h00000000 +`define CFG_ICACHE_LIMIT 32'h7fffffff + +// Data cache +`define CFG_DCACHE_ENABLED +`define CFG_DCACHE_ASSOCIATIVITY 1 +`define CFG_DCACHE_SETS 256 +`define CFG_DCACHE_BYTES_PER_LINE 16 +`define CFG_DCACHE_BASE_ADDRESS 32'h00000000 +`define CFG_DCACHE_LIMIT 32'h7fffffff + + +// +// DEBUG OPTION +// + +// Globally enable debugging +//`define CFG_DEBUG_ENABLED + +// Enable the hardware JTAG debugging interface. +// Note: to use this, there must be a special JTAG module for your +// device. At the moment, there is only support for the +// Spartan-6. +//`define CFG_JTAG_ENABLED + +// JTAG UART is a communication channel which uses JTAG to transmit +// and receive bytes to and from the host computer. +//`define CFG_JTAG_UART_ENABLED + +// Enable reading and writing to the memory and writing CSRs using +// the JTAG interface. +//`define CFG_HW_DEBUG_ENABLED + +// Number of hardware watchpoints, max. 4 +//`define CFG_WATCHPOINTS 32'h4 + +// Enable hardware breakpoints +//`define CFG_ROM_DEBUG_ENABLED + +// Number of hardware breakpoints, max. 4 +//`define CFG_BREAKPOINTS 32'h4 + +// Put the processor into debug mode by an external signal. That is, +// raise a breakpoint exception. This is useful if you have a debug +// monitor and a serial line and you want to trap into the monitor on a +// BREAK symbol on the serial line. +//`define CFG_EXTERNAL_BREAK_ENABLED + + +// +// REGISTER FILE +// + +// The following option explicitly infers block RAM for the register +// file. There is extra logic to avoid parallel writes and reads. +// Normally, if your synthesizer is smart enough, this should not be +// necessary because it will automatically infer block RAM for you. +//`define CFG_EBR_POSEDGE_REGISTER_FILE + +// Explicitly infers block RAM, too. But it uses two different clocks, +// one being shifted by 180deg, for the read and write port. Therefore, +// no additional logic to avoid the parallel write/reads. +//`define CFG_EBR_NEGEDGE_REGISTER_FILE + + +// +// MISCELLANEOUS +// + +// Exceptions on wishbone bus errors +//`define CFG_BUS_ERRORS_ENABLED + +// Enable the cycle counter +`define CFG_CYCLE_COUNTER_ENABLED + +// Embedded instruction ROM using on-chip block RAM +//`define CFG_IROM_ENABLED +//`define CFG_IROM_INIT_FILE "NONE" +//`define CFG_IROM_BASE_ADDRESS 32'h10000000 +//`define CFG_IROM_LIMIT 32'h10000fff + +// Embedded data RAM using on-chip block RAM +//`define CFG_DRAM_ENABLED +//`define CFG_DRAM_INIT_FILE "NONE" +//`define CFG_DRAM_BASE_ADDRESS 32'h20000000 +//`define CFG_DRAM_LIMIT 32'h20000fff + +// Trace unit +//`define CFG_TRACE_ENABLED + +// Resolve unconditional branches already in the X stage (UNTESTED!) +//`define CFG_FAST_UNCONDITIONAL_BRANCH + +// log2 function +// If your simulator/synthesizer does not support the $clog2 system +// function you can use a constant function instead. + +function integer clog2; + input integer value; + begin + value = value - 1; + for (clog2 = 0; value > 0; clog2 = clog2 + 1) + value = value >> 1; + end +endfunction + +`define CLOG2 clog2 + +//`define CLOG2 $clog2 + +`endif diff --git a/misoc/mem/__init__.py b/misoc/mem/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/misoc/mem/flash/__init__.py b/misoc/mem/flash/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/misoc/mem/flash/norflash16.py b/misoc/mem/flash/norflash16.py deleted file mode 100644 index 2649a4ae..00000000 --- a/misoc/mem/flash/norflash16.py +++ /dev/null @@ -1,103 +0,0 @@ -from migen import * -from migen.bus import wishbone -from migen.genlib.fsm import FSM, NextState - - -class NorFlash16(Module): - def __init__(self, pads, rd_timing, wr_timing): - self.bus = wishbone.Interface() - - ### - - data = TSTriple(16) - lsb = Signal() - - self.specials += data.get_tristate(pads.d) - self.comb += [ - data.oe.eq(pads.oe_n), - pads.ce_n.eq(0) - ] - - load_lo = Signal() - load_hi = Signal() - store = Signal() - - pads.oe_n.reset, pads.we_n.reset = 1, 1 - self.sync += [ - pads.oe_n.eq(1), - pads.we_n.eq(1), - - # Register data/address to avoid off-chip glitches - If(self.bus.cyc & self.bus.stb, - pads.adr.eq(Cat(lsb, self.bus.adr)), - If(self.bus.we, - # Only 16-bit writes are supported. Assume sel=0011 or 1100. - If(self.bus.sel[0], - data.o.eq(self.bus.dat_w[:16]) - ).Else( - data.o.eq(self.bus.dat_w[16:]) - ) - ).Else( - pads.oe_n.eq(0) - ) - ), - - If(load_lo, self.bus.dat_r[:16].eq(data.i)), - If(load_hi, self.bus.dat_r[16:].eq(data.i)), - If(store, pads.we_n.eq(0)) - ] - - # Typical timing of the flash chips: - # - 110ns address to output - # - 50ns write pulse width - counter = Signal(max=max(rd_timing, wr_timing)+1) - counter_en = Signal() - counter_wr_mode = Signal() - counter_done = Signal() - self.comb += counter_done.eq(counter == Mux(counter_wr_mode, wr_timing, rd_timing)) - self.sync += If(counter_en & ~counter_done, - counter.eq(counter + 1) - ).Else( - counter.eq(0) - ) - - fsm = FSM() - self.submodules += fsm - - fsm.act("IDLE", - If(self.bus.cyc & self.bus.stb, - If(self.bus.we, - NextState("WR") - ).Else( - NextState("RD_HI") - ) - ) - ) - fsm.act("RD_HI", - lsb.eq(0), - counter_en.eq(1), - If(counter_done, - load_hi.eq(1), - NextState("RD_LO") - ) - ) - fsm.act("RD_LO", - lsb.eq(1), - counter_en.eq(1), - If(counter_done, - load_lo.eq(1), - NextState("ACK") - ) - ) - fsm.act("WR", - # supported cases: sel=0011 [lsb=1] and sel=1100 [lsb=0] - lsb.eq(self.bus.sel[0]), - counter_wr_mode.eq(1), - counter_en.eq(1), - store.eq(1), - If(counter_done, NextState("ACK")) - ) - fsm.act("ACK", - self.bus.ack.eq(1), - NextState("IDLE") - ) diff --git a/misoc/mem/flash/spiflash.py b/misoc/mem/flash/spiflash.py deleted file mode 100644 index b63840c5..00000000 --- a/misoc/mem/flash/spiflash.py +++ /dev/null @@ -1,181 +0,0 @@ -from migen import * -from migen.bus.transactions import * -from migen.bus import wishbone -from migen.genlib.misc import timeline -from migen.genlib.record import Record -from migen.bank.description import AutoCSR, CSRStorage, CSRStatus - -_FAST_READ = 0x0b -_DIOFR = 0xbb -_QIOFR = 0xeb - - -def _format_cmd(cmd, spi_width): - """ - `cmd` is the read instruction. Since everything is transmitted on all - dq lines (cmd, adr and data), extend/interleave cmd to full pads.dq - width even if dq1-dq3 are don't care during the command phase: - For example, for N25Q128, 0xeb is the quad i/o fast read, and - extended to 4 bits (dq1,dq2,dq3 high) is: 0xfffefeff - """ - c = 2**(8*spi_width)-1 - for b in range(8): - if not (cmd>>b)%2: - c &= ~(1<<(b*spi_width)) - return c - - -class SpiFlash(Module, AutoCSR): - def __init__(self, pads, dummy=15, div=2, with_bitbang=True): - """ - Simple SPI flash, e.g. N25Q128 on the LX9 Microboard. - - Supports multi-bit pseudo-parallel reads (aka Dual or Quad I/O Fast - Read). Only supports mode0 (cpol=0, cpha=0). - Optionally supports software bitbanging (for write, erase, or other commands). - """ - self.bus = bus = wishbone.Interface() - spi_width = flen(pads.dq) - if with_bitbang: - self.bitbang = CSRStorage(4) - self.miso = CSRStatus() - self.bitbang_en = CSRStorage() - - ### - - cs_n = Signal(reset=1) - clk = Signal() - dq_oe = Signal() - wbone_width = flen(bus.dat_r) - - - read_cmd_params = { - 4: (_format_cmd(_QIOFR, 4), 4*8), - 2: (_format_cmd(_DIOFR, 2), 2*8), - 1: (_format_cmd(_FAST_READ, 1), 1*8) - } - read_cmd, cmd_width = read_cmd_params[spi_width] - addr_width = 24 - - pads.cs_n.reset = 1 - - dq = TSTriple(spi_width) - self.specials.dq = dq.get_tristate(pads.dq) - - sr = Signal(max(cmd_width, addr_width, wbone_width)) - dqs = Replicate(1, spi_width-1) - - self.comb += bus.dat_r.eq(sr) - - hw_read_logic = [ - pads.clk.eq(clk), - pads.cs_n.eq(cs_n), - dq.o.eq(sr[-spi_width:]), - dq.oe.eq(dq_oe) - ] - - if with_bitbang: - bitbang_logic = [ - pads.clk.eq(self.bitbang.storage[1]), - pads.cs_n.eq(self.bitbang.storage[2]), - dq.o.eq(Cat(self.bitbang.storage[0], dqs)), - If(self.bitbang.storage[3], - dq.oe.eq(0) - ).Else( - dq.oe.eq(1) - ), - If(self.bitbang.storage[1], - self.miso.status.eq(dq.i[1]) - ) - ] - - self.comb += \ - If(self.bitbang_en.storage, - bitbang_logic - ).Else( - hw_read_logic - ) - else: - self.comb += hw_read_logic - - if div < 2: - raise ValueError("Unsupported value \'{}\' for div parameter for SpiFlash core".format(div)) - else: - i = Signal(max=div) - dqi = Signal(spi_width) - self.sync += [ - If(i == div//2 - 1, - clk.eq(1), - dqi.eq(dq.i), - ), - If(i == div - 1, - i.eq(0), - clk.eq(0), - sr.eq(Cat(dqi, sr[:-spi_width])) - ).Else( - i.eq(i + 1), - ), - ] - - # spi is byte-addressed, prefix by zeros - z = Replicate(0, log2_int(wbone_width//8)) - - seq = [ - (cmd_width//spi_width*div, - [dq_oe.eq(1), cs_n.eq(0), sr[-cmd_width:].eq(read_cmd)]), - (addr_width//spi_width*div, - [sr[-addr_width:].eq(Cat(z, bus.adr))]), - ((dummy + wbone_width//spi_width)*div, - [dq_oe.eq(0)]), - (1, - [bus.ack.eq(1), cs_n.eq(1)]), - (div, # tSHSL! - [bus.ack.eq(0)]), - (0, - []), - ] - - # accumulate timeline deltas - t, tseq = 0, [] - for dt, a in seq: - tseq.append((t, a)) - t += dt - - self.sync += timeline(bus.cyc & bus.stb & (i == div - 1), tseq) - - -class SpiFlashTB(Module): - def __init__(self): - self.submodules.master = wishbone.Initiator(self.gen_reads()) - self.pads = Record([("cs_n", 1), ("clk", 1), ("dq", 4)]) - self.submodules.slave = SpiFlash(self.pads) - self.submodules.tap = wishbone.Tap(self.slave.bus) - self.submodules.intercon = wishbone.InterconnectPointToPoint( - self.master.bus, self.slave.bus) - self.cycle = 0 - - def gen_reads(self): - for a in range(10): - t = TRead(a) - yield t - print("read {} in {} cycles(s)".format(t.data, t.latency)) - - def do_simulation(self, selfp): - if selfp.pads.cs_n: - self.cycle = 0 - else: - self.cycle += 1 - if not selfp.slave.dq.oe: - selfp.slave.dq.i = self.cycle & 0xf - do_simulation.passive = True - -if __name__ == "__main__": - from migen.sim.generic import run_simulation - from migen.fhdl import verilog - - pads = Record([("cs_n", 1), ("clk", 1), ("dq", 4)]) - s = SpiFlash(pads) - print(verilog.convert(s, ios={pads.clk, pads.cs_n, pads.dq, s.bus.adr, - s.bus.dat_r, s.bus.cyc, s.bus.ack, s.bus.stb})) - - run_simulation(SpiFlashTB(), vcd_name="spiflash.vcd") diff --git a/misoc/mor1kx/__init__.py b/misoc/mor1kx/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/misoc/mor1kx/core.py b/misoc/mor1kx/core.py new file mode 100644 index 00000000..f9c69642 --- /dev/null +++ b/misoc/mor1kx/core.py @@ -0,0 +1,80 @@ +import os + +from migen import * +from migen.bus import wishbone + + +class MOR1KX(Module): + def __init__(self, platform, reset_pc): + self.ibus = i = wishbone.Interface() + self.dbus = d = wishbone.Interface() + self.interrupt = Signal(32) + + ### + + i_adr_o = Signal(32) + d_adr_o = Signal(32) + self.specials += Instance("mor1kx", + p_FEATURE_INSTRUCTIONCACHE="ENABLED", + p_OPTION_ICACHE_BLOCK_WIDTH=4, + p_OPTION_ICACHE_SET_WIDTH=8, + p_OPTION_ICACHE_WAYS=1, + p_OPTION_ICACHE_LIMIT_WIDTH=31, + p_FEATURE_DATACACHE="ENABLED", + p_OPTION_DCACHE_BLOCK_WIDTH=4, + p_OPTION_DCACHE_SET_WIDTH=8, + p_OPTION_DCACHE_WAYS=1, + p_OPTION_DCACHE_LIMIT_WIDTH=31, + p_FEATURE_TIMER="NONE", + p_OPTION_PIC_TRIGGER="LEVEL", + p_FEATURE_SYSCALL="NONE", + p_FEATURE_TRAP="NONE", + p_FEATURE_RANGE="NONE", + p_FEATURE_OVERFLOW="NONE", + p_FEATURE_ADDC="ENABLED", + p_FEATURE_CMOV="ENABLED", + p_FEATURE_FFL1="ENABLED", + p_OPTION_CPU0="CAPPUCCINO", + p_OPTION_RESET_PC=reset_pc, + p_IBUS_WB_TYPE="B3_REGISTERED_FEEDBACK", + p_DBUS_WB_TYPE="B3_REGISTERED_FEEDBACK", + + i_clk=ClockSignal(), + i_rst=ResetSignal(), + + i_irq_i=self.interrupt, + + o_iwbm_adr_o=i_adr_o, + o_iwbm_dat_o=i.dat_w, + o_iwbm_sel_o=i.sel, + o_iwbm_cyc_o=i.cyc, + o_iwbm_stb_o=i.stb, + o_iwbm_we_o=i.we, + o_iwbm_cti_o=i.cti, + o_iwbm_bte_o=i.bte, + i_iwbm_dat_i=i.dat_r, + i_iwbm_ack_i=i.ack, + i_iwbm_err_i=i.err, + i_iwbm_rty_i=0, + + o_dwbm_adr_o=d_adr_o, + o_dwbm_dat_o=d.dat_w, + o_dwbm_sel_o=d.sel, + o_dwbm_cyc_o=d.cyc, + o_dwbm_stb_o=d.stb, + o_dwbm_we_o=d.we, + o_dwbm_cti_o=d.cti, + o_dwbm_bte_o=d.bte, + i_dwbm_dat_i=d.dat_r, + i_dwbm_ack_i=d.ack, + i_dwbm_err_i=d.err, + i_dwbm_rty_i=0) + + self.comb += [ + self.ibus.adr.eq(i_adr_o[2:]), + self.dbus.adr.eq(d_adr_o[2:]) + ] + + # add Verilog sources + platform.add_source_dir(os.path.join("extcores", "mor1kx", "submodule", + "rtl", "verilog")) diff --git a/misoc/norflash16.py b/misoc/norflash16.py new file mode 100644 index 00000000..2649a4ae --- /dev/null +++ b/misoc/norflash16.py @@ -0,0 +1,103 @@ +from migen import * +from migen.bus import wishbone +from migen.genlib.fsm import FSM, NextState + + +class NorFlash16(Module): + def __init__(self, pads, rd_timing, wr_timing): + self.bus = wishbone.Interface() + + ### + + data = TSTriple(16) + lsb = Signal() + + self.specials += data.get_tristate(pads.d) + self.comb += [ + data.oe.eq(pads.oe_n), + pads.ce_n.eq(0) + ] + + load_lo = Signal() + load_hi = Signal() + store = Signal() + + pads.oe_n.reset, pads.we_n.reset = 1, 1 + self.sync += [ + pads.oe_n.eq(1), + pads.we_n.eq(1), + + # Register data/address to avoid off-chip glitches + If(self.bus.cyc & self.bus.stb, + pads.adr.eq(Cat(lsb, self.bus.adr)), + If(self.bus.we, + # Only 16-bit writes are supported. Assume sel=0011 or 1100. + If(self.bus.sel[0], + data.o.eq(self.bus.dat_w[:16]) + ).Else( + data.o.eq(self.bus.dat_w[16:]) + ) + ).Else( + pads.oe_n.eq(0) + ) + ), + + If(load_lo, self.bus.dat_r[:16].eq(data.i)), + If(load_hi, self.bus.dat_r[16:].eq(data.i)), + If(store, pads.we_n.eq(0)) + ] + + # Typical timing of the flash chips: + # - 110ns address to output + # - 50ns write pulse width + counter = Signal(max=max(rd_timing, wr_timing)+1) + counter_en = Signal() + counter_wr_mode = Signal() + counter_done = Signal() + self.comb += counter_done.eq(counter == Mux(counter_wr_mode, wr_timing, rd_timing)) + self.sync += If(counter_en & ~counter_done, + counter.eq(counter + 1) + ).Else( + counter.eq(0) + ) + + fsm = FSM() + self.submodules += fsm + + fsm.act("IDLE", + If(self.bus.cyc & self.bus.stb, + If(self.bus.we, + NextState("WR") + ).Else( + NextState("RD_HI") + ) + ) + ) + fsm.act("RD_HI", + lsb.eq(0), + counter_en.eq(1), + If(counter_done, + load_hi.eq(1), + NextState("RD_LO") + ) + ) + fsm.act("RD_LO", + lsb.eq(1), + counter_en.eq(1), + If(counter_done, + load_lo.eq(1), + NextState("ACK") + ) + ) + fsm.act("WR", + # supported cases: sel=0011 [lsb=1] and sel=1100 [lsb=0] + lsb.eq(self.bus.sel[0]), + counter_wr_mode.eq(1), + counter_en.eq(1), + store.eq(1), + If(counter_done, NextState("ACK")) + ) + fsm.act("ACK", + self.bus.ack.eq(1), + NextState("IDLE") + ) diff --git a/misoc/soc/__init__.py b/misoc/soc/__init__.py deleted file mode 100644 index c114dbb1..00000000 --- a/misoc/soc/__init__.py +++ /dev/null @@ -1,204 +0,0 @@ -from operator import itemgetter - -from migen import * -from migen.bank import csrgen -from migen.bus import wishbone, csr, wishbone2csr - -from misoc.com.uart.phy import UARTPHY -from misoc.com import uart -from misoc.cpu import lm32, mor1kx -from misoc.cpu import identifier, timer - - -def mem_decoder(address, start=26, end=29): - return lambda a: a[start:end] == ((address >> (start+2)) & (2**(end-start))-1) - - -class SoC(Module): - csr_map = { - "crg": 0, # user - "uart_phy": 1, # provided by default (optional) - "uart": 2, # provided by default (optional) - "identifier": 3, # provided by default (optional) - "timer0": 4, # provided by default (optional) - "buttons": 5, # user - "leds": 6, # user - } - interrupt_map = { - "uart": 0, - "timer0": 1, - } - mem_map = { - "rom": 0x00000000, # (default shadow @0x80000000) - "sram": 0x10000000, # (default shadow @0x90000000) - "main_ram": 0x40000000, # (default shadow @0xc0000000) - "csr": 0x60000000, # (default shadow @0xe0000000) - } - def __init__(self, platform, clk_freq, - cpu_type="lm32", cpu_reset_address=0x00000000, - integrated_rom_size=0, - integrated_sram_size=4096, - integrated_main_ram_size=0, - shadow_base=0x80000000, - with_csr=True, csr_data_width=8, csr_address_width=14, - with_uart=True, uart_baudrate=115200, - with_identifier=True, - with_timer=True): - self.platform = platform - self.clk_freq = clk_freq - - self.cpu_type = cpu_type - if integrated_rom_size: - cpu_reset_address = 0 - self.cpu_reset_address = cpu_reset_address - - self.integrated_rom_size = integrated_rom_size - self.integrated_sram_size = integrated_sram_size - self.integrated_main_ram_size = integrated_main_ram_size - - self.with_uart = with_uart - self.uart_baudrate = uart_baudrate - - self.with_identifier = with_identifier - - self.shadow_base = shadow_base - - self.with_csr = with_csr - self.csr_data_width = csr_data_width - self.csr_address_width = csr_address_width - - self._memory_regions = [] # list of (name, origin, length) - self._csr_regions = [] # list of (name, origin, busword, csr_list/Memory) - self._constants = [] # list of (name, value) - - self._wb_masters = [] - self._wb_slaves = [] - - if cpu_type != "none": - if cpu_type == "lm32": - self.add_cpu_or_bridge(lm32.LM32(platform, self.cpu_reset_address)) - elif cpu_type == "or1k": - self.add_cpu_or_bridge(mor1kx.MOR1KX(platform, self.cpu_reset_address)) - else: - raise ValueError("Unsupported CPU type: {}".format(cpu_type)) - self.add_wb_master(self.cpu_or_bridge.ibus) - self.add_wb_master(self.cpu_or_bridge.dbus) - - if integrated_rom_size: - self.submodules.rom = wishbone.SRAM(integrated_rom_size, read_only=True) - self.register_rom(self.rom.bus, integrated_rom_size) - - if integrated_sram_size: - self.submodules.sram = wishbone.SRAM(integrated_sram_size) - self.register_mem("sram", self.mem_map["sram"], self.sram.bus, integrated_sram_size) - - # Note: Main Ram can be used when no external SDRAM is available and use SDRAM mapping. - if integrated_main_ram_size: - self.submodules.main_ram = wishbone.SRAM(integrated_main_ram_size) - self.register_mem("main_ram", self.mem_map["main_ram"], self.main_ram.bus, integrated_main_ram_size) - - if with_csr: - self.submodules.wishbone2csr = wishbone2csr.WB2CSR(bus_csr=csr.Interface(csr_data_width, csr_address_width)) - self.register_mem("csr", self.mem_map["csr"], self.wishbone2csr.wishbone) - - if with_uart: - self.submodules.uart_phy = UARTPHY(platform.request("serial"), clk_freq, uart_baudrate) - self.submodules.uart = uart.UART(self.uart_phy) - - if with_identifier: - platform_id = 0x554E if not hasattr(platform, "identifier") else platform.identifier - self.submodules.identifier = identifier.Identifier(platform_id, int(clk_freq)) - - if with_timer: - self.submodules.timer0 = timer.Timer() - - def add_cpu_or_bridge(self, cpu_or_bridge): - if self.finalized: - raise FinalizeError - if hasattr(self, "cpu_or_bridge"): - raise NotImplementedError("More than one CPU is not supported") - self.submodules.cpu_or_bridge = cpu_or_bridge - - def init_rom(self, data): - self.rom.mem.init = data - - def add_wb_master(self, wbm): - if self.finalized: - raise FinalizeError - self._wb_masters.append(wbm) - - def add_wb_slave(self, address_decoder, interface): - if self.finalized: - raise FinalizeError - self._wb_slaves.append((address_decoder, interface)) - - def add_memory_region(self, name, origin, length): - def in_this_region(addr): - return addr >= origin and addr < origin + length - for n, o, l in self._memory_regions: - if n == name or in_this_region(o) or in_this_region(o+l-1): - raise ValueError("Memory region conflict between {} and {}".format(n, name)) - - self._memory_regions.append((name, origin, length)) - - def register_mem(self, name, address, interface, size=None): - self.add_wb_slave(mem_decoder(address), interface) - if size is not None: - self.add_memory_region(name, address, size) - - def register_rom(self, interface, rom_size=0xa000): - self.add_wb_slave(mem_decoder(self.mem_map["rom"]), interface) - self.add_memory_region("rom", self.cpu_reset_address, rom_size) - - def get_memory_regions(self): - return self._memory_regions - - def check_csr_region(self, name, origin): - for n, o, l, obj in self._csr_regions: - if n == name or o == origin: - raise ValueError("CSR region conflict between {} and {}".format(n, name)) - - def add_csr_region(self, name, origin, busword, obj): - self.check_csr_region(name, origin) - self._csr_regions.append((name, origin, busword, obj)) - - def get_csr_regions(self): - return self._csr_regions - - def add_constant(self, name, value=None): - self._constants.append((name, value)) - - def get_constants(self): - r = [] - for name, interrupt in sorted(self.interrupt_map.items(), key=itemgetter(1)): - r.append((name.upper() + "_INTERRUPT", interrupt)) - r += self._constants - return r - - def do_finalize(self): - registered_mems = {regions[0] for regions in self._memory_regions} - if self.cpu_type != "none": - for mem in "rom", "sram": - if mem not in registered_mems: - raise FinalizeError("CPU needs a {} to be registered with SoC.register_mem()".format(mem)) - - # Wishbone - self.submodules.wishbonecon = wishbone.InterconnectShared(self._wb_masters, - self._wb_slaves, register=True) - - # CSR - if self.with_csr: - self.submodules.csrbankarray = csrgen.BankArray(self, - lambda name, memory: self.csr_map[name if memory is None else name + "_" + memory.name_override], - data_width=self.csr_data_width, address_width=self.csr_address_width) - self.submodules.csrcon = csr.Interconnect(self.wishbone2csr.csr, self.csrbankarray.get_buses()) - for name, csrs, mapaddr, rmap in self.csrbankarray.banks: - self.add_csr_region(name, (self.mem_map["csr"] + 0x800*mapaddr) | self.shadow_base, self.csr_data_width, csrs) - for name, memory, mapaddr, mmap in self.csrbankarray.srams: - self.add_csr_region(name + "_" + memory.name_override, (self.mem_map["csr"] + 0x800*mapaddr) | self.shadow_base, self.csr_data_width, memory) - - # Interrupts - if hasattr(self.cpu_or_bridge, "interrupt"): - for k, v in sorted(self.interrupt_map.items(), key=itemgetter(1)): - if hasattr(self, k): - self.comb += self.cpu_or_bridge.interrupt[v].eq(getattr(self, k).ev.irq) diff --git a/misoc/soc/cpuif.py b/misoc/soc/cpuif.py deleted file mode 100644 index 3ed2dd6d..00000000 --- a/misoc/soc/cpuif.py +++ /dev/null @@ -1,119 +0,0 @@ -from migen import * -from migen.bank.description import CSRStatus - - -def get_cpu_mak(cpu_type): - if cpu_type == "lm32": - triple = "lm32-elf" - cpuflags = "-mbarrel-shift-enabled -mmultiply-enabled -mdivide-enabled -msign-extend-enabled" - clang = "" - elif cpu_type == "or1k": - triple = "or1k-linux" - cpuflags = "-mhard-mul -mhard-div -mror -mffl1 -maddc" - clang = "1" - else: - raise ValueError("Unsupported CPU type: "+cpu_type) - return "TRIPLE={}\nCPU={}\nCPUFLAGS={}\nCLANG={}".format(triple, cpu_type, cpuflags, clang) - - -def get_linker_output_format(cpu_type): - return "OUTPUT_FORMAT(\"elf32-{}\")\n".format(cpu_type) - - -def get_linker_regions(regions): - r = "MEMORY {\n" - for name, origin, length in regions: - r += "\t{} : ORIGIN = 0x{:08x}, LENGTH = 0x{:08x}\n".format(name, origin, length) - r += "}\n" - return r - - -def get_mem_header(regions, flash_boot_address): - r = "#ifndef __GENERATED_MEM_H\n#define __GENERATED_MEM_H\n\n" - for name, base, size in regions: - r += "#define {name}_BASE 0x{base:08x}\n#define {name}_SIZE 0x{size:08x}\n\n".format(name=name.upper(), base=base, size=size) - if flash_boot_address is not None: - r += "#define FLASH_BOOT_ADDRESS 0x{:08x}\n\n".format(flash_boot_address) - r += "#endif\n" - return r - - -def _get_rw_functions(reg_name, reg_base, nwords, busword, read_only, with_access_functions): - r = "" - - r += "#define CSR_"+reg_name.upper()+"_ADDR "+hex(reg_base)+"\n" - r += "#define CSR_"+reg_name.upper()+"_SIZE "+str(nwords)+"\n" - - size = nwords*busword - if size > 64: - return r - elif size > 32: - ctype = "unsigned long long int" - elif size > 16: - ctype = "unsigned int" - elif size > 8: - ctype = "unsigned short int" - else: - ctype = "unsigned char" - - if with_access_functions: - r += "static inline "+ctype+" "+reg_name+"_read(void) {\n" - if size > 1: - r += "\t"+ctype+" r = MMPTR("+hex(reg_base)+");\n" - for byte in range(1, nwords): - r += "\tr <<= "+str(busword)+";\n\tr |= MMPTR("+hex(reg_base+4*byte)+");\n" - r += "\treturn r;\n}\n" - else: - r += "\treturn MMPTR("+hex(reg_base)+");\n}\n" - - if not read_only: - r += "static inline void "+reg_name+"_write("+ctype+" value) {\n" - for word in range(nwords): - shift = (nwords-word-1)*busword - if shift: - value_shifted = "value >> "+str(shift) - else: - value_shifted = "value" - r += "\tMMPTR("+hex(reg_base+4*word)+") = "+value_shifted+";\n" - r += "}\n" - return r - - -def get_csr_header(regions, constants, with_access_functions=True): - r = "#ifndef __GENERATED_CSR_H\n#define __GENERATED_CSR_H\n" - if with_access_functions: - r += "#include \n" - for name, origin, busword, obj in regions: - if isinstance(obj, Memory): - r += "#define CSR_"+name.upper()+"_BASE "+hex(origin)+"\n" - else: - r += "\n/* "+name+" */\n" - r += "#define CSR_"+name.upper()+"_BASE "+hex(origin)+"\n" - for csr in obj: - nr = (csr.size + busword - 1)//busword - r += _get_rw_functions(name + "_" + csr.name, origin, nr, busword, isinstance(csr, CSRStatus), with_access_functions) - origin += 4*nr - - r += "\n/* constants */\n" - for name, value in constants: - r += "#define " + name - if value is not None: - if isinstance(value, str): - r += " \"" + value + "\"" - else: - r += " " + str(value) - r += "\n" - - r += "\n#endif\n" - return r - - -def get_csr_csv(regions): - r = "" - for name, origin, busword, obj in regions: - if not isinstance(obj, Memory): - for csr in obj: - nr = (csr.size + busword - 1)//busword - r += "{}_{},0x{:08x},{},{}\n".format(name, csr.name, origin, nr, "ro" if isinstance(csr, CSRStatus) else "rw") - origin += 4*nr - return r diff --git a/misoc/soc/sdram.py b/misoc/soc/sdram.py deleted file mode 100644 index e8d86a70..00000000 --- a/misoc/soc/sdram.py +++ /dev/null @@ -1,111 +0,0 @@ -from migen import * -from migen.bus import wishbone -from migen.genlib.record import * - -from misoc.mem.sdram.core import SDRAMCore -from misoc.mem.sdram.core.lasmicon import LASMIconSettings -from misoc.mem.sdram.core.minicon import MiniconSettings -from misoc.mem.sdram.frontend import memtest, wishbone2lasmi -from misoc.soc import SoC - - -class SDRAMSoC(SoC): - csr_map = { - "sdram": 8, - "l2_cache": 9, - "memtest_w": 10, - "memtest_r": 11 - } - csr_map.update(SoC.csr_map) - - def __init__(self, platform, clk_freq, sdram_controller_settings, - **kwargs): - SoC.__init__(self, platform, clk_freq, **kwargs) - if isinstance(sdram_controller_settings, str): - self.sdram_controller_settings = eval(sdram_controller_settings) - else: - self.sdram_controller_settings = sdram_controller_settings - self._sdram_phy_registered = False - self._wb_sdram_ifs = [] - self._wb_sdram = wishbone.Interface() - - def add_wb_sdram_if(self, interface): - if self.finalized: - raise FinalizeError - self._wb_sdram_ifs.append(interface) - - def register_sdram_phy(self, phy): - if self._sdram_phy_registered: - raise FinalizeError - self._sdram_phy_registered = True - - # Core - self.submodules.sdram = SDRAMCore(phy, - phy.module.geom_settings, - phy.module.timing_settings, - self.sdram_controller_settings) - - dfi_databits_divisor = 1 if phy.settings.memtype == "SDR" else 2 - sdram_width = phy.settings.dfi_databits//dfi_databits_divisor - main_ram_size = 2**(phy.module.geom_settings.bankbits + - phy.module.geom_settings.rowbits + - phy.module.geom_settings.colbits)*sdram_width//8 - # XXX: Limit main_ram_size to 256MB, we should modify mem_map to allow larger memories. - main_ram_size = min(main_ram_size, 256*1024*1024) - l2_size = self.sdram_controller_settings.l2_size - if l2_size: - self.add_constant("L2_SIZE", l2_size) - - # add a Wishbone interface to the DRAM - wb_sdram = wishbone.Interface() - self.add_wb_sdram_if(wb_sdram) - self.register_mem("main_ram", self.mem_map["main_ram"], wb_sdram, main_ram_size) - - # LASMICON frontend - if isinstance(self.sdram_controller_settings, LASMIconSettings): - if self.sdram_controller_settings.with_bandwidth: - self.sdram.controller.multiplexer.add_bandwidth() - - if self.sdram_controller_settings.with_memtest: - self.submodules.memtest_w = memtest.MemtestWriter(self.sdram.crossbar.get_master()) - self.submodules.memtest_r = memtest.MemtestReader(self.sdram.crossbar.get_master()) - - if l2_size: - lasmim = self.sdram.crossbar.get_master() - l2_cache = wishbone.Cache(l2_size//4, self._wb_sdram, wishbone.Interface(lasmim.dw)) - # XXX Vivado ->2015.1 workaround, Vivado is not able to map correctly our L2 cache. - # Issue is reported to Xilinx and should be fixed in next releases (2015.2?). - # Remove this workaround when fixed by Xilinx. - from mibuild.xilinx.vivado import XilinxVivadoToolchain - if isinstance(self.platform.toolchain, XilinxVivadoToolchain): - from migen.fhdl.simplify import FullMemoryWE - self.submodules.l2_cache = FullMemoryWE()(l2_cache) - else: - self.submodules.l2_cache = l2_cache - self.submodules.wishbone2lasmi = wishbone2lasmi.WB2LASMI(self.l2_cache.slave, lasmim) - - # MINICON frontend - elif isinstance(self.sdram_controller_settings, MiniconSettings): - if l2_size: - l2_cache = wishbone.Cache(l2_size//4, self._wb_sdram, self.sdram.controller.bus) - # XXX Vivado ->2015.1 workaround, Vivado is not able to map correctly our L2 cache. - # Issue is reported to Xilinx and should be fixed in next releases (2015.2?). - # Remove this workaround when fixed by Xilinx. - from mibuild.xilinx.vivado import XilinxVivadoToolchain - if isinstance(self.platform.toolchain, XilinxVivadoToolchain): - from migen.fhdl.simplify import FullMemoryWE - self.submodules.l2_cache = FullMemoryWE()(l2_cache) - else: - self.submodules.l2_cache = l2_cache - else: - self.submodules.converter = wishbone.Converter(self._wb_sdram, self.sdram.controller.bus) - - def do_finalize(self): - if not self.integrated_main_ram_size: - if not self._sdram_phy_registered: - raise FinalizeError("Need to call SDRAMSoC.register_sdram_phy()") - - # arbitrate wishbone interfaces to the DRAM - self.submodules.wb_sdram_con = wishbone.Arbiter(self._wb_sdram_ifs, - self._wb_sdram) - SoC.do_finalize(self) diff --git a/misoc/spi/__init__.py b/misoc/spi/__init__.py new file mode 100644 index 00000000..289e12f4 --- /dev/null +++ b/misoc/spi/__init__.py @@ -0,0 +1 @@ +from misoc.spi.core import SPIMaster diff --git a/misoc/spi/core.py b/misoc/spi/core.py new file mode 100644 index 00000000..1658d949 --- /dev/null +++ b/misoc/spi/core.py @@ -0,0 +1,152 @@ +from migen import * +from migen.bank.description import * +from migen.genlib.fsm import FSM, NextState + + +class SPIMaster(Module, AutoCSR): + def __init__(self, pads, width=24, div=2, cpha=1): + self.pads = pads + + self._ctrl = CSR() + self._length = CSRStorage(8) + self._status = CSRStatus() + if hasattr(pads, "mosi"): + self._mosi = CSRStorage(width) + if hasattr(pads, "miso"): + self._miso = CSRStatus(width) + + self.irq = Signal() + + ### + + # ctrl + start = Signal() + length = self._length.storage + enable_cs = Signal() + enable_shift = Signal() + done = Signal() + + self.comb += [ + start.eq(self._ctrl.re & self._ctrl.r[0]), + self._status.status.eq(done) + ] + + # clk + i = Signal(max=div) + set_clk = Signal() + clr_clk = Signal() + self.sync += [ + If(set_clk, + pads.clk.eq(enable_cs) + ), + If(clr_clk, + pads.clk.eq(0), + i.eq(0) + ).Else( + i.eq(i + 1), + ) + ] + + self.comb += [ + set_clk.eq(i == (div//2-1)), + clr_clk.eq(i == (div-1)) + ] + + # fsm + cnt = Signal(8) + clr_cnt = Signal() + inc_cnt = Signal() + self.sync += \ + If(clr_cnt, + cnt.eq(0) + ).Elif(inc_cnt, + cnt.eq(cnt+1) + ) + + fsm = FSM(reset_state="IDLE") + self.submodules += fsm + fsm.act("IDLE", + If(start, + NextState("WAIT_CLK") + ), + done.eq(1), + clr_cnt.eq(1) + ) + fsm.act("WAIT_CLK", + If(clr_clk, + NextState("SHIFT") + ), + ) + fsm.act("SHIFT", + If(cnt == length, + NextState("END") + ).Else( + inc_cnt.eq(clr_clk), + ), + enable_cs.eq(1), + enable_shift.eq(1), + ) + fsm.act("END", + If(set_clk, + NextState("IDLE") + ), + enable_shift.eq(1), + self.irq.eq(1) + ) + + # miso + if hasattr(pads, "miso"): + miso = Signal() + sr_miso = Signal(width) + + # (cpha = 1: capture on clk falling edge) + if cpha: + self.sync += \ + If(enable_shift, + If(clr_clk, + miso.eq(pads.miso), + ).Elif(set_clk, + sr_miso.eq(Cat(miso, sr_miso[:-1])) + ) + ) + # (cpha = 0: capture on clk rising edge) + else: + self.sync += \ + If(enable_shift, + If(set_clk, + miso.eq(pads.miso), + ).Elif(clr_clk, + sr_miso.eq(Cat(miso, sr_miso[:-1])) + ) + ) + self.comb += self._miso.status.eq(sr_miso) + + # mosi + if hasattr(pads, "mosi"): + sr_mosi = Signal(width) + + # (cpha = 1: propagated on clk rising edge) + if cpha: + self.sync += \ + If(start, + sr_mosi.eq(self._mosi.storage) + ).Elif(clr_clk & enable_shift, + sr_mosi.eq(Cat(Signal(), sr_mosi[:-1])) + ).Elif(set_clk, + pads.mosi.eq(sr_mosi[-1]) + ) + + # (cpha = 0: propagated on clk falling edge) + else: + self.sync += [ + If(start, + sr_mosi.eq(self._mosi.storage) + ).Elif(set_clk & enable_shift, + sr_mosi.eq(Cat(Signal(), sr_mosi[:-1])) + ).Elif(clr_clk, + pads.mosi.eq(sr_mosi[-1]) + ) + ] + + # cs_n + self.comb += pads.cs_n.eq(~enable_cs) diff --git a/misoc/spi/test.py b/misoc/spi/test.py new file mode 100644 index 00000000..e0febf53 --- /dev/null +++ b/misoc/spi/test.py @@ -0,0 +1,86 @@ +from migen import * +from migen.genlib.record import * +from migen.sim.generic import run_simulation + +from misoc.com.spi import SPIMaster + + +class SPISlave(Module): + def __init__(self, pads, width): + self.pads = pads + self.width = width + + ### + + self.mosi = 0 + self.miso = 0 + + self.last_cs_n = 1 + self.last_clk = 0 + + + def get_mosi(self): + return self.mosi + + def set_miso(self, value): + self.miso = value + + def do_simulation(self, selfp): + # detect edges + cs_n_rising = 0 + cs_n_falling = 0 + clk_rising = 0 + clk_falling = 0 + if selfp.pads.cs_n and not self.last_cs_n: + cs_n_rising = 1 + if not selfp.pads.cs_n and self.last_cs_n: + cs_n_falling = 1 + if selfp.pads.clk and not self.last_clk: + clk_rising = 1 + if not selfp.pads.clk and self.last_clk: + clk_falling = 1 + + # input mosi + if clk_falling and not selfp.pads.cs_n: + self.mosi = self.mosi << 1 + self.mosi |= selfp.pads.mosi + + # output miso + if (clk_rising and not selfp.pads.cs_n): + selfp.pads.miso = (self.miso >> (self.width-1)) & 0x1 + self.miso = self.miso << 1 + + # save signal states + self.last_cs_n = selfp.pads.cs_n + self.last_clk = selfp.pads.clk + + +def spi_access(selfp, length, mosi): + selfp.spi_master._mosi.storage = mosi + yield + selfp.spi_master._ctrl.r = (length << 8) | 1 + selfp.spi_master._ctrl.re = 1 + yield + selfp.spi_master._ctrl.r = 0 + selfp.spi_master._ctrl.re = 0 + yield + while not (selfp.spi_master._status.status & 0x1): + yield + + +class TB(Module): + def __init__(self): + pads = Record([("cs_n", 1), ("clk", 1), ("mosi", 1), ("miso", 1)]) + self.submodules.spi_master = SPIMaster(pads, 24, 4) + self.submodules.spi_slave = SPISlave(pads, 24) + + def gen_simulation(self, selfp): + for i in range(16): + yield + self.spi_slave.set_miso(0x123457) + yield from spi_access(selfp, 8, 0x123457) + print("{:08x}".format(self.spi_slave.get_mosi())) + print("{:08x}".format(selfp.spi_master._miso.status)) + +if __name__ == "__main__": + run_simulation(TB(), ncycles=1000, vcd_name="my.vcd", keep_files=True) diff --git a/misoc/spiflash.py b/misoc/spiflash.py new file mode 100644 index 00000000..b63840c5 --- /dev/null +++ b/misoc/spiflash.py @@ -0,0 +1,181 @@ +from migen import * +from migen.bus.transactions import * +from migen.bus import wishbone +from migen.genlib.misc import timeline +from migen.genlib.record import Record +from migen.bank.description import AutoCSR, CSRStorage, CSRStatus + +_FAST_READ = 0x0b +_DIOFR = 0xbb +_QIOFR = 0xeb + + +def _format_cmd(cmd, spi_width): + """ + `cmd` is the read instruction. Since everything is transmitted on all + dq lines (cmd, adr and data), extend/interleave cmd to full pads.dq + width even if dq1-dq3 are don't care during the command phase: + For example, for N25Q128, 0xeb is the quad i/o fast read, and + extended to 4 bits (dq1,dq2,dq3 high) is: 0xfffefeff + """ + c = 2**(8*spi_width)-1 + for b in range(8): + if not (cmd>>b)%2: + c &= ~(1<<(b*spi_width)) + return c + + +class SpiFlash(Module, AutoCSR): + def __init__(self, pads, dummy=15, div=2, with_bitbang=True): + """ + Simple SPI flash, e.g. N25Q128 on the LX9 Microboard. + + Supports multi-bit pseudo-parallel reads (aka Dual or Quad I/O Fast + Read). Only supports mode0 (cpol=0, cpha=0). + Optionally supports software bitbanging (for write, erase, or other commands). + """ + self.bus = bus = wishbone.Interface() + spi_width = flen(pads.dq) + if with_bitbang: + self.bitbang = CSRStorage(4) + self.miso = CSRStatus() + self.bitbang_en = CSRStorage() + + ### + + cs_n = Signal(reset=1) + clk = Signal() + dq_oe = Signal() + wbone_width = flen(bus.dat_r) + + + read_cmd_params = { + 4: (_format_cmd(_QIOFR, 4), 4*8), + 2: (_format_cmd(_DIOFR, 2), 2*8), + 1: (_format_cmd(_FAST_READ, 1), 1*8) + } + read_cmd, cmd_width = read_cmd_params[spi_width] + addr_width = 24 + + pads.cs_n.reset = 1 + + dq = TSTriple(spi_width) + self.specials.dq = dq.get_tristate(pads.dq) + + sr = Signal(max(cmd_width, addr_width, wbone_width)) + dqs = Replicate(1, spi_width-1) + + self.comb += bus.dat_r.eq(sr) + + hw_read_logic = [ + pads.clk.eq(clk), + pads.cs_n.eq(cs_n), + dq.o.eq(sr[-spi_width:]), + dq.oe.eq(dq_oe) + ] + + if with_bitbang: + bitbang_logic = [ + pads.clk.eq(self.bitbang.storage[1]), + pads.cs_n.eq(self.bitbang.storage[2]), + dq.o.eq(Cat(self.bitbang.storage[0], dqs)), + If(self.bitbang.storage[3], + dq.oe.eq(0) + ).Else( + dq.oe.eq(1) + ), + If(self.bitbang.storage[1], + self.miso.status.eq(dq.i[1]) + ) + ] + + self.comb += \ + If(self.bitbang_en.storage, + bitbang_logic + ).Else( + hw_read_logic + ) + else: + self.comb += hw_read_logic + + if div < 2: + raise ValueError("Unsupported value \'{}\' for div parameter for SpiFlash core".format(div)) + else: + i = Signal(max=div) + dqi = Signal(spi_width) + self.sync += [ + If(i == div//2 - 1, + clk.eq(1), + dqi.eq(dq.i), + ), + If(i == div - 1, + i.eq(0), + clk.eq(0), + sr.eq(Cat(dqi, sr[:-spi_width])) + ).Else( + i.eq(i + 1), + ), + ] + + # spi is byte-addressed, prefix by zeros + z = Replicate(0, log2_int(wbone_width//8)) + + seq = [ + (cmd_width//spi_width*div, + [dq_oe.eq(1), cs_n.eq(0), sr[-cmd_width:].eq(read_cmd)]), + (addr_width//spi_width*div, + [sr[-addr_width:].eq(Cat(z, bus.adr))]), + ((dummy + wbone_width//spi_width)*div, + [dq_oe.eq(0)]), + (1, + [bus.ack.eq(1), cs_n.eq(1)]), + (div, # tSHSL! + [bus.ack.eq(0)]), + (0, + []), + ] + + # accumulate timeline deltas + t, tseq = 0, [] + for dt, a in seq: + tseq.append((t, a)) + t += dt + + self.sync += timeline(bus.cyc & bus.stb & (i == div - 1), tseq) + + +class SpiFlashTB(Module): + def __init__(self): + self.submodules.master = wishbone.Initiator(self.gen_reads()) + self.pads = Record([("cs_n", 1), ("clk", 1), ("dq", 4)]) + self.submodules.slave = SpiFlash(self.pads) + self.submodules.tap = wishbone.Tap(self.slave.bus) + self.submodules.intercon = wishbone.InterconnectPointToPoint( + self.master.bus, self.slave.bus) + self.cycle = 0 + + def gen_reads(self): + for a in range(10): + t = TRead(a) + yield t + print("read {} in {} cycles(s)".format(t.data, t.latency)) + + def do_simulation(self, selfp): + if selfp.pads.cs_n: + self.cycle = 0 + else: + self.cycle += 1 + if not selfp.slave.dq.oe: + selfp.slave.dq.i = self.cycle & 0xf + do_simulation.passive = True + +if __name__ == "__main__": + from migen.sim.generic import run_simulation + from migen.fhdl import verilog + + pads = Record([("cs_n", 1), ("clk", 1), ("dq", 4)]) + s = SpiFlash(pads) + print(verilog.convert(s, ios={pads.clk, pads.cs_n, pads.dq, s.bus.adr, + s.bus.dat_r, s.bus.cyc, s.bus.ack, s.bus.stb})) + + run_simulation(SpiFlashTB(), vcd_name="spiflash.vcd") diff --git a/misoc/timer.py b/misoc/timer.py new file mode 100644 index 00000000..62a88acf --- /dev/null +++ b/misoc/timer.py @@ -0,0 +1,34 @@ +from migen import * +from migen.bank.description import * +from migen.bank.eventmanager import * + + +class Timer(Module, AutoCSR): + def __init__(self, width=32): + self._load = CSRStorage(width) + self._reload = CSRStorage(width) + self._en = CSRStorage() + self._update_value = CSR() + self._value = CSRStatus(width) + + self.submodules.ev = EventManager() + self.ev.zero = EventSourceProcess() + self.ev.finalize() + + ### + + value = Signal(width) + self.sync += [ + If(self._en.storage, + If(value == 0, + # set reload to 0 to disable reloading + value.eq(self._reload.storage) + ).Else( + value.eq(value - 1) + ) + ).Else( + value.eq(self._load.storage) + ), + If(self._update_value.re, self._value.status.eq(value)) + ] + self.comb += self.ev.zero.trigger.eq(value != 0) diff --git a/misoc/tools/__init__.py b/misoc/tools/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/misoc/tools/wishbone.py b/misoc/tools/wishbone.py deleted file mode 100644 index 99b4c43e..00000000 --- a/misoc/tools/wishbone.py +++ /dev/null @@ -1,149 +0,0 @@ -from migen import * -from migen.bus import wishbone -from migen.genlib.misc import chooser, Counter, WaitTimer -from migen.genlib.record import Record -from migen.genlib.fsm import FSM, NextState -from migen.flow.actor import Sink, Source - - -class WishboneStreamingBridge(Module): - cmds = { - "write": 0x01, - "read": 0x02 - } - - def __init__(self, phy, clk_freq): - self.wishbone = wishbone.Interface() - - # # # - - byte_counter = Counter(3) - word_counter = Counter(8) - self.submodules += byte_counter, word_counter - - cmd = Signal(8) - cmd_ce = Signal() - - length = Signal(8) - length_ce = Signal() - - address = Signal(32) - address_ce = Signal() - - data = Signal(32) - rx_data_ce = Signal() - tx_data_ce = Signal() - - self.sync += [ - If(cmd_ce, cmd.eq(phy.source.data)), - If(length_ce, length.eq(phy.source.data)), - If(address_ce, address.eq(Cat(phy.source.data, address[0:24]))), - If(rx_data_ce, - data.eq(Cat(phy.source.data, data[0:24])) - ).Elif(tx_data_ce, - data.eq(self.wishbone.dat_r) - ) - ] - - fsm = InsertReset(FSM(reset_state="IDLE")) - timer = WaitTimer(clk_freq//10) - self.submodules += fsm, timer - self.comb += [ - fsm.reset.eq(timer.done), - phy.source.ack.eq(1) - ] - fsm.act("IDLE", - If(phy.source.stb, - cmd_ce.eq(1), - If((phy.source.data == self.cmds["write"]) | - (phy.source.data == self.cmds["read"]), - NextState("RECEIVE_LENGTH") - ), - byte_counter.reset.eq(1), - word_counter.reset.eq(1) - ) - ) - fsm.act("RECEIVE_LENGTH", - If(phy.source.stb, - length_ce.eq(1), - NextState("RECEIVE_ADDRESS") - ) - ) - fsm.act("RECEIVE_ADDRESS", - If(phy.source.stb, - address_ce.eq(1), - byte_counter.ce.eq(1), - If(byte_counter.value == 3, - If(cmd == self.cmds["write"], - NextState("RECEIVE_DATA") - ).Elif(cmd == self.cmds["read"], - NextState("READ_DATA") - ), - byte_counter.reset.eq(1), - ) - ) - ) - fsm.act("RECEIVE_DATA", - If(phy.source.stb, - rx_data_ce.eq(1), - byte_counter.ce.eq(1), - If(byte_counter.value == 3, - NextState("WRITE_DATA"), - byte_counter.reset.eq(1) - ) - ) - ) - self.comb += [ - self.wishbone.adr.eq(address + word_counter.value), - self.wishbone.dat_w.eq(data), - self.wishbone.sel.eq(2**flen(self.wishbone.sel)-1) - ] - fsm.act("WRITE_DATA", - self.wishbone.stb.eq(1), - self.wishbone.we.eq(1), - self.wishbone.cyc.eq(1), - If(self.wishbone.ack, - word_counter.ce.eq(1), - If(word_counter.value == (length-1), - NextState("IDLE") - ).Else( - NextState("RECEIVE_DATA") - ) - ) - ) - fsm.act("READ_DATA", - self.wishbone.stb.eq(1), - self.wishbone.we.eq(0), - self.wishbone.cyc.eq(1), - If(self.wishbone.ack, - tx_data_ce.eq(1), - NextState("SEND_DATA") - ) - ) - self.comb += \ - chooser(data, byte_counter.value, phy.sink.data, n=4, reverse=True) - fsm.act("SEND_DATA", - phy.sink.stb.eq(1), - If(phy.sink.ack, - byte_counter.ce.eq(1), - If(byte_counter.value == 3, - word_counter.ce.eq(1), - If(word_counter.value == (length-1), - NextState("IDLE") - ).Else( - NextState("READ_DATA"), - byte_counter.reset.eq(1) - ) - ) - ) - ) - - self.comb += timer.wait.eq(~fsm.ongoing("IDLE")) - - if phy.sink.description.packetized: - self.comb += [ - phy.sink.sop.eq((byte_counter.value == 0) & (word_counter.value == 0)), - phy.sink.eop.eq((byte_counter.value == 3) & (word_counter.value == (length-1))) - ] - if hasattr(phy.sink, "length"): - self.comb += phy.sink.length.eq(4*length) diff --git a/misoc/uart/__init__.py b/misoc/uart/__init__.py new file mode 100644 index 00000000..f1ee8458 --- /dev/null +++ b/misoc/uart/__init__.py @@ -0,0 +1 @@ +from misoc.uart.core import UART, RS232PHY diff --git a/misoc/uart/core.py b/misoc/uart/core.py new file mode 100644 index 00000000..4c8b400b --- /dev/null +++ b/misoc/uart/core.py @@ -0,0 +1,160 @@ +from migen import * +from migen.bank.description import * +from migen.bank.eventmanager import * +from migen.genlib.record import Record +from migen.actorlib.fifo import SyncFIFO, AsyncFIFO + + +class RS232PHYRX(Module): + def __init__(self, pads, tuning_word): + self.source = Source([("data", 8)]) + + ### + + uart_clk_rxen = Signal() + phase_accumulator_rx = Signal(32) + + rx = Signal() + self.specials += MultiReg(pads.rx, rx) + rx_r = Signal() + rx_reg = Signal(8) + rx_bitcount = Signal(4) + rx_busy = Signal() + rx_done = self.source.stb + rx_data = self.source.data + self.sync += [ + rx_done.eq(0), + rx_r.eq(rx), + If(~rx_busy, + If(~rx & rx_r, # look for start bit + rx_busy.eq(1), + rx_bitcount.eq(0), + ) + ).Else( + If(uart_clk_rxen, + rx_bitcount.eq(rx_bitcount + 1), + If(rx_bitcount == 0, + If(rx, # verify start bit + rx_busy.eq(0) + ) + ).Elif(rx_bitcount == 9, + rx_busy.eq(0), + If(rx, # verify stop bit + rx_data.eq(rx_reg), + rx_done.eq(1) + ) + ).Else( + rx_reg.eq(Cat(rx_reg[1:], rx)) + ) + ) + ) + ] + self.sync += \ + If(rx_busy, + Cat(phase_accumulator_rx, uart_clk_rxen).eq(phase_accumulator_rx + tuning_word) + ).Else( + Cat(phase_accumulator_rx, uart_clk_rxen).eq(2**31) + ) + + +class RS232PHYTX(Module): + def __init__(self, pads, tuning_word): + self.sink = Sink([("data", 8)]) + + # # # + + uart_clk_txen = Signal() + phase_accumulator_tx = Signal(32) + + pads.tx.reset = 1 + + tx_reg = Signal(8) + tx_bitcount = Signal(4) + tx_busy = Signal() + self.sync += [ + self.sink.ack.eq(0), + If(self.sink.stb & ~tx_busy & ~self.sink.ack, + tx_reg.eq(self.sink.data), + tx_bitcount.eq(0), + tx_busy.eq(1), + pads.tx.eq(0) + ).Elif(uart_clk_txen & tx_busy, + tx_bitcount.eq(tx_bitcount + 1), + If(tx_bitcount == 8, + pads.tx.eq(1) + ).Elif(tx_bitcount == 9, + pads.tx.eq(1), + tx_busy.eq(0), + self.sink.ack.eq(1), + ).Else( + pads.tx.eq(tx_reg[0]), + tx_reg.eq(Cat(tx_reg[1:], 0)) + ) + ) + ] + self.sync += [ + If(tx_busy, + Cat(phase_accumulator_tx, uart_clk_txen).eq(phase_accumulator_tx + tuning_word) + ).Else( + Cat(phase_accumulator_tx, uart_clk_txen).eq(0) + ) + ] + + +class RS232PHY(Module, AutoCSR): + def __init__(self, pads, clk_freq, baudrate=115200): + self._tuning_word = CSRStorage(32, reset=int((baudrate/clk_freq)*2**32)) + self.submodules.tx = RS232PHYTX(pads, self._tuning_word.storage) + self.submodules.rx = RS232PHYRX(pads, self._tuning_word.storage) + self.sink, self.source = self.tx.sink, self.rx.source + + +def _get_uart_fifo(depth, sink_cd="sys", source_cd="sys"): + if sink_cd != source_cd: + fifo = AsyncFIFO([("data", 8)], depth) + return ClockDomainsRenamer({"write": sink_cd, "read": source_cd})(fifo) + else: + return SyncFIFO([("data", 8)], depth) + + +class UART(Module, AutoCSR): + def __init__(self, phy, + tx_fifo_depth=16, + rx_fifo_depth=16, + phy_cd="sys"): + self._rxtx = CSR(8) + self._txfull = CSRStatus() + self._rxempty = CSRStatus() + + self.submodules.ev = EventManager() + self.ev.tx = EventSourceProcess() + self.ev.rx = EventSourceProcess() + self.ev.finalize() + + # # # + + # TX + tx_fifo = _get_uart_fifo(tx_fifo_depth, source_cd=phy_cd) + self.submodules += tx_fifo + + self.comb += [ + tx_fifo.sink.stb.eq(self._rxtx.re), + tx_fifo.sink.data.eq(self._rxtx.r), + self._txfull.status.eq(~tx_fifo.sink.ack), + Record.connect(tx_fifo.source, phy.sink), + # Generate TX IRQ when tx_fifo becomes non-full + self.ev.tx.trigger.eq(~tx_fifo.sink.ack) + ] + + # RX + rx_fifo = _get_uart_fifo(rx_fifo_depth, sink_cd=phy_cd) + self.submodules += rx_fifo + + self.comb += [ + Record.connect(phy.source, rx_fifo.sink), + self._rxempty.status.eq(~rx_fifo.source.stb), + self._rxtx.w.eq(rx_fifo.source.data), + rx_fifo.source.ack.eq(self.ev.rx.clear), + # Generate RX IRQ when tx_fifo becomes non-empty + self.ev.rx.trigger.eq(~rx_fifo.source.stb) + ] diff --git a/misoc/uart/test.py b/misoc/uart/test.py new file mode 100644 index 00000000..71318c7a --- /dev/null +++ b/misoc/uart/test.py @@ -0,0 +1,97 @@ +# XXX Adapt test to new architecture +class UARTTB(Module): + def __init__(self): + self.clk_freq = 83333333 + self.baud = 3000000 + self.pads = Record([("rx", 1), ("tx", 1)]) + self.submodules.slave = UART(self.pads, self.clk_freq, self.baud) + + def wait_for(self, ns_time): + freq_in_ghz = self.clk_freq/(10**9) + period = 1/freq_in_ghz + num_loops = int(ns_time/period) + for i in range(num_loops+1): + yield + + def gen_simulation(self, selfp): + baud_in_ghz = self.baud/(10**9) + uart_period = int(1/baud_in_ghz) + half_uart_period = int(1/(2*baud_in_ghz)) + + # Set TX an RX lines idle + selfp.pads.tx = 1 + selfp.pads.rx = 1 + yield + + # First send a few characters + + tx_string = "01234" + print("Sending string: " + tx_string) + for c in tx_string: + selfp.slave._r_rxtx.r = ord(c) + selfp.slave._r_rxtx.re = 1 + yield + selfp.slave._r_rxtx.re = 0 + + yield from self.wait_for(half_uart_period) + + if selfp.pads.tx: + print("FAILURE: no start bit sent") + + val = 0 + for i in range(8): + yield from self.wait_for(uart_period) + val >>= 1 + if selfp.pads.tx: + val |= 0x80 + + yield from self.wait_for(uart_period) + + if selfp.pads.tx == 0: + print("FAILURE: no stop bit sent") + + if ord(c) != val: + print("FAILURE: sent decimal value "+str(val)+" (char "+chr(val)+") instead of "+c) + else: + print("SUCCESS: sent "+c) + while selfp.slave.ev.tx.trigger != 1: + yield + + # Then receive a character + + rx_string = '5' + print("Receiving character "+rx_string) + rx_value = ord(rx_string) + for i in range(11): + if (i == 0): + # start bit + selfp.pads.rx = 0 + elif (i == 9): + # stop bit + selfp.pads.rx = 1 + elif (i == 10): + selfp.pads.rx = 1 + break + else: + selfp.pads.rx = 1 if (rx_value & 1) else 0 + rx_value >>= 1 + yield from self.wait_for(uart_period) + + rx_value = ord(rx_string) + received_value = selfp.slave._r_rxtx.w + if (received_value == rx_value): + print("RX SUCCESS: ") + else: + print("RX FAILURE: ") + + print("received "+chr(received_value)) + + while True: + yield + +if __name__ == "__main__": + from migen.sim.generic import Simulator, TopLevel + from migen.sim import icarus + with Simulator(UARTTB(), TopLevel("top.vcd", clk_period=int(1/0.08333333)), + icarus.Runner(keep_files=False)) as s: + s.run(20000) diff --git a/misoc/video/__init__.py b/misoc/video/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/misoc/video/dvisampler/__init__.py b/misoc/video/dvisampler/__init__.py deleted file mode 100644 index fad0f9da..00000000 --- a/misoc/video/dvisampler/__init__.py +++ /dev/null @@ -1,80 +0,0 @@ -from migen import * -from migen.bank.description import AutoCSR - -from misoc.video.dvisampler.edid import EDID -from misoc.video.dvisampler.clocking import Clocking -from misoc.video.dvisampler.datacapture import DataCapture -from misoc.video.dvisampler.charsync import CharSync -from misoc.video.dvisampler.wer import WER -from misoc.video.dvisampler.decoding import Decoding -from misoc.video.dvisampler.chansync import ChanSync -from misoc.video.dvisampler.analysis import SyncPolarity, ResolutionDetection, FrameExtraction -from misoc.video.dvisampler.dma import DMA - - -class DVISampler(Module, AutoCSR): - def __init__(self, pads, lasmim, n_dma_slots=2, fifo_depth=512): - self.submodules.edid = EDID(pads) - self.submodules.clocking = Clocking(pads) - - for datan in range(3): - name = "data" + str(datan) - - cap = DataCapture(getattr(pads, name + "_p"), getattr(pads, name + "_n"), 8) - setattr(self.submodules, name + "_cap", cap) - self.comb += cap.serdesstrobe.eq(self.clocking.serdesstrobe) - - charsync = CharSync() - setattr(self.submodules, name + "_charsync", charsync) - self.comb += charsync.raw_data.eq(cap.d) - - wer = WER() - setattr(self.submodules, name + "_wer", wer) - self.comb += wer.data.eq(charsync.data) - - decoding = Decoding() - setattr(self.submodules, name + "_decod", decoding) - self.comb += [ - decoding.valid_i.eq(charsync.synced), - decoding.input.eq(charsync.data) - ] - - self.submodules.chansync = ChanSync() - self.comb += [ - self.chansync.valid_i.eq(self.data0_decod.valid_o & \ - self.data1_decod.valid_o & self.data2_decod.valid_o), - self.chansync.data_in0.eq(self.data0_decod.output), - self.chansync.data_in1.eq(self.data1_decod.output), - self.chansync.data_in2.eq(self.data2_decod.output), - ] - - self.submodules.syncpol = SyncPolarity() - self.comb += [ - self.syncpol.valid_i.eq(self.chansync.chan_synced), - self.syncpol.data_in0.eq(self.chansync.data_out0), - self.syncpol.data_in1.eq(self.chansync.data_out1), - self.syncpol.data_in2.eq(self.chansync.data_out2) - ] - - self.submodules.resdetection = ResolutionDetection() - self.comb += [ - self.resdetection.valid_i.eq(self.syncpol.valid_o), - self.resdetection.de.eq(self.syncpol.de), - self.resdetection.vsync.eq(self.syncpol.vsync) - ] - - self.submodules.frame = FrameExtraction(24*lasmim.dw//32, fifo_depth) - self.comb += [ - self.frame.valid_i.eq(self.syncpol.valid_o), - self.frame.de.eq(self.syncpol.de), - self.frame.vsync.eq(self.syncpol.vsync), - self.frame.r.eq(self.syncpol.r), - self.frame.g.eq(self.syncpol.g), - self.frame.b.eq(self.syncpol.b) - ] - - self.submodules.dma = DMA(lasmim, n_dma_slots) - self.comb += self.frame.frame.connect(self.dma.frame) - self.ev = self.dma.ev - - autocsr_exclude = {"ev"} diff --git a/misoc/video/dvisampler/analysis.py b/misoc/video/dvisampler/analysis.py deleted file mode 100644 index 189413fe..00000000 --- a/misoc/video/dvisampler/analysis.py +++ /dev/null @@ -1,208 +0,0 @@ -from migen import * -from migen.genlib.cdc import MultiReg, PulseSynchronizer -from migen.genlib.fifo import AsyncFIFO -from migen.genlib.record import Record -from migen.bank.description import * -from migen.flow.actor import * - -from misoc.video.dvisampler.common import channel_layout - - -class SyncPolarity(Module): - def __init__(self): - self.valid_i = Signal() - self.data_in0 = Record(channel_layout) - self.data_in1 = Record(channel_layout) - self.data_in2 = Record(channel_layout) - - self.valid_o = Signal() - self.de = Signal() - self.hsync = Signal() - self.vsync = Signal() - self.r = Signal(8) - self.g = Signal(8) - self.b = Signal(8) - - ### - - de = self.data_in0.de - de_r = Signal() - c = self.data_in0.c - c_polarity = Signal(2) - c_out = Signal(2) - - self.comb += [ - self.de.eq(de_r), - self.hsync.eq(c_out[0]), - self.vsync.eq(c_out[1]) - ] - - self.sync.pix += [ - self.valid_o.eq(self.valid_i), - self.r.eq(self.data_in2.d), - self.g.eq(self.data_in1.d), - self.b.eq(self.data_in0.d), - - de_r.eq(de), - If(de_r & ~de, - c_polarity.eq(c), - c_out.eq(0) - ).Else( - c_out.eq(c ^ c_polarity) - ) - ] - - -class ResolutionDetection(Module, AutoCSR): - def __init__(self, nbits=11): - self.valid_i = Signal() - self.vsync = Signal() - self.de = Signal() - - self._hres = CSRStatus(nbits) - self._vres = CSRStatus(nbits) - - ### - - # Detect DE transitions - de_r = Signal() - pn_de = Signal() - self.sync.pix += de_r.eq(self.de) - self.comb += pn_de.eq(~self.de & de_r) - - # HRES - hcounter = Signal(nbits) - self.sync.pix += If(self.valid_i & self.de, - hcounter.eq(hcounter + 1) - ).Else( - hcounter.eq(0) - ) - - hcounter_st = Signal(nbits) - self.sync.pix += If(self.valid_i, - If(pn_de, hcounter_st.eq(hcounter)) - ).Else( - hcounter_st.eq(0) - ) - self.specials += MultiReg(hcounter_st, self._hres.status) - - # VRES - vsync_r = Signal() - p_vsync = Signal() - self.sync.pix += vsync_r.eq(self.vsync), - self.comb += p_vsync.eq(self.vsync & ~vsync_r) - - vcounter = Signal(nbits) - self.sync.pix += If(self.valid_i & p_vsync, - vcounter.eq(0) - ).Elif(pn_de, - vcounter.eq(vcounter + 1) - ) - - vcounter_st = Signal(nbits) - self.sync.pix += If(self.valid_i, - If(p_vsync, vcounter_st.eq(vcounter)) - ).Else( - vcounter_st.eq(0) - ) - self.specials += MultiReg(vcounter_st, self._vres.status) - - -class FrameExtraction(Module, AutoCSR): - def __init__(self, word_width, fifo_depth): - # in pix clock domain - self.valid_i = Signal() - self.vsync = Signal() - self.de = Signal() - self.r = Signal(8) - self.g = Signal(8) - self.b = Signal(8) - - # in sys clock domain - word_layout = [("sof", 1), ("pixels", word_width)] - self.frame = Source(word_layout) - self.busy = Signal() - - self._overflow = CSR() - - ### - - # start of frame detection - vsync_r = Signal() - new_frame = Signal() - self.comb += new_frame.eq(self.vsync & ~vsync_r) - self.sync.pix += vsync_r.eq(self.vsync) - - # pack pixels into words - cur_word = Signal(word_width) - cur_word_valid = Signal() - encoded_pixel = Signal(24) - self.comb += encoded_pixel.eq(Cat(self.b, self.g, self.r)) - pack_factor = word_width//24 - assert(pack_factor & (pack_factor - 1) == 0) # only support powers of 2 - pack_counter = Signal(max=pack_factor) - self.sync.pix += [ - cur_word_valid.eq(0), - If(new_frame, - cur_word_valid.eq(pack_counter == (pack_factor - 1)), - pack_counter.eq(0), - ).Elif(self.valid_i & self.de, - [If(pack_counter == (pack_factor-i-1), - cur_word[24*i:24*(i+1)].eq(encoded_pixel)) for i in range(pack_factor)], - cur_word_valid.eq(pack_counter == (pack_factor - 1)), - pack_counter.eq(pack_counter + 1) - ) - ] - - # FIFO - fifo = RenameClockDomains(AsyncFIFO(word_layout, fifo_depth), - {"write": "pix", "read": "sys"}) - self.submodules += fifo - self.comb += [ - fifo.din.pixels.eq(cur_word), - fifo.we.eq(cur_word_valid) - ] - self.sync.pix += \ - If(new_frame, - fifo.din.sof.eq(1) - ).Elif(cur_word_valid, - fifo.din.sof.eq(0) - ) - self.comb += [ - self.frame.stb.eq(fifo.readable), - self.frame.payload.eq(fifo.dout), - fifo.re.eq(self.frame.ack), - self.busy.eq(0) - ] - - # overflow detection - pix_overflow = Signal() - pix_overflow_reset = Signal() - self.sync.pix += [ - If(fifo.we & ~fifo.writable, - pix_overflow.eq(1) - ).Elif(pix_overflow_reset, - pix_overflow.eq(0) - ) - ] - - sys_overflow = Signal() - self.specials += MultiReg(pix_overflow, sys_overflow) - self.submodules.overflow_reset = PulseSynchronizer("sys", "pix") - self.submodules.overflow_reset_ack = PulseSynchronizer("pix", "sys") - self.comb += [ - pix_overflow_reset.eq(self.overflow_reset.o), - self.overflow_reset_ack.i.eq(pix_overflow_reset) - ] - - overflow_mask = Signal() - self.comb += [ - self._overflow.w.eq(sys_overflow & ~overflow_mask), - self.overflow_reset.i.eq(self._overflow.re) - ] - self.sync += \ - If(self._overflow.re, - overflow_mask.eq(1) - ).Elif(self.overflow_reset_ack.o, - overflow_mask.eq(0) - ) diff --git a/misoc/video/dvisampler/chansync.py b/misoc/video/dvisampler/chansync.py deleted file mode 100644 index 3bd3fdba..00000000 --- a/misoc/video/dvisampler/chansync.py +++ /dev/null @@ -1,132 +0,0 @@ -from migen import * -from migen.genlib.cdc import MultiReg -from migen.genlib.fifo import _inc -from migen.genlib.record import Record, layout_len -from migen.genlib.misc import optree -from migen.bank.description import * - -from misoc.video.dvisampler.common import channel_layout - - -class _SyncBuffer(Module): - def __init__(self, width, depth): - self.din = Signal(width) - self.dout = Signal(width) - self.re = Signal() - - ### - - produce = Signal(max=depth) - consume = Signal(max=depth) - storage = Memory(width, depth) - self.specials += storage - - wrport = storage.get_port(write_capable=True) - self.specials += wrport - self.comb += [ - wrport.adr.eq(produce), - wrport.dat_w.eq(self.din), - wrport.we.eq(1) - ] - self.sync += _inc(produce, depth) - - rdport = storage.get_port(async_read=True) - self.specials += rdport - self.comb += [ - rdport.adr.eq(consume), - self.dout.eq(rdport.dat_r) - ] - self.sync += If(self.re, _inc(consume, depth)) - - -class ChanSync(Module, AutoCSR): - def __init__(self, nchan=3, depth=8): - self.valid_i = Signal() - self.chan_synced = Signal() - - self._channels_synced = CSRStatus() - - lst_control = [] - all_control = Signal() - for i in range(nchan): - name = "data_in" + str(i) - data_in = Record(channel_layout, name=name) - setattr(self, name, data_in) - name = "data_out" + str(i) - data_out = Record(channel_layout, name=name) - setattr(self, name, data_out) - - ### - - syncbuffer = RenameClockDomains(_SyncBuffer(layout_len(channel_layout), depth), "pix") - self.submodules += syncbuffer - self.comb += [ - syncbuffer.din.eq(data_in.raw_bits()), - data_out.raw_bits().eq(syncbuffer.dout) - ] - is_control = Signal() - self.comb += [ - is_control.eq(~data_out.de), - syncbuffer.re.eq(~is_control | all_control) - ] - lst_control.append(is_control) - - some_control = Signal() - self.comb += [ - all_control.eq(optree("&", lst_control)), - some_control.eq(optree("|", lst_control)) - ] - self.sync.pix += If(~self.valid_i, - self.chan_synced.eq(0) - ).Else( - If(some_control, - If(all_control, - self.chan_synced.eq(1) - ).Else( - self.chan_synced.eq(0) - ) - ) - ) - self.specials += MultiReg(self.chan_synced, self._channels_synced.status) - - -class _TB(Module): - def __init__(self, test_seq_it): - self.test_seq_it = test_seq_it - - self.submodules.chansync = RenameClockDomains(ChanSync(), {"pix": "sys"}) - self.comb += self.chansync.valid_i.eq(1) - - def do_simulation(self, selfp): - try: - de0, de1, de2 = next(self.test_seq_it) - except StopIteration: - raise StopSimulation - - selfp.chansync.data_in0.de = de0 - selfp.chansync.data_in1.de = de1 - selfp.chansync.data_in2.de = de2 - selfp.chansync.data_in0.d = selfp.simulator.cycle_counter - selfp.chansync.data_in1.d = selfp.simulator.cycle_counter - selfp.chansync.data_in2.d = selfp.simulator.cycle_counter - - out0 = selfp.chansync.data_out0.d - out1 = selfp.chansync.data_out1.d - out2 = selfp.chansync.data_out2.d - - print("{0:5} {1:5} {2:5}".format(out0, out1, out2)) - -if __name__ == "__main__": - from migen.sim.generic import run_simulation - - test_seq = [ - (1, 1, 1), - (1, 1, 0), - (0, 0, 0), - (0, 0, 0), - (0, 0, 1), - (1, 1, 1), - (1, 1, 1), - ] - tb = _TB(iter(test_seq*2)) - run_simulation(tb) diff --git a/misoc/video/dvisampler/charsync.py b/misoc/video/dvisampler/charsync.py deleted file mode 100644 index 93b0e556..00000000 --- a/misoc/video/dvisampler/charsync.py +++ /dev/null @@ -1,54 +0,0 @@ -from migen import * -from migen.genlib.cdc import MultiReg -from migen.genlib.misc import optree -from migen.bank.description import * - -from misoc.video.dvisampler.common import control_tokens - - -class CharSync(Module, AutoCSR): - def __init__(self, required_controls=8): - self.raw_data = Signal(10) - self.synced = Signal() - self.data = Signal(10) - - self._char_synced = CSRStatus() - self._ctl_pos = CSRStatus(bits_for(9)) - - ### - - raw_data1 = Signal(10) - self.sync.pix += raw_data1.eq(self.raw_data) - raw = Signal(20) - self.comb += raw.eq(Cat(raw_data1, self.raw_data)) - - found_control = Signal() - control_position = Signal(max=10) - self.sync.pix += found_control.eq(0) - for i in range(10): - self.sync.pix += If(optree("|", [raw[i:i+10] == t for t in control_tokens]), - found_control.eq(1), - control_position.eq(i) - ) - - control_counter = Signal(max=required_controls) - previous_control_position = Signal(max=10) - word_sel = Signal(max=10) - self.sync.pix += [ - If(found_control & (control_position == previous_control_position), - If(control_counter == (required_controls - 1), - control_counter.eq(0), - self.synced.eq(1), - word_sel.eq(control_position) - ).Else( - control_counter.eq(control_counter + 1) - ) - ).Else( - control_counter.eq(0) - ), - previous_control_position.eq(control_position) - ] - self.specials += MultiReg(self.synced, self._char_synced.status) - self.specials += MultiReg(word_sel, self._ctl_pos.status) - - self.sync.pix += self.data.eq(raw >> word_sel) diff --git a/misoc/video/dvisampler/clocking.py b/misoc/video/dvisampler/clocking.py deleted file mode 100644 index 0fc8969b..00000000 --- a/misoc/video/dvisampler/clocking.py +++ /dev/null @@ -1,80 +0,0 @@ -from migen import * -from migen.genlib.cdc import MultiReg -from migen.bank.description import * - - -class Clocking(Module, AutoCSR): - def __init__(self, pads): - self._pll_reset = CSRStorage(reset=1) - self._locked = CSRStatus() - - # DRP - self._pll_adr = CSRStorage(5) - self._pll_dat_r = CSRStatus(16) - self._pll_dat_w = CSRStorage(16) - self._pll_read = CSR() - self._pll_write = CSR() - self._pll_drdy = CSRStatus() - - self.locked = Signal() - self.serdesstrobe = Signal() - self.clock_domains._cd_pix = ClockDomain() - self.clock_domains._cd_pix2x = ClockDomain() - self.clock_domains._cd_pix10x = ClockDomain(reset_less=True) - - ### - - clk_se = Signal() - self.specials += Instance("IBUFDS", i_I=pads.clk_p, i_IB=pads.clk_n, o_O=clk_se) - - clkfbout = Signal() - pll_locked = Signal() - pll_clk0 = Signal() - pll_clk1 = Signal() - pll_clk2 = Signal() - pll_drdy = Signal() - self.sync += If(self._pll_read.re | self._pll_write.re, - self._pll_drdy.status.eq(0) - ).Elif(pll_drdy, - self._pll_drdy.status.eq(1) - ) - self.specials += Instance("PLL_ADV", - p_CLKFBOUT_MULT=10, - p_CLKOUT0_DIVIDE=1, # pix10x - p_CLKOUT1_DIVIDE=5, # pix2x - p_CLKOUT2_DIVIDE=10, # pix - p_COMPENSATION="INTERNAL", - - i_CLKINSEL=1, - i_CLKIN1=clk_se, - o_CLKOUT0=pll_clk0, o_CLKOUT1=pll_clk1, o_CLKOUT2=pll_clk2, - o_CLKFBOUT=clkfbout, i_CLKFBIN=clkfbout, - o_LOCKED=pll_locked, i_RST=self._pll_reset.storage, - - i_DADDR=self._pll_adr.storage, - o_DO=self._pll_dat_r.status, - i_DI=self._pll_dat_w.storage, - i_DEN=self._pll_read.re | self._pll_write.re, - i_DWE=self._pll_write.re, - o_DRDY=pll_drdy, - i_DCLK=ClockSignal()) - - locked_async = Signal() - self.specials += [ - Instance("BUFPLL", p_DIVIDE=5, - i_PLLIN=pll_clk0, i_GCLK=ClockSignal("pix2x"), i_LOCKED=pll_locked, - o_IOCLK=self._cd_pix10x.clk, o_LOCK=locked_async, o_SERDESSTROBE=self.serdesstrobe), - Instance("BUFG", i_I=pll_clk1, o_O=self._cd_pix2x.clk), - Instance("BUFG", i_I=pll_clk2, o_O=self._cd_pix.clk), - MultiReg(locked_async, self.locked, "sys") - ] - self.comb += self._locked.status.eq(self.locked) - - # sychronize pix+pix2x reset - pix_rst_n = 1 - for i in range(2): - new_pix_rst_n = Signal() - self.specials += Instance("FDCE", i_D=pix_rst_n, i_CE=1, i_C=ClockSignal("pix"), - i_CLR=~locked_async, o_Q=new_pix_rst_n) - pix_rst_n = new_pix_rst_n - self.comb += self._cd_pix.rst.eq(~pix_rst_n), self._cd_pix2x.rst.eq(~pix_rst_n) diff --git a/misoc/video/dvisampler/common.py b/misoc/video/dvisampler/common.py deleted file mode 100644 index 7fb9a420..00000000 --- a/misoc/video/dvisampler/common.py +++ /dev/null @@ -1,2 +0,0 @@ -control_tokens = [0b1101010100, 0b0010101011, 0b0101010100, 0b1010101011] -channel_layout = [("d", 8), ("c", 2), ("de", 1)] diff --git a/misoc/video/dvisampler/datacapture.py b/misoc/video/dvisampler/datacapture.py deleted file mode 100644 index 49f0ccef..00000000 --- a/misoc/video/dvisampler/datacapture.py +++ /dev/null @@ -1,187 +0,0 @@ -from migen import * -from migen.genlib.cdc import MultiReg, PulseSynchronizer -from migen.bank.description import * - - -class DataCapture(Module, AutoCSR): - def __init__(self, pad_p, pad_n, ntbits): - self.serdesstrobe = Signal() - self.d = Signal(10) - - self._dly_ctl = CSR(6) - self._dly_busy = CSRStatus(2) - self._phase = CSRStatus(2) - self._phase_reset = CSR() - - ### - - # IO - pad_se = Signal() - self.specials += Instance("IBUFDS", i_I=pad_p, i_IB=pad_n, o_O=pad_se) - - pad_delayed_master = Signal() - pad_delayed_slave = Signal() - delay_inc = Signal() - delay_ce = Signal() - delay_master_cal = Signal() - delay_master_rst = Signal() - delay_master_busy = Signal() - delay_slave_cal = Signal() - delay_slave_rst = Signal() - delay_slave_busy = Signal() - self.specials += Instance("IODELAY2", - p_SERDES_MODE="MASTER", - p_DELAY_SRC="IDATAIN", p_IDELAY_TYPE="DIFF_PHASE_DETECTOR", - p_COUNTER_WRAPAROUND="STAY_AT_LIMIT", p_DATA_RATE="SDR", - - i_IDATAIN=pad_se, o_DATAOUT=pad_delayed_master, - i_CLK=ClockSignal("pix2x"), i_IOCLK0=ClockSignal("pix10x"), - - i_INC=delay_inc, i_CE=delay_ce, - i_CAL=delay_master_cal, i_RST=delay_master_rst, o_BUSY=delay_master_busy, - i_T=1) - self.specials += Instance("IODELAY2", - p_SERDES_MODE="SLAVE", - p_DELAY_SRC="IDATAIN", p_IDELAY_TYPE="DIFF_PHASE_DETECTOR", - p_COUNTER_WRAPAROUND="WRAPAROUND", p_DATA_RATE="SDR", - - i_IDATAIN=pad_se, o_DATAOUT=pad_delayed_slave, - i_CLK=ClockSignal("pix2x"), i_IOCLK0=ClockSignal("pix10x"), - - i_INC=delay_inc, i_CE=delay_ce, - i_CAL=delay_slave_cal, i_RST=delay_slave_rst, o_BUSY=delay_slave_busy, - i_T=1) - - dsr2 = Signal(5) - pd_valid = Signal() - pd_incdec = Signal() - pd_edge = Signal() - pd_cascade = Signal() - self.specials += Instance("ISERDES2", - p_SERDES_MODE="MASTER", - p_BITSLIP_ENABLE="FALSE", p_DATA_RATE="SDR", p_DATA_WIDTH=5, - p_INTERFACE_TYPE="RETIMED", - - i_D=pad_delayed_master, - o_Q4=dsr2[4], o_Q3=dsr2[3], o_Q2=dsr2[2], o_Q1=dsr2[1], - - i_BITSLIP=0, i_CE0=1, i_RST=0, - i_CLK0=ClockSignal("pix10x"), i_CLKDIV=ClockSignal("pix2x"), - i_IOCE=self.serdesstrobe, - - o_VALID=pd_valid, o_INCDEC=pd_incdec, - i_SHIFTIN=pd_edge, o_SHIFTOUT=pd_cascade) - self.specials += Instance("ISERDES2", - p_SERDES_MODE="SLAVE", - p_BITSLIP_ENABLE="FALSE", p_DATA_RATE="SDR", p_DATA_WIDTH=5, - p_INTERFACE_TYPE="RETIMED", - - i_D=pad_delayed_slave, - o_Q4=dsr2[0], - - i_BITSLIP=0, i_CE0=1, i_RST=0, - i_CLK0=ClockSignal("pix10x"), i_CLKDIV=ClockSignal("pix2x"), - i_IOCE=self.serdesstrobe, - - i_SHIFTIN=pd_cascade, o_SHIFTOUT=pd_edge) - - # Phase error accumulator - lateness = Signal(ntbits, reset=2**(ntbits - 1)) - too_late = Signal() - too_early = Signal() - reset_lateness = Signal() - self.comb += [ - too_late.eq(lateness == (2**ntbits - 1)), - too_early.eq(lateness == 0) - ] - self.sync.pix2x += [ - If(reset_lateness, - lateness.eq(2**(ntbits - 1)) - ).Elif(~delay_master_busy & ~delay_slave_busy & ~too_late & ~too_early, - If(pd_valid & pd_incdec, lateness.eq(lateness - 1)), - If(pd_valid & ~pd_incdec, lateness.eq(lateness + 1)) - ) - ] - - # Delay control - self.submodules.delay_master_done = PulseSynchronizer("pix2x", "sys") - delay_master_pending = Signal() - self.sync.pix2x += [ - self.delay_master_done.i.eq(0), - If(~delay_master_pending, - If(delay_master_cal | delay_ce, delay_master_pending.eq(1)) - ).Else( - If(~delay_master_busy, - self.delay_master_done.i.eq(1), - delay_master_pending.eq(0) - ) - ) - ] - self.submodules.delay_slave_done = PulseSynchronizer("pix2x", "sys") - delay_slave_pending = Signal() - self.sync.pix2x += [ - self.delay_slave_done.i.eq(0), - If(~delay_slave_pending, - If(delay_slave_cal | delay_ce, delay_slave_pending.eq(1)) - ).Else( - If(~delay_slave_busy, - self.delay_slave_done.i.eq(1), - delay_slave_pending.eq(0) - ) - ) - ] - - self.submodules.do_delay_master_cal = PulseSynchronizer("sys", "pix2x") - self.submodules.do_delay_master_rst = PulseSynchronizer("sys", "pix2x") - self.submodules.do_delay_slave_cal = PulseSynchronizer("sys", "pix2x") - self.submodules.do_delay_slave_rst = PulseSynchronizer("sys", "pix2x") - self.submodules.do_delay_inc = PulseSynchronizer("sys", "pix2x") - self.submodules.do_delay_dec = PulseSynchronizer("sys", "pix2x") - self.comb += [ - delay_master_cal.eq(self.do_delay_master_cal.o), - delay_master_rst.eq(self.do_delay_master_rst.o), - delay_slave_cal.eq(self.do_delay_slave_cal.o), - delay_slave_rst.eq(self.do_delay_slave_rst.o), - delay_inc.eq(self.do_delay_inc.o), - delay_ce.eq(self.do_delay_inc.o | self.do_delay_dec.o), - ] - - sys_delay_master_pending = Signal() - self.sync += [ - If(self.do_delay_master_cal.i | self.do_delay_inc.i | self.do_delay_dec.i, - sys_delay_master_pending.eq(1) - ).Elif(self.delay_master_done.o, - sys_delay_master_pending.eq(0) - ) - ] - sys_delay_slave_pending = Signal() - self.sync += [ - If(self.do_delay_slave_cal.i | self.do_delay_inc.i | self.do_delay_dec.i, - sys_delay_slave_pending.eq(1) - ).Elif(self.delay_slave_done.o, - sys_delay_slave_pending.eq(0) - ) - ] - - self.comb += [ - self.do_delay_master_cal.i.eq(self._dly_ctl.re & self._dly_ctl.r[0]), - self.do_delay_master_rst.i.eq(self._dly_ctl.re & self._dly_ctl.r[1]), - self.do_delay_slave_cal.i.eq(self._dly_ctl.re & self._dly_ctl.r[2]), - self.do_delay_slave_rst.i.eq(self._dly_ctl.re & self._dly_ctl.r[3]), - self.do_delay_inc.i.eq(self._dly_ctl.re & self._dly_ctl.r[4]), - self.do_delay_dec.i.eq(self._dly_ctl.re & self._dly_ctl.r[5]), - self._dly_busy.status.eq(Cat(sys_delay_master_pending, sys_delay_slave_pending)) - ] - - # Phase detector control - self.specials += MultiReg(Cat(too_late, too_early), self._phase.status) - self.submodules.do_reset_lateness = PulseSynchronizer("sys", "pix2x") - self.comb += [ - reset_lateness.eq(self.do_reset_lateness.o), - self.do_reset_lateness.i.eq(self._phase_reset.re) - ] - - # 5:10 deserialization - dsr = Signal(10) - self.sync.pix2x += dsr.eq(Cat(dsr[5:], dsr2)) - self.sync.pix += self.d.eq(dsr) diff --git a/misoc/video/dvisampler/debug.py b/misoc/video/dvisampler/debug.py deleted file mode 100644 index 9e561383..00000000 --- a/misoc/video/dvisampler/debug.py +++ /dev/null @@ -1,48 +0,0 @@ -from migen import * -from migen.genlib.fifo import AsyncFIFO -from migen.genlib.record import layout_len -from migen.bank.description import AutoCSR -from migen.actorlib import structuring, spi - -from misoc.mem.sdram.frontend import dma_lasmi -from misoc.video.dvisampler.edid import EDID -from misoc.video.dvisampler.clocking import Clocking -from misoc.video.dvisampler.datacapture import DataCapture - - -class RawDVISampler(Module, AutoCSR): - def __init__(self, pads, asmiport): - self.submodules.edid = EDID(pads) - self.submodules.clocking = Clocking(pads) - - invert = False - try: - s = getattr(pads, "data0") - except AttributeError: - s = getattr(pads, "data0_n") - invert = True - self.submodules.data0_cap = DataCapture(8, invert) - self.comb += [ - self.data0_cap.pad.eq(s), - self.data0_cap.serdesstrobe.eq(self.clocking.serdesstrobe) - ] - - fifo = RenameClockDomains(AsyncFIFO(10, 256), - {"write": "pix", "read": "sys"}) - self.submodules += fifo - self.comb += [ - fifo.din.eq(self.data0_cap.d), - fifo.we.eq(1) - ] - - pack_factor = asmiport.hub.dw//16 - self.submodules.packer = structuring.Pack([("word", 10), ("pad", 6)], pack_factor) - self.submodules.cast = structuring.Cast(self.packer.source.payload.layout, asmiport.hub.dw) - self.submodules.dma = spi.DMAWriteController(dma_lasmi.Writer(lasmim), spi.MODE_SINGLE_SHOT) - self.comb += [ - self.packer.sink.stb.eq(fifo.readable), - fifo.re.eq(self.packer.sink.ack), - self.packer.sink.word.eq(fifo.dout), - self.packer.source.connect_flat(self.cast.sink), - self.cast.source.connect_flat(self.dma.data) - ] diff --git a/misoc/video/dvisampler/decoding.py b/misoc/video/dvisampler/decoding.py deleted file mode 100644 index fd8c0c61..00000000 --- a/misoc/video/dvisampler/decoding.py +++ /dev/null @@ -1,25 +0,0 @@ -from migen import * -from migen.genlib.record import Record - -from misoc.video.dvisampler.common import control_tokens, channel_layout - - -class Decoding(Module): - def __init__(self): - self.valid_i = Signal() - self.input = Signal(10) - self.valid_o = Signal() - self.output = Record(channel_layout) - - ### - - self.sync.pix += self.output.de.eq(1) - for i, t in enumerate(control_tokens): - self.sync.pix += If(self.input == t, - self.output.de.eq(0), - self.output.c.eq(i) - ) - self.sync.pix += self.output.d[0].eq(self.input[0] ^ self.input[9]) - for i in range(1, 8): - self.sync.pix += self.output.d[i].eq(self.input[i] ^ self.input[i-1] ^ ~self.input[8]) - self.sync.pix += self.valid_o.eq(self.valid_i) diff --git a/misoc/video/dvisampler/dma.py b/misoc/video/dvisampler/dma.py deleted file mode 100644 index 7ed4f829..00000000 --- a/misoc/video/dvisampler/dma.py +++ /dev/null @@ -1,144 +0,0 @@ -from migen import * -from migen.genlib.fsm import FSM, NextState -from migen.bank.description import * -from migen.bank.eventmanager import * -from migen.flow.actor import * - -from misoc.mem.sdram.frontend import dma_lasmi - - -# Slot status: EMPTY=0 LOADED=1 PENDING=2 -class _Slot(Module, AutoCSR): - def __init__(self, addr_bits, alignment_bits): - self.ev_source = EventSourceLevel() - self.address = Signal(addr_bits) - self.address_reached = Signal(addr_bits) - self.address_valid = Signal() - self.address_done = Signal() - - self._status = CSRStorage(2, write_from_dev=True) - self._address = CSRStorage(addr_bits + alignment_bits, alignment_bits=alignment_bits, write_from_dev=True) - - ### - - self.comb += [ - self.address.eq(self._address.storage), - self.address_valid.eq(self._status.storage[0]), - self._status.dat_w.eq(2), - self._status.we.eq(self.address_done), - self._address.dat_w.eq(self.address_reached), - self._address.we.eq(self.address_done), - self.ev_source.trigger.eq(self._status.storage[1]) - ] - - -class _SlotArray(Module, AutoCSR): - def __init__(self, nslots, addr_bits, alignment_bits): - self.submodules.ev = EventManager() - self.address = Signal(addr_bits) - self.address_reached = Signal(addr_bits) - self.address_valid = Signal() - self.address_done = Signal() - - ### - - slots = [_Slot(addr_bits, alignment_bits) for i in range(nslots)] - for n, slot in enumerate(slots): - setattr(self.submodules, "slot"+str(n), slot) - setattr(self.ev, "slot"+str(n), slot.ev_source) - self.ev.finalize() - - change_slot = Signal() - current_slot = Signal(max=nslots) - self.sync += If(change_slot, [If(slot.address_valid, current_slot.eq(n)) for n, slot in reversed(list(enumerate(slots)))]) - self.comb += change_slot.eq(~self.address_valid | self.address_done) - - self.comb += [ - self.address.eq(Array(slot.address for slot in slots)[current_slot]), - self.address_valid.eq(Array(slot.address_valid for slot in slots)[current_slot]) - ] - self.comb += [slot.address_reached.eq(self.address_reached) for slot in slots] - self.comb += [slot.address_done.eq(self.address_done & (current_slot == n)) for n, slot in enumerate(slots)] - - -class DMA(Module): - def __init__(self, lasmim, nslots): - bus_aw = lasmim.aw - bus_dw = lasmim.dw - alignment_bits = bits_for(bus_dw//8) - 1 - - fifo_word_width = 24*bus_dw//32 - self.frame = Sink([("sof", 1), ("pixels", fifo_word_width)]) - self._frame_size = CSRStorage(bus_aw + alignment_bits, alignment_bits=alignment_bits) - self.submodules._slot_array = _SlotArray(nslots, bus_aw, alignment_bits) - self.ev = self._slot_array.ev - - ### - - # address generator + maximum memory word count to prevent DMA buffer overrun - reset_words = Signal() - count_word = Signal() - last_word = Signal() - current_address = Signal(bus_aw) - mwords_remaining = Signal(bus_aw) - self.comb += [ - self._slot_array.address_reached.eq(current_address), - last_word.eq(mwords_remaining == 1) - ] - self.sync += [ - If(reset_words, - current_address.eq(self._slot_array.address), - mwords_remaining.eq(self._frame_size.storage) - ).Elif(count_word, - current_address.eq(current_address + 1), - mwords_remaining.eq(mwords_remaining - 1) - ) - ] - - # 24bpp -> 32bpp - memory_word = Signal(bus_dw) - pixbits = [] - for i in range(bus_dw//32): - for j in range(3): - b = (i*3+j)*8 - pixbits.append(self.frame.pixels[b+6:b+8]) - pixbits.append(self.frame.pixels[b:b+8]) - pixbits.append(0) - pixbits.append(0) - self.comb += memory_word.eq(Cat(*pixbits)) - - # bus accessor - self.submodules._bus_accessor = dma_lasmi.Writer(lasmim) - self.comb += [ - self._bus_accessor.address_data.a.eq(current_address), - self._bus_accessor.address_data.d.eq(memory_word) - ] - - # control FSM - fsm = FSM() - self.submodules += fsm - - fsm.act("WAIT_SOF", - reset_words.eq(1), - self.frame.ack.eq(~self._slot_array.address_valid | ~self.frame.sof), - If(self._slot_array.address_valid & self.frame.sof & self.frame.stb, NextState("TRANSFER_PIXELS")) - ) - fsm.act("TRANSFER_PIXELS", - self.frame.ack.eq(self._bus_accessor.address_data.ack), - If(self.frame.stb, - self._bus_accessor.address_data.stb.eq(1), - If(self._bus_accessor.address_data.ack, - count_word.eq(1), - If(last_word, NextState("EOF")) - ) - ) - ) - fsm.act("EOF", - If(~self._bus_accessor.busy, - self._slot_array.address_done.eq(1), - NextState("WAIT_SOF") - ) - ) - - def get_csrs(self): - return [self._frame_size] + self._slot_array.get_csrs() diff --git a/misoc/video/dvisampler/edid.py b/misoc/video/dvisampler/edid.py deleted file mode 100644 index eb674b3e..00000000 --- a/misoc/video/dvisampler/edid.py +++ /dev/null @@ -1,194 +0,0 @@ -from migen import * -from migen.fhdl.specials import Tristate -from migen.genlib.cdc import MultiReg -from migen.genlib.fsm import FSM, NextState -from migen.genlib.misc import chooser -from migen.bank.description import CSRStorage, CSRStatus, AutoCSR - -_default_edid = [ - 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x3D, 0x17, 0x32, 0x12, 0x2A, 0x6A, 0xBF, 0x00, - 0x05, 0x17, 0x01, 0x03, 0x80, 0x28, 0x1E, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0xB2, 0x0C, 0x00, 0x40, 0x41, 0x00, 0x26, 0x30, 0x18, 0x88, - 0x36, 0x00, 0x28, 0x1E, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x4D, 0x31, 0x20, - 0x44, 0x56, 0x49, 0x20, 0x6D, 0x69, 0x78, 0x65, 0x72, 0x0A, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, -] - - -class EDID(Module, AutoCSR): - def __init__(self, pads, default=_default_edid): - self._hpd_notif = CSRStatus() - self._hpd_en = CSRStorage() - self.specials.mem = Memory(8, 128, init=default) - - ### - - # HPD - if hasattr(pads, "hpd_notif"): - self.specials += MultiReg(pads.hpd_notif, self._hpd_notif.status) - else: - self.comb += self._hpd_notif.status.eq(1) - if hasattr(pads, "hpd_en"): - self.comb += pads.hpd_en.eq(self._hpd_en.storage) - - # EDID - scl_raw = Signal() - sda_i = Signal() - sda_raw = Signal() - sda_drv = Signal() - _sda_drv_reg = Signal() - _sda_i_async = Signal() - self.sync += _sda_drv_reg.eq(sda_drv) - self.specials += [ - MultiReg(pads.scl, scl_raw), - Tristate(pads.sda, 0, _sda_drv_reg, _sda_i_async), - MultiReg(_sda_i_async, sda_raw) - ] - - scl_i = Signal() - samp_count = Signal(6) - samp_carry = Signal() - self.sync += [ - Cat(samp_count, samp_carry).eq(samp_count + 1), - If(samp_carry, - scl_i.eq(scl_raw), - sda_i.eq(sda_raw) - ) - ] - - scl_r = Signal() - sda_r = Signal() - scl_rising = Signal() - sda_rising = Signal() - sda_falling = Signal() - self.sync += [ - scl_r.eq(scl_i), - sda_r.eq(sda_i) - ] - self.comb += [ - scl_rising.eq(scl_i & ~scl_r), - sda_rising.eq(sda_i & ~sda_r), - sda_falling.eq(~sda_i & sda_r) - ] - - start = Signal() - self.comb += start.eq(scl_i & sda_falling) - - din = Signal(8) - counter = Signal(max=9) - self.sync += [ - If(start, counter.eq(0)), - If(scl_rising, - If(counter == 8, - counter.eq(0) - ).Else( - counter.eq(counter + 1), - din.eq(Cat(sda_i, din[:7])) - ) - ) - ] - - is_read = Signal() - update_is_read = Signal() - self.sync += If(update_is_read, is_read.eq(din[0])) - - offset_counter = Signal(max=128) - oc_load = Signal() - oc_inc = Signal() - self.sync += [ - If(oc_load, - offset_counter.eq(din) - ).Elif(oc_inc, - offset_counter.eq(offset_counter + 1) - ) - ] - rdport = self.mem.get_port() - self.specials += rdport - self.comb += rdport.adr.eq(offset_counter) - data_bit = Signal() - - zero_drv = Signal() - data_drv = Signal() - self.comb += If(zero_drv, sda_drv.eq(1)).Elif(data_drv, sda_drv.eq(~data_bit)) - - data_drv_en = Signal() - data_drv_stop = Signal() - self.sync += If(data_drv_en, data_drv.eq(1)).Elif(data_drv_stop, data_drv.eq(0)) - self.sync += If(data_drv_en, chooser(rdport.dat_r, counter, data_bit, 8, reverse=True)) - - fsm = FSM() - self.submodules += fsm - - fsm.act("WAIT_START") - fsm.act("RCV_ADDRESS", - If(counter == 8, - If(din[1:] == 0x50, - update_is_read.eq(1), - NextState("ACK_ADDRESS0") - ).Else( - NextState("WAIT_START") - ) - ) - ) - fsm.act("ACK_ADDRESS0", - If(~scl_i, NextState("ACK_ADDRESS1")) - ) - fsm.act("ACK_ADDRESS1", - zero_drv.eq(1), - If(scl_i, NextState("ACK_ADDRESS2")) - ) - fsm.act("ACK_ADDRESS2", - zero_drv.eq(1), - If(~scl_i, - If(is_read, - NextState("READ") - ).Else( - NextState("RCV_OFFSET") - ) - ) - ) - - fsm.act("RCV_OFFSET", - If(counter == 8, - oc_load.eq(1), - NextState("ACK_OFFSET0") - ) - ) - fsm.act("ACK_OFFSET0", - If(~scl_i, NextState("ACK_OFFSET1")) - ) - fsm.act("ACK_OFFSET1", - zero_drv.eq(1), - If(scl_i, NextState("ACK_OFFSET2")) - ) - fsm.act("ACK_OFFSET2", - zero_drv.eq(1), - If(~scl_i, NextState("RCV_ADDRESS")) - ) - - fsm.act("READ", - If(~scl_i, - If(counter == 8, - data_drv_stop.eq(1), - NextState("ACK_READ") - ).Else( - data_drv_en.eq(1) - ) - ) - ) - fsm.act("ACK_READ", - If(scl_rising, - oc_inc.eq(1), - If(sda_i, - NextState("WAIT_START") - ).Else( - NextState("READ") - ) - ) - ) - - for state in fsm.actions.keys(): - fsm.act(state, If(start, NextState("RCV_ADDRESS"))) - fsm.act(state, If(~self._hpd_en.storage, NextState("WAIT_START"))) diff --git a/misoc/video/dvisampler/wer.py b/misoc/video/dvisampler/wer.py deleted file mode 100644 index 39d20289..00000000 --- a/misoc/video/dvisampler/wer.py +++ /dev/null @@ -1,60 +0,0 @@ -from migen import * -from migen.bank.description import * -from migen.genlib.misc import optree -from migen.genlib.cdc import PulseSynchronizer - -from misoc.video.dvisampler.common import control_tokens - - -class WER(Module, AutoCSR): - def __init__(self, period_bits=24): - self.data = Signal(10) - self._update = CSR() - self._value = CSRStatus(period_bits) - - ### - - # pipeline stage 1 - # we ignore the 10th (inversion) bit, as it is independent of the transition minimization - data_r = Signal(9) - self.sync.pix += data_r.eq(self.data[:9]) - - # pipeline stage 2 - transitions = Signal(8) - self.comb += [transitions[i].eq(data_r[i] ^ data_r[i+1]) for i in range(8)] - transition_count = Signal(max=9) - self.sync.pix += transition_count.eq(optree("+", [transitions[i] for i in range(8)])) - - is_control = Signal() - self.sync.pix += is_control.eq(optree("|", [data_r == ct for ct in control_tokens])) - - # pipeline stage 3 - is_error = Signal() - self.sync.pix += is_error.eq((transition_count > 4) & ~is_control) - - # counter - period_counter = Signal(period_bits) - period_done = Signal() - self.sync.pix += Cat(period_counter, period_done).eq(period_counter + 1) - - wer_counter = Signal(period_bits) - wer_counter_r = Signal(period_bits) - wer_counter_r_updated = Signal() - self.sync.pix += [ - wer_counter_r_updated.eq(period_done), - If(period_done, - wer_counter_r.eq(wer_counter), - wer_counter.eq(0) - ).Elif(is_error, - wer_counter.eq(wer_counter + 1) - ) - ] - - # sync to system clock domain - wer_counter_sys = Signal(period_bits) - self.submodules.ps_counter = PulseSynchronizer("pix", "sys") - self.comb += self.ps_counter.i.eq(wer_counter_r_updated) - self.sync += If(self.ps_counter.o, wer_counter_sys.eq(wer_counter_r)) - - # register interface - self.sync += If(self._update.re, self._value.status.eq(wer_counter_sys)) diff --git a/misoc/video/framebuffer/__init__.py b/misoc/video/framebuffer/__init__.py deleted file mode 100644 index 5071c943..00000000 --- a/misoc/video/framebuffer/__init__.py +++ /dev/null @@ -1,33 +0,0 @@ -from migen import * -from migen.flow.network import * -from migen.flow import plumbing -from migen.bank.description import AutoCSR -from migen.actorlib import structuring, misc - -from misoc.mem.sdram.frontend import dma_lasmi -from misoc.video.framebuffer.format import bpp, pixel_layout, FrameInitiator, VTG -from misoc.video.framebuffer.phy import Driver - - -class Framebuffer(Module, AutoCSR): - def __init__(self, pads_vga, pads_dvi, lasmim): - pack_factor = lasmim.dw//bpp - - g = DataFlowGraph() - - self.fi = FrameInitiator(lasmim.aw, pack_factor) - - intseq = misc.IntSequence(lasmim.aw, lasmim.aw) - dma_out = AbstractActor(plumbing.Buffer) - g.add_connection(self.fi, intseq, source_subr=self.fi.dma_subr()) - g.add_pipeline(intseq, AbstractActor(plumbing.Buffer), dma_lasmi.Reader(lasmim), dma_out) - - cast = structuring.Cast(lasmim.dw, pixel_layout(pack_factor), reverse_to=True) - vtg = VTG(pack_factor) - self.driver = Driver(pack_factor, pads_vga, pads_dvi) - - g.add_connection(self.fi, vtg, source_subr=self.fi.timing_subr, sink_ep="timing") - g.add_connection(dma_out, cast) - g.add_connection(cast, vtg, sink_ep="pixels") - g.add_connection(vtg, self.driver) - self.submodules += CompositeActor(g) diff --git a/misoc/video/framebuffer/dvi.py b/misoc/video/framebuffer/dvi.py deleted file mode 100644 index 03bb6acd..00000000 --- a/misoc/video/framebuffer/dvi.py +++ /dev/null @@ -1,224 +0,0 @@ -from migen import * -from migen.genlib.misc import optree - -control_tokens = [0b1101010100, 0b0010101011, 0b0101010100, 0b1010101011] - - -class Encoder(Module): - def __init__(self): - self.d = Signal(8) - self.c = Signal(2) - self.de = Signal() - - self.out = Signal(10) - - ### - - # stage 1 - count number of 1s in data - d = Signal(8) - n1d = Signal(max=9) - self.sync += [ - n1d.eq(optree("+", [self.d[i] for i in range(8)])), - d.eq(self.d) - ] - - # stage 2 - add 9th bit - q_m = Signal(9) - q_m8_n = Signal() - self.comb += q_m8_n.eq((n1d > 4) | ((n1d == 4) & ~d[0])) - for i in range(8): - if i: - curval = curval ^ d[i] ^ q_m8_n - else: - curval = d[0] - self.sync += q_m[i].eq(curval) - self.sync += q_m[8].eq(~q_m8_n) - - # stage 3 - count number of 1s and 0s in q_m[:8] - q_m_r = Signal(9) - n0q_m = Signal(max=9) - n1q_m = Signal(max=9) - self.sync += [ - n0q_m.eq(optree("+", [~q_m[i] for i in range(8)])), - n1q_m.eq(optree("+", [q_m[i] for i in range(8)])), - q_m_r.eq(q_m) - ] - - # stage 4 - final encoding - cnt = Signal((6, True)) - - s_c = self.c - s_de = self.de - for p in range(3): - new_c = Signal(2) - new_de = Signal() - self.sync += new_c.eq(s_c), new_de.eq(s_de) - s_c, s_de = new_c, new_de - - self.sync += If(s_de, - If((cnt == 0) | (n1q_m == n0q_m), - self.out[9].eq(~q_m_r[8]), - self.out[8].eq(q_m_r[8]), - If(q_m_r[8], - self.out[:8].eq(q_m_r[:8]), - cnt.eq(cnt + n1q_m - n0q_m) - ).Else( - self.out[:8].eq(~q_m_r[:8]), - cnt.eq(cnt + n0q_m - n1q_m) - ) - ).Else( - If((~cnt[5] & (n1q_m > n0q_m)) | (cnt[5] & (n0q_m > n1q_m)), - self.out[9].eq(1), - self.out[8].eq(q_m_r[8]), - self.out[:8].eq(~q_m_r[:8]), - cnt.eq(cnt + Cat(0, q_m_r[8]) + n0q_m - n1q_m) - ).Else( - self.out[9].eq(0), - self.out[8].eq(q_m_r[8]), - self.out[:8].eq(q_m_r[:8]), - cnt.eq(cnt - Cat(0, ~q_m_r[8]) + n1q_m - n0q_m) - ) - ) - ).Else( - self.out.eq(Array(control_tokens)[s_c]), - cnt.eq(0) - ) - - -class _EncoderSerializer(Module): - def __init__(self, serdesstrobe, pad_p, pad_n): - self.submodules.encoder = RenameClockDomains(Encoder(), "pix") - self.d, self.c, self.de = self.encoder.d, self.encoder.c, self.encoder.de - - ### - - # 2X soft serialization - ed_2x = Signal(5) - self.sync.pix2x += ed_2x.eq(Mux(ClockSignal("pix"), self.encoder.out[:5], self.encoder.out[5:])) - - # 5X hard serialization - cascade_di = Signal() - cascade_do = Signal() - cascade_ti = Signal() - cascade_to = Signal() - pad_se = Signal() - self.specials += [ - Instance("OSERDES2", - p_DATA_WIDTH=5, p_DATA_RATE_OQ="SDR", p_DATA_RATE_OT="SDR", - p_SERDES_MODE="MASTER", p_OUTPUT_MODE="DIFFERENTIAL", - - o_OQ=pad_se, - i_OCE=1, i_IOCE=serdesstrobe, i_RST=0, - i_CLK0=ClockSignal("pix10x"), i_CLK1=0, i_CLKDIV=ClockSignal("pix2x"), - i_D1=ed_2x[4], i_D2=0, i_D3=0, i_D4=0, - i_T1=0, i_T2=0, i_T3=0, i_T4=0, - i_TRAIN=0, i_TCE=1, - i_SHIFTIN1=1, i_SHIFTIN2=1, - i_SHIFTIN3=cascade_do, i_SHIFTIN4=cascade_to, - o_SHIFTOUT1=cascade_di, o_SHIFTOUT2=cascade_ti), - Instance("OSERDES2", - p_DATA_WIDTH=5, p_DATA_RATE_OQ="SDR", p_DATA_RATE_OT="SDR", - p_SERDES_MODE="SLAVE", p_OUTPUT_MODE="DIFFERENTIAL", - - i_OCE=1, i_IOCE=serdesstrobe, i_RST=0, - i_CLK0=ClockSignal("pix10x"), i_CLK1=0, i_CLKDIV=ClockSignal("pix2x"), - i_D1=ed_2x[0], i_D2=ed_2x[1], i_D3=ed_2x[2], i_D4=ed_2x[3], - i_T1=0, i_T2=0, i_T3=0, i_T4=0, - i_TRAIN=0, i_TCE=1, - i_SHIFTIN1=cascade_di, i_SHIFTIN2=cascade_ti, - i_SHIFTIN3=1, i_SHIFTIN4=1, - o_SHIFTOUT3=cascade_do, o_SHIFTOUT4=cascade_to), - Instance("OBUFDS", i_I=pad_se, o_O=pad_p, o_OB=pad_n) - ] - - -class PHY(Module): - def __init__(self, serdesstrobe, pads): - self.hsync = Signal() - self.vsync = Signal() - self.de = Signal() - self.r = Signal(8) - self.g = Signal(8) - self.b = Signal(8) - - ### - - self.submodules.es0 = _EncoderSerializer(serdesstrobe, pads.data0_p, pads.data0_n) - self.submodules.es1 = _EncoderSerializer(serdesstrobe, pads.data1_p, pads.data1_n) - self.submodules.es2 = _EncoderSerializer(serdesstrobe, pads.data2_p, pads.data2_n) - self.comb += [ - self.es0.d.eq(self.r), - self.es1.d.eq(self.g), - self.es2.d.eq(self.b), - self.es0.c.eq(Cat(self.hsync, self.vsync)), - self.es1.c.eq(0), - self.es2.c.eq(0), - self.es0.de.eq(self.de), - self.es1.de.eq(self.de), - self.es2.de.eq(self.de), - ] - - -class _EncoderTB(Module): - def __init__(self, inputs): - self.outs = [] - self._iter_inputs = iter(inputs) - self._end_cycle = None - self.submodules.dut = Encoder() - self.comb += self.dut.de.eq(1) - - def do_simulation(self, selfp): - if self._end_cycle is None: - try: - nv = next(self._iter_inputs) - except StopIteration: - self._end_cycle = selfp.simulator.cycle_counter + 4 - else: - selfp.dut.d = nv - if selfp.simulator.cycle_counter == self._end_cycle: - raise StopSimulation - if selfp.simulator.cycle_counter > 4: - self.outs.append(selfp.dut.out) - - -def _bit(i, n): - return (i >> n) & 1 - - -def _decode_tmds(b): - try: - c = control_tokens.index(b) - de = False - except ValueError: - c = 0 - de = True - vsync = bool(c & 2) - hsync = bool(c & 1) - - value = _bit(b, 0) ^ _bit(b, 9) - for i in range(1, 8): - value |= (_bit(b, i) ^ _bit(b, i-1) ^ (~_bit(b, 8) & 1)) << i - - return de, hsync, vsync, value - -if __name__ == "__main__": - from migen.sim.generic import run_simulation - from random import Random - - rng = Random(788) - test_list = [rng.randrange(256) for i in range(500)] - tb = _EncoderTB(test_list) - run_simulation(tb) - - check = [_decode_tmds(out)[3] for out in tb.outs] - assert(check == test_list) - - nb0 = 0 - nb1 = 0 - for out in tb.outs: - for i in range(10): - if _bit(out, i): - nb1 += 1 - else: - nb0 += 1 - print("0/1: {}/{} ({:.2f})".format(nb0, nb1, nb0/nb1)) diff --git a/misoc/video/framebuffer/format.py b/misoc/video/framebuffer/format.py deleted file mode 100644 index 210ec5ce..00000000 --- a/misoc/video/framebuffer/format.py +++ /dev/null @@ -1,150 +0,0 @@ -from migen import * -from migen.flow.actor import * -from migen.bank.description import CSRStorage -from migen.genlib.record import Record -from migen.genlib.fsm import FSM, NextState -from migen.actorlib import spi - -_hbits = 12 -_vbits = 12 - -bpp = 32 -bpc = 10 -pixel_layout_s = [ - ("pad", bpp-3*bpc), - ("r", bpc), - ("g", bpc), - ("b", bpc) -] - - -def pixel_layout(pack_factor): - return [("p"+str(i), pixel_layout_s) for i in range(pack_factor)] - -bpc_phy = 8 -phy_layout_s = [ - ("r", bpc_phy), - ("g", bpc_phy), - ("b", bpc_phy) -] - - -def phy_layout(pack_factor): - r = [("hsync", 1), ("vsync", 1), ("de", 1)] - for i in range(pack_factor): - r.append(("p"+str(i), phy_layout_s)) - return r - - -class FrameInitiator(spi.SingleGenerator): - def __init__(self, bus_aw, pack_factor, ndmas=1): - h_alignment_bits = log2_int(pack_factor) - hbits_dyn = _hbits - h_alignment_bits - bus_alignment_bits = h_alignment_bits + log2_int(bpp//8) - layout = [ - ("hres", hbits_dyn, 640, h_alignment_bits), - ("hsync_start", hbits_dyn, 656, h_alignment_bits), - ("hsync_end", hbits_dyn, 752, h_alignment_bits), - ("hscan", hbits_dyn, 800, h_alignment_bits), - - ("vres", _vbits, 480), - ("vsync_start", _vbits, 492), - ("vsync_end", _vbits, 494), - ("vscan", _vbits, 525), - - ("length", bus_aw + bus_alignment_bits, 640*480*bpp//8, bus_alignment_bits) - ] - layout += [("base"+str(i), bus_aw + bus_alignment_bits, 0, bus_alignment_bits) - for i in range(ndmas)] - spi.SingleGenerator.__init__(self, layout, spi.MODE_CONTINUOUS) - - timing_subr = ["hres", "hsync_start", "hsync_end", "hscan", - "vres", "vsync_start", "vsync_end", "vscan"] - - def dma_subr(self, i=0): - return ["length", "base"+str(i)] - - -class VTG(Module): - def __init__(self, pack_factor): - hbits_dyn = _hbits - log2_int(pack_factor) - timing_layout = [ - ("hres", hbits_dyn), - ("hsync_start", hbits_dyn), - ("hsync_end", hbits_dyn), - ("hscan", hbits_dyn), - ("vres", _vbits), - ("vsync_start", _vbits), - ("vsync_end", _vbits), - ("vscan", _vbits)] - self.timing = Sink(timing_layout) - self.pixels = Sink(pixel_layout(pack_factor)) - self.phy = Source(phy_layout(pack_factor)) - self.busy = Signal() - - ### - - hactive = Signal() - vactive = Signal() - active = Signal() - - hcounter = Signal(hbits_dyn) - vcounter = Signal(_vbits) - - skip = bpc - bpc_phy - self.comb += [ - active.eq(hactive & vactive), - If(active, - [getattr(getattr(self.phy.payload, p), c).eq(getattr(getattr(self.pixels.payload, p), c)[skip:]) - for p in ["p"+str(i) for i in range(pack_factor)] for c in ["r", "g", "b"]], - self.phy.de.eq(1) - ), - self.pixels.ack.eq(self.phy.ack & active) - ] - - load_timing = Signal() - tr = Record(timing_layout) - self.sync += If(load_timing, tr.eq(self.timing.payload)) - - generate_en = Signal() - generate_frame_done = Signal() - self.sync += [ - generate_frame_done.eq(0), - If(generate_en, - hcounter.eq(hcounter + 1), - - If(hcounter == 0, hactive.eq(1)), - If(hcounter == tr.hres, hactive.eq(0)), - If(hcounter == tr.hsync_start, self.phy.hsync.eq(1)), - If(hcounter == tr.hsync_end, self.phy.hsync.eq(0)), - If(hcounter == tr.hscan, - hcounter.eq(0), - If(vcounter == tr.vscan, - vcounter.eq(0), - generate_frame_done.eq(1) - ).Else( - vcounter.eq(vcounter + 1) - ) - ), - - If(vcounter == 0, vactive.eq(1)), - If(vcounter == tr.vres, vactive.eq(0)), - If(vcounter == tr.vsync_start, self.phy.vsync.eq(1)), - If(vcounter == tr.vsync_end, self.phy.vsync.eq(0)) - ) - ] - - self.submodules.fsm = FSM() - self.fsm.act("GET_TIMING", - self.timing.ack.eq(1), - load_timing.eq(1), - If(self.timing.stb, NextState("GENERATE")) - ) - self.fsm.act("GENERATE", - self.busy.eq(1), - If(~active | self.pixels.stb, - self.phy.stb.eq(1), - If(self.phy.ack, generate_en.eq(1)) - ), - If(generate_frame_done, NextState("GET_TIMING")) - ) diff --git a/misoc/video/framebuffer/phy.py b/misoc/video/framebuffer/phy.py deleted file mode 100644 index 2bb128fb..00000000 --- a/misoc/video/framebuffer/phy.py +++ /dev/null @@ -1,222 +0,0 @@ -from migen import * -from migen.genlib.fifo import AsyncFIFO -from migen.genlib.cdc import MultiReg -from migen.bank.description import * -from migen.flow.actor import * - -from misoc.video.framebuffer.format import bpc_phy, phy_layout -from misoc.video.framebuffer import dvi - - -class _FIFO(Module): - def __init__(self, pack_factor): - self.phy = Sink(phy_layout(pack_factor)) - self.busy = Signal() - - self.pix_hsync = Signal() - self.pix_vsync = Signal() - self.pix_de = Signal() - self.pix_r = Signal(bpc_phy) - self.pix_g = Signal(bpc_phy) - self.pix_b = Signal(bpc_phy) - - ### - - fifo = RenameClockDomains(AsyncFIFO(phy_layout(pack_factor), 512), - {"write": "sys", "read": "pix"}) - self.submodules += fifo - self.comb += [ - self.phy.ack.eq(fifo.writable), - fifo.we.eq(self.phy.stb), - fifo.din.eq(self.phy.payload), - self.busy.eq(0) - ] - - unpack_counter = Signal(max=pack_factor) - assert(pack_factor & (pack_factor - 1) == 0) # only support powers of 2 - self.sync.pix += [ - unpack_counter.eq(unpack_counter + 1), - self.pix_hsync.eq(fifo.dout.hsync), - self.pix_vsync.eq(fifo.dout.vsync), - self.pix_de.eq(fifo.dout.de) - ] - for i in range(pack_factor): - pixel = getattr(fifo.dout, "p"+str(i)) - self.sync.pix += If(unpack_counter == i, - self.pix_r.eq(pixel.r), - self.pix_g.eq(pixel.g), - self.pix_b.eq(pixel.b) - ) - self.comb += fifo.re.eq(unpack_counter == (pack_factor - 1)) - - -# This assumes a 50MHz base clock -class _Clocking(Module, AutoCSR): - def __init__(self, pads_vga, pads_dvi): - self._cmd_data = CSRStorage(10) - self._send_cmd_data = CSR() - self._send_go = CSR() - self._status = CSRStatus(4) - - self.clock_domains.cd_pix = ClockDomain(reset_less=True) - if pads_dvi is not None: - self._pll_reset = CSRStorage() - self._pll_adr = CSRStorage(5) - self._pll_dat_r = CSRStatus(16) - self._pll_dat_w = CSRStorage(16) - self._pll_read = CSR() - self._pll_write = CSR() - self._pll_drdy = CSRStatus() - - self.clock_domains.cd_pix2x = ClockDomain(reset_less=True) - self.clock_domains.cd_pix10x = ClockDomain(reset_less=True) - self.serdesstrobe = Signal() - - ### - - # Generate 1x pixel clock - clk_pix_unbuffered = Signal() - pix_progdata = Signal() - pix_progen = Signal() - pix_progdone = Signal() - pix_locked = Signal() - self.specials += Instance("DCM_CLKGEN", - p_CLKFXDV_DIVIDE=2, p_CLKFX_DIVIDE=4, p_CLKFX_MD_MAX=1.0, p_CLKFX_MULTIPLY=2, - p_CLKIN_PERIOD=20.0, p_SPREAD_SPECTRUM="NONE", p_STARTUP_WAIT="FALSE", - - i_CLKIN=ClockSignal("base50"), o_CLKFX=clk_pix_unbuffered, - i_PROGCLK=ClockSignal(), i_PROGDATA=pix_progdata, i_PROGEN=pix_progen, - o_PROGDONE=pix_progdone, o_LOCKED=pix_locked, - i_FREEZEDCM=0, i_RST=ResetSignal()) - - remaining_bits = Signal(max=11) - transmitting = Signal() - self.comb += transmitting.eq(remaining_bits != 0) - sr = Signal(10) - self.sync += [ - If(self._send_cmd_data.re, - remaining_bits.eq(10), - sr.eq(self._cmd_data.storage) - ).Elif(transmitting, - remaining_bits.eq(remaining_bits - 1), - sr.eq(sr[1:]) - ) - ] - self.comb += [ - pix_progdata.eq(transmitting & sr[0]), - pix_progen.eq(transmitting | self._send_go.re) - ] - - # enforce gap between commands - busy_counter = Signal(max=14) - busy = Signal() - self.comb += busy.eq(busy_counter != 0) - self.sync += If(self._send_cmd_data.re, - busy_counter.eq(13) - ).Elif(busy, - busy_counter.eq(busy_counter - 1) - ) - - mult_locked = Signal() - self.comb += self._status.status.eq(Cat(busy, pix_progdone, pix_locked, mult_locked)) - - # Clock multiplication and buffering - if pads_dvi is None: - # Just buffer 1x pixel clock - self.specials += Instance("BUFG", i_I=clk_pix_unbuffered, o_O=self.cd_pix.clk) - self.comb += mult_locked.eq(pix_locked) - else: - # Route unbuffered 1x pixel clock to PLL - # Generate 1x, 2x and 10x IO pixel clocks - clkfbout = Signal() - pll_locked = Signal() - pll_clk0 = Signal() - pll_clk1 = Signal() - pll_clk2 = Signal() - locked_async = Signal() - pll_drdy = Signal() - self.sync += If(self._pll_read.re | self._pll_write.re, - self._pll_drdy.status.eq(0) - ).Elif(pll_drdy, - self._pll_drdy.status.eq(1) - ) - self.specials += [ - Instance("PLL_ADV", - p_CLKFBOUT_MULT=10, - p_CLKOUT0_DIVIDE=1, # pix10x - p_CLKOUT1_DIVIDE=5, # pix2x - p_CLKOUT2_DIVIDE=10, # pix - p_COMPENSATION="INTERNAL", - - i_CLKINSEL=1, - i_CLKIN1=clk_pix_unbuffered, - o_CLKOUT0=pll_clk0, o_CLKOUT1=pll_clk1, o_CLKOUT2=pll_clk2, - o_CLKFBOUT=clkfbout, i_CLKFBIN=clkfbout, - o_LOCKED=pll_locked, - i_RST=~pix_locked | self._pll_reset.storage, - - i_DADDR=self._pll_adr.storage, - o_DO=self._pll_dat_r.status, - i_DI=self._pll_dat_w.storage, - i_DEN=self._pll_read.re | self._pll_write.re, - i_DWE=self._pll_write.re, - o_DRDY=pll_drdy, - i_DCLK=ClockSignal()), - Instance("BUFPLL", p_DIVIDE=5, - i_PLLIN=pll_clk0, i_GCLK=ClockSignal("pix2x"), i_LOCKED=pll_locked, - o_IOCLK=self.cd_pix10x.clk, o_LOCK=locked_async, o_SERDESSTROBE=self.serdesstrobe), - Instance("BUFG", i_I=pll_clk1, o_O=self.cd_pix2x.clk), - Instance("BUFG", name="dviout_pix_bufg", i_I=pll_clk2, o_O=self.cd_pix.clk), - MultiReg(locked_async, mult_locked, "sys") - ] - - # Drive VGA/DVI clock pads - if pads_vga is not None: - self.specials += Instance("ODDR2", - p_DDR_ALIGNMENT="NONE", p_INIT=0, p_SRTYPE="SYNC", - o_Q=pads_vga.clk, - i_C0=ClockSignal("pix"), - i_C1=~ClockSignal("pix"), - i_CE=1, i_D0=1, i_D1=0, - i_R=0, i_S=0) - if pads_dvi is not None: - dvi_clk_se = Signal() - self.specials += Instance("ODDR2", - p_DDR_ALIGNMENT="NONE", p_INIT=0, p_SRTYPE="SYNC", - o_Q=dvi_clk_se, - i_C0=ClockSignal("pix"), - i_C1=~ClockSignal("pix"), - i_CE=1, i_D0=1, i_D1=0, - i_R=0, i_S=0) - self.specials += Instance("OBUFDS", i_I=dvi_clk_se, - o_O=pads_dvi.clk_p, o_OB=pads_dvi.clk_n) - - -class Driver(Module, AutoCSR): - def __init__(self, pack_factor, pads_vga, pads_dvi): - fifo = _FIFO(pack_factor) - self.submodules += fifo - self.phy = fifo.phy - self.busy = fifo.busy - - self.submodules.clocking = _Clocking(pads_vga, pads_dvi) - - if pads_vga is not None: - self.comb += [ - pads_vga.hsync_n.eq(~fifo.pix_hsync), - pads_vga.vsync_n.eq(~fifo.pix_vsync), - pads_vga.r.eq(fifo.pix_r), - pads_vga.g.eq(fifo.pix_g), - pads_vga.b.eq(fifo.pix_b), - pads_vga.psave_n.eq(1) - ] - if pads_dvi is not None: - self.submodules.dvi_phy = dvi.PHY(self.clocking.serdesstrobe, pads_dvi) - self.comb += [ - self.dvi_phy.hsync.eq(fifo.pix_hsync), - self.dvi_phy.vsync.eq(fifo.pix_vsync), - self.dvi_phy.de.eq(fifo.pix_de), - self.dvi_phy.r.eq(fifo.pix_r), - self.dvi_phy.g.eq(fifo.pix_g), - self.dvi_phy.b.eq(fifo.pix_b) - ] diff --git a/setup.py b/setup.py index e73cf8ca..ba67556e 100644 --- a/setup.py +++ b/setup.py @@ -5,10 +5,9 @@ from setuptools import setup from setuptools import find_packages -required_version = (3, 3) -if sys.version_info < required_version: - raise SystemExit("MiSoC requires python {0} or greater".format( - ".".join(map(str, required_version)))) +if sys.version_info[:3] < (3, 3): + raise SystemExit("You need Python 3.3+") + setup( name="misoc",