Merge pull request #1916 from motec-research/spi_mmap
SPIMMAP bug fixes and new features
This commit is contained in:
commit
2bc41928a3
|
@ -35,6 +35,7 @@ SPI_SLOT_MODE_3 = 0b11
|
||||||
SPI_SLOT_LENGTH_32B = 0b00
|
SPI_SLOT_LENGTH_32B = 0b00
|
||||||
SPI_SLOT_LENGTH_16B = 0b01
|
SPI_SLOT_LENGTH_16B = 0b01
|
||||||
SPI_SLOT_LENGTH_8B = 0b10
|
SPI_SLOT_LENGTH_8B = 0b10
|
||||||
|
SPI_SLOT_LENGTH_24B = 0b11
|
||||||
|
|
||||||
SPI_SLOT_BITORDER_MSB_FIRST = 0b0
|
SPI_SLOT_BITORDER_MSB_FIRST = 0b0
|
||||||
SPI_SLOT_BITORDER_LSB_FIRST = 0b1
|
SPI_SLOT_BITORDER_LSB_FIRST = 0b1
|
||||||
|
@ -206,7 +207,10 @@ class SPIMaster(LiteXModule):
|
||||||
self.sync += [
|
self.sync += [
|
||||||
If(miso_shift,
|
If(miso_shift,
|
||||||
miso_data.eq(Cat(miso, miso_data))
|
miso_data.eq(Cat(miso, miso_data))
|
||||||
)
|
),
|
||||||
|
If(self.start,
|
||||||
|
miso_data.eq(0)
|
||||||
|
),
|
||||||
]
|
]
|
||||||
self.comb += self.miso.eq(miso_data)
|
self.comb += self.miso.eq(miso_data)
|
||||||
|
|
||||||
|
@ -239,10 +243,18 @@ class SPICtrl(LiteXModule):
|
||||||
default_slot_bitorder = SPI_SLOT_BITORDER_MSB_FIRST,
|
default_slot_bitorder = SPI_SLOT_BITORDER_MSB_FIRST,
|
||||||
default_slot_loopback = 0b1,
|
default_slot_loopback = 0b1,
|
||||||
default_slot_divider = 2,
|
default_slot_divider = 2,
|
||||||
|
default_enable = 0b1,
|
||||||
|
default_slot_wait = 0,
|
||||||
):
|
):
|
||||||
self.nslots = nslots
|
self.nslots = nslots
|
||||||
self.slot_controls = []
|
self.slot_controls = []
|
||||||
self.slot_status = []
|
|
||||||
|
version = "SPI0"
|
||||||
|
self._version = CSRStatus(size=32, description="""SPI Module Version.""",
|
||||||
|
reset=int.from_bytes(str.encode(version), 'little'))
|
||||||
|
|
||||||
|
self.slot_count = CSRStatus(size=32, description="""SPI Module Slot Count.""",
|
||||||
|
reset=nslots)
|
||||||
|
|
||||||
# Create TX/RX Control/Status registers.
|
# Create TX/RX Control/Status registers.
|
||||||
self.tx_control = CSRStorage(fields=[
|
self.tx_control = CSRStorage(fields=[
|
||||||
|
@ -302,6 +314,13 @@ class SPICtrl(LiteXModule):
|
||||||
self.ev.rx.trigger.eq(self.rx_status.fields.level > self.rx_control.fields.threshold),
|
self.ev.rx.trigger.eq(self.rx_status.fields.level > self.rx_control.fields.threshold),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
self.engine = CSRStorage(fields=[
|
||||||
|
CSRField("enable", size=1, offset=0, values=[
|
||||||
|
("``0b0``", "SPI Engine Disabled."),
|
||||||
|
("``0b1``", "SPI Engine Enabled."),
|
||||||
|
], reset=default_enable),
|
||||||
|
])
|
||||||
|
|
||||||
# Create Slots Control/Status registers.
|
# Create Slots Control/Status registers.
|
||||||
for slot in range(nslots):
|
for slot in range(nslots):
|
||||||
control = CSRStorage(name=f"slot_control{slot}", fields=[
|
control = CSRStorage(name=f"slot_control{slot}", fields=[
|
||||||
|
@ -319,7 +338,7 @@ class SPICtrl(LiteXModule):
|
||||||
("``0b00``", "32-bit Max."),
|
("``0b00``", "32-bit Max."),
|
||||||
("``0b01``", "16-bit Max."),
|
("``0b01``", "16-bit Max."),
|
||||||
("``0b10``", " 8-bit Max."),
|
("``0b10``", " 8-bit Max."),
|
||||||
("``0b11``", "Reserved."),
|
("``0b11``", "24-bit Max."),
|
||||||
], reset=default_slot_length),
|
], reset=default_slot_length),
|
||||||
CSRField("bitorder", size=1, offset=5, values=[
|
CSRField("bitorder", size=1, offset=5, values=[
|
||||||
("``0b0``", "MSB-First."),
|
("``0b0``", "MSB-First."),
|
||||||
|
@ -335,13 +354,15 @@ class SPICtrl(LiteXModule):
|
||||||
("``0x0002``", "SPI-Clk = Sys-Clk/2."),
|
("``0x0002``", "SPI-Clk = Sys-Clk/2."),
|
||||||
("``0x0004``", "SPI-Clk = Sys-Clk/4."),
|
("``0x0004``", "SPI-Clk = Sys-Clk/4."),
|
||||||
("``0xxxxx``", "SPI-Clk = Sys-Clk/xxxxx."),
|
("``0xxxxx``", "SPI-Clk = Sys-Clk/xxxxx."),
|
||||||
], reset=default_slot_divider)
|
], reset=default_slot_divider),
|
||||||
|
CSRField("wait", size=16, offset=32, values=[
|
||||||
|
("``0x0000``", "No wait time."),
|
||||||
|
("``0x0001``", "wait = 1 / Sys-Clk."),
|
||||||
|
("``0xxxxx``", "wait = xxxx / Sys-Clk."),
|
||||||
|
], reset=default_slot_wait),
|
||||||
])
|
])
|
||||||
status = CSRStatus(name=f"slot_status{slot}") # CHECKME: Useful?
|
|
||||||
setattr(self, f"slot_control{slot}", control)
|
setattr(self, f"slot_control{slot}", control)
|
||||||
setattr(self, f"slot_status{slot}", status)
|
|
||||||
self.slot_controls.append(control)
|
self.slot_controls.append(control)
|
||||||
self.slot_status.append(status)
|
|
||||||
|
|
||||||
def get_ctrl(self, name, slot=None, cs=None):
|
def get_ctrl(self, name, slot=None, cs=None):
|
||||||
assert not ((slot is None) and (cs is None))
|
assert not ((slot is None) and (cs is None))
|
||||||
|
@ -487,7 +508,7 @@ class SPIRXMMAP(LiteXModule):
|
||||||
# SPI Engine ---------------------------------------------------------------------------------------
|
# SPI Engine ---------------------------------------------------------------------------------------
|
||||||
|
|
||||||
class SPIEngine(LiteXModule):
|
class SPIEngine(LiteXModule):
|
||||||
def __init__(self, pads, ctrl, data_width, sys_clk_freq, default_enable=0b1):
|
def __init__(self, pads, ctrl, data_width, sys_clk_freq):
|
||||||
self.sink = sink = stream.Endpoint(spi_layout(
|
self.sink = sink = stream.Endpoint(spi_layout(
|
||||||
data_width = data_width,
|
data_width = data_width,
|
||||||
be_width = data_width//8,
|
be_width = data_width//8,
|
||||||
|
@ -499,13 +520,6 @@ class SPIEngine(LiteXModule):
|
||||||
cs_width = len(pads.cs_n)
|
cs_width = len(pads.cs_n)
|
||||||
))
|
))
|
||||||
|
|
||||||
self.control = CSRStorage(fields=[
|
|
||||||
CSRField("enable", size=1, offset=0, values=[
|
|
||||||
("``0b0``", "SPI Engine Disabled."),
|
|
||||||
("``0b1``", "SPI Engine Enabled."),
|
|
||||||
], reset=default_enable),
|
|
||||||
])
|
|
||||||
|
|
||||||
# # #
|
# # #
|
||||||
|
|
||||||
# SPI Master.
|
# SPI Master.
|
||||||
|
@ -532,6 +546,7 @@ class SPIEngine(LiteXModule):
|
||||||
})
|
})
|
||||||
self.comb += Case(ctrl.get_ctrl("length", cs=sink.cs), {
|
self.comb += Case(ctrl.get_ctrl("length", cs=sink.cs), {
|
||||||
SPI_SLOT_LENGTH_32B : spi_length_max.eq(32), # 32-bit access max.
|
SPI_SLOT_LENGTH_32B : spi_length_max.eq(32), # 32-bit access max.
|
||||||
|
SPI_SLOT_LENGTH_24B : spi_length_max.eq(24), # 24-bit access max.
|
||||||
SPI_SLOT_LENGTH_16B : spi_length_max.eq(16), # 16-bit access max.
|
SPI_SLOT_LENGTH_16B : spi_length_max.eq(16), # 16-bit access max.
|
||||||
SPI_SLOT_LENGTH_8B : spi_length_max.eq( 8), # 8-bit access max.
|
SPI_SLOT_LENGTH_8B : spi_length_max.eq( 8), # 8-bit access max.
|
||||||
})
|
})
|
||||||
|
@ -543,8 +558,15 @@ class SPIEngine(LiteXModule):
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# Wait between transfers.
|
||||||
|
ctrl_wait = ctrl.get_ctrl("wait", cs=sink.cs)
|
||||||
|
wait_ticks = Signal.like(ctrl_wait)
|
||||||
|
wait_count = Signal.like(ctrl_wait)
|
||||||
|
self.comb += wait_ticks.eq(ctrl_wait)
|
||||||
|
cs_wait = Signal()
|
||||||
|
|
||||||
# SPI CS. (Use Manual CS to allow back-to-back Xfers).
|
# SPI CS. (Use Manual CS to allow back-to-back Xfers).
|
||||||
self.comb += If(self.control.fields.enable & sink.valid,
|
self.comb += If(ctrl.engine.fields.enable & sink.valid & ~cs_wait,
|
||||||
spi.cs.eq(sink.cs)
|
spi.cs.eq(sink.cs)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -555,7 +577,7 @@ class SPIEngine(LiteXModule):
|
||||||
# Control-Path.
|
# Control-Path.
|
||||||
self.fsm = fsm = FSM(reset_state="START")
|
self.fsm = fsm = FSM(reset_state="START")
|
||||||
fsm.act("START",
|
fsm.act("START",
|
||||||
If(self.control.fields.enable & sink.valid,
|
If(ctrl.engine.fields.enable & sink.valid,
|
||||||
spi.start.eq(1),
|
spi.start.eq(1),
|
||||||
NextState("XFER")
|
NextState("XFER")
|
||||||
)
|
)
|
||||||
|
@ -571,6 +593,20 @@ class SPIEngine(LiteXModule):
|
||||||
source.be.eq(sink.be),
|
source.be.eq(sink.be),
|
||||||
If(source.ready,
|
If(source.ready,
|
||||||
sink.ready.eq(1),
|
sink.ready.eq(1),
|
||||||
|
If(wait_ticks,
|
||||||
|
cs_wait.eq(1),
|
||||||
|
NextValue(wait_count, wait_ticks-1),
|
||||||
|
NextState("WAIT")
|
||||||
|
).Else(
|
||||||
|
NextState("START")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
fsm.act("WAIT",
|
||||||
|
If(wait_count,
|
||||||
|
cs_wait.eq(1),
|
||||||
|
NextValue(wait_count, wait_count-1)
|
||||||
|
).Else(
|
||||||
NextState("START")
|
NextState("START")
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -580,9 +616,10 @@ class SPIEngine(LiteXModule):
|
||||||
# MSB First.
|
# MSB First.
|
||||||
If(spi_bitorder == SPI_SLOT_BITORDER_MSB_FIRST,
|
If(spi_bitorder == SPI_SLOT_BITORDER_MSB_FIRST,
|
||||||
# TX copy/bitshift.
|
# TX copy/bitshift.
|
||||||
Case(spi_length, {
|
Case(spi.length, {
|
||||||
8 : spi.mosi[24:32].eq(sink.data[0: 8]),
|
8 : spi.mosi[24:32].eq(sink.data[0: 8]),
|
||||||
16 : spi.mosi[16:32].eq(sink.data[0:16]),
|
16 : spi.mosi[16:32].eq(sink.data[0:16]),
|
||||||
|
24 : spi.mosi[ 8:32].eq(sink.data[0:24]),
|
||||||
32 : spi.mosi[ 0:32].eq(sink.data[0:32]),
|
32 : spi.mosi[ 0:32].eq(sink.data[0:32]),
|
||||||
}),
|
}),
|
||||||
# RX copy.
|
# RX copy.
|
||||||
|
@ -593,9 +630,10 @@ class SPIEngine(LiteXModule):
|
||||||
# TX copy.
|
# TX copy.
|
||||||
spi.mosi.eq(sink.data[::-1]),
|
spi.mosi.eq(sink.data[::-1]),
|
||||||
# RX copy/bitshift.
|
# RX copy/bitshift.
|
||||||
Case(spi_length, {
|
Case(spi.length, {
|
||||||
8 : source.data[0: 8].eq(spi.miso[::-1][24:32]),
|
8 : source.data[0: 8].eq(spi.miso[::-1][24:32]),
|
||||||
16 : source.data[0:16].eq(spi.miso[::-1][16:32]),
|
16 : source.data[0:16].eq(spi.miso[::-1][16:32]),
|
||||||
|
24 : source.data[0:24].eq(spi.miso[::-1][ 8:32]),
|
||||||
32 : source.data[0:32].eq(spi.miso[::-1][ 0:32]),
|
32 : source.data[0:32].eq(spi.miso[::-1][ 0:32]),
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
|
@ -90,6 +90,8 @@ class Interface(Record):
|
||||||
yield self.bte.eq(bte)
|
yield self.bte.eq(bte)
|
||||||
yield self.we.eq(1)
|
yield self.we.eq(1)
|
||||||
yield from self._do_transaction()
|
yield from self._do_transaction()
|
||||||
|
if (yield self.err):
|
||||||
|
raise ValueError("bus error")
|
||||||
|
|
||||||
def read(self, adr, cti=None, bte=None):
|
def read(self, adr, cti=None, bte=None):
|
||||||
yield self.adr.eq(adr)
|
yield self.adr.eq(adr)
|
||||||
|
@ -99,6 +101,8 @@ class Interface(Record):
|
||||||
if bte is not None:
|
if bte is not None:
|
||||||
yield self.bte.eq(bte)
|
yield self.bte.eq(bte)
|
||||||
yield from self._do_transaction()
|
yield from self._do_transaction()
|
||||||
|
if (yield self.err):
|
||||||
|
raise ValueError("bus error")
|
||||||
return (yield self.dat_r)
|
return (yield self.dat_r)
|
||||||
|
|
||||||
def get_ios(self, bus_name="wb"):
|
def get_ios(self, bus_name="wb"):
|
||||||
|
|
|
@ -6,32 +6,77 @@
|
||||||
# SPDX-License-Identifier: BSD-2-Clause
|
# SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
|
||||||
import unittest
|
import unittest
|
||||||
import random
|
import inspect
|
||||||
|
|
||||||
from migen import *
|
from migen import Record
|
||||||
|
|
||||||
from litex.gen.sim import *
|
from litex.gen.sim import run_simulation
|
||||||
|
|
||||||
|
from litex.soc.cores.spi.spi_mmap import (
|
||||||
|
SPIMaster,
|
||||||
|
SPIMMAP,
|
||||||
|
SPI_SLOT_BITORDER_LSB_FIRST,
|
||||||
|
SPI_SLOT_BITORDER_MSB_FIRST,
|
||||||
|
SPI_SLOT_LENGTH_16B,
|
||||||
|
SPI_SLOT_LENGTH_24B,
|
||||||
|
SPI_SLOT_LENGTH_32B,
|
||||||
|
SPI_SLOT_LENGTH_8B,
|
||||||
|
SPI_SLOT_MODE_0,
|
||||||
|
SPI_SLOT_MODE_3,
|
||||||
|
)
|
||||||
|
|
||||||
|
verbose = None
|
||||||
|
|
||||||
|
|
||||||
|
def unittest_verbosity():
|
||||||
|
"""Return the verbosity setting of the currently running unittest
|
||||||
|
program, or 0 if none is running.
|
||||||
|
|
||||||
|
"""
|
||||||
|
frame = inspect.currentframe()
|
||||||
|
while frame:
|
||||||
|
self = frame.f_locals.get("self")
|
||||||
|
if isinstance(self, unittest.TestProgram):
|
||||||
|
return self.verbosity
|
||||||
|
frame = frame.f_back
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
def vprint(*args):
|
||||||
|
global verbose
|
||||||
|
if verbose is None:
|
||||||
|
verbose = unittest_verbosity()
|
||||||
|
if verbose > 1:
|
||||||
|
print(*args)
|
||||||
|
|
||||||
|
|
||||||
|
def vvprint(*args):
|
||||||
|
global verbose
|
||||||
|
if verbose is None:
|
||||||
|
verbose = unittest_verbosity()
|
||||||
|
if verbose > 2:
|
||||||
|
print(*args)
|
||||||
|
|
||||||
from litex.soc.cores.spi.spi_mmap import SPIMaster
|
|
||||||
|
|
||||||
class TestSPIMMAP(unittest.TestCase):
|
class TestSPIMMAP(unittest.TestCase):
|
||||||
def test_spi_master(self):
|
def test_spi_master(self):
|
||||||
pads = Record([("clk", 1), ("cs_n", 4), ("mosi", 1), ("miso", 1)])
|
pads = Record([("clk", 1), ("cs_n", 4), ("mosi", 1), ("miso", 1)])
|
||||||
dut = SPIMaster(pads=pads, data_width=32, sys_clk_freq=int(100e6))
|
dut = SPIMaster(pads=pads, data_width=32, sys_clk_freq=int(100e6))
|
||||||
|
|
||||||
def generator(dut):
|
def generator(dut):
|
||||||
data = [
|
data = [
|
||||||
0x12345678,
|
0x12345678,
|
||||||
0xdeadbeef,
|
0xDEADBEEF,
|
||||||
]
|
]
|
||||||
#data = [
|
# data = [
|
||||||
# 0x80000001,
|
# 0x80000001,
|
||||||
# 0x80000001,
|
# 0x80000001,
|
||||||
#]
|
# ]
|
||||||
|
|
||||||
# Config: Mode0, Loopback, Sys-Clk/4
|
# Config: Mode0, Loopback, Sys-Clk/4
|
||||||
yield dut.loopback.eq(1)
|
yield dut.loopback.eq(1)
|
||||||
yield dut.clk_divider.eq(4)
|
yield dut.clk_divider.eq(4)
|
||||||
yield dut.mode.eq(0)
|
yield dut.mode.eq(SPI_SLOT_MODE_0)
|
||||||
yield
|
yield
|
||||||
yield dut.mosi.eq(data[0])
|
yield dut.mosi.eq(data[0])
|
||||||
yield dut.cs.eq(0b0001)
|
yield dut.cs.eq(0b0001)
|
||||||
|
@ -49,7 +94,7 @@ class TestSPIMMAP(unittest.TestCase):
|
||||||
# Config: Mode3, Loopback, Sys-Clk/4.
|
# Config: Mode3, Loopback, Sys-Clk/4.
|
||||||
yield dut.loopback.eq(1)
|
yield dut.loopback.eq(1)
|
||||||
yield dut.clk_divider.eq(4)
|
yield dut.clk_divider.eq(4)
|
||||||
yield dut.mode.eq(3)
|
yield dut.mode.eq(SPI_SLOT_MODE_3)
|
||||||
yield
|
yield
|
||||||
yield dut.mosi.eq(data[0])
|
yield dut.mosi.eq(data[0])
|
||||||
yield dut.cs.eq(0b0001)
|
yield dut.cs.eq(0b0001)
|
||||||
|
@ -67,7 +112,7 @@ class TestSPIMMAP(unittest.TestCase):
|
||||||
# Config: Mode0, Loopback, Sys-Clk/8.
|
# Config: Mode0, Loopback, Sys-Clk/8.
|
||||||
yield dut.loopback.eq(1)
|
yield dut.loopback.eq(1)
|
||||||
yield dut.clk_divider.eq(8)
|
yield dut.clk_divider.eq(8)
|
||||||
yield dut.mode.eq(0)
|
yield dut.mode.eq(SPI_SLOT_MODE_0)
|
||||||
yield
|
yield
|
||||||
yield dut.mosi.eq(data[1])
|
yield dut.mosi.eq(data[1])
|
||||||
yield dut.cs.eq(0b0001)
|
yield dut.cs.eq(0b0001)
|
||||||
|
@ -85,7 +130,7 @@ class TestSPIMMAP(unittest.TestCase):
|
||||||
# Config: Mode3, Loopback, Sys-Clk/8.
|
# Config: Mode3, Loopback, Sys-Clk/8.
|
||||||
yield dut.loopback.eq(1)
|
yield dut.loopback.eq(1)
|
||||||
yield dut.clk_divider.eq(8)
|
yield dut.clk_divider.eq(8)
|
||||||
yield dut.mode.eq(3)
|
yield dut.mode.eq(SPI_SLOT_MODE_3)
|
||||||
yield
|
yield
|
||||||
yield dut.mosi.eq(data[1])
|
yield dut.mosi.eq(data[1])
|
||||||
yield dut.cs.eq(0b0001)
|
yield dut.cs.eq(0b0001)
|
||||||
|
@ -101,3 +146,190 @@ class TestSPIMMAP(unittest.TestCase):
|
||||||
print(f"mosi_data : {(yield dut.miso):08x}")
|
print(f"mosi_data : {(yield dut.miso):08x}")
|
||||||
|
|
||||||
run_simulation(dut, generator(dut), vcd_name="sim.vcd")
|
run_simulation(dut, generator(dut), vcd_name="sim.vcd")
|
||||||
|
|
||||||
|
def mmap_test(self, length, bitorder, data, vcd_name=None, sel_override=None, wait=0):
|
||||||
|
pads = Record([("clk", 1), ("cs_n", 4), ("mosi", 1), ("miso", 1)])
|
||||||
|
dut = SPIMMAP(
|
||||||
|
pads=pads,
|
||||||
|
data_width=32,
|
||||||
|
sys_clk_freq=int(100e6), # only used for clock settle time!
|
||||||
|
tx_fifo_depth=32,
|
||||||
|
rx_fifo_depth=32,
|
||||||
|
)
|
||||||
|
|
||||||
|
def generator(dut):
|
||||||
|
# Minimal setup - spi_mmap ctrl defaults are everything enabled and:
|
||||||
|
# SPI_SLOT_MODE_3, SPI_SLOT_LENGTH_32B, SPI_SLOT_BITORDER_MSB_FIRST, loopback, divider=2, wait=0
|
||||||
|
version = yield dut.ctrl._version.status
|
||||||
|
vprint(f"version: {version}")
|
||||||
|
vprint(f"slot_count: {(yield dut.ctrl.slot_count.status)}")
|
||||||
|
# yield dut.ctrl.slot_control0.fields.enable.eq(1)
|
||||||
|
# yield dut.ctrl.slot_control0.fields.mode.eq(SPI_SLOT_MODE_3)
|
||||||
|
yield dut.ctrl.slot_control0.fields.length.eq(length)
|
||||||
|
yield dut.ctrl.slot_control0.fields.bitorder.eq(bitorder)
|
||||||
|
yield dut.ctrl.slot_control1.fields.length.eq(length)
|
||||||
|
yield dut.ctrl.slot_control1.fields.bitorder.eq(bitorder)
|
||||||
|
# yield dut.ctrl.slot_control0.fields.loopback.eq(1)
|
||||||
|
# yield dut.ctrl.slot_control0.fields.divider.eq(2)
|
||||||
|
# yield dut.ctrl.slot_control0.fields.enable.eq(1)
|
||||||
|
yield dut.ctrl.slot_control0.fields.wait.eq(wait)
|
||||||
|
if length == SPI_SLOT_LENGTH_32B:
|
||||||
|
spi_length = 32
|
||||||
|
sel = 0b1111
|
||||||
|
width = 8
|
||||||
|
if length == SPI_SLOT_LENGTH_24B:
|
||||||
|
spi_length = 24
|
||||||
|
sel = 0b1111
|
||||||
|
width = 6
|
||||||
|
if length == SPI_SLOT_LENGTH_16B:
|
||||||
|
spi_length = 16
|
||||||
|
sel = 0b0011
|
||||||
|
width = 4
|
||||||
|
if length == SPI_SLOT_LENGTH_8B:
|
||||||
|
spi_length = 8
|
||||||
|
sel = 0b0001
|
||||||
|
width = 2
|
||||||
|
if sel_override:
|
||||||
|
sel = sel_override
|
||||||
|
|
||||||
|
vprint(f"spi_length {spi_length} width {width} sel {sel:b} len(data) {len(data)}")
|
||||||
|
|
||||||
|
dut_tx_status = dut.ctrl.tx_status.fields
|
||||||
|
dut_rx_status = dut.ctrl.rx_status.fields
|
||||||
|
self.assertEqual((yield dut_tx_status.empty), 1)
|
||||||
|
self.assertEqual((yield dut_tx_status.full), 0)
|
||||||
|
self.assertEqual((yield dut_tx_status.ongoing), 0)
|
||||||
|
self.assertEqual((yield dut_tx_status.level), 0)
|
||||||
|
self.assertEqual((yield dut_rx_status.empty), 1)
|
||||||
|
self.assertEqual((yield dut_rx_status.full), 0)
|
||||||
|
self.assertEqual((yield dut_rx_status.ongoing), 0)
|
||||||
|
self.assertEqual((yield dut_rx_status.level), 0)
|
||||||
|
for slot, d in data:
|
||||||
|
vprint(f"write({slot}):{d:0{width}x}")
|
||||||
|
yield from dut.tx_mmap.bus.write(slot, d, sel)
|
||||||
|
yield
|
||||||
|
self.assertEqual((yield dut_tx_status.empty), 0)
|
||||||
|
self.assertEqual((yield dut_tx_status.full), 0)
|
||||||
|
self.assertEqual((yield dut_tx_status.ongoing), 1)
|
||||||
|
self.assertGreater((yield dut_tx_status.level), 0)
|
||||||
|
self.assertEqual((yield dut_rx_status.empty), 1)
|
||||||
|
self.assertEqual((yield dut_rx_status.full), 0)
|
||||||
|
self.assertEqual((yield dut_rx_status.ongoing), 1)
|
||||||
|
self.assertEqual((yield dut_rx_status.level), 0)
|
||||||
|
|
||||||
|
tx_empty = -1
|
||||||
|
rx_empty = -1
|
||||||
|
miso = -1
|
||||||
|
mosi = -1
|
||||||
|
while (yield dut_rx_status.ongoing) == 0b1 or (yield dut_rx_status.level) != len(data):
|
||||||
|
if rx_empty != (rx_empty := (yield dut_rx_status.empty)):
|
||||||
|
vprint(f"rx_empty:{rx_empty}")
|
||||||
|
if tx_empty != (tx_empty := (yield dut_tx_status.empty)):
|
||||||
|
vprint(f"tx_empty:{tx_empty}")
|
||||||
|
if mosi != (mosi := (yield dut.tx_rx_engine.spi.mosi)):
|
||||||
|
vvprint(f"mosi => {mosi:0{width}x}")
|
||||||
|
if miso != (miso := (yield dut.tx_rx_engine.spi.miso)):
|
||||||
|
vvprint(f"miso <= {miso:0{width}x}")
|
||||||
|
yield
|
||||||
|
|
||||||
|
yield
|
||||||
|
for slot, d in data:
|
||||||
|
read = yield from dut.rx_mmap.bus.read(slot)
|
||||||
|
self.assertEqual(read, d, f"read({slot}) {read:0{width}x} expect: {d:0{width}x}")
|
||||||
|
|
||||||
|
run_simulation(dut, generator(dut), vcd_name=vcd_name)
|
||||||
|
|
||||||
|
# 32 bit write to 32bit slot
|
||||||
|
def test_spi_mmap_32_lsb(self):
|
||||||
|
data = [(0, 0x12345678), (0, 0x9ABCDEF0)]
|
||||||
|
self.mmap_test(SPI_SLOT_LENGTH_32B, SPI_SLOT_BITORDER_LSB_FIRST, data, "mmap_32_lsb.vcd")
|
||||||
|
|
||||||
|
def test_spi_mmap_32_msb(self):
|
||||||
|
data = [(0, 0x12345678), (0, 0x9ABCDEF0)]
|
||||||
|
self.mmap_test(SPI_SLOT_LENGTH_32B, SPI_SLOT_BITORDER_MSB_FIRST, data, "mmap_32_msb.vcd")
|
||||||
|
|
||||||
|
def test_spi_mmap_32_slot0_1_lsb(self):
|
||||||
|
data = [
|
||||||
|
(0, 0x12345678), (0, 0x9ABCDEF0), (0, 0x87654321), (0, 0x0FEDCBA9),
|
||||||
|
(1, 0x0FEDCBA9), (1, 0x87654321), (1, 0x9ABCDEF0), (1, 0x12345678)
|
||||||
|
]
|
||||||
|
self.mmap_test(SPI_SLOT_LENGTH_32B, SPI_SLOT_BITORDER_LSB_FIRST, data, "mmap_32_slot_0_1_lsb.vcd")
|
||||||
|
|
||||||
|
def test_spi_mmap_32_slot0_1_msb(self):
|
||||||
|
data = [
|
||||||
|
(0, 0x12345678), (0, 0x9ABCDEF0), (0, 0x87654321), (0, 0x0FEDCBA9),
|
||||||
|
(1, 0x0FEDCBA9), (1, 0x87654321), (1, 0x9ABCDEF0), (1, 0x12345678)
|
||||||
|
]
|
||||||
|
self.mmap_test(SPI_SLOT_LENGTH_32B, SPI_SLOT_BITORDER_MSB_FIRST, data, "mmap_32_slot_0_1_msb.vcd")
|
||||||
|
|
||||||
|
def test_spi_mmap_24_lsb(self):
|
||||||
|
data = [(0, 0x123456), (0, 0x789ABC), (0, 0xDEF012)]
|
||||||
|
self.mmap_test(SPI_SLOT_LENGTH_24B, SPI_SLOT_BITORDER_LSB_FIRST, data, "mmap_24_lsb.vcd")
|
||||||
|
|
||||||
|
def test_spi_mmap_24_msb(self):
|
||||||
|
data = [(0, 0x123456), (0, 0x789ABC), (0, 0xDEF012)]
|
||||||
|
self.mmap_test(SPI_SLOT_LENGTH_24B, SPI_SLOT_BITORDER_MSB_FIRST, data, "mmap_24_msb.vcd")
|
||||||
|
|
||||||
|
def test_spi_mmap_24_slot0_1_lsb(self):
|
||||||
|
data = [
|
||||||
|
(0, 0x123456), (0, 0x9ABCDE), (0, 0x876543), (0, 0x0FEDCB),
|
||||||
|
(1, 0x0FEDCB), (1, 0x876543), (1, 0x9ABCDE), (1, 0x123456)
|
||||||
|
]
|
||||||
|
self.mmap_test(SPI_SLOT_LENGTH_24B, SPI_SLOT_BITORDER_LSB_FIRST, data, "mmap_24_slot_0_1_lsb.vcd")
|
||||||
|
|
||||||
|
def test_spi_mmap_24_slot0_1_msb(self):
|
||||||
|
data = [
|
||||||
|
(0, 0x123456), (0, 0x9ABCDE), (0, 0x876543), (0, 0x0FEDCB),
|
||||||
|
(1, 0x0FEDCB), (1, 0x876543), (1, 0x9ABCDE), (1, 0x123456)
|
||||||
|
]
|
||||||
|
self.mmap_test(SPI_SLOT_LENGTH_24B, SPI_SLOT_BITORDER_MSB_FIRST, data, "mmap_24_slot_0_1_msb.vcd")
|
||||||
|
|
||||||
|
# 16 bit write to 16bit slot
|
||||||
|
def test_spi_mmap_16_lsb(self):
|
||||||
|
data = [(0, 0x1234), (0, 0x5678), (0, 0x9ABC), (0, 0xDEF0)]
|
||||||
|
self.mmap_test(SPI_SLOT_LENGTH_16B, SPI_SLOT_BITORDER_LSB_FIRST, data, "mmap_16_lsb.vcd")
|
||||||
|
|
||||||
|
def test_spi_mmap_16_msb(self):
|
||||||
|
data = [(0, 0x1234), (0, 0x5678), (0, 0x9ABC), (0, 0xDEF0)]
|
||||||
|
self.mmap_test(SPI_SLOT_LENGTH_16B, SPI_SLOT_BITORDER_MSB_FIRST, data, "mmap_16_msb.vcd")
|
||||||
|
|
||||||
|
# 32 bit write to 16bit slot
|
||||||
|
def test_spi_mmap_16_lsb_wb32(self):
|
||||||
|
data = [(0, 0x1234), (0, 0x5678), (0, 0x9ABC), (0, 0xDEF0)]
|
||||||
|
self.mmap_test(
|
||||||
|
SPI_SLOT_LENGTH_16B,
|
||||||
|
SPI_SLOT_BITORDER_LSB_FIRST,
|
||||||
|
data,
|
||||||
|
"mmap_16_lsb_wb32.vcd",
|
||||||
|
sel_override=0b1111,
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_spi_mmap_16_msb_wb32(self):
|
||||||
|
data = [(0, 0x1234), (0, 0x5678), (0, 0x9ABC), (0, 0xDEF0)]
|
||||||
|
self.mmap_test(
|
||||||
|
SPI_SLOT_LENGTH_16B,
|
||||||
|
SPI_SLOT_BITORDER_MSB_FIRST,
|
||||||
|
data,
|
||||||
|
"mmap_16_msb_wb32.vcd",
|
||||||
|
sel_override=0b1111,
|
||||||
|
)
|
||||||
|
|
||||||
|
# 8 bit write to 8bit slot
|
||||||
|
def test_spi_mmap_8_lsb(self):
|
||||||
|
data = [(0, 0x12), (0, 0x34), (0, 0x56), (0, 0x78), (0, 0x9A), (0, 0xBC), (0, 0xDE), (0, 0xF0)]
|
||||||
|
self.mmap_test(SPI_SLOT_LENGTH_8B, SPI_SLOT_BITORDER_LSB_FIRST, data, "mmap_8_lsb.vcd")
|
||||||
|
|
||||||
|
def test_spi_mmap_8_msb(self):
|
||||||
|
data = [(0, 0x12), (0, 0x34), (0, 0x56), (0, 0x78), (0, 0x9A), (0, 0xBC), (0, 0xDE), (0, 0xF0)]
|
||||||
|
self.mmap_test(SPI_SLOT_LENGTH_8B, SPI_SLOT_BITORDER_MSB_FIRST, data, "mmap_8_msb.vcd")
|
||||||
|
|
||||||
|
def test_spi_mmap_8_msb_wait1(self):
|
||||||
|
data = [(0, 0x12), (0, 0x34), (0, 0x56), (0, 0x78), (0, 0x9A), (0, 0xBC), (0, 0xDE), (0, 0xF0)]
|
||||||
|
self.mmap_test(SPI_SLOT_LENGTH_8B, SPI_SLOT_BITORDER_MSB_FIRST, data, "mmap_8_msb_wait1.vcd", wait=1)
|
||||||
|
|
||||||
|
def test_spi_mmap_8_msb_wait8(self):
|
||||||
|
data = [(0, 0x12), (0, 0x34), (0, 0x56), (0, 0x78), (0, 0x9A), (0, 0xBC), (0, 0xDE), (0, 0xF0)]
|
||||||
|
self.mmap_test(SPI_SLOT_LENGTH_8B, SPI_SLOT_BITORDER_MSB_FIRST, data, "mmap_8_msb_wait8.vcd", wait=8)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
|
|
Loading…
Reference in New Issue