litex/lib/sata/command/__init__.py

281 lines
7.1 KiB
Python

from lib.sata.common import *
tx_to_rx = [
("write", 1),
("read", 1),
("identify", 1),
("count", 4)
]
rx_to_tx = [
("dma_activate", 1),
("data", 1),
("reg_d2h", 1)
]
class SATACommandTX(Module):
def __init__(self, transport, sector_size):
self.sink = sink = Sink(command_tx_description(32))
self.to_rx = to_rx = Source(tx_to_rx)
self.from_rx = from_rx = Sink(rx_to_tx)
###
self.comb += [
transport.sink.pm_port.eq(0),
transport.sink.features.eq(0),
transport.sink.lba.eq(sink.sector),
transport.sink.device.eq(0xe0),
transport.sink.count.eq(sink.count),
transport.sink.icc.eq(0),
transport.sink.control.eq(0),
]
fsm = FSM(reset_state="IDLE")
self.submodules += fsm
fsm.act("IDLE",
If(sink.stb & sink.sop,
If(sink.write,
NextState("SEND_WRITE_DMA_CMD")
).Elif(sink.read,
NextState("SEND_READ_DMA_CMD")
).Elif(sink.identify,
NextState("SEND_IDENTIFY_CMD")
).Else(
sink.ack.eq(1)
)
).Else(
sink.ack.eq(1)
)
)
fsm.act("SEND_WRITE_DMA_CMD",
transport.sink.stb.eq(sink.stb),
transport.sink.sop.eq(1),
transport.sink.eop.eq(1),
transport.sink.type.eq(fis_types["REG_H2D"]),
transport.sink.c.eq(1),
transport.sink.command.eq(regs["WRITE_DMA_EXT"]),
If(sink.stb & transport.sink.ack,
NextState("WAIT_DMA_ACTIVATE")
)
)
fsm.act("WAIT_DMA_ACTIVATE",
If(from_rx.dma_activate,
NextState("SEND_DATA")
)
)
fsm.act("SEND_DATA",
transport.sink.stb.eq(sink.stb),
transport.sink.sop.eq(sink.sop),
transport.sink.eop.eq(sink.eop),
transport.sink.type.eq(fis_types["DATA"]),
transport.sink.data.eq(sink.data),
sink.ack.eq(transport.sink.ack),
If(sink.stb & sink.ack & sink.eop,
NextState("IDLE")
)
)
fsm.act("SEND_READ_DMA_CMD",
transport.sink.stb.eq(sink.stb),
transport.sink.sop.eq(1),
transport.sink.eop.eq(1),
transport.sink.type.eq(fis_types["REG_H2D"]),
transport.sink.c.eq(1),
transport.sink.command.eq(regs["READ_DMA_EXT"]),
sink.ack.eq(transport.sink.ack),
If(sink.stb & sink.ack,
NextState("IDLE")
)
)
fsm.act("SEND_IDENTIFY_CMD",
transport.sink.stb.eq(sink.stb),
transport.sink.sop.eq(1),
transport.sink.eop.eq(1),
transport.sink.type.eq(fis_types["REG_H2D"]),
transport.sink.c.eq(1),
transport.sink.command.eq(regs["IDENTIFY_DEVICE_DMA"]),
sink.ack.eq(transport.sink.ack),
If(sink.stb & sink.ack,
NextState("IDLE")
)
)
self.comb += [
If(sink.stb,
to_rx.write.eq(sink.write),
to_rx.read.eq(sink.read),
to_rx.identify.eq(sink.identify),
to_rx.count.eq(sink.count)
)
]
class SATACommandRX(Module):
def __init__(self, transport, sector_size, max_count):
self.source = source = Source(command_rx_description(32))
self.to_tx = to_tx = Source(rx_to_tx)
self.from_tx = from_tx = Sink(tx_to_rx)
###
cmd_fifo = SyncFIFO(command_rx_cmd_description(32), 2) # Note: ideally depth=1
data_fifo = InsertReset(SyncFIFO(command_rx_data_description(32), sector_size*max_count//4, buffered=True))
self.submodules += cmd_fifo, data_fifo
def test_type(name):
return transport.source.type == fis_types[name]
dma_activate = Signal()
fsm = FSM(reset_state="IDLE")
self.submodules += fsm
fsm.act("IDLE",
transport.source.ack.eq(1),
If(from_tx.write,
NextState("WAIT_WRITE_ACTIVATE")
).Elif(from_tx.read | from_tx.identify,
NextState("WAIT_READ_DATA")
)
)
identify = Signal()
self.sync += \
If(fsm.ongoing("IDLE"),
identify.eq(from_tx.identify)
)
fsm.act("WAIT_WRITE_ACTIVATE",
transport.source.ack.eq(1),
If(transport.source.stb,
If(test_type("DMA_ACTIVATE_D2H"),
dma_activate.eq(1),
NextState("WAIT_WRITE_REG_D2H")
)
)
)
fsm.act("WAIT_WRITE_REG_D2H",
transport.source.ack.eq(1),
If(transport.source.stb,
If(test_type("REG_D2H"),
NextState("PRESENT_WRITE_RESPONSE")
)
)
)
fsm.act("PRESENT_WRITE_RESPONSE",
cmd_fifo.sink.stb.eq(1),
cmd_fifo.sink.write.eq(1),
cmd_fifo.sink.success.eq(1),
If(cmd_fifo.sink.stb & cmd_fifo.sink.ack,
NextState("IDLE")
)
)
fsm.act("WAIT_READ_DATA",
transport.source.ack.eq(1),
If(transport.source.stb,
transport.source.ack.eq(0),
If(test_type("DATA"),
NextState("PRESENT_READ_DATA")
)
)
)
fsm.act("PRESENT_READ_DATA",
data_fifo.sink.stb.eq(transport.source.stb),
data_fifo.sink.sop.eq(transport.source.sop),
data_fifo.sink.eop.eq(transport.source.eop),
data_fifo.sink.data.eq(transport.source.data),
transport.source.ack.eq(data_fifo.sink.ack),
If(data_fifo.sink.stb & data_fifo.sink.eop & data_fifo.sink.ack,
NextState("WAIT_READ_REG_D2H")
)
)
fsm.act("WAIT_READ_REG_D2H",
transport.source.ack.eq(1),
If(transport.source.stb,
If(test_type("REG_D2H"),
NextState("PRESENT_READ_RESPONSE")
)
)
)
fsm.act("PRESENT_READ_RESPONSE",
cmd_fifo.sink.stb.eq(1),
cmd_fifo.sink.read.eq(~identify),
cmd_fifo.sink.identify.eq(identify),
cmd_fifo.sink.success.eq(1),
cmd_fifo.sink.failed.eq(0),
If(~cmd_fifo.fifo.readable, # Note: simulate a depth=1 fifo
If(cmd_fifo.sink.stb & cmd_fifo.sink.ack,
If(cmd_fifo.sink.failed,
data_fifo.reset.eq(1)
),
NextState("IDLE")
)
)
)
out_fsm = FSM(reset_state="IDLE")
self.submodules += out_fsm
out_fsm.act("IDLE",
If(cmd_fifo.source.stb & cmd_fifo.source.write,
NextState("PRESENT_WRITE_RESPONSE"),
).Elif(cmd_fifo.source.stb & (cmd_fifo.source.read | cmd_fifo.source.identify),
If(cmd_fifo.source.success,
NextState("PRESENT_READ_RESPONSE_SUCCESS"),
).Else(
NextState("PRESENT_READ_RESPONSE_FAILED"),
)
)
)
out_fsm.act("PRESENT_WRITE_RESPONSE",
source.stb.eq(1),
source.sop.eq(1),
source.eop.eq(1),
source.write.eq(1),
source.success.eq(cmd_fifo.source.success),
If(source.stb & source.ack,
cmd_fifo.source.ack.eq(1),
NextState("IDLE")
)
)
out_fsm.act("PRESENT_READ_RESPONSE_SUCCESS",
source.stb.eq(data_fifo.source.stb),
source.read.eq(cmd_fifo.source.read),
source.identify.eq(cmd_fifo.source.identify),
source.success.eq(1),
source.sop.eq(data_fifo.source.sop),
source.eop.eq(data_fifo.source.eop),
source.data.eq(data_fifo.source.data),
data_fifo.source.ack.eq(source.ack),
If(source.stb & source.eop & source.ack,
cmd_fifo.source.ack.eq(1),
NextState("IDLE")
)
)
out_fsm.act("PRESENT_READ_RESPONSE_FAILED",
source.stb.eq(1),
source.sop.eq(1),
source.eop.eq(1),
source.read.eq(cmd_fifo.source.read),
source.identify.eq(cmd_fifo.source.identify),
source.failed.eq(1),
If(source.stb & source.ack,
cmd_fifo.source.ack.eq(1),
NextState("IDLE")
)
)
self.comb += [
to_tx.dma_activate.eq(dma_activate),
]
class SATACommand(Module):
def __init__(self, transport, sector_size=512, max_count=4):
if max_count*sector_size > 8192:
raise ValueError("sector_size * max_count must be <= 8192")
self.tx = SATACommandTX(transport, sector_size)
self.rx = SATACommandRX(transport, sector_size, max_count)
self.comb += [
self.rx.to_tx.connect(self.tx.from_rx),
self.tx.to_rx.connect(self.rx.from_tx)
]
self.sink, self.source = self.tx.sink, self.rx.source