soc/cores/i2s: cleanup pass, rename to S7I2SSlave (since 7-Series specific for now), rename fifodepth to fifo_depth for consistency with others cores.

This commit is contained in:
Florent Kermarrec 2020-02-06 17:00:04 +01:00
parent c2c80b5d0a
commit f58e8188b7
2 changed files with 288 additions and 222 deletions

View File

@ -1,38 +1,41 @@
# This file is Copyright (c) 2020 bunnie <bunnie@kosagi.com> # This file is Copyright (c) 2020 bunnie <bunnie@kosagi.com>
# License: BSD # License: BSD
from litex.soc.interconnect.csr_eventmanager import *
from litex.soc.interconnect import wishbone
from litex.soc.integration.doc import AutoDoc, ModuleDoc
from migen.genlib.cdc import MultiReg from migen.genlib.cdc import MultiReg
class i2s_slave(Module, AutoCSR, AutoDoc): from litex.soc.interconnect import wishbone
def __init__(self, pads, fifodepth=256): from litex.soc.interconnect.csr_eventmanager import *
from litex.soc.integration.doc import AutoDoc, ModuleDoc
class S7I2SSlave(Module, AutoCSR, AutoDoc):
def __init__(self, pads, fifo_depth=256):
self.intro = ModuleDoc(""" self.intro = ModuleDoc("""
Intro Intro
******* *******
I2S slave creates a slave audio interface instance. Tx and Rx interfaces are inferred I2S slave creates a slave audio interface instance. Tx and Rx interfaces are inferred based
based upon the presence or absence of the respective pins in the "pads" argument. upon the presence or absence of the respective pins in the "pads" argument.
The interface is I2S-like, but note the deviation that the bits are justified The interface is I2S-like, but note the deviation that the bits are justified left without a
left without a 1-bit pad after sync edges. This isn't a problem for talking to the LM49352 1-bit pad after sync edges. This isn't a problem for talking to the LM49352 codec this was
codec this was designed for, as the bit offset is programmable, but this will not work well if designed for, as the bit offset is programmable, but this will not work well if are talking
are talking to a CODEC without a programmable bit offset! to a CODEC without a programmable bit offset!
System Interface System Interface
================= =================
Audio interchange is done with the system using 16-bit stereo samples, with the right channel Audio interchange is done with the system using 16-bit stereo samples, with the right channel
mapped to the least significant word of a 32-bit word. Thus each 32-bit word is a single mapped to the least significant word of a 32-bit word. Thus each 32-bit word is a single
stereo sample. As this is a slave I2S interface, sampling rate and framing is set by the programming stereo sample. As this is a slave I2S interface, sampling rate and framing is set by the
of the audio CODEC chip. A slave situation is preferred because this defers the generation of programming of the audio CODEC chip. A slave situation is preferred because this defers the
audio clocks to the CODEC, which has PLLs specialized to generate the correct frequencies for generation of audio clocks to the CODEC, which has PLLs specialized to generate the correct
audio sampling rates. frequencies for audio sampling rates.
`fifodepth` is the depth at which either a read interrupt is fired (guaranteeing at least `fifodepth` `fifo_depth` is the depth at which either a read interrupt is fired (guaranteeing at least
stereo samples in the receive FIFO) or a write interrupt is fired (guaranteeing at least `fifodepth` `fifo_depth` stereo samples in the receive FIFO) or a write interrupt is fired (guaranteeing
free space in the transmit FIFO). The maximum depth is 512. at least `fifo_depth` free space in the transmit FIFO). The maximum depth is 512.
To receive audio data: To receive audio data:
@ -40,7 +43,7 @@ class i2s_slave(Module, AutoCSR, AutoDoc):
- hook the Rx full interrupt with an interrupt handler (optional) - hook the Rx full interrupt with an interrupt handler (optional)
- if the CODEC is not yet transmitting data, initiate data transmission - if the CODEC is not yet transmitting data, initiate data transmission
- enable Rx FIFO to run - enable Rx FIFO to run
- poll or wait for interrupt; upon interrupt, read `fifodepth` words. Repeat. - poll or wait for interrupt; upon interrupt, read `fifo_depth` words. Repeat.
- to close the stream, simply clear the Rx FIFO enable bit. The next initiation should call a - to close the stream, simply clear the Rx FIFO enable bit. The next initiation should call a
reset of the FIFO to ensure leftover previous data is cleared from the FIFO. reset of the FIFO to ensure leftover previous data is cleared from the FIFO.
@ -51,17 +54,17 @@ class i2s_slave(Module, AutoCSR, AutoDoc):
- write 512 words of data into the Tx FIFO, filling it to the max - write 512 words of data into the Tx FIFO, filling it to the max
- if the CODEC is not yet requesting data and unmuted, unmute and initiate reception - if the CODEC is not yet requesting data and unmuted, unmute and initiate reception
- enable the Tx FIFO to run - enable the Tx FIFO to run
- poll or wait for interrupt; upon interrupt, write `fifodepth` words. Repeat. - poll or wait for interrupt; upon interrupt, write `fifo_depth` words. Repeat.
- to close stream, mute the DAC and stop the request clock. Ideally, this can be completed before - to close stream, mute the DAC and stop the request clock. Ideally, this can be completed
the FIFO is emptied, so there is no jarring pop or truncation of data before the FIFO is emptied, so there is no jarring pop or truncation of data
- stop FIFO running. Next initiation should reset the FIFO to ensure leftover previous data in - stop FIFO running. Next initiation should reset the FIFO to ensure leftover previous data
FIFO is cleared. in FIFO is cleared.
CODEC Interface CODEC Interface
================ ================
The interface assumes we have a sysclk domain running around 100MHz, and that our typical The interface assumes we have a sysclk domain running around 100MHz, and that our typical max
max audio rate is 44.1kHz * 24bits * 2channels = 2.1168MHz audio clock. Thus, the architecture audio rate is 44.1kHz * 24bits * 2channels = 2.1168MHz audio clock. Thus, the architecture
treats the audio clock and data as asynchronous inputs that are MultiReg-syncd into the clock treats the audio clock and data as asynchronous inputs that are MultiReg-syncd into the clock
domain. Probably the slowest sysclk rate this might work with is around 20-25MHz (10x over domain. Probably the slowest sysclk rate this might work with is around 20-25MHz (10x over
sampling), but at 100MHz things will be quite comfortable. sampling), but at 100MHz things will be quite comfortable.
@ -77,12 +80,14 @@ class i2s_slave(Module, AutoCSR, AutoDoc):
{ "signal" : [ { "signal" : [
{ "name": "clk", "wave": "n....|.......|......" }, { "name": "clk", "wave": "n....|.......|......" },
{ "name": "sync", "wave": "1.0..|....1..|....0." }, { "name": "sync", "wave": "1.0..|....1..|....0." },
{ "name": "tx/rx", "wave": ".====|==x.===|==x.=x", "data": ["L15", "L14", "...", "L1", "L0", "R15", "R14", "...", "R1", "R0", "L15"] }, { "name": "tx/rx", "wave": ".====|==x.===|==x.=x", "data":
["L15", "L14", "...", "L1", "L0", "R15", "R14", "...", "R1", "R0", "L15"] },
]} ]}
- Data is updated on the falling edge - Data is updated on the falling edge
- Data is sampled on the rising edge - Data is sampled on the rising edge
- Words are MSB-to-LSB, left-justified (**NOTE: this is a deviation from strict I2S, which offsets by 1 from the left**) - Words are MSB-to-LSB, left-justified (**NOTE: this is a deviation from strict I2S, which
offsets by 1 from the left**)
- Sync is an input (FPGA is slave, codec is master): low => left channel, high => right channel - Sync is an input (FPGA is slave, codec is master): low => left channel, high => right channel
- Sync can be longer than the wordlen, extra bits are just ignored - Sync can be longer than the wordlen, extra bits are just ignored
- Tx is data to the codec (SDI pin on LM49352) - Tx is data to the codec (SDI pin on LM49352)
@ -90,15 +95,16 @@ class i2s_slave(Module, AutoCSR, AutoDoc):
""") """)
# one cache line is 8 32-bit words, need to always have enough space for one line or else nothing works # One cache line is 8 32-bit words, need to always have enough space for one line or else
if fifodepth > 504: # nothing works
fifodepth = 504 if fifo_depth > 504:
fifo_depth = 504
print("I2S warning: fifo depth greater than 504 selected; truncating to 504") print("I2S warning: fifo depth greater than 504 selected; truncating to 504")
if fifodepth < 8: if fifo_depth < 8:
fifodepth = 8 fifo_depth = 8
print("I2S warning: fifo depth less than 8 selected; truncating to 8") print("I2S warning: fifo depth less than 8 selected; truncating to 8")
# connect pins, synchronizers, and edge detectors # Connect pins, synchronizers, and edge detectors
if hasattr(pads, 'tx'): if hasattr(pads, 'tx'):
tx_pin = Signal() tx_pin = Signal()
self.comb += pads.tx.eq(tx_pin) self.comb += pads.tx.eq(tx_pin)
@ -116,25 +122,25 @@ class i2s_slave(Module, AutoCSR, AutoDoc):
falling_edge = Signal() falling_edge = Signal()
self.comb += [rising_edge.eq(clk_pin & ~clk_d), falling_edge.eq(~clk_pin & clk_d)] self.comb += [rising_edge.eq(clk_pin & ~clk_d), falling_edge.eq(~clk_pin & clk_d)]
# wishbone bus # Wishbone bus
self.bus = wishbone.Interface() self.bus = bus = wishbone.Interface()
rd_ack = Signal() rd_ack = Signal()
wr_ack = Signal() wr_ack = Signal()
self.comb +=[ self.comb +=[
If(self.bus.we, If(bus.we,
self.bus.ack.eq(wr_ack), bus.ack.eq(wr_ack),
).Else( ).Else(
self.bus.ack.eq(rd_ack), bus.ack.eq(rd_ack),
) )
] ]
# interrupts # Interrupts
self.submodules.ev = EventManager() self.submodules.ev = EventManager()
if hasattr(pads, 'rx'): if hasattr(pads, 'rx'):
self.ev.rx_ready = EventSourcePulse(description="Indicates FIFO is ready to read") # rising edge triggered self.ev.rx_ready = EventSourcePulse(description="Indicates FIFO is ready to read") # Rising edge triggered
self.ev.rx_error = EventSourcePulse(description="Indicates an Rx error has happened (over/underflow)") self.ev.rx_error = EventSourcePulse(description="Indicates an Rx error has happened (over/underflow)")
if hasattr(pads, 'tx'): if hasattr(pads, 'tx'):
self.ev.tx_ready = EventSourcePulse(description="Indicates enough space available for next Tx quanta of {} words".format(fifodepth)) self.ev.tx_ready = EventSourcePulse(description="Indicates enough space available for next Tx quanta of {} words".format(fifo_depth))
self.ev.tx_error = EventSourcePulse(description="Indicates a Tx error has happened (over/underflow") self.ev.tx_error = EventSourcePulse(description="Indicates a Tx error has happened (over/underflow")
self.ev.finalize() self.ev.finalize()
@ -163,13 +169,13 @@ class i2s_slave(Module, AutoCSR, AutoDoc):
fields=[ fields=[
CSRField("overflow", size=1, description="Rx overflow"), CSRField("overflow", size=1, description="Rx overflow"),
CSRField("underflow", size=1, description="Rx underflow"), CSRField("underflow", size=1, description="Rx underflow"),
CSRField("dataready", size=1, description="{} words of data loaded and ready to read".format(fifodepth)), CSRField("dataready", size=1, description="{} words of data loaded and ready to read".format(fifo_depth)),
CSRField("empty", size=1, description="No data available in FIFO to read"), # next flags probably never used CSRField("empty", size=1, description="No data available in FIFO to read"), # next flags probably never used
CSRField("wrcount", size=9, description="Write count"), CSRField("wrcount", size=9, description="Write count"),
CSRField("rdcount", size=9, description="Read count"), CSRField("rdcount", size=9, description="Read count"),
CSRField("fifodepth", size=9, description="FIFO depth as synthesized") CSRField("fifo_depth", size=9, description="FIFO depth as synthesized")
]) ])
self.comb += self.rx_stat.fields.fifodepth.eq(fifodepth) self.comb += self.rx_stat.fields.fifo_depth.eq(fifo_depth)
rx_rst_cnt = Signal(3) rx_rst_cnt = Signal(3)
rx_reset = Signal() rx_reset = Signal()
@ -186,19 +192,30 @@ class i2s_slave(Module, AutoCSR, AutoDoc):
) )
) )
] ]
# at a width of 32 bits, an 18kiB fifo is 512 entries deep # At a width of 32 bits, an 18kiB fifo is 512 entries deep
self.specials += Instance("FIFO_SYNC_MACRO", self.specials += Instance("FIFO_SYNC_MACRO",
p_DEVICE="7SERIES", p_FIFO_SIZE="18Kb", p_DATA_WIDTH=32, p_DEVICE = "7SERIES",
p_ALMOST_EMPTY_OFFSET=8, p_ALMOST_FULL_OFFSET=(512 - fifodepth), p_FIFO_SIZE = "18Kb",
p_DO_REG=0, p_DATA_WIDTH = 32,
p_ALMOST_EMPTY_OFFSET = 8,
o_ALMOSTFULL=rx_almostfull, o_ALMOSTEMPTY=rx_almostempty, p_ALMOST_FULL_OFFSET = (512 - fifo_depth),
o_DO=rx_rd_d, o_EMPTY=rx_empty, o_FULL=rx_full, p_DO_REG = 0,
o_RDCOUNT=rx_rdcount, o_RDERR=rx_rderr, o_WRCOUNT=rx_wrcount, o_WRERR=rx_wrerr, i_CLK = ClockSignal(),
i_DI=rx_wr_d, i_CLK=ClockSignal(), i_RDEN=rx_rden & ~rx_reset, i_RST = rx_reset,
i_WREN=rx_wren & ~rx_reset, i_RST=rx_reset, o_ALMOSTFULL = rx_almostfull,
o_ALMOSTEMPTY = rx_almostempty,
o_FULL = rx_full,
o_EMPTY = rx_empty,
i_WREN = rx_wren & ~rx_reset,
i_DI = rx_wr_d,
i_RDEN = rx_rden & ~rx_reset,
o_DO = rx_rd_d,
o_RDCOUNT = rx_rdcount,
o_RDERR = rx_rderr,
o_WRCOUNT = rx_wrcount,
o_WRERR = rx_wrerr,
) )
self.comb += [ # wire up the status signals and interrupts self.comb += [ # Wire up the status signals and interrupts
self.rx_stat.fields.overflow.eq(rx_wrerr), self.rx_stat.fields.overflow.eq(rx_wrerr),
self.rx_stat.fields.underflow.eq(rx_rderr), self.rx_stat.fields.underflow.eq(rx_rderr),
self.rx_stat.fields.dataready.eq(rx_almostfull), self.rx_stat.fields.dataready.eq(rx_almostfull),
@ -210,16 +227,18 @@ class i2s_slave(Module, AutoCSR, AutoDoc):
bus_read = Signal() bus_read = Signal()
bus_read_d = Signal() bus_read_d = Signal()
rd_ack_pipe = Signal() rd_ack_pipe = Signal()
self.comb += bus_read.eq(self.bus.cyc & self.bus.stb & ~self.bus.we & (self.bus.cti == 0)) self.comb += bus_read.eq(bus.cyc & bus.stb & ~bus.we & (bus.cti == 0))
self.sync += [ # this is the bus responder -- only works for uncached memory regions self.sync += [ # This is the bus responder -- only works for uncached memory regions
bus_read_d.eq(bus_read), bus_read_d.eq(bus_read),
If(bus_read & ~bus_read_d, # one response, one cycle If(bus_read & ~bus_read_d, # One response, one cycle
rd_ack_pipe.eq(1), rd_ack_pipe.eq(1),
If(~rx_empty, If(~rx_empty,
self.bus.dat_r.eq(rx_rd_d), bus.dat_r.eq(rx_rd_d),
rx_rden.eq(1), rx_rden.eq(1),
).Else( ).Else(
self.bus.dat_r.eq(0xDEADBEEF), # don't stall the bus indefinitely if we try to read from an empty fifo...just return garbage # Don't stall the bus indefinitely if we try to read from an empty fifo...just
# return garbage
bus.dat_r.eq(0xdeadbeef),
rx_rden.eq(0), rx_rden.eq(0),
) )
).Else( ).Else(
@ -234,59 +253,69 @@ class i2s_slave(Module, AutoCSR, AutoDoc):
rxi2s.act("IDLE", rxi2s.act("IDLE",
NextValue(rx_wr_d, 0), NextValue(rx_wr_d, 0),
If(self.rx_ctl.fields.enable, If(self.rx_ctl.fields.enable,
If(rising_edge & sync_pin, # wait_sync guarantees we start at the beginning of a left frame, and not in the middle # Wait_sync guarantees we start at the beginning of a left frame, and not in
NextState("WAIT_SYNC"), # the middle
If(rising_edge & sync_pin,
NextState("WAIT_SYNC")
) )
) )
), ),
rxi2s.act("WAIT_SYNC", rxi2s.act("WAIT_SYNC",
If(rising_edge & ~sync_pin, If(rising_edge & ~sync_pin,
NextState("LEFT"), NextState("LEFT"),
NextValue(rx_cnt, 16), NextValue(rx_cnt, 16)
), ),
) )
rxi2s.act("LEFT", rxi2s.act("LEFT",
If(~self.rx_ctl.fields.enable, NextState("IDLE")).Else( If(~self.rx_ctl.fields.enable,
NextState("IDLE")
).Else(
NextValue(rx_wr_d, Cat(rx_pin, rx_wr_d[:-1])), NextValue(rx_wr_d, Cat(rx_pin, rx_wr_d[:-1])),
NextValue(rx_cnt, rx_cnt - 1), NextValue(rx_cnt, rx_cnt - 1),
NextState("LEFT_WAIT"), NextState("LEFT_WAIT")
) )
) )
rxi2s.act("LEFT_WAIT", rxi2s.act("LEFT_WAIT",
If(~self.rx_ctl.fields.enable, NextState("IDLE")).Else( If(~self.rx_ctl.fields.enable,
NextState("IDLE")
).Else(
If(rising_edge, If(rising_edge,
If((rx_cnt == 0) & sync_pin, If((rx_cnt == 0) & sync_pin,
NextValue(rx_cnt, 16), NextValue(rx_cnt, 16),
NextState("RIGHT") NextState("RIGHT")
).Elif(rx_cnt > 0, ).Elif(rx_cnt > 0,
NextState("LEFT"), NextState("LEFT")
) )
) )
) )
) )
rxi2s.act("RIGHT", rxi2s.act("RIGHT",
If(~self.rx_ctl.fields.enable, NextState("IDLE")).Else( If(~self.rx_ctl.fields.enable,
NextState("IDLE")
).Else(
NextValue(rx_wr_d, Cat(rx_pin, rx_wr_d[:-1])), NextValue(rx_wr_d, Cat(rx_pin, rx_wr_d[:-1])),
NextValue(rx_cnt, rx_cnt - 1), NextValue(rx_cnt, rx_cnt - 1),
NextState("RIGHT_WAIT"), NextState("RIGHT_WAIT")
) )
) )
rxi2s.act("RIGHT_WAIT", rxi2s.act("RIGHT_WAIT",
If(~self.rx_ctl.fields.enable, NextState("IDLE")).Else( If(~self.rx_ctl.fields.enable,
NextState("IDLE")
).Else(
If(rising_edge, If(rising_edge,
If((rx_cnt == 0) & ~sync_pin, If((rx_cnt == 0) & ~sync_pin,
NextValue(rx_cnt, 16), NextValue(rx_cnt, 16),
NextState("LEFT"), NextState("LEFT"),
rx_wren.eq(1), # pulse rx_wren to write the current data word rx_wren.eq(1) # Pulse rx_wren to write the current data word
).Elif(rx_cnt > 0, ).Elif(rx_cnt > 0,
NextState("RIGHT"), NextState("RIGHT")
) )
) )
) )
) )
# build the TX subsystem # Build the TX subsystem
if hasattr(pads, 'tx'): if hasattr(pads, 'tx'):
tx_rd_d = Signal(32) tx_rd_d = Signal(32)
tx_almostfull = Signal() tx_almostfull = Signal()
@ -310,7 +339,7 @@ class i2s_slave(Module, AutoCSR, AutoDoc):
fields=[ fields=[
CSRField("overflow", size=1, description="Tx overflow"), CSRField("overflow", size=1, description="Tx overflow"),
CSRField("underflow", size=1, description="Tx underflow"), CSRField("underflow", size=1, description="Tx underflow"),
CSRField("free", size=1, description="At least {} words of space free".format(fifodepth)), CSRField("free", size=1, description="At least {} words of space free".format(fifo_depth)),
CSRField("almostfull", size=1, description="Less than 8 words space available"), # the next few flags should be rarely used CSRField("almostfull", size=1, description="Less than 8 words space available"), # the next few flags should be rarely used
CSRField("full", size=1, description="FIFO is full or overfull"), CSRField("full", size=1, description="FIFO is full or overfull"),
CSRField("empty", size=1, description="FIFO is empty"), CSRField("empty", size=1, description="FIFO is empty"),
@ -333,19 +362,31 @@ class i2s_slave(Module, AutoCSR, AutoDoc):
) )
) )
] ]
# at a width of 32 bits, an 18kiB fifo is 512 entries deep # At a width of 32 bits, an 18kiB fifo is 512 entries deep
self.specials += Instance("FIFO_SYNC_MACRO", self.specials += Instance("FIFO_SYNC_MACRO",
p_DEVICE="7SERIES", p_FIFO_SIZE="18Kb", p_DATA_WIDTH=32, p_DEVICE = "7SERIES",
p_ALMOST_EMPTY_OFFSET=fifodepth, p_ALMOST_FULL_OFFSET=8, p_FIFO_SIZE = "18Kb",
p_DO_REG=0, p_DATA_WIDTH = 32,
p_ALMOST_EMPTY_OFFSET = fifo_depth,
o_ALMOSTFULL=tx_almostfull, o_ALMOSTEMPTY=tx_almostempty, p_ALMOST_FULL_OFFSET = 8,
o_DO=tx_rd_d, o_EMPTY=tx_empty, o_FULL=tx_full, p_DO_REG = 0,
o_RDCOUNT=tx_rdcount, o_RDERR=tx_rderr, o_WRCOUNT=tx_wrcount, o_WRERR=tx_wrerr, i_CLK = ClockSignal(),
i_DI=tx_wr_d, i_CLK=ClockSignal(), i_RDEN=tx_rden & ~tx_reset, i_RST = tx_reset,
i_WREN=tx_wren & ~tx_reset, i_RST=tx_reset, o_ALMOSTFULL = tx_almostfull,
o_ALMOSTEMPTY = tx_almostempty,
o_FULL = tx_full,
o_EMPTY = tx_empty,
i_WREN = tx_wren & ~tx_reset,
i_DI = tx_wr_d,
i_RDEN = tx_rden & ~tx_reset,
o_DO = tx_rd_d,
o_RDCOUNT = tx_rdcount,
o_RDERR = tx_rderr,
o_WRCOUNT = tx_wrcount,
o_WRERR = tx_wrerr,
) )
self.comb += [ # wire up the status signals and interrupts
self.comb += [ # Wire up the status signals and interrupts
self.tx_stat.fields.overflow.eq(tx_wrerr), self.tx_stat.fields.overflow.eq(tx_wrerr),
self.tx_stat.fields.underflow.eq(tx_rderr), self.tx_stat.fields.underflow.eq(tx_rderr),
self.tx_stat.fields.free.eq(tx_almostempty), self.tx_stat.fields.free.eq(tx_almostempty),
@ -357,10 +398,12 @@ class i2s_slave(Module, AutoCSR, AutoDoc):
self.ev.tx_ready.trigger.eq(tx_almostempty), self.ev.tx_ready.trigger.eq(tx_almostempty),
self.ev.tx_error.trigger.eq(tx_wrerr | tx_rderr), self.ev.tx_error.trigger.eq(tx_wrerr | tx_rderr),
] ]
self.sync += [ # this is the bus responder -- need to check how this interacts with uncached memory region self.sync += [
If(self.bus.cyc & self.bus.stb & self.bus.we & ~self.bus.ack, # This is the bus responder -- need to check how this interacts with uncached memory
# region
If(bus.cyc & bus.stb & bus.we & ~bus.ack,
If(~tx_full, If(~tx_full,
tx_wr_d.eq(self.bus.dat_w), tx_wr_d.eq(bus.dat_w),
tx_wren.eq(1), tx_wren.eq(1),
wr_ack.eq(1), wr_ack.eq(1),
).Else( ).Else(
@ -388,47 +431,55 @@ class i2s_slave(Module, AutoCSR, AutoDoc):
NextState("LEFT"), NextState("LEFT"),
NextValue(tx_cnt, 16), NextValue(tx_cnt, 16),
NextValue(tx_buf, tx_rd_d), NextValue(tx_buf, tx_rd_d),
tx_rden.eq(1), tx_rden.eq(1)
), )
) )
txi2s.act("LEFT", txi2s.act("LEFT",
If(~self.tx_ctl.fields.enable, NextState("IDLE")).Else( If(~self.tx_ctl.fields.enable,
NextState("IDLE")
).Else(
NextValue(tx_pin, tx_buf[31]), NextValue(tx_pin, tx_buf[31]),
NextValue(tx_buf, Cat(0, tx_buf[:-1])), NextValue(tx_buf, Cat(0, tx_buf[:-1])),
NextValue(tx_cnt, tx_cnt - 1), NextValue(tx_cnt, tx_cnt - 1),
NextState("LEFT_WAIT"), NextState("LEFT_WAIT")
) )
) )
txi2s.act("LEFT_WAIT", txi2s.act("LEFT_WAIT",
If(~self.tx_ctl.fields.enable, NextState("IDLE")).Else( If(~self.tx_ctl.fields.enable,
NextState("IDLE")
).Else(
If(falling_edge, If(falling_edge,
If((tx_cnt == 0) & sync_pin, If((tx_cnt == 0) & sync_pin,
NextValue(tx_cnt, 16), NextValue(tx_cnt, 16),
NextState("RIGHT") NextState("RIGHT")
).Elif(tx_cnt > 0, ).Elif(tx_cnt > 0,
NextState("LEFT"), NextState("LEFT")
) )
) )
) )
) )
txi2s.act("RIGHT", txi2s.act("RIGHT",
If(~self.tx_ctl.fields.enable, NextState("IDLE")).Else( If(~self.tx_ctl.fields.enable,
NextState("IDLE")
).Else(
NextValue(tx_pin, tx_buf[31]), NextValue(tx_pin, tx_buf[31]),
NextValue(tx_buf, Cat(0, tx_buf[:-1])), NextValue(tx_buf, Cat(0, tx_buf[:-1])),
NextValue(tx_cnt, tx_cnt - 1), NextValue(tx_cnt, tx_cnt - 1),
NextState("RIGHT_WAIT"), NextState("RIGHT_WAIT")
) )
) )
txi2s.act("RIGHT_WAIT", txi2s.act("RIGHT_WAIT",
If(~self.tx_ctl.fields.enable, NextState("IDLE")).Else( If(~self.tx_ctl.fields.enable,
NextState("IDLE")
).Else(
If(falling_edge, If(falling_edge,
If((tx_cnt == 0) & ~sync_pin, If((tx_cnt == 0) & ~sync_pin,
NextValue(tx_cnt, 16), NextValue(tx_cnt, 16),
NextState("LEFT"), NextState("LEFT"),
NextValue(tx_buf, tx_rd_d), NextValue(tx_buf, tx_rd_d),
tx_rden.eq(1), tx_rden.eq(1)
).Elif(tx_cnt > 0, ).Elif(tx_cnt > 0,
NextState("RIGHT"), NextState("RIGHT")
) )
) )
) )

15
test/test_i2s.py Normal file
View File

@ -0,0 +1,15 @@
# This file is Copyright (c) 2020 Florent Kermarrec <florent@enjoy-digital.fr>
# License: BSD
import unittest
from migen import *
from litex.soc.cores.i2s import S7I2SSlave
class TestI2S(unittest.TestCase):
def test_s7i2sslave_syntax(self):
i2s_pads = Record([("rx", 1), ("tx", 1), ("sync", 1), ("clk", 1)])
i2s = S7I2SSlave(pads=i2s_pads, fifo_depth=256)