start refactoring and change name to LiteScope

This commit is contained in:
Florent Kermarrec 2015-01-22 21:40:07 +01:00
parent 609f8f9abb
commit f35f93a7c5
31 changed files with 679 additions and 631 deletions

28
LICENSE Normal file
View File

@ -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.

156
README
View File

@ -1,48 +1,130 @@
_____ _ ____ _ _ _ _ __ _ __ ____
| __|___ |_|___ _ _ | \|_|___|_| |_ ___| | / / (_) /____ / __/______ ___ ___
| __| | | | . | | | | | | | . | | _| .'| | / /__/ / __/ -_)\ \/ __/ _ \/ _ \/ -_)
|_____|_|_|_| |___|_ | |____/|_|_ |_|_| |__,|_| /____/_/\__/\__/___/\__/\___/ .__/\__/
|___| |___| |___| /_/
Copyright 2012-2015 / EnjoyDigital
florent@enjoy-digital.fr
Copyright 2012-2014 / Florent Kermarrec / florent@enjoy-digital.fr A small footprint and configurable embedded FPGA
logic analyzer core by EnjoyDigital
Miscope [> Intro
-------------------------------------------------------------------------------- ---------
LiteScope small footprint and configurable embedded logic analyzer that you
[> Miscope can use in your FPGA and aims to provide a a free, portable and flexible
------------
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
alternatve to vendor's solutions! 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...
Miscope provides Migen cores to embed in the design and Python drivers to control The core uses simple and specific streaming buses and will provides in the future
the logic analyzer from the Host. Miscope automatically interconnects all cores adapters to use standardized AXI or Avalon-ST streaming buses.
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!
Miscope produces .vcd output files to be analyzed in your favorite waveform viewer. Since Python is used to describe the HDL, the core is highly and easily
configurable.
Since Miscope also provides an Uart2Wishbone bridge, you only need 2 external Rx/Tx LiteScope uses technologies developed in partnership with M-Labs Ltd:
pins to be ready to debug! - Migen enables generating HDL with Python in an efficient way.
- MiSoC provides the basic blocks to build a powerful and small footprint SoC.
[> Status: LiteScope can be used as a Migen/MiSoC library (by simply installing it
MiIo & Mila working on board with standard term. with the provided setup.py) or can be integrated with your standard design flow
RLE working on board. by generating the verilog rtl that you will use as a standard core.
RangeDetector and EdgeDector terms not tested.
[> Examples: LiteScope produces "vcd" files that can be read in your regular waveforms viewer.
Have a look at http://github.com/Florent-Kermarrec/misoc-de0nano
test_miio.py : Led & Switch Test controlled by Python Host. Since LiteScope also provides an UART <--> Wishbone brige you only need 2 external
test_mila.py : Logic Analyzer controlled by Python Host. 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
[> 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.
[> Support and Consulting
--------------------------
We love open-source hardware and like sharing our designs with others.
LiteScope is developed and maintained by EnjoyDigital.
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.
So feel free to contact us, we'd love to work with you! (and eventually shorten
the list of the possible improvements :)
[> Contact [> Contact
E-mail: florent@enjoy-digital.fr E-mail: florent [AT] enjoy-digital.fr

2
litesata-version.txt Normal file
View File

@ -0,0 +1,2 @@
0.9.0

View File

@ -7,28 +7,7 @@ from migen.bus import wishbone
from misoclib.uart import UARTRX, UARTTX from misoclib.uart import UARTRX, UARTTX
@DecorateModule(InsertReset) from litescope.common import *
@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): class UART(Module, AutoCSR):
def __init__(self, pads, clk_freq, baud=115200): def __init__(self, pads, clk_freq, baud=115200):
@ -74,7 +53,7 @@ class UARTMux(Module):
pads.tx.eq(self.bridge_pads.tx) pads.tx.eq(self.bridge_pads.tx)
) )
class UART2Wishbone(Module, AutoCSR): class LiteScopeUART2WB(Module, AutoCSR):
cmds = { cmds = {
"write" : 0x01, "write" : 0x01,
"read" : 0x02 "read" : 0x02
@ -85,19 +64,19 @@ class UART2Wishbone(Module, AutoCSR):
self._sel = CSRStorage() self._sel = CSRStorage()
### ###
if share_uart: if share_uart:
self.uart_mux = UARTMux(pads) uart_mux = UARTMux(pads)
uart = UART(self.uart_mux.bridge_pads, clk_freq, baud) uart = UART(uart_mux.bridge_pads, clk_freq, baud)
self.shared_pads = self.uart_mux.shared_pads self.submodules += uart_mux, uart
self.comb += self.uart_mux.sel.eq(self._sel.storage) self.shared_pads = uart_mux.shared_pads
self.comb += uart_mux.sel.eq(self._sel.storage)
else: else:
uart = UART(pads, clk_freq, baud) uart = UART(pads, clk_freq, baud)
self.submodules += uart self.submodules += uart
byte_counter = Counter(bits_sign=3) byte_counter = Counter(bits_sign=3)
word_counter = Counter(bits_sign=8) word_counter = Counter(bits_sign=8)
self.submodules += byte_counter, word_counter self.submodules += byte_counter, word_counter
cmd = Signal(8) cmd = Signal(8)
cmd_ce = Signal() cmd_ce = Signal()
@ -126,7 +105,6 @@ class UART2Wishbone(Module, AutoCSR):
fsm = InsertReset(FSM(reset_state="IDLE")) fsm = InsertReset(FSM(reset_state="IDLE"))
timeout = Timeout(clk_freq//10) timeout = Timeout(clk_freq//10)
self.submodules += fsm, timeout self.submodules += fsm, timeout
self.comb += [ self.comb += [
timeout.ce.eq(1), timeout.ce.eq(1),
fsm.reset.eq(timeout.reached) fsm.reset.eq(timeout.reached)

36
litescope/common.py Normal file
View File

@ -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)
]

View File

@ -4,9 +4,9 @@ from migen.genlib.fifo import SyncFIFOBuffered as SyncFIFO
from migen.genlib.fsm import FSM, NextState from migen.genlib.fsm import FSM, NextState
from migen.genlib.record import * 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): def __init__(self, width, length=1024):
self.width = width self.width = width
self.length = length self.length = length
@ -58,7 +58,7 @@ class RunLengthEncoder(Module, AutoCSR):
) )
), ),
class Recorder(Module, AutoCSR): class LiteScopeRecorder(Module, AutoCSR):
def __init__(self, width, depth): def __init__(self, width, depth):
self.width = width self.width = width

View File

@ -3,9 +3,9 @@ from migen.fhdl.specials import Memory
from migen.bank.description import * from migen.bank.description import *
from migen.genlib.record 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): def __init__(self, width):
self.width = width self.width = width
@ -27,7 +27,7 @@ class Term(Module, AutoCSR):
self.source.stb.eq(self.sink.stb) self.source.stb.eq(self.sink.stb)
] ]
class RangeDetector(Module, AutoCSR): class LiteScopeRangeDetector(Module, AutoCSR):
def __init__(self, width): def __init__(self, width):
self.width = width self.width = width
@ -49,7 +49,7 @@ class RangeDetector(Module, AutoCSR):
self.source.stb.eq(self.sink.stb) self.source.stb.eq(self.sink.stb)
] ]
class EdgeDetector(Module, AutoCSR): class LiteScopeEdgeDetector(Module, AutoCSR):
def __init__(self, width): def __init__(self, width):
self.width = width self.width = width
@ -83,7 +83,7 @@ class EdgeDetector(Module, AutoCSR):
self.source.stb.eq(self.sink.stb) self.source.stb.eq(self.sink.stb)
] ]
class Sum(Module, AutoCSR): class LiteScopeSum(Module, AutoCSR):
def __init__(self, ports=4): def __init__(self, ports=4):
self.sinks = [Record(hit_layout()) for p in range(ports)] 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): def __init__(self, width, ports):
self.width = width self.width = width
self.ports = ports self.ports = ports
self.submodules.sum = Sum(len(ports)) self.submodules.sum = LiteScopeSum(len(ports))
for i, port in enumerate(ports): for i, port in enumerate(ports):
setattr(self.submodules, "port"+str(i), port) setattr(self.submodules, "port"+str(i), port)

