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/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/arty.dtb ../boot/
|
||||||
docker cp upsilon-hardware:/home/user/upsilon/gateware/csr.json ../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/pico0.json ../boot/
|
||||||
docker cp upsilon-hardware:/home/user/upsilon/gateware/mmio.py ../boot/
|
docker cp upsilon-hardware:/home/user/upsilon/gateware/mmio.py ../boot/
|
||||||
hardware-clean:
|
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
|
They are used to directly access memory locations on the main CPU bus. Note
|
||||||
that ``mem32`` accesses must be word aligned.
|
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
|
Accessing Registers
|
||||||
point there will be a standard library that wraps these accesses as functions.
|
-------------------
|
||||||
|
|
||||||
----------------
|
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
|
Computer Control
|
||||||
----------------
|
================
|
||||||
|
|
||||||
Micropython code can be loaded manually with SSH but this gets cumbersome.
|
Micropython code can be loaded manually with SSH but this gets cumbersome.
|
||||||
Python scripts on the controlling computer connected to the Upsilon FPGA can
|
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
|
python3 soc.py
|
||||||
|
|
||||||
mmio.py: csr.json csr2mp.py
|
mmio.py: csr.json csr2mp.py
|
||||||
python3 csr2mp.py csr.json > mmio.py
|
python3 csr2mp.py > mmio.py
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf build csr.json arty.dts arty.dtb mmio.py
|
rm -rf build csr.json arty.dts arty.dtb mmio.py
|
||||||
|
|
|
@ -13,17 +13,22 @@
|
||||||
import collections
|
import collections
|
||||||
import argparse
|
import argparse
|
||||||
import json
|
import json
|
||||||
import sys
|
|
||||||
import mmio_descr
|
|
||||||
|
|
||||||
with open(sys.argv[1], 'rt') as f:
|
with open('csr.json', 'rt') as f:
|
||||||
j = json.load(f)
|
csrs = json.load(f)
|
||||||
|
|
||||||
print("from micropython import const")
|
print("from micropython import const")
|
||||||
|
|
||||||
for key in j["csr_registers"]:
|
for key in csrs["csr_registers"]:
|
||||||
if key.startswith("pico0"):
|
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"]})')
|
with open('soc_subregions.json', 'rt') as f:
|
||||||
print(f'pico0_dbg_reg = const({j["memories"]["pico0_dbg_reg"]["base"]})')
|
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 *
|
from util import *
|
||||||
|
|
||||||
class SPIMaster(Module):
|
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. """
|
""" 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,
|
polarity = 0,
|
||||||
phase = 0,
|
phase = 0,
|
||||||
ss_wait = 1,
|
ss_wait = 1,
|
||||||
|
@ -28,8 +71,8 @@ class SPIMaster(Module):
|
||||||
:param ss: SS signal.
|
:param ss: SS signal.
|
||||||
:param phase: Phase of SPI master. This phase is not the standard
|
: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
|
SPI phase because it is defined in terms of the rising edge, not
|
||||||
the leading edge. 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 polarity: See https://software.mcgoron.com/peter/spi.
|
||||||
:param enable_miso: If ``False``, the module does not read data
|
:param enable_miso: If ``False``, the module does not read data
|
||||||
from MISO into a register.
|
from MISO into a register.
|
||||||
:param enable_mosi: If ``False``, the module does not write data
|
: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.bus = Interface(data_width = 32, address_width=32, addressing="byte")
|
||||||
self.addr_space_size = 0x10
|
|
||||||
|
|
||||||
self.comb += [
|
self.comb += [
|
||||||
self.bus.err.eq(0),
|
self.bus.err.eq(0),
|
||||||
|
@ -62,7 +104,7 @@ class SPIMaster(Module):
|
||||||
i_miso = miso,
|
i_miso = miso,
|
||||||
o_mosi = mosi,
|
o_mosi = mosi,
|
||||||
o_sck_wire = sck,
|
o_sck_wire = sck,
|
||||||
o_ss_L = ss,
|
o_ss_L = ss_L,
|
||||||
|
|
||||||
i_wb_cyc = self.bus.cyc,
|
i_wb_cyc = self.bus.cyc,
|
||||||
i_wb_stb = self.bus.stb,
|
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:
|
class BasicRegion:
|
||||||
""" Simple class for storing a RAM region. """
|
""" 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
|
:param origin: Positive integer denoting the start location
|
||||||
of the memory region.
|
of the memory region.
|
||||||
:param size: Size of the memory region. This must be of the form
|
:param size: Size of the memory region. This must be of the form
|
||||||
(2**N - 1).
|
(2**N - 1).
|
||||||
:param bus: Instance of a wishbone bus interface.
|
: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.origin = origin
|
||||||
self.size = size
|
self.size = size
|
||||||
self.bus = bus
|
self.bus = bus
|
||||||
|
self.registers = registers
|
||||||
|
|
||||||
def decoder(self):
|
def decoder(self):
|
||||||
"""
|
"""
|
||||||
|
@ -56,7 +61,7 @@ class BasicRegion:
|
||||||
return lambda addr: addr[rightbits:32] == (self.origin >> rightbits)
|
return lambda addr: addr[rightbits:32] == (self.origin >> rightbits)
|
||||||
|
|
||||||
def to_dict(self):
|
def to_dict(self):
|
||||||
return {"origin" : self.origin, "size": self.size}
|
return {"origin" : self.origin, "size": self.size, "registers": self.registers}
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return str(self.to_dict())
|
return str(self.to_dict())
|
||||||
|
|
|
@ -63,6 +63,7 @@ from liteeth.phy.mii import LiteEthPHYMII
|
||||||
from util import *
|
from util import *
|
||||||
from swic import *
|
from swic import *
|
||||||
from extio import *
|
from extio import *
|
||||||
|
from region import BasicRegion
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Keep this diagram up to date! This is the wiring diagram from the ADC to
|
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_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_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")),
|
# ("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_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_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")),
|
# ("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):
|
for seg_num, ip_byte in enumerate(ip_str.split('.'),start=1):
|
||||||
self.add_constant(f"{ip_name}{seg_num}", int(ip_byte))
|
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):
|
def add_blockram(self, name, size, connect_now=True):
|
||||||
mod = SRAM(size)
|
mod = SRAM(size)
|
||||||
self.add_module(name, mod)
|
self.add_module(name, mod)
|
||||||
|
@ -163,24 +171,49 @@ class UpsilonSoC(SoCCore):
|
||||||
self.add_module(name, mod)
|
self.add_module(name, mod)
|
||||||
return 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)
|
pico = PicoRV32(name, origin, origin+0x10)
|
||||||
self.add_module(name, pico)
|
self.add_module(name, pico)
|
||||||
self.bus.add_slave(name + "_dbg_reg", pico.debug_reg_read.bus,
|
self.add_slave_with_registers(name + "_dbg_reg", pico.debug_reg_read.bus,
|
||||||
SoCRegion(origin=None, size=pico.debug_reg_read.width, cached=False))
|
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 = self.add_blockram(name + "_ram", size=size, connect_now=False)
|
||||||
ram_iface = self.add_preemptive_interface(name + "ram_iface", 2, ram)
|
ram_iface = self.add_preemptive_interface(name + "ram_iface", 2, ram)
|
||||||
pico.mmap.add_region("main",
|
pico.mmap.add_region("main",
|
||||||
BasicRegion(origin=origin, size=size, bus=ram_iface.buses[1]))
|
BasicRegion(origin=origin, size=size, bus=ram_iface.buses[1]))
|
||||||
|
|
||||||
self.bus.add_slave(name + "_ram", ram_iface.buses[0],
|
self.add_slave_with_registers(name + "_ram", ram_iface.buses[0],
|
||||||
SoCRegion(origin=None, size=size, cached=True))
|
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")
|
param_iface = pico.add_cl_params(param_origin, name + "_cl.json")
|
||||||
self.bus.add_slave(name + "_cl", param_iface,
|
self.bus.add_slave(name + "_cl", param_iface,
|
||||||
SoCRegion(origin=None, size=size, cached=False))
|
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,
|
def __init__(self,
|
||||||
variant="a7-100",
|
variant="a7-100",
|
||||||
local_ip="192.168.2.50",
|
local_ip="192.168.2.50",
|
||||||
|
@ -199,6 +232,11 @@ class UpsilonSoC(SoCCore):
|
||||||
rst = platform.request("cpu_reset")
|
rst = platform.request("cpu_reset")
|
||||||
self.submodules.crg = _CRG(platform, sys_clk_freq, True, rst)
|
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
|
These source files need to be sorted so that modules
|
||||||
that rely on another module come later. For instance,
|
that rely on another module come later. For instance,
|
||||||
|
@ -263,16 +301,49 @@ class UpsilonSoC(SoCCore):
|
||||||
|
|
||||||
# Add pins
|
# Add pins
|
||||||
platform.add_extension(io)
|
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")
|
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():
|
def main():
|
||||||
from config import config
|
from config import config
|
||||||
|
|
|
@ -182,20 +182,22 @@ class RegisterInterface(LiteXModule):
|
||||||
bus_logic(self.mainbus, main_case)
|
bus_logic(self.mainbus, main_case)
|
||||||
bus_logic(self.picobus, pico_case)
|
bus_logic(self.picobus, pico_case)
|
||||||
|
|
||||||
def dump_json(self, filename):
|
# Generate addresses
|
||||||
""" Dump description of offsets to JSON file. """
|
self.addresses = {}
|
||||||
d = {}
|
|
||||||
for reg, off in self.registers:
|
for reg, off in self.registers:
|
||||||
d[reg.name] = {
|
self.addresses[reg.name] = {
|
||||||
"width" : reg.width,
|
"width" : reg.width,
|
||||||
"direction" : reg.direction,
|
"direction" : reg.direction,
|
||||||
"offset": off
|
"offset": off
|
||||||
}
|
}
|
||||||
import json
|
|
||||||
with open(filename, 'wt') as f:
|
|
||||||
json.dump(d, f)
|
|
||||||
|
|
||||||
class RegisterRead(LiteXModule):
|
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. """
|
""" Inspect PicoRV32 registers via Wishbone bus. """
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.regs = [Signal(32) for i in range(1,32)]
|
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 origin: Origin of the region for the PicoRV32.
|
||||||
:param dumpname: File to dump offsets within the region (common to
|
:param dumpname: File to dump offsets within the region (common to
|
||||||
both Pico and Main CPU).
|
both Pico and Main CPU).
|
||||||
:return: Interface used by main cpu to control variables.
|
:return: Parameter module (used for accessing metadata).
|
||||||
"""
|
"""
|
||||||
params = RegisterInterface(
|
params = RegisterInterface(
|
||||||
SpecialRegister.from_tuples(
|
SpecialRegister.from_tuples(
|
||||||
|
@ -245,9 +247,9 @@ class PicoRV32(LiteXModule):
|
||||||
("zpos", "PW", 32),
|
("zpos", "PW", 32),
|
||||||
))
|
))
|
||||||
self.add_module("cl_params", params)
|
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)
|
params.dump_json(dumpname)
|
||||||
return params.mainbus
|
return params
|
||||||
|
|
||||||
def __init__(self, name, start_addr=0x10000, irq_addr=0x10010, stackaddr=0x100FF):
|
def __init__(self, name, start_addr=0x10000, irq_addr=0x10010, stackaddr=0x100FF):
|
||||||
self.name = name
|
self.name = name
|
||||||
|
@ -292,7 +294,6 @@ class PicoRV32(LiteXModule):
|
||||||
self.d_dat_w.status.eq(mem_wdata),
|
self.d_dat_w.status.eq(mem_wdata),
|
||||||
]
|
]
|
||||||
|
|
||||||
# NOTE: need to compile to these extact instructions
|
|
||||||
self.specials += Instance("picorv32",
|
self.specials += Instance("picorv32",
|
||||||
p_COMPRESSED_ISA = 1,
|
p_COMPRESSED_ISA = 1,
|
||||||
p_ENABLE_MUL = 1,
|
p_ENABLE_MUL = 1,
|
||||||
|
|
Loading…
Reference in New Issue