unify region generation and added SPI to PicoRV32
This commit is contained in:
parent
6f61d7db7a
commit
da1e9238ab
|
@ -49,6 +49,7 @@ hardware-get:
|
|||
docker cp upsilon-hardware:/home/user/upsilon/gateware/build/digilent_arty/gateware/digilent_arty.bit ../boot/
|
||||
docker cp upsilon-hardware:/home/user/upsilon/gateware/arty.dtb ../boot/
|
||||
docker cp upsilon-hardware:/home/user/upsilon/gateware/csr.json ../boot/
|
||||
docker cp upsilon-hardware:/home/user/upsilon/gateware/soc_subregions.json ../boot/
|
||||
docker cp upsilon-hardware:/home/user/upsilon/gateware/pico0.json ../boot/
|
||||
docker cp upsilon-hardware:/home/user/upsilon/gateware/mmio.py ../boot/
|
||||
hardware-clean:
|
||||
|
|
|
@ -34,13 +34,39 @@ The ``machine`` module contains arrays called ``mem8``, ``mem16``, and ``mem32``
|
|||
They are used to directly access memory locations on the main CPU bus. Note
|
||||
that ``mem32`` accesses must be word aligned.
|
||||
|
||||
Locations of interest are included as constants in the ``mmio`` module, which
|
||||
is generated from memory map JSON files (``csr.json`` and others). At some
|
||||
point there will be a standard library that wraps these accesses as functions.
|
||||
-------------------
|
||||
Accessing Registers
|
||||
-------------------
|
||||
|
||||
----------------
|
||||
At the lowest level, a program will write to and read from "registers." These
|
||||
registers control the operations of various parts of the system.
|
||||
|
||||
The main bus has two register buses: "CSR" (which is the LiteX default), and
|
||||
custom Wishbone code. CSR register information is in the ``csr.json`` file.
|
||||
Wishbone bus registers are allocated with regions that are specified in
|
||||
``csr.json``, while the actual registers inside that region are located in
|
||||
``soc_subregions.json``. These should be automatically dumped to the Micropython
|
||||
file ``mmio.py`` for easy usage.
|
||||
|
||||
====================
|
||||
System Within a Chip
|
||||
====================
|
||||
|
||||
Systems Within a Chip (**SWiCs**) are CPUs that are controlled by the main CPU
|
||||
but run seperately (they have their own registers, RAM, etc.) They can be
|
||||
programmed and controlled through Micropython.
|
||||
|
||||
The SWiC is a RV32IMC core. Code for the SWiC needs to be compiled for a start
|
||||
address of ``0x10000`` and a IRQ handler at ``0x10010``. The default length of
|
||||
the SWiC region is ``0x1000`` bytes.
|
||||
|
||||
Each core is given the name ``pico0``, ``pico1``, etc. The regions of each CPU
|
||||
are stored in ``pico0.json``, ``pico1.json``, etc. The system used to control
|
||||
slave access to the CPU bus is a CSR (and should be in ``mmio.py``).
|
||||
|
||||
================
|
||||
Computer Control
|
||||
----------------
|
||||
================
|
||||
|
||||
Micropython code can be loaded manually with SSH but this gets cumbersome.
|
||||
Python scripts on the controlling computer connected to the Upsilon FPGA can
|
||||
|
|
|
@ -15,7 +15,7 @@ csr.json build/digilent_arty/digilent_arty.bit: soc.py
|
|||
python3 soc.py
|
||||
|
||||
mmio.py: csr.json csr2mp.py
|
||||
python3 csr2mp.py csr.json > mmio.py
|
||||
python3 csr2mp.py > mmio.py
|
||||
|
||||
clean:
|
||||
rm -rf build csr.json arty.dts arty.dtb mmio.py
|
||||
|
|
|
@ -13,17 +13,22 @@
|
|||
import collections
|
||||
import argparse
|
||||
import json
|
||||
import sys
|
||||
import mmio_descr
|
||||
|
||||
with open(sys.argv[1], 'rt') as f:
|
||||
j = json.load(f)
|
||||
with open('csr.json', 'rt') as f:
|
||||
csrs = json.load(f)
|
||||
|
||||
print("from micropython import const")
|
||||
|
||||
for key in j["csr_registers"]:
|
||||
for key in csrs["csr_registers"]:
|
||||
if key.startswith("pico0"):
|
||||
print(f'{key} = const({j["csr_registers"][key]["addr"]})')
|
||||
print(f'{key} = const({csrs["csr_registers"][key]["addr"]})')
|
||||
|
||||
print(f'pico0_ram = const({j["memories"]["pico0_ram"]["base"]})')
|
||||
print(f'pico0_dbg_reg = const({j["memories"]["pico0_dbg_reg"]["base"]})')
|
||||
with open('soc_subregions.json', 'rt') as f:
|
||||
subregions = json.load(f)
|
||||
|
||||
for key in subregions:
|
||||
if subregions[key] is None:
|
||||
print(f'{key} = const({csrs["memories"][key]["base"]})')
|
||||
else:
|
||||
print(f'{key}_base = const({csrs["memorys"][key]["base"]})')
|
||||
print(f'{key} = {subregions[key].__repr__()}')
|
||||
|
|
|
@ -10,8 +10,51 @@ from litex.soc.interconnect.wishbone import Interface
|
|||
from util import *
|
||||
|
||||
class SPIMaster(Module):
|
||||
AD5791_PARAMS = {
|
||||
"polarity" :0,
|
||||
"phase" :1,
|
||||
"spi_cycle_half_wait" : 10,
|
||||
"ss_wait" : 5,
|
||||
"enable_miso" : 1,
|
||||
"enable_mosi" : 1,
|
||||
"spi_wid" : 24,
|
||||
}
|
||||
|
||||
LT_ADC_PARAMS = {
|
||||
"polarity" : 1,
|
||||
"phase" : 0,
|
||||
"spi_cycle_half_wait" : 5,
|
||||
"ss_wait" : 60,
|
||||
"enable_mosi" : 0,
|
||||
}
|
||||
|
||||
width = 0x10
|
||||
|
||||
registers = {
|
||||
"finished_or_ready": {
|
||||
"origin" : 0,
|
||||
"width" : 4,
|
||||
"rw": False,
|
||||
},
|
||||
"arm" : {
|
||||
"origin": 4,
|
||||
"width": 4,
|
||||
"rw": True,
|
||||
},
|
||||
"from_slave": {
|
||||
"origin": 8,
|
||||
"width": 4,
|
||||
"rw": False,
|
||||
},
|
||||
"to_slave": {
|
||||
"origin": 0xC,
|
||||
"width": 4,
|
||||
"rw": True
|
||||
},
|
||||
}
|
||||
|
||||
""" Wrapper for the SPI master verilog code. """
|
||||
def __init__(self, rst, miso, mosi, sck, ss,
|
||||
def __init__(self, rst, miso, mosi, sck, ss_L,
|
||||
polarity = 0,
|
||||
phase = 0,
|
||||
ss_wait = 1,
|
||||
|
@ -28,8 +71,8 @@ class SPIMaster(Module):
|
|||
:param ss: SS signal.
|
||||
:param phase: Phase of SPI master. This phase is not the standard
|
||||
SPI phase because it is defined in terms of the rising edge, not
|
||||
the leading edge. See <https://software.mcgoron.com/peter/spi>
|
||||
:param polarity: See <https://software.mcgoron.com/peter/spi>.
|
||||
the leading edge. See https://software.mcgoron.com/peter/spi
|
||||
:param polarity: See https://software.mcgoron.com/peter/spi.
|
||||
:param enable_miso: If ``False``, the module does not read data
|
||||
from MISO into a register.
|
||||
:param enable_mosi: If ``False``, the module does not write data
|
||||
|
@ -39,7 +82,6 @@ class SPIMaster(Module):
|
|||
"""
|
||||
|
||||
self.bus = Interface(data_width = 32, address_width=32, addressing="byte")
|
||||
self.addr_space_size = 0x10
|
||||
|
||||
self.comb += [
|
||||
self.bus.err.eq(0),
|
||||
|
@ -62,7 +104,7 @@ class SPIMaster(Module):
|
|||
i_miso = miso,
|
||||
o_mosi = mosi,
|
||||
o_sck_wire = sck,
|
||||
o_ss_L = ss,
|
||||
o_ss_L = ss_L,
|
||||
|
||||
i_wb_cyc = self.bus.cyc,
|
||||
i_wb_stb = self.bus.stb,
|
||||
|
|
|
@ -1,221 +0,0 @@
|
|||
import textwrap
|
||||
|
||||
class Descr:
|
||||
def __init__(self, name, blen, rwperm, num, descr):
|
||||
"""
|
||||
:param name: Name of the pin without numerical suffix.
|
||||
:param blen: Bit length of the pin.
|
||||
:param doc: Restructured text documentation of the register.
|
||||
:param num: The amount of registers of the same type.
|
||||
:param read_only: A string that must be either "read-only" or "write-write".
|
||||
"""
|
||||
self.name = name
|
||||
self.blen = blen
|
||||
self.doc = textwrap.dedent(descr)
|
||||
self.num = num
|
||||
self.rwperm = rwperm
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, jsdict, name):
|
||||
return cls(name, jsdict[name]["len"], jsdict[name]["ro"], jsdict[name]["num"], jsdict[name]["doc"])
|
||||
def store_to_dict(self, d):
|
||||
d[self.name] = {
|
||||
"len": self.blen,
|
||||
"doc": self.doc,
|
||||
"num": self.num,
|
||||
"ro": ro
|
||||
}
|
||||
|
||||
registers = [
|
||||
Descr("adc_sel", 3, "read-write", 8, """\
|
||||
Select which on-FPGA SPI master controls the ADC.
|
||||
|
||||
Valid settings:
|
||||
|
||||
* ``0``: ADC is controlled by MMIO registers.
|
||||
* ``0b10``: ADC is controlled by MMIO registers, but conversion is
|
||||
disabled. This is used to flush output from an out-of-sync ADC.
|
||||
* ``0b100``: ADC 0 only. ADC is controlled by control loop."""),
|
||||
Descr("adc_finished", 1, "read-only", 8, """\
|
||||
Signals that an ADC master has finished an SPI cycle.
|
||||
|
||||
Values:
|
||||
|
||||
* ``0``: MMIO master is either not armed or currently in a
|
||||
SPI transfer.
|
||||
* ``1``: MMIO master has finished.
|
||||
|
||||
This flag is on only when ``adc_arm`` is high. The flag does not
|
||||
mean that data has been received successfully, only that the master
|
||||
has finished it's SPI transfer."""),
|
||||
Descr("adc_arm", 1, "read-write", 8, """\
|
||||
Start a DAC master SPI transfer.
|
||||
|
||||
If ``adc_arm`` is raised from and the master is currently not in a SPI
|
||||
transfer, the SPI master will start an SPI transfer and write data
|
||||
into ``adc_recv_buf``.
|
||||
|
||||
If ``adc_arm`` is raised while the master is in an SPI transfer,
|
||||
nothing changes.
|
||||
|
||||
If ``adc_arm`` is lowered while the master is in an SPI transfer,
|
||||
nothing changes. The SPI cycle will continue to execute and it will
|
||||
continue to write data to ``adc_recv_buf``.
|
||||
|
||||
If the SPI transfer finishes and ``adc_arm`` is still set to
|
||||
1, then ``adc_finished`` is raised to 1. If ``adc_arm`` is lowered
|
||||
in this state, then ``adc_finished`` is lowered.
|
||||
|
||||
Linear Technologies ADCs must not have their SPI transfers
|
||||
interrupted. The transfer can be interrupted by
|
||||
|
||||
1. Interrupt the signal physically (i.e. pulling out cable connecting
|
||||
the FPGA to the ADC)
|
||||
2. Reset of the ADC master
|
||||
3. Reset of the FPGA
|
||||
4. Switching ``adc_sel`` to the control loop
|
||||
|
||||
If the ADC is interrupted then it will be in an unknown transfer
|
||||
state. To recover from an unknown transfer state, set ``adc_sel``
|
||||
to ``0b10`` and run a SPI transfer cycle. This will run the SPI
|
||||
clock and flush the ADC buffer. The only other way is to power-cycle
|
||||
the ADC.
|
||||
|
||||
If ``adc_sel`` is not set to 0 then the transfer will proceed
|
||||
as normal, but no data will be received from the ADC."""),
|
||||
Descr("adc_recv_buf", 18, "read-only", 8, """\
|
||||
ADC Master receive buffer.
|
||||
|
||||
This buffer is stable if there is no ADC transfer caused by ``adc_arm``
|
||||
is in process.
|
||||
|
||||
This register only changes if an SPI transfer is triggered by the MMIO
|
||||
registers. SPI transfers by other masters will not affect this register.
|
||||
buffer."""),
|
||||
|
||||
Descr("dac_sel", 2, "read-write", 8, """\
|
||||
Select which on-FPGA SPI master controls the DAC.
|
||||
|
||||
Valid settings:
|
||||
|
||||
* ``0``: DAC is controlled by MMIO registers.
|
||||
* ``0b10``: DAC 0 only. DAC is controlled by control loop."""),
|
||||
Descr("dac_finished", 1, "read-only", 8, """\
|
||||
Signals that the DAC master has finished transmitting data.
|
||||
|
||||
Values:
|
||||
|
||||
* ``0``: MMIO master is either not armed or currently in a
|
||||
SPI transfer.
|
||||
* ``1``: MMIO master has finished transmitting.
|
||||
|
||||
This flag is on only when ``dac_arm`` is high. The flag does not
|
||||
mean that data has been received or transmitted successfully, only that
|
||||
the master has finished it's SPI transfer."""),
|
||||
Descr("dac_arm", 1, "read-write", 8, """\
|
||||
Start a DAC master SPI transfer.
|
||||
|
||||
If ``dac_arm`` is raised from and the master is currently not in a SPI
|
||||
transfer, the SPI master reads from the ``dac_send_buf`` register and sends
|
||||
it over the wire to the DAC, while reading data from the DAC into
|
||||
``dac_recv_buf``.
|
||||
|
||||
If ``dac_arm`` is raised while the master is in an SPI transfer,
|
||||
nothing changes.
|
||||
|
||||
If ``dac_arm`` is lowered while the master is in an SPI transfer,
|
||||
nothing changes. The SPI cycle will continue to execute and it will
|
||||
continue to write data to ``dac_recv_buf``.
|
||||
|
||||
If the SPI transfer finishes and ``dac_arm`` is still set to
|
||||
1, then ``dac_finished`` is raised to 1. If ``dac_arm`` is lowered
|
||||
in this state, then ``dac_finished`` is lowered.
|
||||
|
||||
Analog Devices DACs can have their SPI transfers interrupted without
|
||||
issue. However it is currently not possible to interrupt SPI transfers
|
||||
in software without resetting the entire device.
|
||||
|
||||
If ``dac_sel`` is set to another master then the transfer will proceed
|
||||
as normal, but no data will be sent to or received from the DAC."""),
|
||||
Descr("dac_recv_buf", 24, "read-only", 8, """\
|
||||
DAC master receive buffer.
|
||||
|
||||
This buffer is stable if there is no DAC transfer caused by ``dac_arm``
|
||||
is in process.
|
||||
|
||||
This register only changes if an SPI transfer is triggered by the MMIO
|
||||
registers. SPI transfers by other masters will not affect this register.
|
||||
buffer."""),
|
||||
Descr("dac_send_buf", 24, "read-write", 8, """\
|
||||
DAC master send buffer.
|
||||
|
||||
Fill this buffer with a 24 bit Analog Devices DAC command. Updating
|
||||
this buffer does not start an SPI transfer. To send data to the DAC,
|
||||
fill this buffer and raise ``dac_arm``.
|
||||
|
||||
The DAC copies this buffer into an internal register when writing data.
|
||||
Modifying this buffer during a transfer does not disrupt an in-process
|
||||
transfer."""),
|
||||
|
||||
Descr("cl_assert_change", 1, "read-write", 1, """\
|
||||
Flush parameter changes to control loop.
|
||||
|
||||
When this bit is raised from low to high, this signals the control
|
||||
loop that it should read in new values from the MMIO registers.
|
||||
While the bit is raised high, the control loop will read the constants
|
||||
at most once.
|
||||
|
||||
When this bit is raised from high to low before ``cl_change_made``
|
||||
is asserted by the control loop, nothing happens."""),
|
||||
Descr("cl_change_made", 1, "read-only", 1, """\
|
||||
Signal from the control loop that the parameters have been applied.
|
||||
|
||||
This signal goes high only while ``cl_assert_change`` is high. No
|
||||
change will be applied afterwards while both are high."""),
|
||||
|
||||
Descr("cl_in_loop", 1, "read-only", 1, """\
|
||||
This bit is high if the control loop is running."""),
|
||||
Descr("cl_run_loop_in", 1, "read-write", 1, """\
|
||||
Set this bit high to start the control loop."""),
|
||||
Descr("cl_setpt_in", 18, "read-write", 1, """\
|
||||
Setpoint of the control loop.
|
||||
|
||||
This is a twos-complement number in ADC units.
|
||||
|
||||
This is a parameter: see ``cl_assert_change``."""),
|
||||
Descr("cl_P_in", 64, "read-write", 1, """\
|
||||
Proportional parameter of the control loop.
|
||||
|
||||
This is a twos-complement fixed point number with 21 whole
|
||||
bits and 43 fractional bits. This is applied to the error
|
||||
in DAC units.
|
||||
|
||||
This is a parameter: see ``cl_assert_change``."""),
|
||||
Descr("cl_I_in", 64, "read-write", 1, """\
|
||||
Integral parameter of the control loop.
|
||||
|
||||
This is a twos-complement fixed point number with 21 whole
|
||||
bits and 43 fractional bits. This is applied to the error
|
||||
in DAC units.
|
||||
|
||||
This is a parameter: see ``cl_assert_change``."""),
|
||||
Descr("cl_delay_in", 16, "read-write", 1, """\
|
||||
Delay parameter of the loop.
|
||||
|
||||
This is an unsigned number denoting the number of cycles
|
||||
the loop should wait between loop executions.
|
||||
|
||||
This is a parameter: see ``cl_assert_change``."""),
|
||||
|
||||
Descr("cl_cycle_count", 18, "read-only", 1, """\
|
||||
Delay parameter of the loop.
|
||||
|
||||
This is an unsigned number denoting the number of cycles
|
||||
the loop should wait between loop executions."""),
|
||||
Descr("cl_z_pos", 20, "read-only", 1, """\
|
||||
Control loop DAC Z position.
|
||||
"""),
|
||||
Descr("cl_z_measured", 18, "read-only", 1, """\
|
||||
Control loop ADC Z position.
|
||||
"""),
|
||||
]
|
|
@ -21,18 +21,23 @@ added manually and there is no sanity checking.
|
|||
|
||||
class BasicRegion:
|
||||
""" Simple class for storing a RAM region. """
|
||||
def __init__(self, origin, size, bus=None):
|
||||
def __init__(self, origin, size, bus=None, registers=None):
|
||||
"""
|
||||
:param origin: Positive integer denoting the start location
|
||||
of the memory region.
|
||||
:param size: Size of the memory region. This must be of the form
|
||||
(2**N - 1).
|
||||
:param bus: Instance of a wishbone bus interface.
|
||||
:param registers: Dictionary where keys are names of addressable
|
||||
areas in the region, values have "offset" and "width", and
|
||||
optionally other parameters that help with describing the
|
||||
subregion.
|
||||
"""
|
||||
|
||||
self.origin = origin
|
||||
self.size = size
|
||||
self.bus = bus
|
||||
self.registers = registers
|
||||
|
||||
def decoder(self):
|
||||
"""
|
||||
|
@ -56,7 +61,7 @@ class BasicRegion:
|
|||
return lambda addr: addr[rightbits:32] == (self.origin >> rightbits)
|
||||
|
||||
def to_dict(self):
|
||||
return {"origin" : self.origin, "size": self.size}
|
||||
return {"origin" : self.origin, "size": self.size, "registers": self.registers}
|
||||
|
||||
def __str__(self):
|
||||
return str(self.to_dict())
|
||||
|
|
|
@ -63,6 +63,7 @@ from liteeth.phy.mii import LiteEthPHYMII
|
|||
from util import *
|
||||
from swic import *
|
||||
from extio import *
|
||||
from region import BasicRegion
|
||||
|
||||
"""
|
||||
Keep this diagram up to date! This is the wiring diagram from the ADC to
|
||||
|
@ -104,6 +105,9 @@ io = [
|
|||
# ("dac_mosi", 0, Pins("B11 B18 E16 D8 V12 D5 D3 D2"), IOStandard("LVCMOS33")),
|
||||
# ("dac_miso", 0, Pins("A11 A18 D15 C7 V10 B7 F4 H2"), IOStandard("LVCMOS33")),
|
||||
# ("dac_sck", 0, Pins("D12 K16 C15 E7 V11 E6 F3 G2"), IOStandard("LVCMOS33")),
|
||||
("adc_conv_0", 0, Pins("V15"), IOStandard("LVCMOS33")),
|
||||
("adc_sck_0", 0, Pins("U16"), IOStandard("LVCMOS33")),
|
||||
("adc_sdo_0", 0, Pins("P14"), IOStandard("LVCMOS33")),
|
||||
# ("adc_conv", 0, Pins("V15 T11 N15 U18 U11 R10 R16 U17"), IOStandard("LVCMOS33")),
|
||||
# ("adc_sck", 0, Pins("U16 R12 M16 R17 V16 R11 N16 T18"), IOStandard("LVCMOS33")),
|
||||
# ("adc_sdo", 0, Pins("P14 T14 V17 P17 M13 R13 N14 R18"), IOStandard("LVCMOS33")),
|
||||
|
@ -149,6 +153,10 @@ class UpsilonSoC(SoCCore):
|
|||
for seg_num, ip_byte in enumerate(ip_str.split('.'),start=1):
|
||||
self.add_constant(f"{ip_name}{seg_num}", int(ip_byte))
|
||||
|
||||
def add_slave_with_registers(self, name, bus, region, registers):
|
||||
self.bus.add_slave(name, bus, region)
|
||||
self.soc_subregions[name] = registers
|
||||
|
||||
def add_blockram(self, name, size, connect_now=True):
|
||||
mod = SRAM(size)
|
||||
self.add_module(name, mod)
|
||||
|
@ -163,24 +171,49 @@ class UpsilonSoC(SoCCore):
|
|||
self.add_module(name, mod)
|
||||
return mod
|
||||
|
||||
def add_picorv32(self, name, size=0x1000, origin=0x10000, param_origin=0x100000):
|
||||
def add_picorv32(self, name, size=0x1000, origin=0x10000):
|
||||
pico = PicoRV32(name, origin, origin+0x10)
|
||||
self.add_module(name, pico)
|
||||
self.bus.add_slave(name + "_dbg_reg", pico.debug_reg_read.bus,
|
||||
SoCRegion(origin=None, size=pico.debug_reg_read.width, cached=False))
|
||||
self.add_slave_with_registers(name + "_dbg_reg", pico.debug_reg_read.bus,
|
||||
SoCRegion(origin=None, size=pico.debug_reg_read.width, cached=False),
|
||||
pico.debug_reg_read.registers)
|
||||
|
||||
ram = self.add_blockram(name + "_ram", size=size, connect_now=False)
|
||||
ram_iface = self.add_preemptive_interface(name + "ram_iface", 2, ram)
|
||||
pico.mmap.add_region("main",
|
||||
BasicRegion(origin=origin, size=size, bus=ram_iface.buses[1]))
|
||||
|
||||
self.bus.add_slave(name + "_ram", ram_iface.buses[0],
|
||||
SoCRegion(origin=None, size=size, cached=True))
|
||||
self.add_slave_with_registers(name + "_ram", ram_iface.buses[0],
|
||||
SoCRegion(origin=None, size=size, cached=True),
|
||||
None)
|
||||
|
||||
def picorv32_add_cl(self, name, param_origin=0x100000):
|
||||
pico = self.get_module(name)
|
||||
param_iface = pico.add_cl_params(param_origin, name + "_cl.json")
|
||||
self.bus.add_slave(name + "_cl", param_iface,
|
||||
SoCRegion(origin=None, size=size, cached=False))
|
||||
|
||||
def add_AD5791(self, name, **kwargs):
|
||||
args = SPIMaster.AD5791_PARAMS
|
||||
args.update(kwargs)
|
||||
spi = SPIMaster(**args)
|
||||
self.add_module(name, spi)
|
||||
return spi
|
||||
|
||||
def add_LT_adc(self, name, **kwargs):
|
||||
args = SPIMaster.LT_ADC_PARAMS
|
||||
args.update(kwargs)
|
||||
args["mosi"] = Signal()
|
||||
|
||||
# SPI Master brings ss_L low when converting and keeps it high
|
||||
# when idle. The ADC is the opposite, so invert the signal here.
|
||||
conv_high = Signal()
|
||||
self.comb += conv_high.eq(~kwargs["ss_L"])
|
||||
|
||||
spi = SPIMaster(**args)
|
||||
self.add_module(name, spi)
|
||||
return spi
|
||||
|
||||
def __init__(self,
|
||||
variant="a7-100",
|
||||
local_ip="192.168.2.50",
|
||||
|
@ -199,6 +232,11 @@ class UpsilonSoC(SoCCore):
|
|||
rst = platform.request("cpu_reset")
|
||||
self.submodules.crg = _CRG(platform, sys_clk_freq, True, rst)
|
||||
|
||||
# The SoC won't know the origins until LiteX sorts out all the
|
||||
# memory regions, so they go into a dictionary directly instead
|
||||
# of through MemoryMap.
|
||||
self.soc_subregions = {}
|
||||
|
||||
"""
|
||||
These source files need to be sorted so that modules
|
||||
that rely on another module come later. For instance,
|
||||
|
@ -263,16 +301,49 @@ class UpsilonSoC(SoCCore):
|
|||
|
||||
# Add pins
|
||||
platform.add_extension(io)
|
||||
self.submodules.spi0 = SPIMaster(
|
||||
platform.request("module_reset"),
|
||||
platform.request("dac_miso_0"),
|
||||
platform.request("dac_mosi_0"),
|
||||
platform.request("dac_sck_0"),
|
||||
platform.request("dac_ss_L_0"),
|
||||
)
|
||||
self.bus.add_slave("spi0", self.spi0.bus, SoCRegion(origin=None, size=self.spi0.addr_space_size, cached=False))
|
||||
|
||||
|
||||
# Add control loop DACs and ADCs.
|
||||
self.add_picorv32("pico0")
|
||||
# XXX: I don't have the time to restructure my code to make it
|
||||
# elegant, that comes when things work
|
||||
module_reset = platform.request("module_reset")
|
||||
self.add_AD5791("dac0",
|
||||
rst=module_reset,
|
||||
miso=platform.request("dac_miso_0"),
|
||||
mosi=platform.request("dac_mosi_0"),
|
||||
sck=platform.request("dac_sck_0"),
|
||||
ss_L=platform.request("dac_ss_L_0"),
|
||||
)
|
||||
|
||||
self.add_preemptive_interface("dac0_PI", 2, self.dac0)
|
||||
self.add_slave_with_registers("dac0", self.dac0_PI.buses[0],
|
||||
SoCRegion(origin=None, size=self.dac0.width, cached=False),
|
||||
self.dac0.registers)
|
||||
self.pico0.mmap.add_region("dac0",
|
||||
BasicRegion(origin=0x200000, size=self.dac0.width,
|
||||
bus=self.dac0_PI.buses[1],
|
||||
registers=self.dac0.registers))
|
||||
|
||||
self.add_LT_adc("adc0",
|
||||
rst=module_reset,
|
||||
miso=platform.request("adc_sdo_0"),
|
||||
sck=platform.request("adc_sck_0"),
|
||||
ss_L=platform.request("adc_conv_0"),
|
||||
spi_wid=18,
|
||||
)
|
||||
self.add_preemptive_interface("adc0_PI", 2, self.adc0)
|
||||
self.add_slave_with_registers("adc0", self.adc0_PI.buses[0],
|
||||
SoCRegion(origin=None, size=self.adc0.width, cached=False),
|
||||
self.adc0.registers)
|
||||
self.pico0.mmap.add_region("adc0",
|
||||
BasicRegion(origin=0x300000, size=self.adc0.width,
|
||||
bus=self.adc0_PI.buses[1],
|
||||
registers=self.adc0.registers))
|
||||
|
||||
def do_finalize(self):
|
||||
with open('soc_subregions.json', 'wt') as f:
|
||||
import json
|
||||
json.dump(self.soc_subregions, f)
|
||||
|
||||
def main():
|
||||
from config import config
|
||||
|
|
|
@ -182,20 +182,22 @@ class RegisterInterface(LiteXModule):
|
|||
bus_logic(self.mainbus, main_case)
|
||||
bus_logic(self.picobus, pico_case)
|
||||
|
||||
def dump_json(self, filename):
|
||||
""" Dump description of offsets to JSON file. """
|
||||
d = {}
|
||||
# Generate addresses
|
||||
self.addresses = {}
|
||||
for reg, off in self.registers:
|
||||
d[reg.name] = {
|
||||
self.addresses[reg.name] = {
|
||||
"width" : reg.width,
|
||||
"direction" : reg.direction,
|
||||
"offset": off
|
||||
}
|
||||
import json
|
||||
with open(filename, 'wt') as f:
|
||||
json.dump(d, f)
|
||||
|
||||
class RegisterRead(LiteXModule):
|
||||
pico_registers = {
|
||||
"ra", "sp", "gp", "tp", "t0", "t1", "t2", "s0", "t1", "t2",
|
||||
"s0/fp", "s1", "a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7", "s2",
|
||||
"s3", "s4", "s5", "s6", "s7", "t3", "t4", "t5", "t6",
|
||||
}
|
||||
registers = {name: {"origin" : num * 4, "size" : 4, "rw": False} for num, name in enumerate(pico_registers)}
|
||||
""" Inspect PicoRV32 registers via Wishbone bus. """
|
||||
def __init__(self):
|
||||
self.regs = [Signal(32) for i in range(1,32)]
|
||||
|
@ -233,7 +235,7 @@ class PicoRV32(LiteXModule):
|
|||
:param origin: Origin of the region for the PicoRV32.
|
||||
:param dumpname: File to dump offsets within the region (common to
|
||||
both Pico and Main CPU).
|
||||
:return: Interface used by main cpu to control variables.
|
||||
:return: Parameter module (used for accessing metadata).
|
||||
"""
|
||||
params = RegisterInterface(
|
||||
SpecialRegister.from_tuples(
|
||||
|
@ -245,9 +247,9 @@ class PicoRV32(LiteXModule):
|
|||
("zpos", "PW", 32),
|
||||
))
|
||||
self.add_module("cl_params", params)
|
||||
self.mmap.add_region("cl_params", BasicRegion(origin, params.width, params.picobus))
|
||||
self.mmap.add_region("cl_params", BasicRegion(origin, params.width, params.picobus, params.addresses))
|
||||
params.dump_json(dumpname)
|
||||
return params.mainbus
|
||||
return params
|
||||
|
||||
def __init__(self, name, start_addr=0x10000, irq_addr=0x10010, stackaddr=0x100FF):
|
||||
self.name = name
|
||||
|
@ -292,7 +294,6 @@ class PicoRV32(LiteXModule):
|
|||
self.d_dat_w.status.eq(mem_wdata),
|
||||
]
|
||||
|
||||
# NOTE: need to compile to these extact instructions
|
||||
self.specials += Instance("picorv32",
|
||||
p_COMPRESSED_ISA = 1,
|
||||
p_ENABLE_MUL = 1,
|
||||
|
|
Loading…
Reference in New Issue