View File

@ -1,7 +1,7 @@
from migen.fhdl.structure import * from migen.fhdl.structure import *
from migen.bank.description import * from migen.bank.description import *
class MiIo(Module, AutoCSR): class LiteScopeIO(Module, AutoCSR):
def __init__(self, width): def __init__(self, width):
self._r_i = CSRStatus(width) self._r_i = CSRStatus(width)
self._r_o = CSRStorage(width) self._r_o = CSRStorage(width)

View File

@ -3,9 +3,9 @@ from migen.fhdl import verilog
from migen.bank.description import * from migen.bank.description import *
from migen.actorlib.fifo import AsyncFIFO from migen.actorlib.fifo import AsyncFIFO
from miscope.std import * from litescope.common import *
from miscope.trigger import Trigger from litescope.core.trigger import LiteScopeTrigger
from miscope.storage import Recorder, RunLengthEncoder from litescope.core.storage import LiteScopeRecorder, LiteScopeRunLengthEncoder
from mibuild.tools import write_to_file from mibuild.tools import write_to_file
@ -17,7 +17,7 @@ def _getattr_all(l, attr):
raise ValueError raise ValueError
return r return r
class MiLa(Module, AutoCSR): class LiteScopeLA(Module, AutoCSR):
def __init__(self, depth, dat, with_rle=False, clk_domain="sys", pipe=False): def __init__(self, depth, dat, with_rle=False, clk_domain="sys", pipe=False):
self.depth = depth self.depth = depth
self.with_rle = with_rle self.with_rle = with_rle
@ -67,15 +67,15 @@ class MiLa(Module, AutoCSR):
sink.dat.eq(dat) sink.dat.eq(dat)
] ]
self.submodules.trigger = trigger = Trigger(self.width, self.ports) self.submodules.trigger = trigger = LiteScopeTrigger(self.width, self.ports)
self.submodules.recorder = recorder = Recorder(self.width, self.depth) self.submodules.recorder = recorder = LiteScopeRecorder(self.width, self.depth)
self.comb += [ self.comb += [
sink.connect(trigger.sink), sink.connect(trigger.sink),
trigger.source.connect(recorder.trig_sink) trigger.source.connect(recorder.trig_sink)
] ]
if self.with_rle: if self.with_rle:
self.submodules.rle = rle = RunLengthEncoder(self.width) self.submodules.rle = rle = LiteScopeRunLengthEncoder(self.width)
self.comb += [ self.comb += [
sink.connect(rle.sink), sink.connect(rle.sink),
rle.source.connect(recorder.dat_sink) rle.source.connect(recorder.dat_sink)

View File

230
litescope/host/driver.py Normal file
View File

@ -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

124
make.py Normal file
View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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)))

