from lib.sata.common import *
from migen.genlib.misc import chooser
+from migen.flow.plumbing import Multiplexer, Demultiplexer
class SATAPHYDatapathRX(Module):
def __init__(self):
chooser(fifo.source.charisk, mux, self.source.charisk)
]
+class SATAPHYAlignInserter(Module):
+ def __init__(self, ctrl):
+ self.sink = sink = Sink(phy_description(32))
+ self.source = source = Source(phy_description(32))
+ ###
+ # send 2 ALIGN every 256 DWORDs
+ # used for clock compensation between
+ # HOST and device
+ cnt = Signal(8)
+ send = Signal()
+ self.sync += \
+ If(~ctrl.ready,
+ cnt.eq(0)
+ ).Elif(source.stb & source.ack,
+ cnt.eq(cnt+1)
+ )
+ self.comb += [
+ send.eq(cnt < 2),
+ If(send,
+ source.stb.eq(1),
+ source.charisk.eq(0b0001),
+ source.data.eq(primitives["ALIGN"]),
+ sink.ack.eq(0)
+ ).Else(
+ source.stb.eq(sink.stb),
+ source.data.eq(sink.data),
+ source.charisk.eq(sink.charisk),
+ sink.ack.eq(source.ack)
+ )
+ ]
+
+class SATAPHYAlignRemover(Module):
+ def __init__(self):
+ self.sink = sink = Sink(phy_description(32))
+ self.source = source = Source(phy_description(32))
+ ###
+ charisk_match = sink.charisk == 0b0001
+ data_match = sink.data == primitives["ALIGN"]
+
+ self.comb += \
+ If(sink.stb & charisk_match & data_match,
+ sink.ack.eq(1),
+ ).Else(
+ Record.connect(sink, source)
+ )
+
class SATAPHYDatapath(Module):
def __init__(self, trx, ctrl):
self.sink = Sink(phy_description(32))
###
- # change data width & cross domain crossing
- rx = SATAPHYDatapathRX()
- tx = SATAPHYDatapathTX()
- self.submodules += rx, tx
+ # TX path
+ self.align_inserter = SATAPHYAlignInserter(ctrl)
+ self.mux = Multiplexer(phy_description(32), 2)
+ self.tx = SATAPHYDatapathTX()
self.comb += [
- trx.source.connect(rx.sink),
- tx.source.connect(trx.sink)
+ self.mux.sel.eq(ctrl.ready),
+ Record.connect(self.sink, self.align_inserter.sink),
+ Record.connect(ctrl.source, self.mux.sink0),
+ Record.connect(self.align_inserter.source, self.mux.sink1),
+ Record.connect(self.mux.source, self.tx.sink),
+ Record.connect(self.tx.source, trx.sink)
]
- # Align cnt (send 2 Align DWORDs every 256 DWORDs)
- align_cnt = Signal(8)
- self.sync += \
- If(~ctrl.ready,
- align_cnt.eq(0)
- ).Elif(tx.sink.stb & tx.sink.ack,
- align_cnt.eq(align_cnt+1)
- )
- send_align = (align_cnt < 2)
-
- receive_align = Signal()
- self.comb += receive_align.eq(rx.source.stb &
- (rx.source.charisk == 0b0001) &
- (rx.source.data == primitives["ALIGN"]))
-
- # user / ctrl mux
+ # RX path
+ self.rx = SATAPHYDatapathRX()
+ self.demux = Demultiplexer(phy_description(32), 2)
+ self.align_remover = SATAPHYAlignRemover()
self.comb += [
- # user
- If(ctrl.ready,
- If(send_align,
- tx.sink.stb.eq(1),
- tx.sink.data.eq(primitives["ALIGN"]),
- tx.sink.charisk.eq(0b0001),
- self.sink.ack.eq(0)
- ).Else(
- tx.sink.stb.eq(self.sink.stb),
- tx.sink.data.eq(self.sink.data),
- tx.sink.charisk.eq(self.sink.charisk),
- self.sink.ack.eq(tx.sink.ack)
- ),
- If(receive_align,
- rx.source.ack.eq(1)
- ).Else(
- self.source.stb.eq(rx.source.stb),
- self.source.data.eq(rx.source.data),
- self.source.charisk.eq(rx.source.charisk),
- rx.source.ack.eq(1)
- )
- # ctrl
- ).Else(
- tx.sink.stb.eq(ctrl.source.stb),
- tx.sink.data.eq(ctrl.source.data),
- tx.sink.charisk.eq(ctrl.source.charisk),
-
- ctrl.sink.stb.eq(rx.source.stb),
- ctrl.sink.data.eq(rx.source.data),
- rx.source.ack.eq(1),
- )
+ self.demux.sel.eq(ctrl.ready),
+ Record.connect(trx.source, self.rx.sink),
+ Record.connect(self.rx.source, self.demux.sink),
+ Record.connect(self.demux.source0, ctrl.sink),
+ Record.connect(self.demux.source1, self.align_remover.sink),
+ Record.connect(self.align_remover.source, self.source)
]
--- /dev/null
+from lib.sata.common import *
+from lib.sata.phy.datapath import SATAPHYDatapath
+
+from lib.sata.test.common import *
+
+class DataPacket(list):
+ def __init__(self, data=[]):
+ self.ongoing = False
+ self.done = False
+ for d in data:
+ self.append(d)
+
+class DataStreamer(PacketStreamer):
+ def __init__(self):
+ PacketStreamer.__init__(self, phy_description(32), DataPacket)
+
+ def do_simulation(self, selfp):
+ PacketStreamer.do_simulation(self, selfp)
+ selfp.source.charisk = 0
+ # Note: for simplicity we generate charisk by detecting
+ # primitives in data
+ for k, v in primitives.items():
+ try:
+ if self.source_data == v:
+ selfp.source.charisk = 0b0001
+ except:
+ pass
+
+class DataLogger(PacketLogger):
+ def __init__(self):
+ PacketLogger.__init__(self, phy_description(32), DataPacket)
+
+class TRX(Module):
+ def __init__(self):
+ self.sink = Sink(phy_description(32))
+ self.source = Source(phy_description(32))
+ self.comb += Record.connect(self.sink, self.source)
+
+class CTRL(Module):
+ def __init__(self):
+ self.sink = Sink(phy_description(32))
+ self.source = Source(phy_description(32))
+ self.ready = Signal(reset=1)
+
+class TB(Module):
+ def __init__(self):
+ # use sys_clk for each clock_domain
+ self.clock_domains.cd_sata_rx = ClockDomain()
+ self.clock_domains.cd_sata_tx = ClockDomain()
+ self.comb += [
+ self.cd_sata_rx.clk.eq(ClockSignal()),
+ self.cd_sata_rx.rst.eq(ResetSignal()),
+ self.cd_sata_tx.clk.eq(ClockSignal()),
+ self.cd_sata_tx.rst.eq(ResetSignal()),
+ ]
+
+ self.streamer = DataStreamer()
+ self.streamer_randomizer = Randomizer(phy_description(32), level=0)
+ self.trx = TRX()
+ self.ctrl = CTRL()
+ self.datapath = SATAPHYDatapath(self.trx, self.ctrl)
+ self.logger_randomizer = Randomizer(phy_description(32), level=0)
+ self.logger = DataLogger()
+
+ self.pipeline = Pipeline(
+ self.streamer,
+ self.streamer_randomizer,
+ self.datapath,
+ self.logger_randomizer,
+ self.logger
+ )
+
+ def gen_simulation(self, selfp):
+ streamer_packet = DataPacket([seed_to_data(i, False) for i in range(512)])
+ yield from self.streamer.send(streamer_packet)
+ yield from self.logger.receive(512)
+ for d in self.logger.packet:
+ r = "%08x " %d
+ r +=decode_primitive(d)
+ print(r)
+
+ # check results
+ #s, l, e = check(streamer_packet, self.logger.packet)
+ #print("shift "+ str(s) + " / length " + str(l) + " / errors " + str(e))
+
+
+if __name__ == "__main__":
+ run_simulation(TB(), ncycles=4096, vcd_name="my.vcd", keep_files=True)