# This file is Copyright (c) 2020 bunnie <bunnie@kosagi.com>
+# This file is Copyright (c) 2020 Antmicro <www.antmicro.com>
# License: BSD
from migen.genlib.cdc import MultiReg
+from litex.soc.cores.clock import *
from litex.soc.interconnect import wishbone
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):
+from enum import Enum
+import math
+class I2S_FORMAT(Enum):
+ I2S_STANDARD = 1
+ I2S_LEFT_JUSTIFIED = 2
+
+class S7I2S(Module, AutoCSR, AutoDoc):
+ def __init__(self, pads, fifo_depth=256, master=False, concatenate_channels=True, sample_width=16, frame_format=I2S_FORMAT.I2S_LEFT_JUSTIFIED, lrck_ref_freq=100e6, lrck_freq=44100, bits_per_channel=28):
self.intro = ModuleDoc("""Intro
- I2S slave creates a slave audio interface instance. Tx and Rx interfaces are inferred based
- upon the presence or absence of the respective pins in the "pads" argument.
+ I2S master/slave creates a master/slave audio interface instance depending on a configured master variable.
+ Tx and Rx interfaces are inferred based upon the presence or absence of the respective pins in the "pads" argument.
+
+ When device is configured as master you can manipulate LRCK and SCLK signals using below variables.
+
+ - lrck_ref_freq - is a reference signal that is required to achive desired LRCK and SCLK frequencies.
+ Have be the same as your sys_clk.
+ - lrck_freq - this variable defines requested LRCK frequency. Mind you, that based on sys_clk frequency,
+ configured value will be more or less acurate.
+ - bits_per_channel - defines SCLK frequency. Mind you, that based on sys_clk frequency,
+ the requested amount of bits per channel may vary from configured.
+
+ When device is configured as slave I2S interface, sampling rate and framing is set by the
+ programming of the audio CODEC chip. A slave configuration defers the
+ generation of audio clocks to the CODEC, which has PLLs specialized to generate the correct
+ frequencies for audio sampling rates.
+
+ I2S core supports two formats: standard and left-justified.
+
+ - Standard format requires a device to receive and send data with one bit offset for both channels.
+ Left channel begins with low signal on LRCK.
+ - Left justified format requires from device to receive and send data without any bit offset for both channels.
+ Left channel begins with high signal on LRCK.
+
+ Sample width can be any of 1 to 32 bits.
- The interface is I2S-like, but note the deviation that the bits are justified left without a
- 1-bit pad after sync edges. This isn't a problem for talking to the LM49352 codec this was
- designed for, as the bit offset is programmable, but this will not work well if are talking
- to a CODEC without a programmable bit offset!
+ When sample_width is less than or equal to 16-bit and concatenate_channels is enabled,
+ sending and reciving channels is performed atomically. eg. both samples are transfered from/to fifo in single operation.
System Interface
----------------
- 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
- stereo sample. As this is a slave I2S interface, sampling rate and framing is set by the
- programming of the audio CODEC chip. A slave situation is preferred because this defers the
- generation of audio clocks to the CODEC, which has PLLs specialized to generate the correct
- frequencies for audio sampling rates.
-
`fifo_depth` is the depth at which either a read interrupt is fired (guaranteeing at least
`fifo_depth` stereo samples in the receive FIFO) or a write interrupt is fired (guaranteeing
at least `fifo_depth` free space in the transmit FIFO). The maximum depth is 512.
- Data is updated on the falling 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**)
- - Sync is an input (FPGA is slave, codec is master): low => left channel, high => right channel
+ - Words are MSB-to-LSB,
+ - Sync is an input or output based on configure mode,
- Sync can be longer than the wordlen, extra bits are just ignored
- Tx is data to the codec (SDI pin on LM49352)
- Rx is data from the codec (SDO pin on LM49352)
if fifo_depth < 8:
fifo_depth = 8
print("I2S warning: fifo depth less than 8 selected; truncating to 8")
+ if sample_width > 32:
+ sample_width = 32
+ print("I2S warning: sample width greater than 32 bits. truncating to 32")
# Connect pins, synchronizers, and edge detectors
if hasattr(pads, 'tx'):
if hasattr(pads, 'rx'):
rx_pin = Signal()
self.specials += MultiReg(pads.rx, rx_pin)
+
+ fifo_data_width = sample_width
+ if concatenate_channels:
+ if sample_width <= 16:
+ fifo_data_width = sample_width * 2
+ else:
+ concatenate_channels = False
+ print("I2S warning: sample width greater than 16 bits. your channels can't be glued")
+
sync_pin = Signal()
self.specials += MultiReg(pads.sync, sync_pin)
self.bus = bus = wishbone.Interface()
rd_ack = Signal()
wr_ack = Signal()
- self.comb +=[
+ self.comb += [
If(bus.we,
bus.ack.eq(wr_ack),
).Else(
)
]
+
+ if master == True:
+ if bits_per_channel < sample_width and frame_format == I2S_FORMAT.I2S_STANDARD:
+ bits_per_channel = sample_width + 1
+ print("I2S warning: bits per channel can't be smaller than sample_width. Setting bits per channel to {}".format(sample_width + 1))
+ # implementing LRCK signal
+ lrck_period = int(lrck_ref_freq / (lrck_freq * 2))
+ lrck_counter = Signal(16)
+ self.sync += [
+ If((lrck_counter == lrck_period),
+ lrck_counter.eq(0),
+ pads.sync.eq(~pads.sync),
+ ).Else(
+ lrck_counter.eq(lrck_counter + 1)
+ )
+ ]
+ # implementing SCLK signal
+ sclk_period = int(lrck_period / (bits_per_channel * 2))
+ sclk_counter = Signal(16)
+ self.sync += [
+ If((sclk_counter == sclk_period),
+ sclk_counter.eq(0),
+ pads.clk.eq(~pads.clk),
+ ).Else(
+ sclk_counter.eq(sclk_counter + 1)
+ )
+ ]
+
# Interrupts
self.submodules.ev = EventManager()
if hasattr(pads, 'rx'):
self.ev.tx_error = EventSourcePulse(description="Indicates a Tx error has happened (over/underflow")
self.ev.finalize()
-
# build the RX subsystem
if hasattr(pads, 'rx'):
- rx_rd_d = Signal(32)
+ rx_rd_d = Signal(fifo_data_width)
rx_almostfull = Signal()
rx_almostempty = Signal()
rx_full = Signal()
rx_wrerr = Signal()
rx_wrcount = Signal(9)
rx_rden = Signal()
- rx_wr_d = Signal(32)
+ rx_wr_d = Signal(fifo_data_width)
rx_wren = Signal()
self.rx_ctl = CSRStorage(description="Rx data path control",
])
self.rx_stat = CSRStatus(description="Rx data path status",
fields=[
- CSRField("overflow", size=1, description="Rx overflow"),
+ CSRField("overflow", size=1, description="Rx overflow"),
CSRField("underflow", size=1, description="Rx underflow"),
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("wrcount", size=9, description="Write count"),
CSRField("rdcount", size=9, description="Read count"),
- CSRField("fifo_depth", size=9, description="FIFO depth as synthesized")
+ CSRField("fifo_depth", size=9, description="FIFO depth as synthesized"),
+ CSRField("concatenate_channels", size=1, reset=concatenate_channels, description="Receive and send both channels atomically")
+ ])
+ self.rx_conf = CSRStatus(description="Rx configuration",
+ fields=[
+ CSRField("format", size=2, reset=frame_format.value, description="I2S sample format. {} is left-justified, {} is I2S standard".format(I2S_FORMAT.I2S_LEFT_JUSTIFIED, I2S_FORMAT.I2S_STANDARD)),
+ CSRField("sample_width", size=6, reset=sample_width, description="Single sample width"),
+ CSRField("lrck_freq", size=24, reset=lrck_freq, description="Audio sampling rate frequency"),
])
self.comb += self.rx_stat.fields.fifo_depth.eq(fifo_depth)
)
)
]
+
# At a width of 32 bits, an 18kiB fifo is 512 entries deep
self.specials += Instance("FIFO_SYNC_MACRO",
p_DEVICE = "7SERIES",
p_FIFO_SIZE = "18Kb",
- p_DATA_WIDTH = 32,
+ p_DATA_WIDTH = fifo_data_width,
p_ALMOST_EMPTY_OFFSET = 8,
p_ALMOST_FULL_OFFSET = (512 - fifo_depth),
p_DO_REG = 0,
o_WRERR = rx_wrerr,
)
self.comb += [ # Wire up the status signals and interrupts
- self.rx_stat.fields.overflow.eq(rx_wrerr),
self.rx_stat.fields.underflow.eq(rx_rderr),
self.rx_stat.fields.dataready.eq(rx_almostfull),
self.rx_stat.fields.wrcount.eq(rx_wrcount),
),
rd_ack.eq(rd_ack_pipe),
]
+ rx_cnt_width = math.ceil(math.log(fifo_data_width,2))
+ rx_cnt = Signal(rx_cnt_width)
+ rx_delay_cnt = Signal()
+ rx_delay_val = 1 if frame_format == I2S_FORMAT.I2S_STANDARD else 0
- rx_cnt = Signal(5)
self.submodules.rxi2s = rxi2s = FSM(reset_state="IDLE")
rxi2s.act("IDLE",
NextValue(rx_wr_d, 0),
If(self.rx_ctl.fields.enable,
# Wait_sync guarantees we start at the beginning of a left frame, and not in
# the middle
- If(rising_edge & sync_pin,
- NextState("WAIT_SYNC")
+ If(rising_edge & (~sync_pin if frame_format == I2S_FORMAT.I2S_STANDARD else sync_pin),
+ NextState("WAIT_SYNC"),
+ NextValue(rx_delay_cnt, rx_delay_val)
)
)
),
rxi2s.act("WAIT_SYNC",
- If(rising_edge & ~sync_pin,
- NextState("LEFT"),
- NextValue(rx_cnt, 16)
+ If(rising_edge & (~sync_pin if frame_format == I2S_FORMAT.I2S_STANDARD else sync_pin),
+ If(rx_delay_cnt > 0,
+ NextValue(rx_delay_cnt, rx_delay_cnt - 1),
+ NextState("WAIT_SYNC")
+ ).Else(
+ NextState("LEFT"),
+ NextValue(rx_delay_cnt, rx_delay_val),
+ NextValue(rx_cnt, sample_width)
+ )
),
)
rxi2s.act("LEFT",
NextState("LEFT_WAIT")
)
)
- rxi2s.act("LEFT_WAIT",
- If(~self.rx_ctl.fields.enable,
- NextState("IDLE")
- ).Else(
- If(rising_edge,
- If((rx_cnt == 0) & sync_pin,
- NextValue(rx_cnt, 16),
- NextState("RIGHT")
- ).Elif(rx_cnt > 0,
- NextState("LEFT")
+ if concatenate_channels:
+ rxi2s.act("LEFT_WAIT",
+ If(~self.rx_ctl.fields.enable,
+ NextState("IDLE")
+ ).Else(
+ If(rising_edge,
+ If((rx_cnt == 0),
+ If((sync_pin if frame_format == I2S_FORMAT.I2S_STANDARD else ~sync_pin),
+ If(rx_delay_cnt == 0,
+ NextValue(rx_cnt, sample_width),
+ NextValue(rx_delay_cnt,rx_delay_val),
+ NextState("RIGHT"),
+ ).Else(
+ NextValue(rx_delay_cnt, rx_delay_cnt - 1),
+ NextState("LEFT_WAIT")
+ )
+ ).Else(
+ NextState("LEFT_WAIT")
+ )
+ ).Elif(rx_cnt > 0,
+ NextState("LEFT")
+ )
)
)
)
- )
+ else:
+ rxi2s.act("LEFT_WAIT",
+ If(~self.rx_ctl.fields.enable,
+ NextState("IDLE")
+ ).Else(
+ If(rising_edge,
+ If((rx_cnt == 0),
+ If((sync_pin if frame_format == I2S_FORMAT.I2S_STANDARD else ~sync_pin),
+ If(rx_delay_cnt == 0,
+ NextValue(rx_cnt, sample_width),
+ NextValue(rx_delay_cnt,rx_delay_val),
+ NextState("RIGHT"),
+ rx_wren.eq(1) # Pulse rx_wren to write the current data word
+ ).Else(
+ NextValue(rx_delay_cnt, rx_delay_cnt - 1),
+ NextState("LEFT_WAIT")
+ )
+ ).Else(
+ NextState("LEFT_WAIT")
+ )
+ ).Elif(rx_cnt > 0,
+ NextState("LEFT")
+ )
+ )
+ )
+ )
+
rxi2s.act("RIGHT",
If(~self.rx_ctl.fields.enable,
NextState("IDLE")
NextState("IDLE")
).Else(
If(rising_edge,
- If((rx_cnt == 0) & ~sync_pin,
- NextValue(rx_cnt, 16),
- NextState("LEFT"),
- rx_wren.eq(1) # Pulse rx_wren to write the current data word
+ If((rx_cnt == 0) & (~sync_pin if frame_format == I2S_FORMAT.I2S_STANDARD else sync_pin),
+ If(rx_delay_cnt == 0,
+ NextValue(rx_cnt, sample_width),
+ NextValue(rx_delay_cnt,rx_delay_val),
+ NextState("LEFT"),
+ rx_wren.eq(1) # Pulse rx_wren to write the current data word
+ ).Else(
+ NextValue(rx_delay_cnt, rx_delay_cnt - 1),
+ NextState("RIGHT_WAIT")
+ )
).Elif(rx_cnt > 0,
NextState("RIGHT")
)
# Build the TX subsystem
if hasattr(pads, 'tx'):
- tx_rd_d = Signal(32)
+ tx_rd_d = Signal(fifo_data_width)
tx_almostfull = Signal()
tx_almostempty = Signal()
tx_full = Signal()
tx_wrerr = Signal()
tx_wrcount = Signal(9)
tx_rden = Signal()
- tx_wr_d = Signal(32)
+ tx_wr_d = Signal(fifo_data_width)
tx_wren = Signal()
self.tx_ctl = CSRStorage(description="Tx data path control",
])
self.tx_stat = CSRStatus(description="Tx data path status",
fields=[
- CSRField("overflow", size=1, description="Tx overflow"),
+ CSRField("overflow", size=1, description="Tx overflow"),
CSRField("underflow", size=1, description="Tx underflow"),
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("empty", size=1, description="FIFO is empty"),
CSRField("wrcount", size=9, description="Tx write count"),
CSRField("rdcount", size=9, description="Tx read count"),
+ CSRField("concatenate_channels", size=1, reset=concatenate_channels, description="Receive and send both channels atomically")
+ ])
+ self.tx_conf = CSRStatus(description="TX configuration",
+ fields=[
+ CSRField("format", size=2, reset=frame_format.value, description="I2S sample format. {} is left-justified, {} is I2S standard".format(I2S_FORMAT.I2S_LEFT_JUSTIFIED, I2S_FORMAT.I2S_STANDARD)),
+ CSRField("sample_width", size=6, reset=sample_width, description="Single sample width"),
+ CSRField("lrck_freq", size=24, reset=lrck_freq, description="Audio sampling rate frequency"),
])
tx_rst_cnt = Signal(3)
)
)
]
+
# At a width of 32 bits, an 18kiB fifo is 512 entries deep
self.specials += Instance("FIFO_SYNC_MACRO",
p_DEVICE = "7SERIES",
p_FIFO_SIZE = "18Kb",
- p_DATA_WIDTH = 32,
+ p_DATA_WIDTH = fifo_data_width,
p_ALMOST_EMPTY_OFFSET = fifo_depth,
p_ALMOST_FULL_OFFSET = 8,
p_DO_REG = 0,
)
self.comb += [ # Wire up the status signals and interrupts
- self.tx_stat.fields.overflow.eq(tx_wrerr),
self.tx_stat.fields.underflow.eq(tx_rderr),
self.tx_stat.fields.free.eq(tx_almostempty),
self.tx_stat.fields.almostfull.eq(tx_almostfull),
]
self.sync += [
# This is the bus responder -- need to check how this interacts with uncached memory
- # region
+ # region
If(bus.cyc & bus.stb & bus.we & ~bus.ack,
If(~tx_full,
tx_wr_d.eq(bus.dat_w),
)
]
- tx_cnt = Signal(5)
- tx_buf = Signal(32)
+ tx_buf_width = fifo_data_width + 1 if frame_format == I2S_FORMAT.I2S_STANDARD else fifo_data_width
+ sample_width = sample_width + 1 if frame_format == I2S_FORMAT.I2S_STANDARD else sample_width
+ offset = [0] if frame_format == I2S_FORMAT.I2S_STANDARD else []
+
+ tx_cnt_width = math.ceil(math.log(fifo_data_width,2))
+ tx_cnt = Signal(tx_cnt_width)
+ tx_buf = Signal(tx_buf_width)
+ sample_msb = fifo_data_width - 1
self.submodules.txi2s = txi2s = FSM(reset_state="IDLE")
txi2s.act("IDLE",
If(self.tx_ctl.fields.enable,
- If(falling_edge & sync_pin,
+ If(falling_edge & (~sync_pin if frame_format == I2S_FORMAT.I2S_STANDARD else sync_pin),
NextState("WAIT_SYNC"),
)
)
),
txi2s.act("WAIT_SYNC",
- If(falling_edge & ~sync_pin,
+ If(falling_edge & (~sync_pin if frame_format == I2S_FORMAT.I2S_STANDARD else sync_pin),
NextState("LEFT"),
- NextValue(tx_cnt, 16),
- NextValue(tx_buf, tx_rd_d),
- tx_rden.eq(1)
+ NextValue(tx_cnt, sample_width),
+ NextValue(tx_buf, Cat(tx_rd_d, offset)),
+ tx_rden.eq(1),
)
)
txi2s.act("LEFT",
If(~self.tx_ctl.fields.enable,
NextState("IDLE")
).Else(
- NextValue(tx_pin, tx_buf[31]),
+ NextValue(tx_pin, tx_buf[sample_msb]),
NextValue(tx_buf, Cat(0, tx_buf[:-1])),
NextValue(tx_cnt, tx_cnt - 1),
NextState("LEFT_WAIT")
)
)
- txi2s.act("LEFT_WAIT",
- If(~self.tx_ctl.fields.enable,
- NextState("IDLE")
- ).Else(
- If(falling_edge,
- If((tx_cnt == 0) & sync_pin,
- NextValue(tx_cnt, 16),
- NextState("RIGHT")
- ).Elif(tx_cnt > 0,
- NextState("LEFT")
+ if concatenate_channels:
+ txi2s.act("LEFT_WAIT",
+ If(~self.tx_ctl.fields.enable,
+ NextState("IDLE")
+ ).Else(
+ If(falling_edge,
+ If((tx_cnt == 0),
+ If((sync_pin if frame_format == I2S_FORMAT.I2S_STANDARD else ~sync_pin),
+ NextValue(tx_cnt, sample_width),
+ NextState("RIGHT"),
+ ).Else(
+ NextState("LEFT_WAIT"),
+ )
+ ).Elif(tx_cnt > 0,
+ NextState("LEFT"),
+ )
)
)
)
- )
+ else:
+ txi2s.act("LEFT_WAIT",
+ If(~self.tx_ctl.fields.enable,
+ NextState("IDLE")
+ ).Else(
+ If(falling_edge,
+ If((tx_cnt == 0),
+ If((sync_pin if frame_format == I2S_FORMAT.I2S_STANDARD else ~sync_pin),
+ NextValue(tx_cnt, sample_width),
+ NextState("RIGHT"),
+ NextValue(tx_buf, Cat(tx_rd_d,offset)),
+ tx_rden.eq(1),
+ ).Else(
+ NextState("LEFT_WAIT"),
+ )
+ ).Elif(tx_cnt > 0,
+ NextState("LEFT"),
+ )
+ )
+ )
+ )
+
txi2s.act("RIGHT",
If(~self.tx_ctl.fields.enable,
NextState("IDLE")
).Else(
- NextValue(tx_pin, tx_buf[31]),
+ NextValue(tx_pin, tx_buf[sample_msb]),
NextValue(tx_buf, Cat(0, tx_buf[:-1])),
NextValue(tx_cnt, tx_cnt - 1),
NextState("RIGHT_WAIT")
NextState("IDLE")
).Else(
If(falling_edge,
- If((tx_cnt == 0) & ~sync_pin,
- NextValue(tx_cnt, 16),
+ If((tx_cnt == 0) & (~sync_pin if frame_format == I2S_FORMAT.I2S_STANDARD else sync_pin),
+ NextValue(tx_cnt, sample_width),
NextState("LEFT"),
- NextValue(tx_buf, tx_rd_d),
+ NextValue(tx_buf, Cat(tx_rd_d,offset)),
tx_rden.eq(1)
).Elif(tx_cnt > 0,
NextState("RIGHT")