--- /dev/null
+Unless otherwise noted, LiteScope 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.
- _____ _ ____ _ _ _ _
- | __|___ |_|___ _ _ | \|_|___|_| |_ ___| |
- | __| | | | . | | | | | | | . | | _| .'| |
- |_____|_|_|_| |___|_ | |____/|_|_ |_|_| |__,|_|
- |___| |___| |___|
-
- Copyright 2012-2014 / Florent Kermarrec / florent@enjoy-digital.fr
-
- Miscope
---------------------------------------------------------------------------------
-
-[> Miscope
-------------
-
-Miscope is a small logic analyzer to embed in an FPGA.
-
-While free vendor toolchains are generally used by beginners or for prototyping
-(situations where having a logic analyzer in the design is generally helpful)
-free toolchains are always provided without the proprietary logic analyzer
-solution... :(
-
-Baseid on Migen, Miscope aims to provide a free, portable and flexible
+ __ _ __ ____
+ / / (_) /____ / __/______ ___ ___
+ / /__/ / __/ -_)\ \/ __/ _ \/ _ \/ -_)
+ /____/_/\__/\__/___/\__/\___/ .__/\__/
+ /_/
+ Copyright 2012-2015 / EnjoyDigital
+ florent@enjoy-digital.fr
+
+ A small footprint and configurable embedded FPGA
+ logic analyzer core by EnjoyDigital
+
+[> Intro
+---------
+LiteScope small footprint and configurable embedded logic analyzer that you
+can use in your FPGA and aims to provide a a free, portable and flexible
alternatve to vendor's solutions!
-[> Specification:
+LiteScope is part of LiteX libraries whose aims is to lower entry level of complex
+FPGA IP cores by providing simple, elegant and efficient implementations of
+components used in today's SoC such as Ethernet, SATA, PCIe, SDRAM Controller...
+
+The core uses simple and specific streaming buses and will provides in the future
+adapters to use standardized AXI or Avalon-ST streaming buses.
+
+Since Python is used to describe the HDL, the core is highly and easily
+configurable.
+
+LiteScope uses technologies developed in partnership with M-Labs Ltd:
+ - Migen enables generating HDL with Python in an efficient way.
+ - MiSoC provides the basic blocks to build a powerful and small footprint SoC.
+
+LiteScope can be used as a Migen/MiSoC library (by simply installing it
+with the provided setup.py) or can be integrated with your standard design flow
+by generating the verilog rtl that you will use as a standard core.
+
+LiteScope produces "vcd" files that can be read in your regular waveforms viewer.
+
+Since LiteScope also provides an UART <--> Wishbone brige you only need 2 external
+Rx/Tx pins to be ready to debug or control all your Wishbone peripherals!
+
+[> Features
+-----------
+- IO peek and poke with LiteScopeIO.
+- Logic analyser with LiteScopeLA:
+ - Various triggering modules: Term, Range, Edge (add yours! :)
+ - Run Length Encoder to "compress" data and increase recording depth
+ - Data storage in block rams
+
+
+[> Possibles improvements
+-------------------------
+- add standardized interfaces (AXI, Avalon-ST)
+- add storage in DRAM
+- add storage in HDD with LiteSATA core (https://github.com/enjoy-digital/litesata)
+- add Ethernet Wishbone bridge
+- add PCIe Wishbone bridge with LitePCIe (to be released soon!)
+- ... 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.
+
+
+[> Getting started
+------------------
+1. Install Python3 and Xilinx's Vivado software
+
+2. Obtain Migen and install it:
+ git clone https://github.com/m-labs/migen
+ cd migen
+ python3 setup.py install
+ cd ..
+
+3. Obtain Miscope and install it:
+ git clone https://github.com/m-labs/miscope
+ cd miscope
+ python3 setup.py install
+ cd ..
+
+4. Obtain MiSoC:
+ git clone https://github.com/m-labs/misoc --recursive
+ XXX add setup.py to MiSoC for external use of misoclib?
+
+5. Obtain LiteScope
+ git clone https://github.com/enjoy-digital/litescope
+
+6. Build and load test design (only for KC705 for now):
+ python3 make.py -s [platform] all
+ Supported platform are the supported platform of Mibuild:
+ de0nano, m1, mixxeo, kc705, zedboard...
+
+7. Test design:
+ go to ./test directory and run:
+ python3 test_io.py
+ python3 test_la.py
+
+[> Simulations:
+ XXX convert simulations
+
+[> Tests :
+ XXX convert tests
-Miscope provides Migen cores to embed in the design and Python drivers to control
-the logic analyzer from the Host. Miscope automatically interconnects all cores
-to a CSR bus. When using Python on the Host, no needs to worry about cores register
-mapping, importing miscope project gives you direct access to all the cores!
+[> License
+-----------
+LiteScope is released under the very permissive two-clause BSD license. Under the
+terms of this license, you are authorized to use LiteScope 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 LiteScope
+ - cite LiteScope 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 LiteScope.
-Miscope produces .vcd output files to be analyzed in your favorite waveform viewer.
+[> Support and Consulting
+--------------------------
+We love open-source hardware and like sharing our designs with others.
-Since Miscope also provides an Uart2Wishbone bridge, you only need 2 external Rx/Tx
-pins to be ready to debug!
+LiteScope is developed and maintained by EnjoyDigital.
-[> Status:
-MiIo & Mila working on board with standard term.
-RLE working on board.
-RangeDetector and EdgeDector terms not tested.
+If you would like to know more about LiteScope 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.
-[> Examples:
-Have a look at http://github.com/Florent-Kermarrec/misoc-de0nano
-test_miio.py : Led & Switch Test controlled by Python Host.
-test_mila.py : Logic Analyzer controlled by Python Host.
+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@enjoy-digital.fr
+E-mail: florent [AT] enjoy-digital.fr
\ No newline at end of file
--- /dev/null
+from migen.fhdl.std import *
+from migen.genlib.record import *
+from migen.genlib.fsm import FSM, NextState
+from migen.genlib.misc import chooser
+from migen.bank.description import *
+from migen.bus import wishbone
+
+from misoclib.uart import UARTRX, UARTTX
+
+from litescope.common import *
+
+class UART(Module, AutoCSR):
+ def __init__(self, pads, clk_freq, baud=115200):
+ self._tuning_word = CSRStorage(32, reset=int((baud/clk_freq)*2**32))
+ tuning_word = self._tuning_word.storage
+
+ ###
+
+ self.rx = UARTRX(pads, tuning_word)
+ self.tx = UARTTX(pads, tuning_word)
+ self.submodules += self.rx, self.tx
+
+class UARTPads:
+ def __init__(self):
+ self.rx = Signal()
+ self.tx = Signal()
+
+class UARTMux(Module):
+ def __init__(self, pads):
+ self.sel = Signal(max=2)
+ self.shared_pads = UARTPads()
+ self.bridge_pads = UARTPads()
+
+ ###
+ # Route rx pad:
+ # when sel==0, route it to shared rx and bridge rx
+ # when sel==1, route it only to bridge rx
+ self.comb += \
+ If(self.sel==0,
+ self.shared_pads.rx.eq(pads.rx),
+ self.bridge_pads.rx.eq(pads.rx)
+ ).Else(
+ self.bridge_pads.rx.eq(pads.rx)
+ )
+
+ # Route tx:
+ # when sel==0, route shared tx to pads tx
+ # when sel==1, route bridge tx to pads tx
+ self.comb += \
+ If(self.sel==0,
+ pads.tx.eq(self.shared_pads.tx)
+ ).Else(
+ pads.tx.eq(self.bridge_pads.tx)
+ )
+
+class LiteScopeUART2WB(Module, AutoCSR):
+ cmds = {
+ "write" : 0x01,
+ "read" : 0x02
+ }
+ def __init__(self, pads, clk_freq, baud=115200, share_uart=False):
+ self.wishbone = wishbone.Interface()
+ if share_uart:
+ self._sel = CSRStorage()
+ ###
+ if share_uart:
+ uart_mux = UARTMux(pads)
+ uart = UART(uart_mux.bridge_pads, clk_freq, baud)
+ self.submodules += uart_mux, uart
+ self.shared_pads = uart_mux.shared_pads
+ self.comb += uart_mux.sel.eq(self._sel.storage)
+ else:
+ uart = UART(pads, clk_freq, baud)
+ self.submodules += uart
+
+ byte_counter = Counter(bits_sign=3)
+ word_counter = Counter(bits_sign=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(uart.rx.source.d)),
+ If(length_ce, length.eq(uart.rx.source.d)),
+ If(address_ce, address.eq(Cat(uart.rx.source.d, address[0:24]))),
+ If(rx_data_ce,
+ data.eq(Cat(uart.rx.source.d, data[0:24]))
+ ).Elif(tx_data_ce,
+ data.eq(self.wishbone.dat_r)
+ )
+ ]
+
+ ###
+ fsm = InsertReset(FSM(reset_state="IDLE"))
+ timeout = Timeout(clk_freq//10)
+ self.submodules += fsm, timeout
+ self.comb += [
+ timeout.ce.eq(1),
+ fsm.reset.eq(timeout.reached)
+ ]
+ fsm.act("IDLE",
+ timeout.reset.eq(1),
+ If(uart.rx.source.stb,
+ cmd_ce.eq(1),
+ If( (uart.rx.source.d == self.cmds["write"]) |
+ (uart.rx.source.d == self.cmds["read"]),
+ NextState("RECEIVE_LENGTH")
+ ),
+ byte_counter.reset.eq(1),
+ word_counter.reset.eq(1)
+ )
+ )
+ fsm.act("RECEIVE_LENGTH",
+ If(uart.rx.source.stb,
+ length_ce.eq(1),
+ NextState("RECEIVE_ADDRESS")
+ )
+ )
+ fsm.act("RECEIVE_ADDRESS",
+ If(uart.rx.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(uart.rx.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, uart.tx.sink.d, n=4, reverse=True)
+ fsm.act("SEND_DATA",
+ uart.tx.sink.stb.eq(1),
+ If(uart.tx.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)
+ )
+ )
+ )
+ )
--- /dev/null
+from migen.genlib.record import *
+
+def dat_layout(dw):
+ return [
+ ("stb", 1, DIR_M_TO_S),
+ ("dat", dw, DIR_M_TO_S)
+ ]
+
+def hit_layout():
+ return [
+ ("stb", 1, DIR_M_TO_S),
+ ("hit", 1, DIR_M_TO_S)
+ ]
+
+@DecorateModule(InsertReset)
+@DecorateModule(InsertCE)
+class Counter(Module):
+ def __init__(self, signal=None, **kwargs):
+ if signal is None:
+ self.value = Signal(**kwargs)
+ else:
+ self.value = signal
+ self.width = flen(self.value)
+ self.sync += self.value.eq(self.value+1)
+
+@DecorateModule(InsertReset)
+@DecorateModule(InsertCE)
+class Timeout(Module):
+ def __init__(self, length):
+ self.reached = Signal()
+ ###
+ value = Signal(max=length)
+ self.sync += value.eq(value+1)
+ self.comb += [
+ self.reached.eq(value == length)
+ ]
--- /dev/null
+from migen.fhdl.std import *
+from migen.bank.description import *
+from migen.genlib.fifo import SyncFIFOBuffered as SyncFIFO
+from migen.genlib.fsm import FSM, NextState
+from migen.genlib.record import *
+
+from litescope.common import *
+
+class LiteScopeRunLengthEncoder(Module, AutoCSR):
+ def __init__(self, width, length=1024):
+ self.width = width
+ self.length = length
+
+ self.sink = Record(dat_layout(width))
+ self.source = Record(dat_layout(width))
+
+ self._enable = CSRStorage()
+
+ ###
+
+ enable = self._enable.storage
+
+ sink_d = Record(dat_layout(width))
+ self.sync += If(self.sink.stb, sink_d.eq(self.sink))
+
+ cnt = Signal(max=length)
+ cnt_inc = Signal()
+ cnt_reset = Signal()
+ cnt_max = Signal()
+
+ self.sync += \
+ If(cnt_reset,
+ cnt.eq(1),
+ ).Elif(cnt_inc,
+ cnt.eq(cnt+1)
+ )
+ self.comb += cnt_max.eq(cnt == length)
+
+ change = Signal()
+ self.comb += change.eq(self.sink.stb & (self.sink.dat != sink_d.dat))
+
+ fsm = FSM(reset_state="BYPASS")
+ self.submodules += fsm
+
+ fsm.act("BYPASS",
+ sink_d.connect(self.source),
+ cnt_reset.eq(1),
+ If(enable & ~change & self.sink.stb, NextState("COUNT"))
+ )
+
+ fsm.act("COUNT",
+ cnt_inc.eq(self.sink.stb),
+ If(change | cnt_max | ~enable,
+ self.source.stb.eq(1),
+ self.source.dat[width-1].eq(1), # Set RLE bit
+ self.source.dat[:flen(cnt)].eq(cnt),
+ NextState("BYPASS")
+ )
+ ),
+
+class LiteScopeRecorder(Module, AutoCSR):
+ def __init__(self, width, depth):
+ self.width = width
+
+ self.trig_sink = Record(hit_layout())
+ self.dat_sink = Record(dat_layout(width))
+
+ self._trigger = CSR()
+ self._length = CSRStorage(bits_for(depth))
+ self._offset = CSRStorage(bits_for(depth))
+ self._done = CSRStatus()
+
+ self._read_en = CSR()
+ self._read_empty = CSRStatus()
+ self._read_dat = CSRStatus(width)
+
+ ###
+
+ fifo = InsertReset(SyncFIFO(width, depth))
+ self.submodules += fifo
+
+ fsm = FSM(reset_state="IDLE")
+ self.submodules += fsm
+
+
+ self.comb += [
+ self._read_empty.status.eq(~fifo.readable),
+ self._read_dat.status.eq(fifo.dout),
+ ]
+
+ fsm.act("IDLE",
+ If(self._trigger.re & self._trigger.r,
+ NextState("PRE_HIT_RECORDING"),
+ fifo.reset.eq(1),
+ ),
+ fifo.re.eq(self._read_en.re & self._read_en.r),
+ self._done.status.eq(1)
+ )
+
+ fsm.act("PRE_HIT_RECORDING",
+ fifo.we.eq(self.dat_sink.stb),
+ fifo.din.eq(self.dat_sink.dat),
+
+ fifo.re.eq(fifo.level >= self._offset.storage),
+
+ If(self.trig_sink.stb & self.trig_sink.hit, NextState("POST_HIT_RECORDING"))
+ )
+
+ fsm.act("POST_HIT_RECORDING",
+ fifo.we.eq(self.dat_sink.stb),
+ fifo.din.eq(self.dat_sink.dat),
+
+ If(~fifo.writable | (fifo.level >= self._length.storage), NextState("IDLE"))
+ )
--- /dev/null
+from migen.fhdl.std import *
+from migen.fhdl.specials import Memory
+from migen.bank.description import *
+from migen.genlib.record import *
+
+from litescope.common import *
+
+class LiteScopeTerm(Module, AutoCSR):
+ def __init__(self, width):
+ self.width = width
+
+ self.sink = Record(dat_layout(width))
+ self.source = Record(hit_layout())
+
+ self._trig = CSRStorage(width)
+ self._mask = CSRStorage(width)
+
+ ###
+
+ trig = self._trig.storage
+ mask = self._mask.storage
+ dat = self.sink.dat
+ hit = self.source.hit
+
+ self.comb += [
+ hit.eq((dat & mask) == trig),
+ self.source.stb.eq(self.sink.stb)
+ ]
+
+class LiteScopeRangeDetector(Module, AutoCSR):
+ def __init__(self, width):
+ self.width = width
+
+ self.sink = Record(dat_layout(width))
+ self.source = Record(hit_layout())
+
+ self._low = CSRStorage(width)
+ self._high = CSRStorage(width)
+
+ ###
+
+ low = self._low.storage
+ high = self._high.storage
+ dat = self.sink.dat
+ hit = self.source.hit
+
+ self.comb += [
+ hit.eq((dat >= low) & (dat <= high)),
+ self.source.stb.eq(self.sink.stb)
+ ]
+
+class LiteScopeEdgeDetector(Module, AutoCSR):
+ def __init__(self, width):
+ self.width = width
+
+ self.sink = Record(dat_layout(width))
+ self.source = Record(hit_layout())
+
+ self._rising_mask = CSRStorage(width)
+ self._falling_mask = CSRStorage(width)
+ self._both_mask = CSRStorage(width)
+
+ ###
+
+ rising_mask = self._rising_mask.storage
+ falling_mask = self._falling_mask.storage
+ both_mask = self._both_mask.storage
+
+ dat = self.sink.dat
+ dat_d = Signal(width)
+ rising_hit = Signal()
+ falling_hit = Signal()
+ both_hit = Signal()
+ hit = self.source.hit
+
+ self.sync += dat_d.eq(dat)
+
+ self.comb += [
+ rising_hit.eq(rising_mask & dat & ~dat_d),
+ falling_hit.eq(rising_mask & ~dat & dat_d),
+ both_hit.eq((both_mask & dat) != (both_mask & dat_d)),
+ hit.eq(rising_hit | falling_hit | both_hit),
+ self.source.stb.eq(self.sink.stb)
+ ]
+
+class LiteScopeSum(Module, AutoCSR):
+ def __init__(self, ports=4):
+
+ self.sinks = [Record(hit_layout()) for p in range(ports)]
+ self.source = Record(hit_layout())
+
+ self._prog_we = CSRStorage()
+ self._prog_adr = CSRStorage(ports) #FIXME
+ self._prog_dat = CSRStorage()
+
+ mem = Memory(1, 2**ports)
+ lut_port = mem.get_port()
+ prog_port = mem.get_port(write_capable=True)
+
+ self.specials += mem, lut_port, prog_port
+
+ ###
+
+ # Lut prog
+ self.comb += [
+ prog_port.we.eq(self._prog_we.storage),
+ prog_port.adr.eq(self._prog_adr.storage),
+ prog_port.dat_w.eq(self._prog_dat.storage)
+ ]
+
+ # Lut read
+ for i, sink in enumerate(self.sinks):
+ self.comb += lut_port.adr[i].eq(sink.hit)
+
+ # Drive source
+ self.comb += [
+ self.source.stb.eq(optree("&", [sink.stb for sink in self.sinks])),
+ self.source.hit.eq(lut_port.dat_r),
+ ]
+
+
+class LiteScopeTrigger(Module, AutoCSR):
+ def __init__(self, width, ports):
+ self.width = width
+ self.ports = ports
+
+ self.submodules.sum = LiteScopeSum(len(ports))
+ for i, port in enumerate(ports):
+ setattr(self.submodules, "port"+str(i), port)
+
+ self.sink = Record(dat_layout(width))
+ self.source = self.sum.source
+
+ ###
+
+ for i, port in enumerate(ports):
+ self.comb += [
+ self.sink.connect(port.sink),
+ port.source.connect(self.sum.sinks[i])
+ ]
--- /dev/null
+from migen.fhdl.structure import *
+from migen.bank.description import *
+
+class LiteScopeIO(Module, AutoCSR):
+ def __init__(self, width):
+ self._r_i = CSRStatus(width)
+ self._r_o = CSRStorage(width)
+
+ self.i = self._r_i.status
+ self.o = self._r_o.storage
--- /dev/null
+from migen.fhdl.std import *
+from migen.fhdl import verilog
+from migen.bank.description import *
+from migen.actorlib.fifo import AsyncFIFO
+
+from litescope.common import *
+from litescope.core.trigger import LiteScopeTrigger
+from litescope.core.storage import LiteScopeRecorder, LiteScopeRunLengthEncoder
+
+from mibuild.tools import write_to_file
+
+def _getattr_all(l, attr):
+ it = iter(l)
+ r = getattr(next(it), attr)
+ for e in it:
+ if getattr(e, attr) != r:
+ raise ValueError
+ return r
+
+class LiteScopeLA(Module, AutoCSR):
+ def __init__(self, depth, dat, with_rle=False, clk_domain="sys", pipe=False):
+ self.depth = depth
+ self.with_rle = with_rle
+ self.clk_domain = clk_domain
+ self.pipe = pipe
+ self.ports = []
+ self.width = flen(dat)
+
+ self.stb = Signal(reset=1)
+ self.dat = dat
+
+ def add_port(self, port_class):
+ port = port_class(self.width)
+ self.ports.append(port)
+
+ def do_finalize(self):
+ stb = self.stb
+ dat = self.dat
+ if self.pipe:
+ sync = getattr(self.sync, self.clk_domain)
+ stb_new = Signal()
+ dat_new = Signal(flen(dat))
+ sync += [
+ stb_new.eq(stb),
+ dat_new.eq(dat)
+ ]
+ stb = stb_new
+ dat = dat_new
+
+ if self.clk_domain is not "sys":
+ fifo = AsyncFIFO([("dat", self.width)], 32)
+ self.submodules += RenameClockDomains(fifo, {"write": self.clk_domain, "read": "sys"})
+ self.comb += [
+ fifo.sink.stb.eq(stb),
+ fifo.sink.dat.eq(dat)
+ ]
+ sink = Record(dat_layout(self.width))
+ self.comb += [
+ sink.stb.eq(fifo.source.stb),
+ sink.dat.eq(fifo.source.dat),
+ fifo.source.ack.eq(1)
+ ]
+ else:
+ sink = Record(dat_layout(self.width))
+ self.comb += [
+ sink.stb.eq(stb),
+ sink.dat.eq(dat)
+ ]
+
+ self.submodules.trigger = trigger = LiteScopeTrigger(self.width, self.ports)
+ self.submodules.recorder = recorder = LiteScopeRecorder(self.width, self.depth)
+ self.comb += [
+ sink.connect(trigger.sink),
+ trigger.source.connect(recorder.trig_sink)
+ ]
+
+ if self.with_rle:
+ self.submodules.rle = rle = LiteScopeRunLengthEncoder(self.width)
+ self.comb += [
+ sink.connect(rle.sink),
+ rle.source.connect(recorder.dat_sink)
+ ]
+ else:
+ self.comb += sink.connect(recorder.dat_sink)
+
+ def export(self, design, layout, filename):
+ ret, ns = verilog.convert(design, return_ns=True)
+ r = ""
+ def format_line(*args):
+ return ",".join(args) + "\n"
+
+ r += format_line("config", "width", str(self.width))
+ r += format_line("config", "depth", str(self.depth))
+ r += format_line("config", "with_rle", str(int(self.with_rle)))
+
+ for e in layout:
+ r += format_line("layout", ns.get_name(e), str(flen(e)))
+ write_to_file(filename, r)
--- /dev/null
+from migen.bank.description import CSRStatus
+
+def get_csr_csv(csr_base, bank_array):
+ r = ""
+ for name, csrs, mapaddr, rmap in bank_array.banks:
+ reg_base = csr_base + 0x800*mapaddr
+ for csr in csrs:
+ nr = (csr.size + 7)//8
+ r += "{}_{},0x{:08x},{},{}\n".format(name, csr.name, reg_base, nr, "ro" if isinstance(csr, CSRStatus) else "rw")
+ reg_base += 4*nr
+ return r
--- /dev/null
+import csv
+import time
+import sys
+import string
+import serial
+from struct import *
+from migen.fhdl.structure import *
+from litescope.host.reg import *
+from litescope.host.dump import *
+from litescope.host.truthtable import *
+
+def write_b(uart, data):
+ uart.write(pack('B',data))
+
+class LiteScopeUART2WBDriver:
+ WRITE_CMD = 0x01
+ READ_CMD = 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)
+ 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()
+ try:
+ self.regs.uart2wb_sel.write(1)
+ except:
+ pass
+
+ def close(self):
+ try:
+ self.regs.uart2wb_sel.write(0)
+ except:
+ pass
+ self.uart.flushOutput()
+ self.uart.close()
+
+ def read(self, addr, burst_length=1):
+ self.uart.flushInput()
+ write_b(self.uart, self.READ_CMD)
+ write_b(self.uart, burst_length)
+ addr = addr//4
+ write_b(self.uart, (addr & 0xff000000) >> 24)
+ write_b(self.uart, (addr & 0x00ff0000) >> 16)
+ write_b(self.uart, (addr & 0x0000ff00) >> 8)
+ write_b(self.uart, (addr & 0x000000ff))
+ values = []
+ for i in range(burst_length):
+ val = 0
+ for j in range(4):
+ val = val << 8
+ val |= ord(self.uart.read())
+ if self.debug:
+ print("RD %08X @ %08X" %(val, (addr+i)*4))
+ values.append(val)
+ if burst_length == 1:
+ return values[0]
+ else:
+ return values
+
+ def write(self, addr, data):
+ if isinstance(data, list):
+ burst_length = len(data)
+ else:
+ burst_length = 1
+ write_b(self.uart, self.WRITE_CMD)
+ write_b(self.uart, burst_length)
+ addr = addr//4
+ write_b(self.uart, (addr & 0xff000000) >> 24)
+ write_b(self.uart, (addr & 0x00ff0000) >> 16)
+ write_b(self.uart, (addr & 0x0000ff00) >> 8)
+ write_b(self.uart, (addr & 0x000000ff))
+ if isinstance(data, list):
+ for i in range(len(data)):
+ dat = data[i]
+ for j in range(4):
+ write_b(self.uart, (dat & 0xff000000) >> 24)
+ dat = dat << 8
+ if self.debug:
+ print("WR %08X @ %08X" %(data[i], (addr + i)*4))
+ else:
+ dat = data
+ for j in range(4):
+ write_b(self.uart, (dat & 0xff000000) >> 24)
+ dat = dat << 8
+ if self.debug:
+ print("WR %08X @ %08X" %(data, (addr * 4)))
+
+class LiteScopeIODriver():
+ def __init__(self, regs, name):
+ self.regs = regs
+ self.name = name
+ self.build()
+
+ def build(self):
+ for key, value in self.regs.d.items():
+ if self.name in key:
+ key.replace(self.name +"_")
+ setattr(self, key, value)
+
+ def write(self, value):
+ self.o.write(value)
+
+ def read(self):
+ return self.i.read()
+
+class LiteScopeLADriver():
+ def __init__(self, regs, name, config_csv=None, use_rle=False):
+ self.regs = regs
+ self.name = name
+ self.use_rle = use_rle
+ if config_csv is None:
+ self.config_csv = name + ".csv"
+ self.get_config()
+ self.get_layout()
+ self.build()
+ self.dat = Dat(self.width)
+
+ def get_config(self):
+ csv_reader = csv.reader(open(self.config_csv), delimiter=',', quotechar='#')
+ for item in csv_reader:
+ t, n, v = item
+ if t == "config":
+ setattr(self, n, int(v))
+
+ def get_layout(self):
+ self.layout = []
+ csv_reader = csv.reader(open(self.config_csv), delimiter=',', quotechar='#')
+ for item in csv_reader:
+ t, n, v = item
+ if t == "layout":
+ self.layout.append((n, int(v)))
+
+ def build(self):
+ for key, value in self.regs.d.items():
+ if self.name == key[:len(self.name)]:
+ key.replace(self.name + "_")
+ setattr(self, key, value)
+ value = 1
+ for name, length in self.layout:
+ setattr(self, name + "_o", value)
+ value = value*(2**length)
+ value = 0
+ for name, length in self.layout:
+ setattr(self, name + "_m", (2**length-1) << value)
+ value += length
+
+ def show_state(self, s):
+ print(s, end="|")
+ sys.stdout.flush()
+
+ def prog_term(self, port, trigger=0, mask=0, cond=None):
+ if cond is not None:
+ for k, v in cond.items():
+ trigger |= getattr(self, k + "_o")*v
+ mask |= getattr(self, k + "_m")
+ t = getattr(self, "trigger_port{d}_trig".format(d=int(port)))
+ m = getattr(self, "trigger_port{d}_mask".format(d=int(port)))
+ t.write(trigger)
+ m.write(mask)
+
+ def prog_range_detector(self, port, low, high):
+ l = getattr(self, "trigger_port{d}_low".format(d=int(port)))
+ h = getattr(self, "trigger_port{d}_high".format(d=int(port)))
+ l.write(low)
+ h.write(high)
+
+ def prog_edge_detector(self, port, rising_mask, falling_mask, both_mask):
+ rm = getattr(self, "trigger_port{d}_rising_mask".format(d=int(port)))
+ fm = getattr(self, "trigger_port{d}_falling_mask".format(d=int(port)))
+ bm = getattr(self, "trigger_port{d}_both_mask".format(d=int(port)))
+ rm.write(rising_mask)
+ fm.write(falling_mask)
+ bm.write(both_mask)
+
+ def prog_sum(self, equation):
+ datas = gen_truth_table(equation)
+ for adr, dat in enumerate(datas):
+ self.trigger_sum_prog_adr.write(adr)
+ self.trigger_sum_prog_dat.write(dat)
+ self.trigger_sum_prog_we.write(1)
+
+ def config_rle(self, v):
+ self.rle_enable.write(v)
+
+ def is_done(self):
+ return self.recorder_done.read()
+
+ def wait_done(self):
+ self.show_state("WAIT HIT")
+ while(not self.is_done()):
+ time.sleep(0.1)
+
+ def trigger(self, offset, length):
+ self.show_state("TRIG")
+ if self.with_rle:
+ self.config_rle(self.use_rle)
+ self.recorder_offset.write(offset)
+ self.recorder_length.write(length)
+ self.recorder_trigger.write(1)
+
+ def read(self):
+ self.show_state("READ")
+ empty = self.recorder_read_empty.read()
+ while(not empty):
+ self.dat.append(self.recorder_read_dat.read())
+ empty = self.recorder_read_empty.read()
+ self.recorder_read_en.write(1)
+ if self.with_rle:
+ if self.use_rle:
+ self.dat = self.dat.decode_rle()
+ return self.dat
+
+ def export(self, export_fn=None):
+ self.show_state("EXPORT")
+ dump = Dump()
+ dump.add_from_layout(self.layout, self.dat)
+ if ".vcd" in export_fn:
+ VCDExport(dump).write(export_fn)
+ elif ".csv" in export_fn:
+ CSVExport(dump).write(export_fn)
+ elif ".py" in export_fn:
+ PYExport(dump).write(export_fn)
+ else:
+ raise NotImplementedError
--- /dev/null
+import sys
+import datetime
+
+def dec2bin(d, nb=0):
+ if d=="x":
+ return "x"*nb
+ elif d==0:
+ b="0"
+ else:
+ b=""
+ while d!=0:
+ b="01"[d&1]+b
+ d=d>>1
+ return b.zfill(nb)
+
+def get_bits(values, width, low, high=None):
+ r = []
+ for val in values:
+ t = dec2bin(val, width)[::-1]
+ if high == None:
+ t = t[low]
+ else:
+ t = t[low:high]
+ t = t[::-1]
+ t = int(t,2)
+ r.append(t)
+ return r
+
+class Dat(list):
+ def __init__(self, width):
+ self.width = width
+
+ def __getitem__(self, key):
+ if isinstance(key, int):
+ return get_bits(self, self.width, key)
+ elif isinstance(key, slice):
+ if key.start != None:
+ start = key.start
+ else:
+ start = 0
+ if key.stop != None:
+ stop = key.stop
+ else:
+ stop = self.width
+ if stop > self.width:
+ stop = self.width
+ if key.step != None:
+ raise KeyError
+ return get_bits(self, self.width, start, stop)
+ else:
+ raise KeyError
+
+ def decode_rle(self):
+ rle_bit = self[-1]
+ rle_dat = self[:self.width-1]
+
+ dat = Dat(self.width)
+ i=0
+ last = 0
+ for d in self:
+ if rle_bit[i]:
+ if len(dat) >= 1:
+ # FIX ME... why is rle_dat in reverse order...
+ for j in range(int(dec2bin(rle_dat[i])[::-1],2)):
+ dat.append(last)
+ else:
+ dat.append(d)
+ last = d
+ i +=1
+ return dat
+
+class Var:
+ def __init__(self, name, width, values=[], type="wire", default="x"):
+ self.type = type
+ self.width = width
+ self.name = name
+ self.val = default
+ self.values = values
+ self.vcd_id = None
+
+ def set_vcd_id(self, s):
+ self.vcd_id = s
+
+ def __len__(self):
+ return len(self.values)
+
+ def change(self, cnt):
+ r = ""
+ try :
+ if self.values[cnt+1] != self.val:
+ r += "b"
+ r += dec2bin(self.values[cnt+1], self.width)
+ r += " "
+ r += self.vcd_id
+ r += "\n"
+ return r
+ except :
+ return r
+ return r
+
+class Dump:
+ def __init__(self):
+ self.vars = []
+ self.vcd_id = "!"
+
+ def add(self, var):
+ var.set_vcd_id(self.vcd_id)
+ self.vcd_id = chr(ord(self.vcd_id)+1)
+ self.vars.append(var)
+
+ def add_from_layout(self, layout, var):
+ i=0
+ for s, n in layout:
+ self.add(Var(s, n, var[i:i+n]))
+ i += n
+
+ def __len__(self):
+ l = 0
+ for var in self.vars:
+ l = max(len(var),l)
+ return l
+
+class VCDExport():
+ def __init__(self, dump, timescale="1ps", comment=""):
+ self.dump = dump
+ self.timescale = timescale
+ self.comment = comment
+ self.cnt = -1
+
+ def change(self):
+ r = ""
+ c = ""
+ for var in self.dump.vars:
+ c += var.change(self.cnt)
+ if c != "":
+ r += "#"
+ r += str(self.cnt+1)
+ r += "\n"
+ r += c
+ return r
+
+ def p_date(self):
+ now = datetime.datetime.now()
+ r = "$date\n"
+ r += "\t"
+ r += now.strftime("%Y-%m-%d %H:%M")
+ r += "\n"
+ r += "$end\n"
+ return r
+
+ def p_version(self):
+ r = "$version\n"
+ r += "\tmiscope VCD dump\n"
+ r += "$end\n"
+ return r
+
+ def p_comment(self):
+ r = "$comment\n"
+ r += self.comment
+ r += "\n$end\n"
+ return r
+
+ def p_timescale(self):
+ r = "$timescale "
+ r += self.timescale
+ r += " $end\n"
+ return r
+
+ def p_scope(self):
+ r = "$scope "
+ r += self.timescale
+ r += " $end\n"
+ return r
+
+ def p_vars(self):
+ r = ""
+ for var in self.dump.vars:
+ r += "$var "
+ r += var.type
+ r += " "
+ r += str(var.width)
+ r += " "
+ r += var.vcd_id
+ r += " "
+ r += var.name
+ r += " $end\n"
+ return r
+
+ def p_unscope(self):
+ r = "$unscope "
+ r += " $end\n"
+ return r
+
+ def p_enddefinitions(self):
+ r = "$enddefinitions "
+ r += " $end\n"
+ return r
+
+ def p_dumpvars(self):
+ r = "$dumpvars\n"
+ for var in self.dump.vars:
+ r += "b"
+ r += dec2bin(var.val, var.width)
+ r += " "
+ r += var.vcd_id
+ r+= "\n"
+ r += "$end\n"
+ return r
+
+ def p_valuechange(self):
+ r = ""
+ for i in range(len(self.dump)):
+ r += self.change()
+ self.cnt += 1
+ return r
+
+ def __repr__(self):
+ r = ""
+ r += self.p_date()
+ r += self.p_version()
+ r += self.p_comment()
+ r += self.p_timescale()
+ r += self.p_scope()
+ r += self.p_vars()
+ r += self.p_unscope()
+ r += self.p_enddefinitions()
+ r += self.p_dumpvars()
+ r += self.p_valuechange()
+ return r
+
+ def write(self, filename):
+ f = open(filename, "w")
+ f.write(str(self))
+ f.close()
+
+class CSVExport():
+ def __init__(self, dump):
+ self.dump = dump
+
+ def p_vars(self):
+ r = ""
+ for var in self.dump.vars:
+ r += var.name
+ r += ","
+ r += "\n"
+ for var in self.dump.vars:
+ r += str(var.width)
+ r += ","
+ r += "\n"
+ return r
+
+ def p_dumpvars(self):
+ r = ""
+ for i in range(len(self.dump)):
+ for var in self.dump.vars:
+ try:
+ var.val = var.values[i]
+ except:
+ pass
+ if var.val == "x":
+ r += "x"
+ else:
+ r += dec2bin(var.val, var.width)
+ r += ", "
+ r+= "\n"
+ return r
+
+ def __repr__(self):
+ r = ""
+ r += self.p_vars()
+ r += self.p_dumpvars()
+ return r
+
+ def write(self, filename):
+ f = open(filename, "w")
+ f.write(str(self))
+ f.close()
+
+class PYExport():
+ def __init__(self, dump):
+ self.dump = dump
+
+ def __repr__(self):
+ r = "dump = {\n"
+ for var in self.dump.vars:
+ r += "\"" + var.name + "\""
+ r += " : "
+ r += str(var.values)
+ r += ",\n"
+ r += "}"
+ return r
+
+ def write(self, filename):
+ f = open(filename, "w")
+ f.write(str(self))
+ f.close()
+
+def main():
+ dump = Dump()
+ dump.add(Var("foo1", 1, [0,1,0,1,0,1]))
+ dump.add(Var("foo2", 2, [1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0]))
+ ramp = [i%128 for i in range(1024)]
+ dump.add(Var("ramp", 16, ramp))
+
+ VCDExport(dump).write("mydump.vcd")
+ CSVExport(dump).write("mydump.csv")
+ PYExport(dump).write("mydump.py")
+
+if __name__ == '__main__':
+ main()
+
--- /dev/null
+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")
+ r = 0
+ for i in range(self.length):
+ r |= self.readfn(self.addr + 4*i)
+ if i != (self.length-1):
+ r <<= self.busword
+ return r
+
+ def write(self, value):
+ if self.mode not in ["rw", "wo"]:
+ raise KeyError(name + "register not writable")
+ for i in range(self.length):
+ dat = (value >> ((self.length-1-i)*self.busword)) & (2**self.busword-1)
+ self.writefn(self.addr + 4*i, dat)
+
+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)
\ No newline at end of file
--- /dev/null
+import os
+import re
+import sys
+
+def is_number(x):
+ try:
+ _ = float(x)
+ except ValueError:
+ return False
+ return True
+
+def remove_numbers(seq):
+ return [x for x in seq if not is_number(x)]
+
+def remove_duplicates(seq):
+ seen = set()
+ seen_add = seen.add
+ return [x for x in seq if x not in seen and not seen_add(x)]
+
+def get_operands(s):
+ operands = re.findall("[A-z0-9_]+", s)
+ operands = remove_duplicates(operands)
+ operands = remove_numbers(operands)
+ return sorted(operands)
+
+def gen_truth_table(s):
+ operands = get_operands(s)
+ width = len(operands)
+ stim = []
+ for i in range(width):
+ stim_op = []
+ for j in range(2**width):
+ stim_op.append((int(j/(2**i)))%2)
+ stim.append(stim_op)
+
+ truth_table = []
+ for i in range(2**width):
+ for j in range(width):
+ exec("%s = stim[j][i]" %operands[j])
+ truth_table.append(eval(s) != 0)
+ return truth_table
+
+def main():
+ print(gen_truth_table("(A&B&C)|D"))
+
+if __name__ == '__main__':
+ main()
--- /dev/null
+#!/usr/bin/env python3
+
+import sys, os, argparse, subprocess, struct, importlib
+
+from mibuild.tools import write_to_file
+from migen.util.misc import autotype
+from migen.fhdl import verilog, edif
+from migen.fhdl.structure import _Fragment
+from mibuild import tools
+from mibuild.xilinx_common import *
+
+from misoclib.gensoc import cpuif
+
+from litesata.common import *
+
+def _import(default, name):
+ return importlib.import_module(default + "." + name)
+
+def _get_args():
+ parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter,
+ description="""\
+LiteScope - based on Migen.
+
+This program builds and/or loads LiteSATA components.
+One or several actions can be specified:
+
+clean delete previous build(s).
+build-rtl build verilog rtl.
+build-bitstream build-bitstream build FPGA bitstream.
+build-csr-csv save CSR map into CSV file.
+
+load-bitstream load bitstream into volatile storage.
+
+all clean, build-csr-csv, build-bitstream, load-bitstream.
+""")
+
+ parser.add_argument("-t", "--target", default="simple", help="Core type to build")
+ parser.add_argument("-s", "--sub-target", default="", help="variant of the Core type to build")
+ parser.add_argument("-p", "--platform", default=None, help="platform to build for")
+ parser.add_argument("-Ot", "--target-option", default=[], nargs=2, action="append", help="set target-specific option")
+ parser.add_argument("-Op", "--platform-option", default=[], nargs=2, action="append", help="set platform-specific option")
+ parser.add_argument("--csr_csv", default="./test/csr.csv", help="CSV file to save the CSR map into")
+
+ parser.add_argument("action", nargs="+", help="specify an action")
+
+ return parser.parse_args()
+
+# Note: misoclib need to be installed as a python library
+
+if __name__ == "__main__":
+ args = _get_args()
+
+ # create top-level Core object
+ target_module = _import("targets", args.target)
+ if args.sub_target:
+ top_class = getattr(target_module, args.sub_target)
+ else:
+ top_class = target_module.default_subtarget
+
+ if args.platform is None:
+ platform_name = top_class.default_platform
+ else:
+ platform_name = args.platform
+ platform_module = _import("mibuild.platforms", platform_name)
+ platform_kwargs = dict((k, autotype(v)) for k, v in args.platform_option)
+ platform = platform_module.Platform(**platform_kwargs)
+
+ build_name = top_class.__name__.lower() + "-" + platform_name
+ top_kwargs = dict((k, autotype(v)) for k, v in args.target_option)
+ soc = top_class(platform, **top_kwargs)
+ soc.finalize()
+
+ # decode actions
+ action_list = ["clean", "build-csr-csv", "build-bitstream", "load-bitstream", "all"]
+ actions = {k: False for k in action_list}
+ for action in args.action:
+ if action in actions:
+ actions[action] = True
+ else:
+ print("Unknown action: "+action+". Valid actions are:")
+ for a in action_list:
+ print(" "+a)
+ sys.exit(1)
+
+ print("""
+ __ _ __ ____
+ / / (_) /____ / __/______ ___ ___
+ / /__/ / __/ -_)\ \/ __/ _ \/ _ \/ -_)
+ /____/_/\__/\__/___/\__/\___/ .__/\__/
+ /_/
+
+ A small footprint and configurable embedded FPGA
+ based in Migen/MiSoC
+
+====== Building options: ======
+===============================""".format()
+)
+
+ # dependencies
+ if actions["all"]:
+ actions["clean"] = True
+ actions["build-csr-csv"] = True
+ actions["build-bitstream"] = True
+ actions["load-bitstream"] = True
+
+ if actions["build-bitstream"]:
+ actions["clean"] = True
+ actions["build-csr-csv"] = True
+ actions["build-bitstream"] = True
+ actions["load-bitstream"] = True
+
+ if actions["clean"]:
+ subprocess.call(["rm", "-rf", "build/*"])
+
+ if actions["build-csr-csv"]:
+ csr_csv = cpuif.get_csr_csv(soc.cpu_csr_regions)
+ write_to_file(args.csr_csv, csr_csv)
+
+ if actions["build-bitstream"]:
+ platform.build(soc, build_name=build_name)
+
+ if actions["load-bitstream"]:
+ prog = platform.create_programmer()
+ prog.load_bitstream("build/" + build_name + platform.bitstream_ext)
+++ /dev/null
-try:
- from miscope.miio import MiIo
- from miscope.mila import MiLa
- from miscope.trigger import Term, RangeDetector, EdgeDetector
- from miscope.uart2wishbone import UART2Wishbone
-except:
- pass
\ No newline at end of file
+++ /dev/null
-from migen.bank.description import CSRStatus
-
-def get_csr_csv(csr_base, bank_array):
- r = ""
- for name, csrs, mapaddr, rmap in bank_array.banks:
- reg_base = csr_base + 0x800*mapaddr
- for csr in csrs:
- nr = (csr.size + 7)//8
- r += "{}_{},0x{:08x},{},{}\n".format(name, csr.name, reg_base, nr, "ro" if isinstance(csr, CSRStatus) else "rw")
- reg_base += 4*nr
- return r
+++ /dev/null
-import csv
-import time
-import sys
-from miscope.host.dump import *
-from miscope.host.truthtable import *
-
-class MiIoDriver():
- def __init__(self, regs, name):
- self.regs = regs
- self.name = name
- self.build_miio()
-
- def build_miio(self):
- for key, value in self.regs.d.items():
- if self.name in key:
- key.replace(self.name, "miio")
- setattr(self, key, value)
-
- def write(self, value):
- self.miio_o.write(value)
-
- def read(self):
- return self.miio_i.read()
-
-class MiLaDriver():
- def __init__(self, regs, name, config_csv=None, use_rle=False):
- self.regs = regs
- self.name = name
- self.use_rle = use_rle
- if config_csv is None:
- self.config_csv = name + ".csv"
- self.get_config()
- self.get_layout()
- self.build_mila()
- self.dat = Dat(self.width)
-
- def get_config(self):
- csv_reader = csv.reader(open(self.config_csv), delimiter=',', quotechar='#')
- for item in csv_reader:
- t, n, v = item
- if t == "config":
- setattr(self, n, int(v))
-
- def get_layout(self):
- self.layout = []
- csv_reader = csv.reader(open(self.config_csv), delimiter=',', quotechar='#')
- for item in csv_reader:
- t, n, v = item
- if t == "layout":
- self.layout.append((n, int(v)))
-
- def build_mila(self):
- for key, value in self.regs.d.items():
- if self.name == key[:len(self.name)]:
- key.replace(self.name, "mila")
- setattr(self, key, value)
- value = 1
- for name, length in self.layout:
- setattr(self, name+"_o", value)
- value = value*(2**length)
- value = 0
- for name, length in self.layout:
- setattr(self, name+"_m", (2**length-1) << value)
- value += length
-
- def show_state(self, s):
- print(s, end="|")
- sys.stdout.flush()
-
- def prog_term(self, port, trigger=0, mask=0, cond=None):
- if cond is not None:
- for k, v in cond.items():
- trigger |= getattr(self, k+"_o")*v
- mask |= getattr(self, k+"_m")
- t = getattr(self, "mila_trigger_port{d}_trig".format(d=int(port)))
- m = getattr(self, "mila_trigger_port{d}_mask".format(d=int(port)))
- t.write(trigger)
- m.write(mask)
-
- def prog_range_detector(self, port, low, high):
- l = getattr(self, "mila_trigger_port{d}_low".format(d=int(port)))
- h = getattr(self, "mila_trigger_port{d}_high".format(d=int(port)))
- l.write(low)
- h.write(high)
-
- def prog_edge_detector(self, port, rising_mask, falling_mask, both_mask):
- rm = getattr(self, "mila_trigger_port{d}_rising_mask".format(d=int(port)))
- fm = getattr(self, "mila_trigger_port{d}_falling_mask".format(d=int(port)))
- bm = getattr(self, "mila_trigger_port{d}_both_mask".format(d=int(port)))
- rm.write(rising_mask)
- fm.write(falling_mask)
- bm.write(both_mask)
-
- def prog_sum(self, equation):
- datas = gen_truth_table(equation)
- for adr, dat in enumerate(datas):
- self.mila_trigger_sum_prog_adr.write(adr)
- self.mila_trigger_sum_prog_dat.write(dat)
- self.mila_trigger_sum_prog_we.write(1)
-
- def config_rle(self, v):
- self.mila_rle_enable.write(v)
-
- def is_done(self):
- return self.mila_recorder_done.read()
-
- def wait_done(self):
- self.show_state("WAIT HIT")
- while(not self.is_done()):
- time.sleep(0.1)
-
- def trigger(self, offset, length):
- self.show_state("TRIG")
- if self.with_rle:
- self.config_rle(self.use_rle)
- self.mila_recorder_offset.write(offset)
- self.mila_recorder_length.write(length)
- self.mila_recorder_trigger.write(1)
-
- def read(self):
- self.show_state("READ")
- empty = self.mila_recorder_read_empty.read()
- while(not empty):
- self.dat.append(self.mila_recorder_read_dat.read())
- empty = self.mila_recorder_read_empty.read()
- self.mila_recorder_read_en.write(1)
- if self.with_rle:
- if self.use_rle:
- self.dat = self.dat.decode_rle()
- return self.dat
-
- def export(self, export_fn=None):
- self.show_state("EXPORT")
- dump = Dump()
- dump.add_from_layout(self.layout, self.dat)
- if ".vcd" in export_fn:
- VCDExport(dump).write(export_fn)
- elif ".csv" in export_fn:
- CSVExport(dump).write(export_fn)
- elif ".py" in export_fn:
- PYExport(dump).write(export_fn)
- else:
- raise NotImplementedError
+++ /dev/null
-import sys
-import datetime
-
-def dec2bin(d, nb=0):
- if d=="x":
- return "x"*nb
- elif d==0:
- b="0"
- else:
- b=""
- while d!=0:
- b="01"[d&1]+b
- d=d>>1
- return b.zfill(nb)
-
-def get_bits(values, width, low, high=None):
- r = []
- for val in values:
- t = dec2bin(val, width)[::-1]
- if high == None:
- t = t[low]
- else:
- t = t[low:high]
- t = t[::-1]
- t = int(t,2)
- r.append(t)
- return r
-
-class Dat(list):
- def __init__(self, width):
- self.width = width
-
- def __getitem__(self, key):
- if isinstance(key, int):
- return get_bits(self, self.width, key)
- elif isinstance(key, slice):
- if key.start != None:
- start = key.start
- else:
- start = 0
- if key.stop != None:
- stop = key.stop
- else:
- stop = self.width
- if stop > self.width:
- stop = self.width
- if key.step != None:
- raise KeyError
- return get_bits(self, self.width, start, stop)
- else:
- raise KeyError
-
- def decode_rle(self):
- rle_bit = self[-1]
- rle_dat = self[:self.width-1]
-
- dat = Dat(self.width)
- i=0
- last = 0
- for d in self:
- if rle_bit[i]:
- if len(dat) >= 1:
- # FIX ME... why is rle_dat in reverse order...
- for j in range(int(dec2bin(rle_dat[i])[::-1],2)):
- dat.append(last)
- else:
- dat.append(d)
- last = d
- i +=1
- return dat
-
-class Var:
- def __init__(self, name, width, values=[], type="wire", default="x"):
- self.type = type
- self.width = width
- self.name = name
- self.val = default
- self.values = values
- self.vcd_id = None
-
- def set_vcd_id(self, s):
- self.vcd_id = s
-
- def __len__(self):
- return len(self.values)
-
- def change(self, cnt):
- r = ""
- try :
- if self.values[cnt+1] != self.val:
- r += "b"
- r += dec2bin(self.values[cnt+1], self.width)
- r += " "
- r += self.vcd_id
- r += "\n"
- return r
- except :
- return r
- return r
-
-class Dump:
- def __init__(self):
- self.vars = []
- self.vcd_id = "!"
-
- def add(self, var):
- var.set_vcd_id(self.vcd_id)
- self.vcd_id = chr(ord(self.vcd_id)+1)
- self.vars.append(var)
-
- def add_from_layout(self, layout, var):
- i=0
- for s, n in layout:
- self.add(Var(s, n, var[i:i+n]))
- i += n
-
- def __len__(self):
- l = 0
- for var in self.vars:
- l = max(len(var),l)
- return l
-
-class VCDExport():
- def __init__(self, dump, timescale="1ps", comment=""):
- self.dump = dump
- self.timescale = timescale
- self.comment = comment
- self.cnt = -1
-
- def change(self):
- r = ""
- c = ""
- for var in self.dump.vars:
- c += var.change(self.cnt)
- if c != "":
- r += "#"
- r += str(self.cnt+1)
- r += "\n"
- r += c
- return r
-
- def p_date(self):
- now = datetime.datetime.now()
- r = "$date\n"
- r += "\t"
- r += now.strftime("%Y-%m-%d %H:%M")
- r += "\n"
- r += "$end\n"
- return r
-
- def p_version(self):
- r = "$version\n"
- r += "\tmiscope VCD dump\n"
- r += "$end\n"
- return r
-
- def p_comment(self):
- r = "$comment\n"
- r += self.comment
- r += "\n$end\n"
- return r
-
- def p_timescale(self):
- r = "$timescale "
- r += self.timescale
- r += " $end\n"
- return r
-
- def p_scope(self):
- r = "$scope "
- r += self.timescale
- r += " $end\n"
- return r
-
- def p_vars(self):
- r = ""
- for var in self.dump.vars:
- r += "$var "
- r += var.type
- r += " "
- r += str(var.width)
- r += " "
- r += var.vcd_id
- r += " "
- r += var.name
- r += " $end\n"
- return r
-
- def p_unscope(self):
- r = "$unscope "
- r += " $end\n"
- return r
-
- def p_enddefinitions(self):
- r = "$enddefinitions "
- r += " $end\n"
- return r
-
- def p_dumpvars(self):
- r = "$dumpvars\n"
- for var in self.dump.vars:
- r += "b"
- r += dec2bin(var.val, var.width)
- r += " "
- r += var.vcd_id
- r+= "\n"
- r += "$end\n"
- return r
-
- def p_valuechange(self):
- r = ""
- for i in range(len(self.dump)):
- r += self.change()
- self.cnt += 1
- return r
-
- def __repr__(self):
- r = ""
- r += self.p_date()
- r += self.p_version()
- r += self.p_comment()
- r += self.p_timescale()
- r += self.p_scope()
- r += self.p_vars()
- r += self.p_unscope()
- r += self.p_enddefinitions()
- r += self.p_dumpvars()
- r += self.p_valuechange()
- return r
-
- def write(self, filename):
- f = open(filename, "w")
- f.write(str(self))
- f.close()
-
-class CSVExport():
- def __init__(self, dump):
- self.dump = dump
-
- def p_vars(self):
- r = ""
- for var in self.dump.vars:
- r += var.name
- r += ","
- r += "\n"
- for var in self.dump.vars:
- r += str(var.width)
- r += ","
- r += "\n"
- return r
-
- def p_dumpvars(self):
- r = ""
- for i in range(len(self.dump)):
- for var in self.dump.vars:
- try:
- var.val = var.values[i]
- except:
- pass
- if var.val == "x":
- r += "x"
- else:
- r += dec2bin(var.val, var.width)
- r += ", "
- r+= "\n"
- return r
-
- def __repr__(self):
- r = ""
- r += self.p_vars()
- r += self.p_dumpvars()
- return r
-
- def write(self, filename):
- f = open(filename, "w")
- f.write(str(self))
- f.close()
-
-class PYExport():
- def __init__(self, dump):
- self.dump = dump
-
- def __repr__(self):
- r = "dump = {\n"
- for var in self.dump.vars:
- r += "\"" + var.name + "\""
- r += " : "
- r += str(var.values)
- r += ",\n"
- r += "}"
- return r
-
- def write(self, filename):
- f = open(filename, "w")
- f.write(str(self))
- f.close()
-
-def main():
- dump = Dump()
- dump.add(Var("foo1", 1, [0,1,0,1,0,1]))
- dump.add(Var("foo2", 2, [1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0]))
- ramp = [i%128 for i in range(1024)]
- dump.add(Var("ramp", 16, ramp))
-
- VCDExport(dump).write("mydump.vcd")
- CSVExport(dump).write("mydump.csv")
- PYExport(dump).write("mydump.py")
-
-if __name__ == '__main__':
- main()
-
+++ /dev/null
-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")
- r = 0
- for i in range(self.length):
- r |= self.readfn(self.addr + 4*i)
- if i != (self.length-1):
- r <<= self.busword
- return r
-
- def write(self, value):
- if self.mode not in ["rw", "wo"]:
- raise KeyError(name + "register not writable")
- for i in range(self.length):
- dat = (value >> ((self.length-1-i)*self.busword)) & (2**self.busword-1)
- self.writefn(self.addr + 4*i, dat)
-
-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)
\ No newline at end of file
+++ /dev/null
-import os
-import re
-import sys
-
-def is_number(x):
- try:
- _ = float(x)
- except ValueError:
- return False
- return True
-
-def remove_numbers(seq):
- return [x for x in seq if not is_number(x)]
-
-def remove_duplicates(seq):
- seen = set()
- seen_add = seen.add
- return [x for x in seq if x not in seen and not seen_add(x)]
-
-def get_operands(s):
- operands = re.findall("[A-z0-9_]+", s)
- operands = remove_duplicates(operands)
- operands = remove_numbers(operands)
- return sorted(operands)
-
-def gen_truth_table(s):
- operands = get_operands(s)
- width = len(operands)
- stim = []
- for i in range(width):
- stim_op = []
- for j in range(2**width):
- stim_op.append((int(j/(2**i)))%2)
- stim.append(stim_op)
-
- truth_table = []
- for i in range(2**width):
- for j in range(width):
- exec("%s = stim[j][i]" %operands[j])
- truth_table.append(eval(s) != 0)
- return truth_table
-
-def main():
- print(gen_truth_table("(A&B&C)|D"))
-
-if __name__ == '__main__':
- main()
+++ /dev/null
-import string
-import serial
-from struct import *
-from migen.fhdl.structure import *
-from miscope.host.regs import *
-
-def write_b(uart, data):
- uart.write(pack('B',data))
-
-class Uart2Wishbone:
- WRITE_CMD = 0x01
- READ_CMD = 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)
- 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()
- try:
- self.regs.uart2wb_sel.write(1)
- except:
- pass
-
- def close(self):
- try:
- self.regs.uart2wb_sel.write(0)
- except:
- pass
- self.uart.flushOutput()
- self.uart.close()
-
- def read(self, addr, burst_length=1):
- self.uart.flushInput()
- write_b(self.uart, self.READ_CMD)
- write_b(self.uart, burst_length)
- addr = addr//4
- write_b(self.uart, (addr & 0xff000000) >> 24)
- write_b(self.uart, (addr & 0x00ff0000) >> 16)
- write_b(self.uart, (addr & 0x0000ff00) >> 8)
- write_b(self.uart, (addr & 0x000000ff))
- values = []
- for i in range(burst_length):
- val = 0
- for j in range(4):
- val = val << 8
- val |= ord(self.uart.read())
- if self.debug:
- print("RD %08X @ %08X" %(val, (addr+i)*4))
- values.append(val)
- if burst_length == 1:
- return values[0]
- else:
- return values
-
- def write(self, addr, data):
- if isinstance(data, list):
- burst_length = len(data)
- else:
- burst_length = 1
- write_b(self.uart, self.WRITE_CMD)
- write_b(self.uart, burst_length)
- addr = addr//4
- write_b(self.uart, (addr & 0xff000000) >> 24)
- write_b(self.uart, (addr & 0x00ff0000) >> 16)
- write_b(self.uart, (addr & 0x0000ff00) >> 8)
- write_b(self.uart, (addr & 0x000000ff))
- if isinstance(data, list):
- for i in range(len(data)):
- dat = data[i]
- for j in range(4):
- write_b(self.uart, (dat & 0xff000000) >> 24)
- dat = dat << 8
- if self.debug:
- print("WR %08X @ %08X" %(data[i], (addr + i)*4))
- else:
- dat = data
- for j in range(4):
- write_b(self.uart, (dat & 0xff000000) >> 24)
- dat = dat << 8
- if self.debug:
- print("WR %08X @ %08X" %(data, (addr * 4)))
+++ /dev/null
-from migen.fhdl.structure import *
-from migen.bank.description import *
-
-class MiIo(Module, AutoCSR):
- def __init__(self, width):
- self._r_i = CSRStatus(width)
- self._r_o = CSRStorage(width)
-
- self.i = self._r_i.status
- self.o = self._r_o.storage
+++ /dev/null
-from migen.fhdl.std import *
-from migen.fhdl import verilog
-from migen.bank.description import *
-from migen.actorlib.fifo import AsyncFIFO
-
-from miscope.std import *
-from miscope.trigger import Trigger
-from miscope.storage import Recorder, RunLengthEncoder
-
-from mibuild.tools import write_to_file
-
-def _getattr_all(l, attr):
- it = iter(l)
- r = getattr(next(it), attr)
- for e in it:
- if getattr(e, attr) != r:
- raise ValueError
- return r
-
-class MiLa(Module, AutoCSR):
- def __init__(self, depth, dat, with_rle=False, clk_domain="sys", pipe=False):
- self.depth = depth
- self.with_rle = with_rle
- self.clk_domain = clk_domain
- self.pipe = pipe
- self.ports = []
- self.width = flen(dat)
-
- self.stb = Signal(reset=1)
- self.dat = dat
-
- def add_port(self, port_class):
- port = port_class(self.width)
- self.ports.append(port)
-
- def do_finalize(self):
- stb = self.stb
- dat = self.dat
- if self.pipe:
- sync = getattr(self.sync, self.clk_domain)
- stb_new = Signal()
- dat_new = Signal(flen(dat))
- sync += [
- stb_new.eq(stb),
- dat_new.eq(dat)
- ]
- stb = stb_new
- dat = dat_new
-
- if self.clk_domain is not "sys":
- fifo = AsyncFIFO([("dat", self.width)], 32)
- self.submodules += RenameClockDomains(fifo, {"write": self.clk_domain, "read": "sys"})
- self.comb += [
- fifo.sink.stb.eq(stb),
- fifo.sink.dat.eq(dat)
- ]
- sink = Record(dat_layout(self.width))
- self.comb += [
- sink.stb.eq(fifo.source.stb),
- sink.dat.eq(fifo.source.dat),
- fifo.source.ack.eq(1)
- ]
- else:
- sink = Record(dat_layout(self.width))
- self.comb += [
- sink.stb.eq(stb),
- sink.dat.eq(dat)
- ]
-
- self.submodules.trigger = trigger = Trigger(self.width, self.ports)
- self.submodules.recorder = recorder = Recorder(self.width, self.depth)
- self.comb += [
- sink.connect(trigger.sink),
- trigger.source.connect(recorder.trig_sink)
- ]
-
- if self.with_rle:
- self.submodules.rle = rle = RunLengthEncoder(self.width)
- self.comb += [
- sink.connect(rle.sink),
- rle.source.connect(recorder.dat_sink)
- ]
- else:
- self.comb += sink.connect(recorder.dat_sink)
-
- def export(self, design, layout, filename):
- ret, ns = verilog.convert(design, return_ns=True)
- r = ""
- def format_line(*args):
- return ",".join(args) + "\n"
-
- r += format_line("config", "width", str(self.width))
- r += format_line("config", "depth", str(self.depth))
- r += format_line("config", "with_rle", str(int(self.with_rle)))
-
- for e in layout:
- r += format_line("layout", ns.get_name(e), str(flen(e)))
- write_to_file(filename, r)
+++ /dev/null
-from migen.genlib.record import *
-
-def dat_layout(dw):
- return [
- ("stb", 1, DIR_M_TO_S),
- ("dat", dw, DIR_M_TO_S)
- ]
-
-def hit_layout():
- return [
- ("stb", 1, DIR_M_TO_S),
- ("hit", 1, DIR_M_TO_S)
- ]
+++ /dev/null
-from migen.fhdl.std import *
-from migen.bank.description import *
-from migen.genlib.fifo import SyncFIFOBuffered as SyncFIFO
-from migen.genlib.fsm import FSM, NextState
-from migen.genlib.record import *
-
-from miscope.std import *
-
-class RunLengthEncoder(Module, AutoCSR):
- def __init__(self, width, length=1024):
- self.width = width
- self.length = length
-
- self.sink = Record(dat_layout(width))
- self.source = Record(dat_layout(width))
-
- self._enable = CSRStorage()
-
- ###
-
- enable = self._enable.storage
-
- sink_d = Record(dat_layout(width))
- self.sync += If(self.sink.stb, sink_d.eq(self.sink))
-
- cnt = Signal(max=length)
- cnt_inc = Signal()
- cnt_reset = Signal()
- cnt_max = Signal()
-
- self.sync += \
- If(cnt_reset,
- cnt.eq(1),
- ).Elif(cnt_inc,
- cnt.eq(cnt+1)
- )
- self.comb += cnt_max.eq(cnt == length)
-
- change = Signal()
- self.comb += change.eq(self.sink.stb & (self.sink.dat != sink_d.dat))
-
- fsm = FSM(reset_state="BYPASS")
- self.submodules += fsm
-
- fsm.act("BYPASS",
- sink_d.connect(self.source),
- cnt_reset.eq(1),
- If(enable & ~change & self.sink.stb, NextState("COUNT"))
- )
-
- fsm.act("COUNT",
- cnt_inc.eq(self.sink.stb),
- If(change | cnt_max | ~enable,
- self.source.stb.eq(1),
- self.source.dat[width-1].eq(1), # Set RLE bit
- self.source.dat[:flen(cnt)].eq(cnt),
- NextState("BYPASS")
- )
- ),
-
-class Recorder(Module, AutoCSR):
- def __init__(self, width, depth):
- self.width = width
-
- self.trig_sink = Record(hit_layout())
- self.dat_sink = Record(dat_layout(width))
-
- self._trigger = CSR()
- self._length = CSRStorage(bits_for(depth))
- self._offset = CSRStorage(bits_for(depth))
- self._done = CSRStatus()
-
- self._read_en = CSR()
- self._read_empty = CSRStatus()
- self._read_dat = CSRStatus(width)
-
- ###
-
- fifo = InsertReset(SyncFIFO(width, depth))
- self.submodules += fifo
-
- fsm = FSM(reset_state="IDLE")
- self.submodules += fsm
-
-
- self.comb += [
- self._read_empty.status.eq(~fifo.readable),
- self._read_dat.status.eq(fifo.dout),
- ]
-
- fsm.act("IDLE",
- If(self._trigger.re & self._trigger.r,
- NextState("PRE_HIT_RECORDING"),
- fifo.reset.eq(1),
- ),
- fifo.re.eq(self._read_en.re & self._read_en.r),
- self._done.status.eq(1)
- )
-
- fsm.act("PRE_HIT_RECORDING",
- fifo.we.eq(self.dat_sink.stb),
- fifo.din.eq(self.dat_sink.dat),
-
- fifo.re.eq(fifo.level >= self._offset.storage),
-
- If(self.trig_sink.stb & self.trig_sink.hit, NextState("POST_HIT_RECORDING"))
- )
-
- fsm.act("POST_HIT_RECORDING",
- fifo.we.eq(self.dat_sink.stb),
- fifo.din.eq(self.dat_sink.dat),
-
- If(~fifo.writable | (fifo.level >= self._length.storage), NextState("IDLE"))
- )
+++ /dev/null
-from migen.fhdl.std import *
-from migen.fhdl.specials import Memory
-from migen.bank.description import *
-from migen.genlib.record import *
-
-from miscope.std import *
-
-class Term(Module, AutoCSR):
- def __init__(self, width):
- self.width = width
-
- self.sink = Record(dat_layout(width))
- self.source = Record(hit_layout())
-
- self._trig = CSRStorage(width)
- self._mask = CSRStorage(width)
-
- ###
-
- trig = self._trig.storage
- mask = self._mask.storage
- dat = self.sink.dat
- hit = self.source.hit
-
- self.comb += [
- hit.eq((dat & mask) == trig),
- self.source.stb.eq(self.sink.stb)
- ]
-
-class RangeDetector(Module, AutoCSR):
- def __init__(self, width):
- self.width = width
-
- self.sink = Record(dat_layout(width))
- self.source = Record(hit_layout())
-
- self._low = CSRStorage(width)
- self._high = CSRStorage(width)
-
- ###
-
- low = self._low.storage
- high = self._high.storage
- dat = self.sink.dat
- hit = self.source.hit
-
- self.comb += [
- hit.eq((dat >= low) & (dat <= high)),
- self.source.stb.eq(self.sink.stb)
- ]
-
-class EdgeDetector(Module, AutoCSR):
- def __init__(self, width):
- self.width = width
-
- self.sink = Record(dat_layout(width))
- self.source = Record(hit_layout())
-
- self._rising_mask = CSRStorage(width)
- self._falling_mask = CSRStorage(width)
- self._both_mask = CSRStorage(width)
-
- ###
-
- rising_mask = self._rising_mask.storage
- falling_mask = self._falling_mask.storage
- both_mask = self._both_mask.storage
-
- dat = self.sink.dat
- dat_d = Signal(width)
- rising_hit = Signal()
- falling_hit = Signal()
- both_hit = Signal()
- hit = self.source.hit
-
- self.sync += dat_d.eq(dat)
-
- self.comb += [
- rising_hit.eq(rising_mask & dat & ~dat_d),
- falling_hit.eq(rising_mask & ~dat & dat_d),
- both_hit.eq((both_mask & dat) != (both_mask & dat_d)),
- hit.eq(rising_hit | falling_hit | both_hit),
- self.source.stb.eq(self.sink.stb)
- ]
-
-class Sum(Module, AutoCSR):
- def __init__(self, ports=4):
-
- self.sinks = [Record(hit_layout()) for p in range(ports)]
- self.source = Record(hit_layout())
-
- self._prog_we = CSRStorage()
- self._prog_adr = CSRStorage(ports) #FIXME
- self._prog_dat = CSRStorage()
-
- mem = Memory(1, 2**ports)
- lut_port = mem.get_port()
- prog_port = mem.get_port(write_capable=True)
-
- self.specials += mem, lut_port, prog_port
-
- ###
-
- # Lut prog
- self.comb += [
- prog_port.we.eq(self._prog_we.storage),
- prog_port.adr.eq(self._prog_adr.storage),
- prog_port.dat_w.eq(self._prog_dat.storage)
- ]
-
- # Lut read
- for i, sink in enumerate(self.sinks):
- self.comb += lut_port.adr[i].eq(sink.hit)
-
- # Drive source
- self.comb += [
- self.source.stb.eq(optree("&", [sink.stb for sink in self.sinks])),
- self.source.hit.eq(lut_port.dat_r),
- ]
-
-
-class Trigger(Module, AutoCSR):
- def __init__(self, width, ports):
- self.width = width
- self.ports = ports
-
- self.submodules.sum = Sum(len(ports))
- for i, port in enumerate(ports):
- setattr(self.submodules, "port"+str(i), port)
-
- self.sink = Record(dat_layout(width))
- self.source = self.sum.source
-
- ###
-
- for i, port in enumerate(ports):
- self.comb += [
- self.sink.connect(port.sink),
- port.source.connect(self.sum.sinks[i])
- ]
+++ /dev/null
-from migen.fhdl.std import *
-from migen.genlib.record import *
-from migen.genlib.fsm import FSM, NextState
-from migen.genlib.misc import chooser
-from migen.bank.description import *
-from migen.bus import wishbone
-
-from misoclib.uart import UARTRX, UARTTX
-
-@DecorateModule(InsertReset)
-@DecorateModule(InsertCE)
-class Counter(Module):
- def __init__(self, signal=None, **kwargs):
- if signal is None:
- self.value = Signal(**kwargs)
- else:
- self.value = signal
- self.width = flen(self.value)
- self.sync += self.value.eq(self.value+1)
-
-@DecorateModule(InsertReset)
-@DecorateModule(InsertCE)
-class Timeout(Module):
- def __init__(self, length):
- self.reached = Signal()
- ###
- value = Signal(max=length)
- self.sync += value.eq(value+1)
- self.comb += [
- self.reached.eq(value == length)
- ]
-
-class UART(Module, AutoCSR):
- def __init__(self, pads, clk_freq, baud=115200):
- self._tuning_word = CSRStorage(32, reset=int((baud/clk_freq)*2**32))
- tuning_word = self._tuning_word.storage
-
- ###
-
- self.rx = UARTRX(pads, tuning_word)
- self.tx = UARTTX(pads, tuning_word)
- self.submodules += self.rx, self.tx
-
-class UARTPads:
- def __init__(self):
- self.rx = Signal()
- self.tx = Signal()
-
-class UARTMux(Module):
- def __init__(self, pads):
- self.sel = Signal(max=2)
- self.shared_pads = UARTPads()
- self.bridge_pads = UARTPads()
-
- ###
- # Route rx pad:
- # when sel==0, route it to shared rx and bridge rx
- # when sel==1, route it only to bridge rx
- self.comb += \
- If(self.sel==0,
- self.shared_pads.rx.eq(pads.rx),
- self.bridge_pads.rx.eq(pads.rx)
- ).Else(
- self.bridge_pads.rx.eq(pads.rx)
- )
-
- # Route tx:
- # when sel==0, route shared tx to pads tx
- # when sel==1, route bridge tx to pads tx
- self.comb += \
- If(self.sel==0,
- pads.tx.eq(self.shared_pads.tx)
- ).Else(
- pads.tx.eq(self.bridge_pads.tx)
- )
-
-class UART2Wishbone(Module, AutoCSR):
- cmds = {
- "write" : 0x01,
- "read" : 0x02
- }
- def __init__(self, pads, clk_freq, baud=115200, share_uart=False):
- self.wishbone = wishbone.Interface()
- if share_uart:
- self._sel = CSRStorage()
- ###
- if share_uart:
- self.uart_mux = UARTMux(pads)
- uart = UART(self.uart_mux.bridge_pads, clk_freq, baud)
- self.shared_pads = self.uart_mux.shared_pads
- self.comb += self.uart_mux.sel.eq(self._sel.storage)
- else:
- uart = UART(pads, clk_freq, baud)
- self.submodules += uart
-
- byte_counter = Counter(bits_sign=3)
- word_counter = Counter(bits_sign=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(uart.rx.source.d)),
- If(length_ce, length.eq(uart.rx.source.d)),
- If(address_ce, address.eq(Cat(uart.rx.source.d, address[0:24]))),
- If(rx_data_ce,
- data.eq(Cat(uart.rx.source.d, data[0:24]))
- ).Elif(tx_data_ce,
- data.eq(self.wishbone.dat_r)
- )
- ]
-
- ###
- fsm = InsertReset(FSM(reset_state="IDLE"))
- timeout = Timeout(clk_freq//10)
- self.submodules += fsm, timeout
-
- self.comb += [
- timeout.ce.eq(1),
- fsm.reset.eq(timeout.reached)
- ]
- fsm.act("IDLE",
- timeout.reset.eq(1),
- If(uart.rx.source.stb,
- cmd_ce.eq(1),
- If( (uart.rx.source.d == self.cmds["write"]) |
- (uart.rx.source.d == self.cmds["read"]),
- NextState("RECEIVE_LENGTH")
- ),
- byte_counter.reset.eq(1),
- word_counter.reset.eq(1)
- )
- )
- fsm.act("RECEIVE_LENGTH",
- If(uart.rx.source.stb,
- length_ce.eq(1),
- NextState("RECEIVE_ADDRESS")
- )
- )
- fsm.act("RECEIVE_ADDRESS",
- If(uart.rx.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(uart.rx.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, uart.tx.sink.d, n=4, reverse=True)
- fsm.act("SEND_DATA",
- uart.tx.sink.stb.eq(1),
- If(uart.tx.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)
- )
- )
- )
- )
required_version = (3, 3)
if sys.version_info < required_version:
- raise SystemExit("Migscope requires python {0} or greater".format(
+ raise SystemExit("LiteScope requires python {0} or greater".format(
".".join(map(str, required_version))))
setup(
- name="miscope",
+ name="litescope",
version="unknown",
- description="Migen based Fpga logic analyzer",
+ description="small footprint and configurable embedded FPGA logic analyzer",
long_description=README,
author="Florent Kermarrec",
author_email="florent@enjoy-digital.fr",
url="http://enjoy-digital.fr",
- download_url="https://github.com/Florent-Kermarrec/miscope",
+ download_url="https://github.com/Florent-Kermarrec/litescope",
packages=find_packages(here),
license="GPL",
platforms=["Any"],
+++ /dev/null
-from migen.bank.description import CSRStatus
-
-def get_csr_csv(csr_base, bank_array):
- r = ""
- for name, csrs, mapaddr, rmap in bank_array.banks:
- reg_base = csr_base + 0x800*mapaddr
- for csr in csrs:
- nr = (csr.size + 7)//8
- r += "{}_{},0x{:08x},{},{}\n".format(name, csr.name, reg_base, nr, "ro" if isinstance(csr, CSRStatus) else "rw")
- reg_base += 4*nr
- return r
+++ /dev/null
-from migen.fhdl.std import *
-from migen.fhdl import verilog
-from migen.bus import csr
-from migen.sim.generic import run_simulation
-from migen.bus.transactions import *
-
-from miscope.std import *
-from miscope.storage import *
-
-from mibuild.tools import write_to_file
-from miscope.tools.regs import *
-from miscope.tools.truthtable import *
-
-from cpuif import *
-
-class Csr2Trans():
- def __init__(self):
- self.t = []
-
- def write_csr(self, adr, value):
- self.t.append(TWrite(adr//4, value))
-
- def read_csr(self, adr):
- self.t.append(TRead(adr//4))
- return 0
-
-
-triggered = False
-dat = 0
-
-rec_done = False
-
-dat_rdy = False
-
-rec_length = 128
-
-def csr_configure(bus, regs):
- # Length
- regs.recorder_length.write(rec_length)
-
- # Offset
- regs.recorder_offset.write(0)
-
- # Trigger
- regs.recorder_trigger.write(1)
-
- return bus.t
-
-def csr_read_data(bus, regs):
- for i in range(rec_length+100):
- regs.recorder_read_dat.read()
- regs.recorder_read_en.write(1)
- return bus.t
-
-def csr_transactions(bus, regs):
- for t in csr_configure(bus, regs):
- yield t
-
- for t in range(100):
- yield None
-
- global triggered
- triggered = True
-
- for t in range(512):
- yield None
-
- for t in csr_read_data(bus, regs):
- yield t
-
- for t in range(100):
- yield None
-
-
-class TB(Module):
- csr_base = 0
- csr_map = {
- "recorder": 1,
- }
- def __init__(self, addrmap=None):
- self.csr_base = 0
-
- # Recorder
- self.submodules.recorder = Recorder(32, 1024)
-
- # Csr
- self.submodules.csrbankarray = csrgen.BankArray(self,
- lambda name, memory: self.csr_map[name if memory is None else name + "_" + memory.name_override])
-
- # Csr Master
- csr_header = get_csr_csv(self.csr_base, self.csrbankarray)
- write_to_file("csr.csv", csr_header)
-
- bus = Csr2Trans()
- regs = build_map(addrmap, bus.read_csr, bus.write_csr)
- self.submodules.master = csr.Initiator(csr_transactions(bus, regs))
-
- self.submodules.csrcon = csr.Interconnect(self.master.bus, self.csrbankarray.get_buses())
-
- # Recorder Data
- def recorder_data(self, selfp):
- selfp.recorder.dat_sink.stb = 1
- if not hasattr(self, "cnt"):
- self.cnt = 0
- self.cnt += 1
-
- selfp.recorder.dat_sink.dat = self.cnt
-
- global triggered
- if triggered:
- selfp.recorder.trig_sink.stb = 1
- selfp.recorder.trig_sink.hit = 1
- triggered = False
- else:
- selfp.recorder.trig_sink.stb = 0
- selfp.recorder.trig_sink.hit = 0
-
- # Simulation
- def end_simulation(self, selfp):
- if self.master.done:
- raise StopSimulation
-
- def do_simulation(self, selfp):
- self.recorder_data(selfp)
- self.end_simulation(selfp)
-
-
-def main():
- tb = TB(addrmap="csr.csv")
- run_simulation(tb, ncycles=2000, vcd_name="tb_recorder_csr.vcd")
- print("Sim Done")
- input()
-
-main()
\ No newline at end of file
+++ /dev/null
-from migen.fhdl.std import *
-from migen.sim.generic import run_simulation
-
-from miscope import storage
-
-rle_test_seq = iter(
- [ 0x00AA,
- 0x00AB,
- 0x00AC,
- 0x00AC,
- 0x00AC,
- 0x00AC,
- 0x00AD,
- 0x00AE,
- 0x00AE,
- 0x00AE,
- 0x00AE,
- 0x00AE,
- 0x00AE,
- 0x00AE,
- 0x00AE
- ]*10
-)
-
-class TB(Module):
- def __init__(self):
-
- # Rle
- self.submodules.rle = storage.RunLengthEncoder(16, 32)
-
- def do_simulation(self, selfp):
- selfp.rle._r_enable.storage = 1
- selfp.rle.sink.stb = 1
- try:
- selfp.rle.sink.dat = next(rle_test_seq)
- except:
- pass
-
-def main():
- tb = TB()
- run_simulation(tb, ncycles=8000, vcd_name="tb_rle.vcd")
- print("Sim Done")
- input()
-
-main()
+++ /dev/null
-from migen.fhdl.std import *
-from migen.fhdl import verilog
-from migen.bus import csr
-from migen.sim.generic import run_simulation
-from migen.bus.transactions import *
-
-from miscope.std import *
-from miscope.trigger import *
-
-from mibuild.tools import write_to_file
-from miscope.tools.regs import *
-from miscope.tools.truthtable import *
-
-from cpuif import *
-
-class Csr2Trans():
- def __init__(self):
- self.t = []
-
- def write_csr(self, adr, value):
- self.t.append(TWrite(adr//4, value))
-
- def read_csr(self, adr):
- self.t.append(TRead(adr//4))
-
-def csr_prog_mila(bus, regs):
- regs.trigger_port0_mask.write(0xFFFFFFFF)
- regs.trigger_port0_trig.write(0xDEADBEEF)
- regs.trigger_port1_mask.write(0xFFFFFFFF)
- regs.trigger_port1_trig.write(0xCAFEFADE)
- regs.trigger_port2_mask.write(0xFFFFFFFF)
- regs.trigger_port2_trig.write(0xDEADBEEF)
- regs.trigger_port3_mask.write(0xFFFFFFFF)
- regs.trigger_port3_trig.write(0xCAFEFADE)
-
- sum_tt = gen_truth_table("i1 & i2 & i3 & i4")
- sum_trans = []
- for i in range(len(sum_tt)):
- regs.trigger_sum_prog_adr.write(i)
- regs.trigger_sum_prog_dat.write(sum_tt[i])
- regs.trigger_sum_prog_we.write(1)
-
- return bus.t
-
-
-csr_done = False
-
-def csr_transactions(bus, regs):
- for t in csr_prog_mila(bus, regs):
- yield t
- global csr_done
- csr_done = True
- for t in range(100):
- yield None
-
-class TB(Module):
- csr_base = 0
- csr_map = {
- "trigger": 1,
- }
- def __init__(self, addrmap=None):
- self.csr_base = 0
-
- # Trigger
- term0 = Term(32)
- term1 = Term(32)
- term2 = Term(32)
- term3 = Term(32)
- self.submodules.trigger = Trigger(32, [term0, term1, term2, term3])
-
- # Csr
- self.submodules.csrbankarray = csrgen.BankArray(self,
- lambda name, memory: self.csr_map[name if memory is None else name + "_" + memory.name_override])
-
- # Csr Master
- csr_header = get_csr_csv(self.csr_base, self.csrbankarray)
- write_to_file("csr.csv", csr_header)
-
- bus = Csr2Trans()
- regs = build_map(addrmap, bus.read_csr, bus.write_csr)
- self.submodules.master = csr.Initiator(csr_transactions(bus, regs))
-
- self.submodules.csrcon = csr.Interconnect(self.master.bus, self.csrbankarray.get_buses())
-
- self.terms = [term0, term1, term2, term3]
-
- def do_simulation(self, selfp):
- for term in selfp.terms:
- term.sink.stb = 1
- if csr_done:
- selfp.terms[0].sink.dat = 0xDEADBEEF
- selfp.terms[1].sink.dat = 0xCAFEFADE
- selfp.terms[2].sink.dat = 0xDEADBEEF
- selfp.terms[3].sink.dat = 0xCAFEFADE
-
-def main():
- tb = TB(addrmap="csr.csv")
- run_simulation(tb, ncycles=2000, vcd_name="tb_trigger_csr.vcd")
- print("Sim Done")
- input()
-
-main()
--- /dev/null
+import os
+
+from migen.bank import csrgen
+from migen.bus import wishbone, csr
+from migen.bus import wishbone2csr
+from migen.genlib.cdc import *
+from migen.genlib.resetsync import AsyncResetSynchronizer
+from migen.bank.description import *
+
+from misoclib import identifier
+
+from litescope.common import *
+from litescope.bridge.uart2wb import LiteScopeUART2WB
+
+class _CRG(Module):
+ def __init__(self, clk_in):
+ self.clock_domains.cd_sys = ClockDomain()
+ self.clock_domains.cd_por = ClockDomain(reset_less=True)
+
+ # Power on Reset (vendor agnostic)
+ rst_n = Signal()
+ self.sync.por += rst_n.eq(1)
+ self.comb += [
+ self.cd_sys.clk.eq(clk_in),
+ self.cd_por.clk.eq(clk_in),
+ self.cd_sys.rst.eq(~rst_n)
+ ]
+
+class GenSoC(Module):
+ csr_base = 0x00000000
+ csr_data_width = 32
+ csr_map = {
+ "bridge": 0,
+ "identifier": 1,
+ }
+ interrupt_map = {}
+ cpu_type = None
+ def __init__(self, platform, clk_freq):
+ self.clk_freq = clk_freq
+ # UART <--> Wishbone bridge
+ self.submodules.uart2wb = LiteScopeUART2WB(platform.request("serial"), clk_freq, baud=115200)
+
+ # CSR bridge 0x00000000 (shadow @0x00000000)
+ self.submodules.wishbone2csr = wishbone2csr.WB2CSR(bus_csr=csr.Interface(self.csr_data_width))
+ self._wb_masters = [self.uart2wb.wishbone]
+ self._wb_slaves = [(lambda a: a[23:25] == 0, self.wishbone2csr.wishbone)]
+ self.cpu_csr_regions = [] # list of (name, origin, busword, csr_list/Memory)
+
+ # CSR
+ self.submodules.identifier = identifier.Identifier(0, int(clk_freq), 0)
+
+ def add_cpu_memory_region(self, name, origin, length):
+ self.cpu_memory_regions.append((name, origin, length))
+
+ def add_cpu_csr_region(self, name, origin, busword, obj):
+ self.cpu_csr_regions.append((name, origin, busword, obj))
+
+ def do_finalize(self):
+ # Wishbone
+ self.submodules.wishbonecon = wishbone.InterconnectShared(self._wb_masters,
+ self._wb_slaves, register=True)
+
+ # 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)
+ self.submodules.csrcon = csr.Interconnect(self.wishbone2csr.csr, self.csrbankarray.get_buses())
+ for name, csrs, mapaddr, rmap in self.csrbankarray.banks:
+ self.add_cpu_csr_region(name, 0xe0000000+0x800*mapaddr, flen(rmap.bus.dat_w), csrs)
+ for name, memory, mapaddr, mmap in self.csrbankarray.srams:
+ self.add_cpu_csr_region(name, 0xe0000000+0x800*mapaddr, flen(rmap.bus.dat_w), memory)
+
+class LiteScopeSoC(GenSoC, AutoCSR):
+ default_platform = "de0nano"
+ csr_map = {}
+ csr_map.update(GenSoC.csr_map)
+
+ def __init__(self, platform, export_mila=False):
+ clk_freq = 50*1000000
+ GenSoC.__init__(self, platform, clk_freq)
+ self.submodules.crg = _CRG(platform.request("clk50"))
+
+default_subtarget = LiteScopeSoC
--- /dev/null
+LSDIR = ../
+PYTHON = python3
+
+CMD = PYTHONPATH=$(LSDIR) $(PYTHON)
+
+test_regs:
+ $(CMD) test_regs.py
--- /dev/null
+from litescope.host.driver import LiteScopeUART2WBDriver
+
+csr_csv_file = "./csr.csv"
+busword = 32
+debug_wb = False
+
+com = 3
+baud = 115200
+wb = LiteScopeUART2WBDriver(com, baud, csr_csv_file, busword, debug_wb)
\ No newline at end of file
--- /dev/null
+from config import *
+
+wb.open()
+regs = wb.regs
+###
+print("sysid : 0x%04x" %regs.identifier_sysid.read())
+print("revision : 0x%04x" %regs.identifier_revision.read())
+print("frequency : %d MHz" %(regs.identifier_frequency.read()/1000000))
+print("l2_size : %d" %regs.identifier_l2_size.read())
+###
+wb.close()