2014-11-12 12:20:34 -05:00
|
|
|
import subprocess
|
|
|
|
|
2014-11-11 12:47:34 -05:00
|
|
|
from migen.fhdl.std import *
|
|
|
|
|
|
|
|
from lib.sata.std import *
|
2014-12-02 15:34:16 -05:00
|
|
|
from lib.sata.link.test.common import *
|
2014-12-04 19:13:55 -05:00
|
|
|
from lib.sata.transport.std import *
|
2014-11-11 12:47:34 -05:00
|
|
|
|
2014-12-04 17:43:21 -05:00
|
|
|
class PHYDword:
|
2014-11-11 12:47:34 -05:00
|
|
|
def __init__(self, dat=0):
|
|
|
|
self.dat = dat
|
|
|
|
self.start = 1
|
|
|
|
self.done = 0
|
|
|
|
|
2014-12-04 17:43:21 -05:00
|
|
|
class PHYSource(Module):
|
|
|
|
def __init__(self):
|
|
|
|
self.source = Source(phy_layout(32))
|
2014-11-11 12:47:34 -05:00
|
|
|
###
|
2014-12-04 17:43:21 -05:00
|
|
|
self.dword = PHYDword()
|
2014-11-11 12:47:34 -05:00
|
|
|
|
2014-12-03 03:17:51 -05:00
|
|
|
def send(self, dword):
|
|
|
|
self.dword = dword
|
2014-11-11 12:47:34 -05:00
|
|
|
|
|
|
|
def do_simulation(self, selfp):
|
2014-12-02 15:34:16 -05:00
|
|
|
selfp.source.stb = 1
|
|
|
|
selfp.source.charisk = 0b0000
|
|
|
|
for k, v in primitives.items():
|
|
|
|
if v == self.dword.dat:
|
|
|
|
selfp.source.charisk = 0b0001
|
|
|
|
selfp.source.data = self.dword.dat
|
2014-11-11 12:47:34 -05:00
|
|
|
|
2014-12-04 17:43:21 -05:00
|
|
|
class PHYSink(Module):
|
|
|
|
def __init__(self):
|
|
|
|
self.sink = Sink(phy_layout(32))
|
2014-11-11 12:47:34 -05:00
|
|
|
###
|
2014-12-04 17:43:21 -05:00
|
|
|
self.dword = PHYDword()
|
2014-11-11 12:47:34 -05:00
|
|
|
|
|
|
|
def receive(self):
|
|
|
|
self.dword.done = 0
|
|
|
|
while self.dword.done == 0:
|
|
|
|
yield
|
|
|
|
|
|
|
|
def do_simulation(self, selfp):
|
|
|
|
self.dword.done = 0
|
|
|
|
selfp.sink.ack = 1
|
|
|
|
if selfp.sink.stb == 1:
|
|
|
|
self.dword.done = 1
|
|
|
|
self.dword.dat = selfp.sink.data
|
|
|
|
|
2014-12-04 17:43:21 -05:00
|
|
|
class PHYLayer(Module):
|
|
|
|
def __init__(self, debug):
|
|
|
|
self.debug = debug
|
2014-11-11 12:47:34 -05:00
|
|
|
|
2014-12-04 17:43:21 -05:00
|
|
|
self.submodules.rx = PHYSink()
|
|
|
|
self.submodules.tx = PHYSource()
|
2014-11-11 12:47:34 -05:00
|
|
|
|
2014-12-04 17:43:21 -05:00
|
|
|
self.source = self.tx.source
|
|
|
|
self.sink = self.rx.sink
|
2014-11-11 12:47:34 -05:00
|
|
|
|
2014-12-03 03:17:51 -05:00
|
|
|
def send(self, dword):
|
2014-12-04 17:43:21 -05:00
|
|
|
packet = PHYDword(dword)
|
|
|
|
self.tx.send(packet)
|
2014-11-11 12:47:34 -05:00
|
|
|
|
|
|
|
def receive(self):
|
2014-12-04 17:43:21 -05:00
|
|
|
yield from self.rx.receive()
|
|
|
|
if self.debug:
|
|
|
|
print(self)
|
2014-11-11 12:47:34 -05:00
|
|
|
|
2014-12-03 03:17:51 -05:00
|
|
|
def __repr__(self):
|
2014-12-04 17:43:21 -05:00
|
|
|
receiving = "%08x " %self.rx.dword.dat
|
|
|
|
receiving += decode_primitive(self.rx.dword.dat)
|
2014-12-03 03:17:51 -05:00
|
|
|
receiving += " "*(16-len(receiving))
|
|
|
|
|
2014-12-04 17:43:21 -05:00
|
|
|
sending = "%08x " %self.tx.dword.dat
|
|
|
|
sending += decode_primitive(self.tx.dword.dat)
|
2014-12-03 03:17:51 -05:00
|
|
|
sending += " "*(16-len(sending))
|
|
|
|
|
|
|
|
return receiving + sending
|
|
|
|
|
2014-12-04 17:43:21 -05:00
|
|
|
class LinkPacket(list):
|
|
|
|
def __init__(self):
|
|
|
|
self.ongoing = False
|
|
|
|
self.scrambled_datas = self.import_scrambler_datas()
|
2014-12-03 03:17:51 -05:00
|
|
|
|
2014-12-04 17:43:21 -05:00
|
|
|
def import_scrambler_datas(self):
|
|
|
|
with subprocess.Popen(["./scrambler"], stdin=subprocess.PIPE, stdout=subprocess.PIPE) as process:
|
|
|
|
process.stdin.write("0x10000".encode("ASCII"))
|
|
|
|
out, err = process.communicate()
|
|
|
|
return [int(e, 16) for e in out.decode("utf-8").split("\n")[:-1]]
|
2014-11-12 12:20:34 -05:00
|
|
|
|
2014-12-04 17:43:21 -05:00
|
|
|
class LinkRXPacket(LinkPacket):
|
|
|
|
def decode(self):
|
|
|
|
self.descramble()
|
|
|
|
return self.check_crc()
|
2014-11-12 12:20:34 -05:00
|
|
|
|
2014-12-04 17:43:21 -05:00
|
|
|
def descramble(self):
|
|
|
|
for i in range(len(self)):
|
|
|
|
self[i] = self[i] ^ self.scrambled_datas[i]
|
2014-12-03 03:17:51 -05:00
|
|
|
|
2014-12-04 17:43:21 -05:00
|
|
|
def check_crc(self):
|
|
|
|
stdin = ""
|
|
|
|
for v in self[:-1]:
|
|
|
|
stdin += "0x%08x " %v
|
|
|
|
stdin += "exit"
|
|
|
|
with subprocess.Popen("./crc", stdin=subprocess.PIPE, stdout=subprocess.PIPE) as process:
|
|
|
|
process.stdin.write(stdin.encode("ASCII"))
|
2014-12-02 13:53:13 -05:00
|
|
|
out, err = process.communicate()
|
2014-12-04 17:43:21 -05:00
|
|
|
crc = int(out.decode("ASCII"), 16)
|
|
|
|
r = (self[-1] == crc)
|
|
|
|
self.pop()
|
|
|
|
return r
|
2014-11-12 12:20:34 -05:00
|
|
|
|
2014-12-04 17:43:21 -05:00
|
|
|
class LinkTXPacket(LinkPacket):
|
|
|
|
def encode(self):
|
|
|
|
self.scramble()
|
|
|
|
self.insert_crc()
|
2014-11-12 12:20:34 -05:00
|
|
|
|
2014-12-04 17:43:21 -05:00
|
|
|
def scramble(self):
|
|
|
|
for i in range(len(self)):
|
|
|
|
self[i] = self[i] ^ self.scrambled_datas[i]
|
|
|
|
|
|
|
|
def insert_crc(self):
|
2014-12-02 14:02:43 -05:00
|
|
|
stdin = ""
|
2014-12-04 17:43:21 -05:00
|
|
|
for v in self[:-1]:
|
2014-12-02 14:02:43 -05:00
|
|
|
stdin += "0x%08x " %v
|
|
|
|
stdin += "exit"
|
|
|
|
with subprocess.Popen("./crc", stdin=subprocess.PIPE, stdout=subprocess.PIPE) as process:
|
|
|
|
process.stdin.write(stdin.encode("ASCII"))
|
|
|
|
out, err = process.communicate()
|
|
|
|
crc = int(out.decode("ASCII"), 16)
|
2014-12-04 17:43:21 -05:00
|
|
|
self.append(crc)
|
|
|
|
|
|
|
|
class LinkLayer(Module):
|
|
|
|
def __init__(self, phy, debug, hold_random_level=0):
|
|
|
|
self.phy = phy
|
|
|
|
self.debug = debug
|
|
|
|
self.hold_random_level = hold_random_level
|
|
|
|
self.tx_packet = LinkTXPacket()
|
|
|
|
self.rx_packet = LinkRXPacket()
|
|
|
|
self.rx_cont = False
|
|
|
|
|
2014-12-04 19:13:55 -05:00
|
|
|
self.transport_callback = None
|
|
|
|
|
|
|
|
def set_transport_callback(self, callback):
|
|
|
|
self.transport_callback = callback
|
|
|
|
|
2014-12-04 17:43:21 -05:00
|
|
|
def callback(self, dword):
|
2014-12-03 05:12:26 -05:00
|
|
|
if dword == primitives["CONT"]:
|
2014-12-04 17:43:21 -05:00
|
|
|
self.rx_cont = True
|
2014-12-03 05:12:26 -05:00
|
|
|
elif is_primitive(dword):
|
2014-12-04 17:43:21 -05:00
|
|
|
self.rx_cont = False
|
2014-12-03 05:12:26 -05:00
|
|
|
|
2014-11-12 12:20:34 -05:00
|
|
|
if dword == primitives["X_RDY"]:
|
2014-12-03 03:17:51 -05:00
|
|
|
self.phy.send(primitives["R_RDY"])
|
2014-12-02 15:34:16 -05:00
|
|
|
|
|
|
|
elif dword == primitives["WTRM"]:
|
2014-12-03 03:17:51 -05:00
|
|
|
self.phy.send(primitives["R_OK"])
|
2014-11-12 12:20:34 -05:00
|
|
|
|
2014-12-02 15:34:16 -05:00
|
|
|
elif dword == primitives["HOLD"]:
|
2014-12-03 03:17:51 -05:00
|
|
|
self.phy.send(primitives["HOLDA"])
|
2014-12-02 15:34:16 -05:00
|
|
|
|
|
|
|
elif dword == primitives["EOF"]:
|
2014-12-04 17:43:21 -05:00
|
|
|
self.rx_packet.decode()
|
2014-12-04 19:13:55 -05:00
|
|
|
if self.transport_callback is not None:
|
|
|
|
self.transport_callback(self.rx_packet)
|
2014-12-04 17:43:21 -05:00
|
|
|
self.rx_packet.ongoing = False
|
2014-11-12 12:20:34 -05:00
|
|
|
|
2014-12-04 17:43:21 -05:00
|
|
|
elif self.rx_packet.ongoing:
|
2014-12-02 15:34:16 -05:00
|
|
|
if dword != primitives["HOLD"]:
|
|
|
|
n = randn(100)
|
2014-12-03 03:17:51 -05:00
|
|
|
if n < self.hold_random_level:
|
|
|
|
self.phy.send(primitives["HOLD"])
|
2014-12-02 15:34:16 -05:00
|
|
|
else:
|
2014-12-03 03:17:51 -05:00
|
|
|
self.phy.send(primitives["R_RDY"])
|
2014-12-03 05:12:26 -05:00
|
|
|
if not is_primitive(dword):
|
2014-12-04 17:43:21 -05:00
|
|
|
if not self.rx_cont:
|
2014-12-03 05:12:26 -05:00
|
|
|
self.rx_packet.append(dword)
|
2014-12-02 15:34:16 -05:00
|
|
|
|
|
|
|
elif dword == primitives["SOF"]:
|
2014-12-04 17:43:21 -05:00
|
|
|
self.rx_packet = LinkRXPacket()
|
|
|
|
self.rx_packet.ongoing = True
|
|
|
|
|
|
|
|
def send(self, packet):
|
|
|
|
pass
|
2014-11-11 12:47:34 -05:00
|
|
|
|
|
|
|
def gen_simulation(self, selfp):
|
2014-12-03 03:17:51 -05:00
|
|
|
self.phy.send(primitives["SYNC"])
|
2014-11-11 12:47:34 -05:00
|
|
|
while True:
|
|
|
|
yield from self.phy.receive()
|
2014-12-04 17:43:21 -05:00
|
|
|
self.callback(self.phy.rx.dword.dat)
|
|
|
|
|
2014-12-04 19:13:55 -05:00
|
|
|
def get_field_data(field, packet):
|
|
|
|
return (packet[field.dword] >> field.offset) & (2**field.width-1)
|
|
|
|
|
|
|
|
class FIS:
|
|
|
|
def __init__(self, packet, layout):
|
|
|
|
self.packet = packet
|
|
|
|
self.layout = layout
|
|
|
|
self.decode()
|
|
|
|
|
|
|
|
def decode(self):
|
|
|
|
for k, v in self.layout.items():
|
|
|
|
setattr(self, k, get_field_data(v, self.packet))
|
|
|
|
|
|
|
|
def encode(self):
|
|
|
|
for k, v in self.layout.items():
|
|
|
|
self.packet[v.dword] |= (getattr(self, k) << v.offset)
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
r = "--------\n"
|
|
|
|
for k in sorted(self.layout.keys()):
|
|
|
|
r += k + " : 0x%x" %getattr(self,k) + "\n"
|
|
|
|
return r
|
|
|
|
|
|
|
|
class FIS_REG_H2D(FIS):
|
|
|
|
def __init__(self, packet=[0]*fis_reg_h2d_len):
|
|
|
|
FIS.__init__(self, packet,fis_reg_h2d_layout)
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
r = "FIS_REG_H2D\n"
|
|
|
|
r += FIS.__repr__(self)
|
|
|
|
return r
|
|
|
|
|
|
|
|
class FIS_REG_D2H(FIS):
|
|
|
|
def __init__(self, packet=[0]*fis_reg_d2h_len):
|
|
|
|
FIS.__init__(self, packet,fis_reg_d2h_layout)
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
r = "FIS_REG_D2H\n"
|
|
|
|
r += FIS.__repr__(self)
|
|
|
|
return r
|
|
|
|
|
|
|
|
class FIS_DMA_ACTIVATE_D2H(FIS):
|
|
|
|
def __init__(self, packet=[0]*fis_dma_activate_d2h_len):
|
|
|
|
FIS.__init__(self, packet,fis_dma_activate_d2h_layout)
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
r = "FIS_DMA_ACTIVATE_D2H\n"
|
|
|
|
r += FIS.__repr__(self)
|
|
|
|
return r
|
|
|
|
|
|
|
|
class FIS_DMA_SETUP(FIS):
|
|
|
|
def __init__(self, packet=[0]*fis_dma_setup_len):
|
|
|
|
FIS.__init__(self, packet,fis_dma_setup_layout)
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
r = "FIS_DMA_SETUP\n"
|
|
|
|
r += FIS.__repr__(self)
|
|
|
|
return r
|
|
|
|
|
|
|
|
class FIS_DATA(FIS):
|
|
|
|
def __init__(self, packet=[0]):
|
|
|
|
FIS.__init__(self, packet,fis_data_layout)
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
r = "FIS_DATA\n"
|
|
|
|
r += FIS.__repr__(self)
|
|
|
|
return r
|
|
|
|
|
|
|
|
class FIS_PIO_SETUP_D2H(FIS):
|
|
|
|
def __init__(self, packet=[0]*fis_pio_setup_d2h_len):
|
|
|
|
FIS.__init__(self, packet,fis_pio_setup_d2h_layout)
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
r = "FIS_PIO_SETUP\n"
|
|
|
|
r += FIS.__repr__(self)
|
|
|
|
return r
|
|
|
|
|
|
|
|
class FIS_UNKNOWN(FIS):
|
|
|
|
def __init__(self, packet=[0]):
|
|
|
|
FIS.__init__(self, packet, {})
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
r = "UNKNOWN\n"
|
|
|
|
r += "--------\n"
|
|
|
|
for dword in self.packet:
|
|
|
|
r += "%08x\n" %dword
|
|
|
|
return r
|
|
|
|
|
|
|
|
class TransportLayer(Module):
|
|
|
|
def __init__(self, link):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def callback(self, packet):
|
|
|
|
fis_type = packet[0]
|
|
|
|
if fis_type == fis_types["REG_H2D"]:
|
|
|
|
fis = FIS_REG_H2D(packet)
|
|
|
|
elif fis_type == fis_types["REG_D2H"]:
|
|
|
|
fis = FIS_REG_D2H(packet)
|
|
|
|
elif fis_type == fis_types["DMA_ACTIVATE_D2H"]:
|
|
|
|
fis = FIS_DMA_ACTIVATE_D2H(packet)
|
|
|
|
elif fis_type == fis_types["DMA_SETUP"]:
|
|
|
|
fis = FIS_SETUP(packet)
|
|
|
|
elif fis_type == fis_types["DATA"]:
|
|
|
|
fis = FIS_DATA(packet)
|
|
|
|
elif fis_type == fis_types["PIO_SETUP_D2H"]:
|
|
|
|
fis = FIS_PIO_SETUP_D2H(packet)
|
|
|
|
else:
|
|
|
|
fis = FIS_UNKNOWN(packet)
|
|
|
|
print(fis)
|
|
|
|
|
2014-12-04 17:43:21 -05:00
|
|
|
class BFM(Module):
|
|
|
|
def __init__(self, dw, debug=False, hold_random_level=0):
|
|
|
|
###
|
|
|
|
self.submodules.phy = PHYLayer(debug)
|
|
|
|
self.submodules.link = LinkLayer(self.phy, debug, hold_random_level)
|
2014-12-04 19:13:55 -05:00
|
|
|
self.submodules.transport = TransportLayer(self.link)
|
|
|
|
self.link.set_transport_callback(self.transport.callback)
|