cores: i2s: Use FIFOSyncMacro if not built with Vivado

This commit is contained in:
Robert Szczepanski 2022-04-20 14:29:58 +02:00
parent 22abe1d543
commit d5878050b3
1 changed files with 70 additions and 131 deletions

View File

@ -14,12 +14,15 @@ from litex.soc.interconnect.csr_eventmanager import *
from litex.soc.integration.doc import AutoDoc, ModuleDoc
from enum import Enum
import math
from litex.soc.cores.ram.xilinx_fifo_sync_macro import FIFOSyncMacro
class I2S_FORMAT(Enum):
I2S_STANDARD = 1
I2S_LEFT_JUSTIFIED = 2
class S7I2S(Module, AutoCSR, AutoDoc):
def __init__(self, pads, fifo_depth=256, controller=False, 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, document_interrupts=False):
def __init__(self, pads, fifo_depth=256, controller=False, 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, document_interrupts=False, toolchain="vivado"):
if master == True:
print("Master/slave terminology deprecated, please use controller/peripheral. Please see http://oshwa.org/a-resolution-to-redefine-spi-signal-names.")
controller = True
@ -163,13 +166,12 @@ class S7I2S(Module, AutoCSR, AutoDoc):
wr_ack = Signal()
self.comb += [
If(bus.we,
bus.ack.eq(wr_ack),
bus.ack.eq(wr_ack),
).Else(
bus.ack.eq(rd_ack),
)
]
if controller == True:
if bits_per_channel < sample_width and frame_format == I2S_FORMAT.I2S_STANDARD:
bits_per_channel = sample_width + 1
@ -209,19 +211,6 @@ class S7I2S(Module, AutoCSR, AutoDoc):
# build the RX subsystem
if hasattr(pads, 'rx'):
rx_rd_d = Signal(fifo_data_width)
rx_almostfull = Signal()
rx_almostempty = Signal()
rx_full = Signal()
rx_empty = Signal()
rx_rdcount = Signal(9)
rx_rderr = Signal()
rx_wrerr = Signal()
rx_wrcount = Signal(9)
rx_rden = Signal()
rx_wr_d = Signal(fifo_data_width)
rx_wren = Signal()
self.rx_ctl = CSRStorage(description="Rx data path control",
fields=[
CSRField("enable", size=1, description="Enable the receiving data"),
@ -250,11 +239,11 @@ class S7I2S(Module, AutoCSR, AutoDoc):
rx_reset = Signal()
self.sync += [
If(self.rx_ctl.fields.reset,
rx_rst_cnt.eq(5), # 5 cycles reset required by design
rx_reset.eq(1)
rx_rst_cnt.eq(5), # 5 cycles reset required by design
rx_reset.eq(1)
).Else(
If(rx_rst_cnt == 0,
rx_reset.eq(0)
rx_reset.eq(0)
).Else(
rx_rst_cnt.eq(rx_rst_cnt - 1),
rx_reset.eq(1)
@ -262,38 +251,19 @@ class S7I2S(Module, AutoCSR, AutoDoc):
)
]
# 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 = fifo_data_width,
p_ALMOST_EMPTY_OFFSET = 8,
p_ALMOST_FULL_OFFSET = (512 - fifo_depth),
p_DO_REG = 0,
i_CLK = ClockSignal(),
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.submodules.rx_fifo = fifo = FIFOSyncMacro("18Kb", data_width=fifo_data_width,
almost_empty_offset=8, almost_full_offset=(512 - fifo_depth), toolchain=toolchain)
self.comb += fifo.reset.eq(rx_reset)
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),
self.rx_stat.fields.rdcount.eq(rx_rdcount),
self.rx_stat.fields.empty.eq(rx_empty),
self.ev.rx_ready.trigger.eq(rx_almostfull),
self.ev.rx_error.trigger.eq(rx_wrerr | rx_rderr),
self.rx_stat.fields.overflow.eq(fifo.wrerr),
self.rx_stat.fields.underflow.eq(fifo.rderr),
self.rx_stat.fields.dataready.eq(fifo.almostfull),
self.rx_stat.fields.wrcount.eq(fifo.wrcount),
self.rx_stat.fields.rdcount.eq(fifo.rdcount),
self.rx_stat.fields.empty.eq(fifo.empty),
self.ev.rx_ready.trigger.eq(fifo.almostfull),
self.ev.rx_error.trigger.eq(fifo.wrerr | fifo.rderr),
]
bus_read = Signal()
bus_read_d = Signal()
@ -302,18 +272,18 @@ class S7I2S(Module, AutoCSR, AutoDoc):
self.sync += [ # This is the bus responder -- only works for uncached memory regions
bus_read_d.eq(bus_read),
If(bus_read & ~bus_read_d, # One response, one cycle
rd_ack_pipe.eq(1),
If(~rx_empty,
bus.dat_r.eq(rx_rd_d),
rx_rden.eq(1),
).Else(
# 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),
)
rd_ack_pipe.eq(1),
If(~fifo.empty,
bus.dat_r.eq(fifo.rd_d),
fifo.rden.eq(~rx_reset),
).Else(
# Don't stall the bus indefinitely if we try to read from an empty fifo...just
# return garbage
bus.dat_r.eq(0xdeadbeef),
fifo.rden.eq(0),
)
).Else(
rx_rden.eq(0),
fifo.rden.eq(0),
rd_ack_pipe.eq(0),
),
rd_ack.eq(rd_ack_pipe),
@ -325,14 +295,14 @@ class S7I2S(Module, AutoCSR, AutoDoc):
self.submodules.rxi2s = rxi2s = FSM(reset_state="IDLE")
rxi2s.act("IDLE",
NextValue(rx_wr_d, 0),
NextValue(fifo.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 if frame_format == I2S_FORMAT.I2S_STANDARD else sync_pin),
NextState("WAIT_SYNC"),
NextValue(rx_delay_cnt, rx_delay_val)
)
)
)
),
rxi2s.act("WAIT_SYNC",
@ -351,7 +321,7 @@ class S7I2S(Module, AutoCSR, AutoDoc):
If(~self.rx_ctl.fields.enable,
NextState("IDLE")
).Else(
NextValue(rx_wr_d, Cat(rx_pin, rx_wr_d[:-1])),
NextValue(fifo.wr_d, Cat(rx_pin, fifo.wr_d[:-1])),
NextValue(rx_cnt, rx_cnt - 1),
NextState("LEFT_WAIT")
)
@ -393,7 +363,7 @@ class S7I2S(Module, AutoCSR, AutoDoc):
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
fifo.wren.eq(~rx_reset) # Pulse rx_wren to write the current data word
).Else(
NextValue(rx_delay_cnt, rx_delay_cnt - 1),
NextState("LEFT_WAIT")
@ -412,7 +382,7 @@ class S7I2S(Module, AutoCSR, AutoDoc):
If(~self.rx_ctl.fields.enable,
NextState("IDLE")
).Else(
NextValue(rx_wr_d, Cat(rx_pin, rx_wr_d[:-1])),
NextValue(fifo.wr_d, Cat(rx_pin, fifo.wr_d[:-1])),
NextValue(rx_cnt, rx_cnt - 1),
NextState("RIGHT_WAIT")
)
@ -427,7 +397,7 @@ class S7I2S(Module, AutoCSR, AutoDoc):
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
fifo.wren.eq(~rx_reset) # Pulse rx_wren to write the current data word
).Else(
NextValue(rx_delay_cnt, rx_delay_cnt - 1),
NextState("RIGHT_WAIT")
@ -442,19 +412,6 @@ class S7I2S(Module, AutoCSR, AutoDoc):
# Build the TX subsystem
if hasattr(pads, 'tx'):
tx_rd_d = Signal(fifo_data_width)
tx_almostfull = Signal()
tx_almostempty = Signal()
tx_full = Signal()
tx_empty = Signal()
tx_rdcount = Signal(9)
tx_rderr = Signal()
tx_wrerr = Signal()
tx_wrcount = Signal(9)
tx_rden = Signal()
tx_wr_d = Signal(fifo_data_width)
tx_wren = Signal()
self.tx_ctl = CSRStorage(description="Tx data path control",
fields=[
CSRField("enable", size=1, description="Enable the transmission data"),
@ -479,15 +436,17 @@ class S7I2S(Module, AutoCSR, AutoDoc):
CSRField("lrck_freq", size=24, reset=lrck_freq, description="Audio sampling rate frequency"),
])
tx_rst_cnt = Signal(3)
tx_reset = Signal()
tx_reset = Signal()
self.sync += [
If(self.tx_ctl.fields.reset,
tx_rst_cnt.eq(5), # 5 cycles reset required by design
tx_reset.eq(1)
tx_rst_cnt.eq(5), # 5 cycles reset required by design
tx_reset.eq(1)
).Else(
If(tx_rst_cnt == 0,
tx_reset.eq(0)
tx_reset.eq(0)
).Else(
tx_rst_cnt.eq(tx_rst_cnt - 1),
tx_reset.eq(1)
@ -495,55 +454,35 @@ class S7I2S(Module, AutoCSR, AutoDoc):
)
]
# 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 = fifo_data_width,
p_ALMOST_EMPTY_OFFSET = (512 - fifo_depth),
p_ALMOST_FULL_OFFSET = 8,
p_DO_REG = 0,
i_CLK = ClockSignal(),
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.submodules.tx_fifo = fifo = FIFOSyncMacro("18Kb", data_width=fifo_data_width,
almost_empty_offset=(512 - fifo_depth), almost_full_offset=8, toolchain=toolchain)
self.comb += fifo.reset.eq(tx_reset)
self.comb += [ # Wire up the status signals and interrupts
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.tx_stat.fields.full.eq(tx_full),
self.tx_stat.fields.empty.eq(tx_empty),
self.tx_stat.fields.rdcount.eq(tx_rdcount),
self.tx_stat.fields.wrcount.eq(tx_wrcount),
self.ev.tx_ready.trigger.eq(tx_almostempty),
self.ev.tx_error.trigger.eq(tx_wrerr | tx_rderr),
self.tx_stat.fields.underflow.eq(fifo.rderr),
self.tx_stat.fields.free.eq(fifo.almostempty),
self.tx_stat.fields.almostfull.eq(fifo.almostfull),
self.tx_stat.fields.full.eq(fifo.full),
self.tx_stat.fields.empty.eq(fifo.empty),
self.tx_stat.fields.rdcount.eq(fifo.rdcount),
self.tx_stat.fields.wrcount.eq(fifo.wrcount),
self.ev.tx_ready.trigger.eq(fifo.almostempty),
self.ev.tx_error.trigger.eq(fifo.wrerr | fifo.rderr),
]
self.sync += [
# 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,
tx_wr_d.eq(bus.dat_w),
tx_wren.eq(1),
wr_ack.eq(1),
).Else(
tx_wren.eq(0),
wr_ack.eq(0),
)
If(~fifo.full,
fifo.wr_d.eq(bus.dat_w),
fifo.wren.eq(~tx_reset),
wr_ack.eq(1),
).Else(
fifo.wren.eq(0),
wr_ack.eq(0),
)
).Else(
tx_wren.eq(0),
fifo.wren.eq(0),
wr_ack.eq(0),
)
]
@ -568,8 +507,8 @@ class S7I2S(Module, AutoCSR, AutoDoc):
If(rising_edge & (~sync_pin if frame_format == I2S_FORMAT.I2S_STANDARD else sync_pin),
NextState("LEFT_FALL"),
NextValue(tx_cnt, sample_width),
NextValue(tx_buf, Cat(tx_rd_d, offset)),
tx_rden.eq(1),
NextValue(tx_buf, Cat(fifo.rd_d, offset)),
fifo.rden.eq(~tx_reset),
)
)
# sync should be sampled on rising edge, but data should change on falling edge
@ -617,8 +556,8 @@ class S7I2S(Module, AutoCSR, AutoDoc):
If((sync_pin if frame_format == I2S_FORMAT.I2S_STANDARD else ~sync_pin),
NextValue(tx_cnt, sample_width),
NextState("RIGHT_FALL"),
NextValue(tx_buf, Cat(tx_rd_d,offset)),
tx_rden.eq(1),
NextValue(tx_buf, Cat(fifo.rd_d,offset)),
fifo.rden.eq(~tx_reset),
).Else(
NextState("LEFT_WAIT"),
)
@ -652,8 +591,8 @@ class S7I2S(Module, AutoCSR, AutoDoc):
If((tx_cnt == 0) & (~sync_pin if frame_format == I2S_FORMAT.I2S_STANDARD else sync_pin),
NextValue(tx_cnt, sample_width),
NextState("LEFT_FALL"),
NextValue(tx_buf, Cat(tx_rd_d,offset)),
tx_rden.eq(1)
NextValue(tx_buf, Cat(fifo.rd_d,offset)),
fifo.rden.eq(~tx_reset)
).Elif(tx_cnt > 0,
NextState("RIGHT_FALL")
)