View File

@ -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)
]

View File

@ -9,18 +9,18 @@ README = open(os.path.join(here, "README")).read()
required_version = (3, 3) required_version = (3, 3)
if sys.version_info < required_version: 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)))) ".".join(map(str, required_version))))
setup( setup(
name="miscope", name="litescope",
version="unknown", version="unknown",
description="Migen based Fpga logic analyzer", description="small footprint and configurable embedded FPGA logic analyzer",
long_description=README, long_description=README,
author="Florent Kermarrec", author="Florent Kermarrec",
author_email="florent@enjoy-digital.fr", author_email="florent@enjoy-digital.fr",
url="http://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), packages=find_packages(here),
license="GPL", license="GPL",
platforms=["Any"], platforms=["Any"],

View File

@ -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

View File

@ -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()

View File

@ -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()

View File

@ -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()

0
targets/__init__.py Normal file
View File

83
targets/simple.py Normal file
View File

@ -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

7
test/Makefile Normal file
View File

@ -0,0 +1,7 @@
LSDIR = ../
PYTHON = python3
CMD = PYTHONPATH=$(LSDIR) $(PYTHON)
test_regs:
$(CMD) test_regs.py

9
test/config.py Normal file
View File

@ -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)

11
test/test_regs.py Normal file
View File

@ -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()