336 lines
11 KiB
Python
336 lines
11 KiB
Python
# Copyright 2023-2024 (C) Peter McGoron
|
|
#
|
|
# This file is a part of Upsilon, a free and open source software project.
|
|
# For license terms, refer to the files in `doc/copying` in the Upsilon
|
|
# source distribution.
|
|
|
|
from migen import *
|
|
from litex.soc.interconnect.wishbone import Interface
|
|
|
|
from util import *
|
|
from region import *
|
|
|
|
class Waveform(LiteXModule):
|
|
""" A Wishbone bus master that sends a waveform to a SPI master
|
|
by reading from RAM. """
|
|
|
|
public_registers = {
|
|
"run" : {
|
|
"origin": 0,
|
|
"size": 4,
|
|
"rw": True,
|
|
},
|
|
"cntr": {
|
|
"origin": 0x4,
|
|
"size": 4,
|
|
"rw": False,
|
|
},
|
|
"do_loop": {
|
|
"origin": 0x8,
|
|
"size" : 4,
|
|
"rw" : True,
|
|
},
|
|
"finished_or_ready": {
|
|
"origin": 0xC,
|
|
"size" : 4,
|
|
"rw" : False,
|
|
},
|
|
"wform_size": {
|
|
"origin": 0x10,
|
|
"size": 4,
|
|
"rw" : True,
|
|
},
|
|
"timer": {
|
|
"origin": 0x14,
|
|
"size" : 4,
|
|
"rw" : False,
|
|
},
|
|
"timer_spacing": {
|
|
"origin" : 0x18,
|
|
"size" : 4,
|
|
"rw" : True,
|
|
}
|
|
}
|
|
|
|
width = 0x20
|
|
|
|
def __init__(self,
|
|
ram_start_addr = 0,
|
|
spi_start_addr = 0x10000000,
|
|
counter_max_wid = 16,
|
|
timer_wid = 16,
|
|
):
|
|
# This is Waveform's bus to control SPI and RAM devices it owns.
|
|
self.masterbus = Interface(address_width=32, data_width=32, addressing="byte")
|
|
# This is the Waveform's interface that is controlled by the Main CPU.
|
|
self.slavebus = b = Interface(address_width=32, data_width=32, addressing="byte")
|
|
|
|
self.mmap = MemoryMap(self.masterbus)
|
|
self.mmap.add_region("ram",
|
|
BasicRegion(ram_start_addr, None))
|
|
self.mmap.add_region("spi",
|
|
BasicRegion(spi_start_addr, None))
|
|
|
|
run = Signal()
|
|
cntr = Signal(counter_max_wid)
|
|
do_loop = Signal()
|
|
ready = Signal()
|
|
finished = Signal()
|
|
wform_size = Signal(counter_max_wid)
|
|
timer = Signal(timer_wid)
|
|
timer_spacing = Signal(timer_wid)
|
|
|
|
self.comb += If(b.cyc & b.stb & ~b.ack,
|
|
Case(b.adr, {
|
|
0x0: If(b.we,
|
|
run.eq(b.dat_w[0]),
|
|
).Else(
|
|
b.dat_r[0].eq(run)
|
|
),
|
|
0x4: [
|
|
b.dat_r[0:counter_max_wid].eq(cntr),
|
|
],
|
|
0x8: [
|
|
If(b.we,
|
|
do_loop.eq(b.dat_w[0]),
|
|
).Else(
|
|
b.dat_r[0].eq(do_loop),
|
|
)
|
|
],
|
|
0xC: [
|
|
b.dat_r[0].eq(ready),
|
|
b.dat_r[1].eq(finished),
|
|
],
|
|
0x10: If(b.we,
|
|
wform_size.eq(b.dat_w[0:counter_max_wid]),
|
|
).Else(
|
|
b.dat_r[0:counter_max_wid].eq(wform_size)
|
|
),
|
|
0x14: b.dat_r[0:timer_wid].eq(timer),
|
|
0x18: If(b.we,
|
|
timer_spacing.eq(b.dat_w[0:timer_wid]),
|
|
).Else(
|
|
b.dat_r[0:timer_wid].eq(timer_spacing),
|
|
),
|
|
# (W)A(V)EFO(RM)
|
|
"default": b.dat_r.eq(0xAEF0AEF0),
|
|
}
|
|
),
|
|
b.ack.eq(1),
|
|
).Elif(~b.cyc,
|
|
b.ack.eq(0),
|
|
)
|
|
|
|
self.specials += Instance("waveform",
|
|
p_RAM_START_ADDR = ram_start_addr,
|
|
p_SPI_START_ADDR = spi_start_addr,
|
|
p_COUNTER_MAX_WID = counter_max_wid,
|
|
p_TIMER_WID = timer_wid,
|
|
|
|
i_clk = ClockSignal(),
|
|
|
|
i_run = run,
|
|
o_cntr = cntr,
|
|
i_do_loop = do_loop,
|
|
o_finished = finished,
|
|
o_ready = ready,
|
|
i_wform_size = wform_size,
|
|
o_timer = timer,
|
|
i_timer_spacing = timer_spacing,
|
|
|
|
o_wb_adr = self.masterbus.adr,
|
|
o_wb_cyc = self.masterbus.cyc,
|
|
o_wb_we = self.masterbus.we,
|
|
o_wb_stb = self.masterbus.stb,
|
|
o_wb_sel = self.masterbus.sel,
|
|
o_wb_dat_w = self.masterbus.dat_w,
|
|
i_wb_dat_r = self.masterbus.dat_r,
|
|
i_wb_ack = self.masterbus.ack,
|
|
)
|
|
|
|
def add_ram(self, bus, size):
|
|
self.mmap.regions['ram'].bus = bus
|
|
self.mmap.regions['ram'].size = size
|
|
|
|
def add_spi(self, bus):
|
|
# Waveform code has the SPI hardcoded in, because it is a Verilog
|
|
# module.
|
|
self.mmap.regions['spi'].bus = bus
|
|
self.mmap.regions['spi'].size = SPIMaster.width
|
|
|
|
def do_finalize(self):
|
|
self.mmap.finalize()
|
|
|
|
class SPIMaster(Module):
|
|
# IF THESE ARE CHANGED, CHANGE waveform.v !!
|
|
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 = 0x20
|
|
|
|
public_registers = {
|
|
# The first bit is the "finished" bit, when the master is
|
|
# armed and finished with a transmission.
|
|
# The second bit is the "ready" bit, when the master is
|
|
# not armed and ready to be armed.
|
|
"ready_or_finished": {
|
|
"origin" : 0,
|
|
"width" : 4,
|
|
"rw": False,
|
|
},
|
|
|
|
# One bit to initiate a transmission cycle. Transmission
|
|
# cycles CANNOT be interrupted.
|
|
"arm" : {
|
|
"origin": 4,
|
|
"width": 4,
|
|
"rw": True,
|
|
},
|
|
|
|
# Data sent from the SPI slave.
|
|
"from_slave": {
|
|
"origin": 8,
|
|
"width": 4,
|
|
"rw": False,
|
|
},
|
|
|
|
# Data sent to the SPI slave.
|
|
"to_slave": {
|
|
"origin": 0xC,
|
|
"width": 4,
|
|
"rw": True
|
|
},
|
|
|
|
# Same as ready_or_finished, but halts until ready or finished
|
|
# goes high. Dangerous, might cause cores to hang!
|
|
"wait_ready_or_finished": {
|
|
"origin": 0x10,
|
|
"width": 4,
|
|
"rw" : False,
|
|
},
|
|
}
|
|
|
|
""" Wrapper for the SPI master verilog code. """
|
|
def __init__(self, rst, miso, mosi, sck, ss_L,
|
|
polarity = 0,
|
|
phase = 0,
|
|
ss_wait = 1,
|
|
enable_miso = 1,
|
|
enable_mosi = 1,
|
|
spi_wid = 24,
|
|
spi_cycle_half_wait = 1,
|
|
):
|
|
"""
|
|
:param rst: Reset signal.
|
|
:param miso: MISO signal.
|
|
:param mosi: MOSI signal.
|
|
:param sck: SCK signal.
|
|
: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.
|
|
: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
|
|
to MOSI from a register.
|
|
:param spi_wid: Verilog parameter: see file.
|
|
:param spi_cycle_half_wait: Verilog parameter: see file.
|
|
"""
|
|
|
|
self.bus = Interface(data_width = 32, address_width=32, addressing="byte")
|
|
|
|
from_slave = Signal(spi_wid)
|
|
to_slave = Signal(spi_wid)
|
|
finished_or_ready = Signal(2)
|
|
arm = Signal()
|
|
|
|
self.comb += [
|
|
self.bus.err.eq(0),
|
|
]
|
|
|
|
self.sync += [
|
|
If(self.bus.cyc & self.bus.stb & ~self.bus.ack,
|
|
Case(self.bus.adr[0:5], {
|
|
0x0: [
|
|
self.bus.dat_r[2:].eq(0),
|
|
self.bus.dat_r[0:2].eq(finished_or_ready),
|
|
self.bus.ack.eq(1),
|
|
],
|
|
0x4: [
|
|
If(self.bus.we,
|
|
arm.eq(self.bus.dat_w[0]),
|
|
).Else(
|
|
self.bus.dat_r[1:].eq(0),
|
|
self.bus.dat_r[0].eq(arm),
|
|
),
|
|
self.bus.ack.eq(1),
|
|
],
|
|
0x8: [
|
|
self.bus.dat_r[spi_wid:].eq(0),
|
|
self.bus.dat_r[0:spi_wid].eq(from_slave),
|
|
self.bus.ack.eq(1),
|
|
],
|
|
0xC: [
|
|
If(self.bus.we,
|
|
to_slave.eq(self.bus.dat_r[0:spi_wid]),
|
|
).Else(
|
|
self.bus.dat_r[spi_wid:].eq(0),
|
|
self.bus.dat_r[0:spi_wid].eq(to_slave),
|
|
),
|
|
self.bus.ack.eq(1),
|
|
],
|
|
0x10: If(finished_or_ready[0] | finished_or_ready[1],
|
|
self.bus.dat_r[1:].eq(0),
|
|
self.bus.dat_r.eq(finished_or_ready),
|
|
),
|
|
"default":
|
|
# 0xSPI00SPI
|
|
self.bus.dat_r.eq(0x57100571),
|
|
}),
|
|
).Elif(~self.bus.cyc,
|
|
self.bus.ack.eq(0)
|
|
)
|
|
]
|
|
|
|
self.specials += Instance("spi_master_ss",
|
|
p_SS_WAIT = ss_wait,
|
|
p_SS_WAIT_TIMER_LEN = minbits(ss_wait),
|
|
p_CYCLE_HALF_WAIT = spi_cycle_half_wait,
|
|
p_TIMER_LEN = minbits(spi_cycle_half_wait),
|
|
p_WID = spi_wid,
|
|
p_WID_LEN = minbits(spi_wid),
|
|
p_ENABLE_MISO = enable_miso,
|
|
p_ENABLE_MOSI = enable_mosi,
|
|
p_POLARITY = polarity,
|
|
p_PHASE = phase,
|
|
|
|
i_clk = ClockSignal(),
|
|
i_rst_L = rst,
|
|
i_miso = miso,
|
|
o_mosi = mosi,
|
|
o_sck_wire = sck,
|
|
o_ss_L = ss_L,
|
|
|
|
o_from_slave = from_slave,
|
|
i_to_slave = to_slave,
|
|
o_finished = finished_or_ready[1],
|
|
o_ready_to_arm = finished_or_ready[0],
|
|
i_arm = arm,
|
|
)
|