start refactoring and change name to LiteScope
This commit is contained in:
parent
609f8f9abb
commit
f35f93a7c5
|
@ -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
156
README
|
@ -1,48 +1,130 @@
|
|||
_____ _ ____ _ _ _ _
|
||||
| __|___ |_|___ _ _ | \|_|___|_| |_ ___| |
|
||||
| __| | | | . | | | | | | | . | | _| .'| |
|
||||
|_____|_|_|_| |___|_ | |____/|_|_ |_|_| |__,|_|
|
||||
|___| |___| |___|
|
||||
|
||||
Copyright 2012-2014 / Florent Kermarrec / florent@enjoy-digital.fr
|
||||
|
||||
Miscope
|
||||
--------------------------------------------------------------------------------
|
||||
__ _ __ ____
|
||||
/ / (_) /____ / __/______ ___ ___
|
||||
/ /__/ / __/ -_)\ \/ __/ _ \/ _ \/ -_)
|
||||
/____/_/\__/\__/___/\__/\___/ .__/\__/
|
||||
/_/
|
||||
Copyright 2012-2015 / EnjoyDigital
|
||||
florent@enjoy-digital.fr
|
||||
|
||||
[> Miscope
|
||||
------------
|
||||
A small footprint and configurable embedded FPGA
|
||||
logic analyzer core by EnjoyDigital
|
||||
|
||||
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
|
||||
[> 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...
|
||||
|
||||
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!
|
||||
The core uses simple and specific streaming buses and will provides in the future
|
||||
adapters to use standardized AXI or Avalon-ST streaming buses.
|
||||
|
||||
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
|
||||
pins to be ready to debug!
|
||||
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.
|
||||
|
||||
[> Status:
|
||||
MiIo & Mila working on board with standard term.
|
||||
RLE working on board.
|
||||
RangeDetector and EdgeDector terms not tested.
|
||||
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.
|
||||
|
||||
[> 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.
|
||||
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
|
||||
|
||||
[> 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
|
||||
E-mail: florent@enjoy-digital.fr
|
||||
E-mail: florent [AT] enjoy-digital.fr
|
|
@ -0,0 +1,2 @@
|
|||
0.9.0
|
||||
|
|
@ -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)
|
|
@ -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)
|
||||
]
|
|
@ -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
|
||||
|
|
@ -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)
|
||||
|
|
@ -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)
|
|
@ -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)
|
|
@ -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
|
|
@ -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)
|
|
@ -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
|
|
@ -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
|
|
@ -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)))
|
|
@ -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)
|
||||
]
|
8
setup.py
8
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"],
|
||||
|
|
11
sim/cpuif.py
11
sim/cpuif.py
|
@ -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
|
|
@ -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()
|
|
@ -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()
|
|
@ -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,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
|
|
@ -0,0 +1,7 @@
|
|||
LSDIR = ../
|
||||
PYTHON = python3
|
||||
|
||||
CMD = PYTHONPATH=$(LSDIR) $(PYTHON)
|
||||
|
||||
test_regs:
|
||||
$(CMD) test_regs.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)
|
|
@ -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()
|
Loading…
Reference in New Issue