From f35f93a7c5601af9daec89a0de321a432110a729 Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Thu, 22 Jan 2015 21:40:07 +0100 Subject: [PATCH] start refactoring and change name to LiteScope --- LICENSE | 28 +++ README | 160 +++++++++--- litesata-version.txt | 2 + {miscope/host => litescope}/__init__.py | 0 .../bridge/uart2wb.py | 38 +-- litescope/common.py | 36 +++ {miscope => litescope/core}/storage.py | 6 +- {miscope => litescope/core}/trigger.py | 14 +- miscope/miio.py => litescope/frontend/io.py | 2 +- miscope/mila.py => litescope/frontend/la.py | 14 +- litescope/host/__init__.py | 0 {miscope => litescope}/host/cpuif.py | 0 litescope/host/driver.py | 230 ++++++++++++++++++ {miscope => litescope}/host/dump.py | 0 miscope/host/regs.py => litescope/host/reg.py | 0 {miscope => litescope}/host/truthtable.py | 0 make.py | 124 ++++++++++ miscope/__init__.py | 7 - miscope/host/drivers.py | 143 ----------- miscope/host/uart2wishbone.py | 87 ------- miscope/std.py | 13 - setup.py | 8 +- sim/cpuif.py | 11 - sim/tb_recorder_csr.py | 134 ---------- sim/tb_rle.py | 45 ---- sim/tb_trigger_csr.py | 102 -------- targets/__init__.py | 0 targets/simple.py | 83 +++++++ test/Makefile | 7 + test/config.py | 9 + test/test_regs.py | 11 + 31 files changed, 681 insertions(+), 633 deletions(-) create mode 100644 LICENSE create mode 100644 litesata-version.txt rename {miscope/host => litescope}/__init__.py (100%) rename miscope/uart2wishbone.py => litescope/bridge/uart2wb.py (84%) create mode 100644 litescope/common.py rename {miscope => litescope/core}/storage.py (95%) rename {miscope => litescope/core}/trigger.py (91%) rename miscope/miio.py => litescope/frontend/io.py (86%) rename miscope/mila.py => litescope/frontend/la.py (83%) create mode 100644 litescope/host/__init__.py rename {miscope => litescope}/host/cpuif.py (100%) create mode 100644 litescope/host/driver.py rename {miscope => litescope}/host/dump.py (100%) rename miscope/host/regs.py => litescope/host/reg.py (100%) rename {miscope => litescope}/host/truthtable.py (100%) create mode 100644 make.py delete mode 100644 miscope/__init__.py delete mode 100644 miscope/host/drivers.py delete mode 100644 miscope/host/uart2wishbone.py delete mode 100644 miscope/std.py delete mode 100644 sim/cpuif.py delete mode 100644 sim/tb_recorder_csr.py delete mode 100644 sim/tb_rle.py delete mode 100644 sim/tb_trigger_csr.py create mode 100644 targets/__init__.py create mode 100644 targets/simple.py create mode 100644 test/Makefile create mode 100644 test/config.py create mode 100644 test/test_regs.py diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..95ac4a6a --- /dev/null +++ b/LICENSE @@ -0,0 +1,28 @@ +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. diff --git a/README b/README index a2239f92..fd682777 100644 --- a/README +++ b/README @@ -1,48 +1,130 @@ - _____ _ ____ _ _ _ _ - | __|___ |_|___ _ _ | \|_|___|_| |_ ___| | - | __| | | | . | | | | | | | . | | _| .'| | - |_____|_|_|_| |___|_ | |____/|_|_ |_|_| |__,|_| - |___| |___| |___| - - 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 diff --git a/litesata-version.txt b/litesata-version.txt new file mode 100644 index 00000000..eba33402 --- /dev/null +++ b/litesata-version.txt @@ -0,0 +1,2 @@ +0.9.0 + diff --git a/miscope/host/__init__.py b/litescope/__init__.py similarity index 100% rename from miscope/host/__init__.py rename to litescope/__init__.py diff --git a/miscope/uart2wishbone.py b/litescope/bridge/uart2wb.py similarity index 84% rename from miscope/uart2wishbone.py rename to litescope/bridge/uart2wb.py index e8e773f6..697843b3 100644 --- a/miscope/uart2wishbone.py +++ b/litescope/bridge/uart2wb.py @@ -7,28 +7,7 @@ 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) - ] +from litescope.common import * class UART(Module, AutoCSR): def __init__(self, pads, clk_freq, baud=115200): @@ -74,7 +53,7 @@ class UARTMux(Module): pads.tx.eq(self.bridge_pads.tx) ) -class UART2Wishbone(Module, AutoCSR): +class LiteScopeUART2WB(Module, AutoCSR): cmds = { "write" : 0x01, "read" : 0x02 @@ -85,19 +64,19 @@ class UART2Wishbone(Module, AutoCSR): 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) + 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 + 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() @@ -126,7 +105,6 @@ class UART2Wishbone(Module, AutoCSR): 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) diff --git a/litescope/common.py b/litescope/common.py new file mode 100644 index 00000000..b6bc372e --- /dev/null +++ b/litescope/common.py @@ -0,0 +1,36 @@ +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) + ] diff --git a/miscope/storage.py b/litescope/core/storage.py similarity index 95% rename from miscope/storage.py rename to litescope/core/storage.py index 31797045..cbd14c93 100644 --- a/miscope/storage.py +++ b/litescope/core/storage.py @@ -4,9 +4,9 @@ from migen.genlib.fifo import SyncFIFOBuffered as SyncFIFO from migen.genlib.fsm import FSM, NextState from migen.genlib.record import * -from miscope.std import * +from litescope.common import * -class RunLengthEncoder(Module, AutoCSR): +class LiteScopeRunLengthEncoder(Module, AutoCSR): def __init__(self, width, length=1024): self.width = width self.length = length @@ -58,7 +58,7 @@ class RunLengthEncoder(Module, AutoCSR): ) ), -class Recorder(Module, AutoCSR): +class LiteScopeRecorder(Module, AutoCSR): def __init__(self, width, depth): self.width = width diff --git a/miscope/trigger.py b/litescope/core/trigger.py similarity index 91% rename from miscope/trigger.py rename to litescope/core/trigger.py index 6e795f52..f9b792be 100644 --- a/miscope/trigger.py +++ b/litescope/core/trigger.py @@ -3,9 +3,9 @@ from migen.fhdl.specials import Memory from migen.bank.description import * from migen.genlib.record import * -from miscope.std import * +from litescope.common import * -class Term(Module, AutoCSR): +class LiteScopeTerm(Module, AutoCSR): def __init__(self, width): self.width = width @@ -27,7 +27,7 @@ class Term(Module, AutoCSR): self.source.stb.eq(self.sink.stb) ] -class RangeDetector(Module, AutoCSR): +class LiteScopeRangeDetector(Module, AutoCSR): def __init__(self, width): self.width = width @@ -49,7 +49,7 @@ class RangeDetector(Module, AutoCSR): self.source.stb.eq(self.sink.stb) ] -class EdgeDetector(Module, AutoCSR): +class LiteScopeEdgeDetector(Module, AutoCSR): def __init__(self, width): self.width = width @@ -83,7 +83,7 @@ class EdgeDetector(Module, AutoCSR): self.source.stb.eq(self.sink.stb) ] -class Sum(Module, AutoCSR): +class LiteScopeSum(Module, AutoCSR): def __init__(self, ports=4): self.sinks = [Record(hit_layout()) for p in range(ports)] @@ -119,12 +119,12 @@ class Sum(Module, AutoCSR): ] -class Trigger(Module, AutoCSR): +class LiteScopeTrigger(Module, AutoCSR): def __init__(self, width, ports): self.width = width self.ports = ports - self.submodules.sum = Sum(len(ports)) + self.submodules.sum = LiteScopeSum(len(ports)) for i, port in enumerate(ports): setattr(self.submodules, "port"+str(i), port) diff --git a/miscope/miio.py b/litescope/frontend/io.py similarity index 86% rename from miscope/miio.py rename to litescope/frontend/io.py index a78c056b..3fb2d318 100644 --- a/miscope/miio.py +++ b/litescope/frontend/io.py @@ -1,7 +1,7 @@ from migen.fhdl.structure import * from migen.bank.description import * -class MiIo(Module, AutoCSR): +class LiteScopeIO(Module, AutoCSR): def __init__(self, width): self._r_i = CSRStatus(width) self._r_o = CSRStorage(width) diff --git a/miscope/mila.py b/litescope/frontend/la.py similarity index 83% rename from miscope/mila.py rename to litescope/frontend/la.py index abf8e79c..a69f749f 100644 --- a/miscope/mila.py +++ b/litescope/frontend/la.py @@ -3,9 +3,9 @@ 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 litescope.common import * +from litescope.core.trigger import LiteScopeTrigger +from litescope.core.storage import LiteScopeRecorder, LiteScopeRunLengthEncoder from mibuild.tools import write_to_file @@ -17,7 +17,7 @@ def _getattr_all(l, attr): raise ValueError return r -class MiLa(Module, AutoCSR): +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 @@ -67,15 +67,15 @@ class MiLa(Module, AutoCSR): sink.dat.eq(dat) ] - self.submodules.trigger = trigger = Trigger(self.width, self.ports) - self.submodules.recorder = recorder = Recorder(self.width, self.depth) + 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 = RunLengthEncoder(self.width) + self.submodules.rle = rle = LiteScopeRunLengthEncoder(self.width) self.comb += [ sink.connect(rle.sink), rle.source.connect(recorder.dat_sink) diff --git a/litescope/host/__init__.py b/litescope/host/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/miscope/host/cpuif.py b/litescope/host/cpuif.py similarity index 100% rename from miscope/host/cpuif.py rename to litescope/host/cpuif.py diff --git a/litescope/host/driver.py b/litescope/host/driver.py new file mode 100644 index 00000000..31645197 --- /dev/null +++ b/litescope/host/driver.py @@ -0,0 +1,230 @@ +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 diff --git a/miscope/host/dump.py b/litescope/host/dump.py similarity index 100% rename from miscope/host/dump.py rename to litescope/host/dump.py diff --git a/miscope/host/regs.py b/litescope/host/reg.py similarity index 100% rename from miscope/host/regs.py rename to litescope/host/reg.py diff --git a/miscope/host/truthtable.py b/litescope/host/truthtable.py similarity index 100% rename from miscope/host/truthtable.py rename to litescope/host/truthtable.py diff --git a/make.py b/make.py new file mode 100644 index 00000000..987f450e --- /dev/null +++ b/make.py @@ -0,0 +1,124 @@ +#!/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) diff --git a/miscope/__init__.py b/miscope/__init__.py deleted file mode 100644 index 03249cdb..00000000 --- a/miscope/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -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 diff --git a/miscope/host/drivers.py b/miscope/host/drivers.py deleted file mode 100644 index 44084646..00000000 --- a/miscope/host/drivers.py +++ /dev/null @@ -1,143 +0,0 @@ -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 diff --git a/miscope/host/uart2wishbone.py b/miscope/host/uart2wishbone.py deleted file mode 100644 index e9590866..00000000 --- a/miscope/host/uart2wishbone.py +++ /dev/null @@ -1,87 +0,0 @@ -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))) diff --git a/miscope/std.py b/miscope/std.py deleted file mode 100644 index d7ffc7e3..00000000 --- a/miscope/std.py +++ /dev/null @@ -1,13 +0,0 @@ -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) - ] diff --git a/setup.py b/setup.py index 740be67b..979c1655 100644 --- a/setup.py +++ b/setup.py @@ -9,18 +9,18 @@ README = open(os.path.join(here, "README")).read() 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"], diff --git a/sim/cpuif.py b/sim/cpuif.py deleted file mode 100644 index 6a0ed40b..00000000 --- a/sim/cpuif.py +++ /dev/null @@ -1,11 +0,0 @@ -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 diff --git a/sim/tb_recorder_csr.py b/sim/tb_recorder_csr.py deleted file mode 100644 index 368bb800..00000000 --- a/sim/tb_recorder_csr.py +++ /dev/null @@ -1,134 +0,0 @@ -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 diff --git a/sim/tb_rle.py b/sim/tb_rle.py deleted file mode 100644 index 1dd8e75c..00000000 --- a/sim/tb_rle.py +++ /dev/null @@ -1,45 +0,0 @@ -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() diff --git a/sim/tb_trigger_csr.py b/sim/tb_trigger_csr.py deleted file mode 100644 index ef74703b..00000000 --- a/sim/tb_trigger_csr.py +++ /dev/null @@ -1,102 +0,0 @@ -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() diff --git a/targets/__init__.py b/targets/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/targets/simple.py b/targets/simple.py new file mode 100644 index 00000000..40c9993c --- /dev/null +++ b/targets/simple.py @@ -0,0 +1,83 @@ +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 diff --git a/test/Makefile b/test/Makefile new file mode 100644 index 00000000..6d23bdcc --- /dev/null +++ b/test/Makefile @@ -0,0 +1,7 @@ +LSDIR = ../ +PYTHON = python3 + +CMD = PYTHONPATH=$(LSDIR) $(PYTHON) + +test_regs: + $(CMD) test_regs.py diff --git a/test/config.py b/test/config.py new file mode 100644 index 00000000..7fc57068 --- /dev/null +++ b/test/config.py @@ -0,0 +1,9 @@ +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 diff --git a/test/test_regs.py b/test/test_regs.py new file mode 100644 index 00000000..bb59b641 --- /dev/null +++ b/test/test_regs.py @@ -0,0 +1,11 @@ +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() -- 2.30.2