2015-01-16 17:52:41 -05:00
|
|
|
from litesata.common import *
|
2014-12-12 16:26:04 -05:00
|
|
|
|
2014-12-14 09:32:00 -05:00
|
|
|
tx_to_rx = [
|
|
|
|
("write", 1),
|
2015-01-06 10:48:19 -05:00
|
|
|
("read", 1),
|
2015-01-16 16:49:34 -05:00
|
|
|
("identify", 1),
|
2015-01-06 10:48:19 -05:00
|
|
|
("count", 16)
|
2014-12-14 09:32:00 -05:00
|
|
|
]
|
|
|
|
|
|
|
|
rx_to_tx = [
|
2014-12-25 07:11:22 -05:00
|
|
|
("dma_activate", 1)
|
2014-12-12 16:26:04 -05:00
|
|
|
]
|
|
|
|
|
2015-01-16 17:52:41 -05:00
|
|
|
class LiteSATACommandTX(Module):
|
2015-01-06 10:48:19 -05:00
|
|
|
def __init__(self, transport):
|
2014-12-14 04:52:56 -05:00
|
|
|
self.sink = sink = Sink(command_tx_description(32))
|
2014-12-14 09:32:00 -05:00
|
|
|
self.to_rx = to_rx = Source(tx_to_rx)
|
|
|
|
self.from_rx = from_rx = Sink(rx_to_tx)
|
2014-12-12 16:26:04 -05:00
|
|
|
|
|
|
|
###
|
|
|
|
|
|
|
|
self.comb += [
|
|
|
|
transport.sink.pm_port.eq(0),
|
|
|
|
transport.sink.features.eq(0),
|
2014-12-15 07:26:53 -05:00
|
|
|
transport.sink.lba.eq(sink.sector),
|
2014-12-12 16:26:04 -05:00
|
|
|
transport.sink.device.eq(0xe0),
|
2014-12-15 07:26:53 -05:00
|
|
|
transport.sink.count.eq(sink.count),
|
2014-12-12 16:26:04 -05:00
|
|
|
transport.sink.icc.eq(0),
|
|
|
|
transport.sink.control.eq(0),
|
2015-01-14 03:19:41 -05:00
|
|
|
transport.sink.data.eq(sink.data)
|
2014-12-12 16:26:04 -05:00
|
|
|
]
|
|
|
|
|
2015-01-06 10:48:19 -05:00
|
|
|
self.dwords_counter = dwords_counter = Counter(max=fis_max_dwords)
|
|
|
|
|
2014-12-19 16:50:35 -05:00
|
|
|
self.fsm = fsm = FSM(reset_state="IDLE")
|
2014-12-12 16:26:04 -05:00
|
|
|
fsm.act("IDLE",
|
2014-12-19 19:26:02 -05:00
|
|
|
sink.ack.eq(0),
|
2014-12-12 16:26:04 -05:00
|
|
|
If(sink.stb & sink.sop,
|
|
|
|
If(sink.write,
|
|
|
|
NextState("SEND_WRITE_DMA_CMD")
|
|
|
|
).Elif(sink.read,
|
|
|
|
NextState("SEND_READ_DMA_CMD")
|
2015-01-16 16:49:34 -05:00
|
|
|
).Elif(sink.identify,
|
|
|
|
NextState("SEND_IDENTIFY_CMD")
|
2014-12-12 16:26:04 -05:00
|
|
|
).Else(
|
|
|
|
sink.ack.eq(1)
|
|
|
|
)
|
|
|
|
).Else(
|
|
|
|
sink.ack.eq(1)
|
|
|
|
)
|
|
|
|
)
|
|
|
|
fsm.act("SEND_WRITE_DMA_CMD",
|
2014-12-14 15:03:14 -05:00
|
|
|
transport.sink.stb.eq(sink.stb),
|
2014-12-12 16:26:04 -05:00
|
|
|
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"]),
|
2014-12-15 07:26:53 -05:00
|
|
|
If(sink.stb & transport.sink.ack,
|
2014-12-12 16:26:04 -05:00
|
|
|
NextState("WAIT_DMA_ACTIVATE")
|
|
|
|
)
|
|
|
|
)
|
|
|
|
fsm.act("WAIT_DMA_ACTIVATE",
|
2015-01-06 10:48:19 -05:00
|
|
|
dwords_counter.reset.eq(1),
|
2014-12-14 09:32:00 -05:00
|
|
|
If(from_rx.dma_activate,
|
2014-12-12 16:26:04 -05:00
|
|
|
NextState("SEND_DATA")
|
|
|
|
)
|
|
|
|
)
|
|
|
|
fsm.act("SEND_DATA",
|
2015-01-06 10:48:19 -05:00
|
|
|
dwords_counter.ce.eq(sink.stb & sink.ack),
|
|
|
|
|
2014-12-12 16:26:04 -05:00
|
|
|
transport.sink.stb.eq(sink.stb),
|
2015-01-06 10:48:19 -05:00
|
|
|
transport.sink.sop.eq(dwords_counter.value == 0),
|
|
|
|
transport.sink.eop.eq((dwords_counter.value == (fis_max_dwords-1)) | sink.eop),
|
|
|
|
|
2014-12-12 16:26:04 -05:00
|
|
|
transport.sink.type.eq(fis_types["DATA"]),
|
|
|
|
sink.ack.eq(transport.sink.ack),
|
2015-01-06 10:48:19 -05:00
|
|
|
If(sink.stb & sink.ack,
|
|
|
|
If(sink.eop,
|
|
|
|
NextState("IDLE")
|
|
|
|
).Elif(dwords_counter.value == (fis_max_dwords-1),
|
|
|
|
NextState("WAIT_DMA_ACTIVATE")
|
|
|
|
)
|
2014-12-12 16:26:04 -05:00
|
|
|
)
|
|
|
|
)
|
|
|
|
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,
|
2014-12-14 09:32:00 -05:00
|
|
|
NextState("IDLE")
|
2014-12-12 16:26:04 -05:00
|
|
|
)
|
|
|
|
)
|
2015-01-16 16:49:34 -05:00
|
|
|
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"]),
|
|
|
|
sink.ack.eq(transport.sink.ack),
|
|
|
|
If(sink.stb & sink.ack,
|
|
|
|
NextState("IDLE")
|
|
|
|
)
|
|
|
|
)
|
2014-12-12 16:26:04 -05:00
|
|
|
|
2014-12-14 09:32:00 -05:00
|
|
|
self.comb += [
|
|
|
|
If(sink.stb,
|
|
|
|
to_rx.write.eq(sink.write),
|
2015-01-06 10:48:19 -05:00
|
|
|
to_rx.read.eq(sink.read),
|
2015-01-16 16:49:34 -05:00
|
|
|
to_rx.identify.eq(sink.identify),
|
2015-01-06 10:48:19 -05:00
|
|
|
to_rx.count.eq(sink.count)
|
2014-12-14 09:32:00 -05:00
|
|
|
)
|
|
|
|
]
|
|
|
|
|
2015-01-16 17:52:41 -05:00
|
|
|
class LiteSATACommandRX(Module):
|
2015-01-06 10:48:19 -05:00
|
|
|
def __init__(self, transport):
|
2014-12-14 06:49:35 -05:00
|
|
|
self.source = source = Source(command_rx_description(32))
|
2014-12-14 09:32:00 -05:00
|
|
|
self.to_tx = to_tx = Source(rx_to_tx)
|
|
|
|
self.from_tx = from_tx = Sink(tx_to_rx)
|
2014-12-12 16:26:04 -05:00
|
|
|
|
|
|
|
###
|
|
|
|
|
2015-01-06 11:03:27 -05:00
|
|
|
cmd_buffer = Buffer(command_rx_cmd_description(32))
|
|
|
|
cmd_buffer.sink, cmd_buffer.source = cmd_buffer.d, cmd_buffer.q
|
|
|
|
data_buffer = InsertReset(SyncFIFO(command_rx_data_description(32), fis_max_dwords, buffered=True))
|
|
|
|
self.submodules += cmd_buffer, data_buffer
|
2014-12-15 09:31:08 -05:00
|
|
|
|
2014-12-13 05:33:22 -05:00
|
|
|
def test_type(name):
|
|
|
|
return transport.source.type == fis_types[name]
|
|
|
|
|
2015-01-16 16:49:34 -05:00
|
|
|
identify = Signal()
|
2014-12-13 05:33:22 -05:00
|
|
|
dma_activate = Signal()
|
2015-01-06 10:48:19 -05:00
|
|
|
read_ndwords = Signal(max=sectors2dwords(2**16))
|
|
|
|
self.dwords_counter = dwords_counter = Counter(max=sectors2dwords(2**16))
|
|
|
|
read_done = Signal()
|
|
|
|
|
|
|
|
self.sync += \
|
|
|
|
If(from_tx.read,
|
|
|
|
read_ndwords.eq(from_tx.count*sectors2dwords(1)-1)
|
|
|
|
)
|
|
|
|
self.comb += read_done.eq(self.dwords_counter.value == read_ndwords)
|
2014-12-13 05:33:22 -05:00
|
|
|
|
2014-12-19 16:50:35 -05:00
|
|
|
self.fsm = fsm = FSM(reset_state="IDLE")
|
2014-12-14 09:32:00 -05:00
|
|
|
fsm.act("IDLE",
|
2015-01-06 10:48:19 -05:00
|
|
|
self.dwords_counter.reset.eq(1),
|
2014-12-14 09:32:00 -05:00
|
|
|
transport.source.ack.eq(1),
|
|
|
|
If(from_tx.write,
|
2015-01-06 10:48:19 -05:00
|
|
|
NextState("WAIT_WRITE_ACTIVATE_OR_REG_D2H")
|
2014-12-23 12:26:07 -05:00
|
|
|
).Elif(from_tx.read,
|
2015-01-16 16:49:34 -05:00
|
|
|
NextState("WAIT_READ_DATA_OR_REG_D2H"),
|
|
|
|
).Elif(from_tx.identify,
|
|
|
|
NextState("WAIT_PIO_SETUP_D2H"),
|
2014-12-14 09:32:00 -05:00
|
|
|
)
|
|
|
|
)
|
2015-01-16 16:49:34 -05:00
|
|
|
self.sync += \
|
|
|
|
If(fsm.ongoing("IDLE"),
|
|
|
|
identify.eq(from_tx.identify)
|
|
|
|
)
|
2015-01-06 10:48:19 -05:00
|
|
|
fsm.act("WAIT_WRITE_ACTIVATE_OR_REG_D2H",
|
2014-12-14 09:32:00 -05:00
|
|
|
transport.source.ack.eq(1),
|
2014-12-13 05:33:22 -05:00
|
|
|
If(transport.source.stb,
|
2014-12-14 09:32:00 -05:00
|
|
|
If(test_type("DMA_ACTIVATE_D2H"),
|
2014-12-13 05:33:22 -05:00
|
|
|
dma_activate.eq(1),
|
2015-01-06 10:48:19 -05:00
|
|
|
).Elif(test_type("REG_D2H"),
|
2015-01-06 12:17:11 -05:00
|
|
|
# XXX: use status and error fields of REG_D2H
|
2014-12-14 09:32:00 -05:00
|
|
|
NextState("PRESENT_WRITE_RESPONSE")
|
|
|
|
)
|
|
|
|
)
|
|
|
|
)
|
|
|
|
fsm.act("PRESENT_WRITE_RESPONSE",
|
2015-01-06 11:03:27 -05:00
|
|
|
cmd_buffer.sink.stb.eq(1),
|
|
|
|
cmd_buffer.sink.write.eq(1),
|
|
|
|
cmd_buffer.sink.last.eq(1),
|
|
|
|
cmd_buffer.sink.success.eq(~transport.source.error),
|
|
|
|
cmd_buffer.sink.failed.eq(transport.source.error),
|
|
|
|
If(cmd_buffer.sink.stb & cmd_buffer.sink.ack,
|
2014-12-14 09:32:00 -05:00
|
|
|
NextState("IDLE")
|
|
|
|
)
|
|
|
|
)
|
2015-01-06 12:17:11 -05:00
|
|
|
fsm.act("WAIT_READ_DATA_OR_REG_D2H",
|
2014-12-14 09:32:00 -05:00
|
|
|
transport.source.ack.eq(1),
|
|
|
|
If(transport.source.stb,
|
|
|
|
transport.source.ack.eq(0),
|
|
|
|
If(test_type("DATA"),
|
|
|
|
NextState("PRESENT_READ_DATA")
|
2015-01-06 12:17:11 -05:00
|
|
|
).Elif(test_type("REG_D2H"),
|
|
|
|
# XXX: use status and error fields of REG_D2H
|
|
|
|
NextState("PRESENT_READ_RESPONSE")
|
2014-12-13 05:33:22 -05:00
|
|
|
)
|
2014-12-12 16:26:04 -05:00
|
|
|
)
|
2014-12-14 09:32:00 -05:00
|
|
|
)
|
2015-01-16 16:49:34 -05:00
|
|
|
fsm.act("WAIT_PIO_SETUP_D2H",
|
|
|
|
transport.source.ack.eq(1),
|
|
|
|
If(transport.source.stb,
|
|
|
|
transport.source.ack.eq(0),
|
|
|
|
If(test_type("PIO_SETUP_D2H"),
|
|
|
|
NextState("PRESENT_PIO_SETUP_D2H")
|
|
|
|
)
|
|
|
|
)
|
|
|
|
)
|
|
|
|
fsm.act("PRESENT_PIO_SETUP_D2H",
|
|
|
|
transport.source.ack.eq(1),
|
|
|
|
# XXX : Check error/ status
|
|
|
|
If(transport.source.stb & transport.source.eop,
|
|
|
|
NextState("WAIT_READ_DATA_OR_REG_D2H")
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
2015-01-14 03:19:41 -05:00
|
|
|
self.comb += [
|
2015-01-06 11:03:27 -05:00
|
|
|
data_buffer.sink.sop.eq(transport.source.sop),
|
|
|
|
data_buffer.sink.eop.eq(transport.source.eop),
|
2015-01-14 03:19:41 -05:00
|
|
|
data_buffer.sink.data.eq(transport.source.data)
|
|
|
|
]
|
|
|
|
fsm.act("PRESENT_READ_DATA",
|
|
|
|
data_buffer.sink.stb.eq(transport.source.stb),
|
2015-01-06 11:03:27 -05:00
|
|
|
transport.source.ack.eq(data_buffer.sink.ack),
|
|
|
|
If(data_buffer.sink.stb & data_buffer.sink.ack,
|
2015-01-06 10:48:19 -05:00
|
|
|
self.dwords_counter.ce.eq(~read_done),
|
2015-01-06 11:03:27 -05:00
|
|
|
If(data_buffer.sink.eop,
|
2015-01-16 16:49:34 -05:00
|
|
|
If(read_done & ~identify,
|
2015-01-06 12:17:11 -05:00
|
|
|
NextState("WAIT_READ_DATA_OR_REG_D2H")
|
2015-01-06 10:48:19 -05:00
|
|
|
).Else(
|
|
|
|
NextState("PRESENT_READ_RESPONSE")
|
|
|
|
)
|
|
|
|
)
|
2014-12-14 09:32:00 -05:00
|
|
|
)
|
|
|
|
)
|
2014-12-25 07:11:22 -05:00
|
|
|
read_error = Signal()
|
|
|
|
self.sync += \
|
2015-01-06 12:17:11 -05:00
|
|
|
If(fsm.before_entering("PRESENT_READ_DATA"),
|
2014-12-25 07:11:22 -05:00
|
|
|
read_error.eq(1)
|
|
|
|
).Elif(transport.source.stb & transport.source.ack & transport.source.eop,
|
|
|
|
read_error.eq(transport.source.error)
|
|
|
|
)
|
2014-12-14 09:32:00 -05:00
|
|
|
fsm.act("PRESENT_READ_RESPONSE",
|
2015-01-06 11:03:27 -05:00
|
|
|
cmd_buffer.sink.stb.eq(1),
|
2015-01-16 16:49:34 -05:00
|
|
|
cmd_buffer.sink.read.eq(~identify),
|
|
|
|
cmd_buffer.sink.identify.eq(identify),
|
|
|
|
cmd_buffer.sink.last.eq(read_done | identify),
|
2015-01-06 11:03:27 -05:00
|
|
|
cmd_buffer.sink.success.eq(~read_error),
|
|
|
|
cmd_buffer.sink.failed.eq(read_error),
|
|
|
|
If(cmd_buffer.sink.stb & cmd_buffer.sink.ack,
|
|
|
|
If(cmd_buffer.sink.failed,
|
|
|
|
data_buffer.reset.eq(1)
|
2015-01-06 10:48:19 -05:00
|
|
|
),
|
2015-01-16 16:49:34 -05:00
|
|
|
If(read_done | identify,
|
2014-12-15 09:31:08 -05:00
|
|
|
NextState("IDLE")
|
2015-01-06 10:48:19 -05:00
|
|
|
).Else(
|
2015-01-06 12:17:11 -05:00
|
|
|
NextState("WAIT_READ_DATA_OR_REG_D2H")
|
2014-12-15 09:31:08 -05:00
|
|
|
)
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
2014-12-19 16:50:35 -05:00
|
|
|
self.out_fsm = out_fsm = FSM(reset_state="IDLE")
|
2014-12-15 09:31:08 -05:00
|
|
|
out_fsm.act("IDLE",
|
2015-01-06 12:17:11 -05:00
|
|
|
If(cmd_buffer.source.stb,
|
2015-01-16 16:49:34 -05:00
|
|
|
If((cmd_buffer.source.read | cmd_buffer.source.identify) & cmd_buffer.source.success,
|
2015-01-06 12:17:11 -05:00
|
|
|
NextState("PRESENT_RESPONSE_WITH_DATA"),
|
2014-12-15 09:31:08 -05:00
|
|
|
).Else(
|
2015-01-06 12:17:11 -05:00
|
|
|
NextState("PRESENT_RESPONSE_WITHOUT_DATA"),
|
2014-12-15 09:31:08 -05:00
|
|
|
)
|
|
|
|
)
|
|
|
|
)
|
2015-01-06 12:17:11 -05:00
|
|
|
|
|
|
|
self.comb += [
|
|
|
|
source.write.eq(cmd_buffer.source.write),
|
|
|
|
source.read.eq(cmd_buffer.source.read),
|
2015-01-16 16:49:34 -05:00
|
|
|
source.identify.eq(cmd_buffer.source.identify),
|
2015-01-06 11:03:27 -05:00
|
|
|
source.last.eq(cmd_buffer.source.last),
|
|
|
|
source.success.eq(cmd_buffer.source.success),
|
2015-01-16 16:49:34 -05:00
|
|
|
source.failed.eq(cmd_buffer.source.failed),
|
2015-01-14 03:19:41 -05:00
|
|
|
source.data.eq(data_buffer.source.data)
|
2015-01-06 12:17:11 -05:00
|
|
|
]
|
|
|
|
|
|
|
|
out_fsm.act("PRESENT_RESPONSE_WITH_DATA",
|
2015-01-06 11:03:27 -05:00
|
|
|
source.stb.eq(data_buffer.source.stb),
|
|
|
|
source.sop.eq(data_buffer.source.sop),
|
|
|
|
source.eop.eq(data_buffer.source.eop),
|
2015-01-06 12:17:11 -05:00
|
|
|
|
2015-01-06 11:03:27 -05:00
|
|
|
data_buffer.source.ack.eq(source.ack),
|
2015-01-06 12:17:11 -05:00
|
|
|
|
2014-12-15 09:31:08 -05:00
|
|
|
If(source.stb & source.eop & source.ack,
|
2015-01-06 11:03:27 -05:00
|
|
|
cmd_buffer.source.ack.eq(1),
|
2014-12-15 09:31:08 -05:00
|
|
|
NextState("IDLE")
|
|
|
|
)
|
|
|
|
)
|
2015-01-06 12:17:11 -05:00
|
|
|
out_fsm.act("PRESENT_RESPONSE_WITHOUT_DATA",
|
2014-12-15 09:31:08 -05:00
|
|
|
source.stb.eq(1),
|
|
|
|
source.sop.eq(1),
|
|
|
|
source.eop.eq(1),
|
2014-12-15 07:26:53 -05:00
|
|
|
If(source.stb & source.ack,
|
2015-01-06 11:03:27 -05:00
|
|
|
cmd_buffer.source.ack.eq(1),
|
2014-12-14 09:32:00 -05:00
|
|
|
NextState("IDLE")
|
|
|
|
)
|
|
|
|
)
|
2014-12-13 05:33:22 -05:00
|
|
|
|
|
|
|
self.comb += [
|
2014-12-14 09:32:00 -05:00
|
|
|
to_tx.dma_activate.eq(dma_activate),
|
2014-12-12 16:26:04 -05:00
|
|
|
]
|
|
|
|
|
2015-01-16 17:52:41 -05:00
|
|
|
class LiteSATACommand(Module):
|
2015-01-06 10:48:19 -05:00
|
|
|
def __init__(self, transport):
|
2015-01-16 17:52:41 -05:00
|
|
|
self.tx = LiteSATACommandTX(transport)
|
|
|
|
self.rx = LiteSATACommandRX(transport)
|
2014-12-14 09:32:00 -05:00
|
|
|
self.comb += [
|
|
|
|
self.rx.to_tx.connect(self.tx.from_rx),
|
|
|
|
self.tx.to_rx.connect(self.rx.from_tx)
|
|
|
|
]
|
2014-12-13 05:33:22 -05:00
|
|
|
self.sink, self.source = self.tx.sink, self.rx.source
|