You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

200 lines
9.0 KiB
Python

#
# This is an LPC slave to wishbone interface
#
# LPC Clock | System Clock
#
# +---------+ +------------+ +--------+
# LPC | | | ASYNC | | | Wishbone
# pins | +---->| FIFO WR +---->| | IO Space
# | LPC | | | | |<-------->
# <------>| Front | +------------+ | LOGIC |
# | | | |
# | | +------------+ | | Wishbone
# | | | ASYNC | | | FW Space
# | |<----+ FIFO RD |<----+ |<-------->
# | | | | | |
# +---------+ +------------+ +--------+
#
# It takes the lpcfront and and turns it into IO and FW wishbone
# interfaces. The lpcfront operates on the lpc clock domain and the
# wishbone interfaces operate on the standard "sync" domain.
#
# To cross the clock domains async fifos are used. The write fifo
# takes write commands from the lpc interface. The read fifo gets
# information back to respond to these write commands.
#
# The write fifo turns the write commands into either an IO or FW
# wishbone transaction. The read fifo takes the wishbone transaction
# reponses and sends them back to the LPC.
#
# If an address doesn't exist on the wishbone interfaces (common on
# the IO bus), the wishbone interface asserts the err signal (rather
# than the ack). When this occurs the read fifo send an error back to
# the LPC.
#
from nmigen import Signal, Elaboratable, Module
from nmigen import ClockSignal, Cat, DomainRenamer, ResetSignal, ResetInserter
from nmigen.lib.fifo import AsyncFIFO
from nmigen_soc.wishbone import Interface as WishboneInterface
from nmigen.back import verilog
from .lpcfront import lpcfront, LPCCycletype, LPC_FW_DATA_WIDTH, LPC_FW_ADDR_WIDTH, LPC_IO_DATA_WIDTH, LPC_IO_ADDR_WIDTH
class lpc2wb(Elaboratable):
def __init__(self):
# LPC clock pin
self.lclk = Signal()
self.lframe = Signal()
self.lad_in = Signal(4)
self.lad_out = Signal(4)
self.lad_en = Signal()
self.lreset = Signal()
self.io_wb = WishboneInterface(data_width=LPC_IO_DATA_WIDTH,
addr_width=LPC_IO_ADDR_WIDTH,
granularity=8,
features = ["err"])
# 32 bit bus, so address only need to address words
self.fw_wb = WishboneInterface(data_width=LPC_FW_DATA_WIDTH,
addr_width=LPC_FW_ADDR_WIDTH - 2,
granularity=8)
def elaborate(self, platform):
m = Module()
# hook up lclk port to lclk domain
m.d.comb += ClockSignal("lclk").eq(self.lclk)
# Use main reset to reset lclk domain
m.d.comb += ResetSignal("lclk").eq(ResetSignal())
# create lpc front end wth right clock domain
m.submodules.lpc = lpc = DomainRenamer("lclk")(lpcfront())
wr_data = Signal(lpc.wrcmd.data.width)
wr_addr = Signal(lpc.wrcmd.addr.width)
wr_cmd = Signal(lpc.wrcmd.cmd.width)
wr_size = Signal(lpc.wrcmd.size.width)
wr_rdy = Signal()
# hook up lclk port to lclk domain
m.d.comb += ClockSignal("lclkrst").eq(self.lclk)
# Use main reset to reset lclk domain
m.d.comb += ResetSignal("lclkrst").eq(lpc.wrcmd.rst)
# hook up external lpc interface
m.d.comb += lpc.lframe.eq(self.lframe)
m.d.comb += lpc.lreset.eq(self.lreset)
m.d.comb += lpc.lad_in.eq(self.lad_in)
m.d.comb += self.lad_en.eq(lpc.lad_en)
m.d.comb += self.lad_out.eq(lpc.lad_out)
# We have two fifo
# 1) fifowr for getting commands from the LPC and transferring them
# to the wishbone. This has address for writes and reads, data
# for writes and cmd (IO read/write)
# 2) fiford for getting read data from the wishbone back to the LPC.
fifowr = AsyncFIFO(width=lpc.wrcmd.width(), depth=2,
r_domain="sync",
w_domain="lclkrst")
m.submodules += fifowr
fiford = AsyncFIFO(width=lpc.rdcmd.width(), depth=2,
r_domain="lclkrst",
w_domain="sync")
m.submodules += fiford
# lpc clock side
m.d.comb += fifowr.w_data[ 0:32].eq(lpc.wrcmd.data)
m.d.comb += fifowr.w_data[32:60].eq(lpc.wrcmd.addr)
m.d.comb += fifowr.w_data[60:62].eq(lpc.wrcmd.cmd)
m.d.comb += fifowr.w_data[62:64].eq(lpc.wrcmd.size)
m.d.comb += lpc.wrcmd.rdy.eq(fifowr.w_rdy)
m.d.comb += fifowr.w_en.eq(lpc.wrcmd.en)
# system clock side
m.d.comb += wr_data.eq(fifowr.r_data[ 0:32]) # sliced as above
m.d.comb += wr_addr.eq(fifowr.r_data[32:60]) # sliced as above
m.d.comb += wr_cmd.eq (fifowr.r_data[60:62]) # sliced as above
m.d.comb += wr_size.eq(fifowr.r_data[62:64]) # sliced as above
m.d.comb += wr_rdy.eq(fifowr.r_rdy)
m.d.comb += fifowr.r_en.eq(0) # See below for wishbone acks
# turn fifowr into IO wishbone master
m.d.comb += self.io_wb.adr.eq(wr_addr[0:16])
m.d.comb += self.io_wb.dat_w.eq(wr_data[0:8])
m.d.comb += self.io_wb.sel.eq(1)
m.d.comb += self.io_wb.we.eq(wr_cmd == LPCCycletype.IOWR)
with m.If ((wr_cmd == LPCCycletype.IORD) | (wr_cmd == LPCCycletype.IOWR)):
# The fiford should always be ready here but check anyway
m.d.comb += self.io_wb.cyc.eq(wr_rdy & fiford.w_rdy)
m.d.comb += self.io_wb.stb.eq(wr_rdy & fiford.w_rdy)
# turn fifowr into FW wishbone master
m.d.comb += self.fw_wb.adr.eq(wr_addr[2:28])
# data comes in the MSB so we need to shift it down for smaller sizes
m.d.comb += self.fw_wb.dat_w.eq(wr_data)
with m.If (wr_size == 3):
m.d.comb += self.fw_wb.sel.eq(0b1111)
with m.If (wr_size == 1):
with m.If (wr_addr[1] == 0b0):
m.d.comb += self.fw_wb.sel.eq(0b0011)
with m.If (wr_addr[1] == 0b1):
m.d.comb += self.fw_wb.sel.eq(0b1100)
m.d.comb += self.fw_wb.dat_w.eq(wr_data << 16)
with m.If (wr_size == 0):
with m.If (wr_addr[0:2] == 0b00):
m.d.comb += self.fw_wb.sel.eq(0b0001)
with m.If (wr_addr[0:2] == 0b01):
m.d.comb += self.fw_wb.sel.eq(0b0010)
m.d.comb += self.fw_wb.dat_w.eq(wr_data << 8)
with m.If (wr_addr[0:2] == 0b10):
m.d.comb += self.fw_wb.sel.eq(0b0100)
m.d.comb += self.fw_wb.dat_w.eq(wr_data << 16)
with m.If (wr_addr[0:2] == 0b11):
m.d.comb += self.fw_wb.sel.eq(0b1000)
m.d.comb += self.fw_wb.dat_w.eq(wr_data << 24)
m.d.comb += self.fw_wb.we.eq(wr_cmd == LPCCycletype.FWWR)
with m.If ((wr_cmd == LPCCycletype.FWRD) | (wr_cmd == LPCCycletype.FWWR)):
# The fiford should always be ready here but check anyway
m.d.comb += self.fw_wb.cyc.eq(wr_rdy & fiford.w_rdy)
m.d.comb += self.fw_wb.stb.eq(wr_rdy & fiford.w_rdy)
# Arbitrate the acks back into the fifo
with m.If ((wr_cmd == LPCCycletype.IORD) | (wr_cmd == LPCCycletype.IOWR)):
m.d.comb += fifowr.r_en.eq(self.io_wb.ack | self.io_wb.err)
m.d.comb += fiford.w_data[32].eq(self.io_wb.err)
with m.If ((wr_cmd == LPCCycletype.FWRD) | (wr_cmd == LPCCycletype.FWWR)):
m.d.comb += fifowr.r_en.eq(self.fw_wb.ack)
m.d.comb += fiford.w_data[32].eq(0)
# sending data back from IO/FW wishbones to fiford
with m.If (wr_cmd == LPCCycletype.IORD):
m.d.comb += fiford.w_data[0:32].eq(self.io_wb.dat_r)
with m.Elif (wr_cmd == LPCCycletype.FWRD):
m.d.comb += fiford.w_data[0:32].eq(self.fw_wb.dat_r)
with m.If (wr_size == 1):
with m.If (wr_addr[1] == 0b1):
m.d.comb += fiford.w_data[0:16].eq(self.fw_wb.dat_r[16:32])
with m.If (wr_size == 0):
with m.If (wr_addr[0:2] == 0b01):
m.d.comb += fiford.w_data[0:8].eq(self.fw_wb.dat_r[8:16])
with m.If (wr_addr[0:2] == 0b10):
m.d.comb += fiford.w_data[0:8].eq(self.fw_wb.dat_r[16:24])
with m.If (wr_addr[0:2] == 0b11):
m.d.comb += fiford.w_data[0:8].eq(self.fw_wb.dat_r[24:32])
m.d.comb += fiford.w_en.eq(self.io_wb.ack | self.fw_wb.ack | self.io_wb.err)
# lpc side of read fiford
m.d.comb += fiford.r_en.eq(lpc.rdcmd.en)
m.d.comb += lpc.rdcmd.data.eq(fiford.r_data[0:32])
m.d.comb += lpc.rdcmd.error.eq(fiford.r_data[32])
m.d.comb += lpc.rdcmd.rdy.eq(fiford.r_rdy)
return m
if __name__ == "__main__":
top = lpc2wb()
with open("lpc2wb.v", "w") as f:
f.write(verilog.convert(top))