modify addressing (in sectors) and improve hdd model debug

This commit is contained in:
Florent Kermarrec 2014-12-15 13:26:53 +01:00
parent 6a783ad291
commit f226de5ba0
5 changed files with 99 additions and 94 deletions

View file

@ -23,15 +23,12 @@ class SATACommandTX(Module):
### ###
sector_bits = log2_int(sector_size)
dwords_bits = 2
self.comb += [ self.comb += [
transport.sink.pm_port.eq(0), transport.sink.pm_port.eq(0),
transport.sink.features.eq(0), transport.sink.features.eq(0),
transport.sink.lba.eq(sink.address[sector_bits-dwords_bits:]), transport.sink.lba.eq(sink.sector),
transport.sink.device.eq(0xe0), transport.sink.device.eq(0xe0),
transport.sink.count.eq(sink.length[sector_bits-dwords_bits:]), transport.sink.count.eq(sink.count),
transport.sink.icc.eq(0), transport.sink.icc.eq(0),
transport.sink.control.eq(0), transport.sink.control.eq(0),
] ]
@ -61,11 +58,10 @@ class SATACommandTX(Module):
transport.sink.type.eq(fis_types["REG_H2D"]), transport.sink.type.eq(fis_types["REG_H2D"]),
transport.sink.c.eq(1), transport.sink.c.eq(1),
transport.sink.command.eq(regs["WRITE_DMA_EXT"]), transport.sink.command.eq(regs["WRITE_DMA_EXT"]),
If(transport.sink.ack, If(sink.stb & transport.sink.ack,
NextState("WAIT_DMA_ACTIVATE") NextState("WAIT_DMA_ACTIVATE")
) )
) )
# XXX: split when length > 2048 dwords
fsm.act("WAIT_DMA_ACTIVATE", fsm.act("WAIT_DMA_ACTIVATE",
If(from_rx.dma_activate, If(from_rx.dma_activate,
NextState("SEND_DATA") NextState("SEND_DATA")
@ -116,7 +112,7 @@ class SATACommandTX(Module):
] ]
class SATACommandRX(Module): class SATACommandRX(Module):
def __init__(self, transport, sector_size): def __init__(self, transport, sector_size, max_count):
self.source = source = Source(command_rx_description(32)) self.source = source = Source(command_rx_description(32))
self.to_tx = to_tx = Source(rx_to_tx) self.to_tx = to_tx = Source(rx_to_tx)
self.from_tx = from_tx = Sink(tx_to_rx) self.from_tx = from_tx = Sink(tx_to_rx)
@ -167,7 +163,7 @@ class SATACommandRX(Module):
source.eop.eq(1), source.eop.eq(1),
source.write.eq(1), source.write.eq(1),
source.success.eq(1), source.success.eq(1),
If(source.ack, If(source.stb & source.ack,
NextState("IDLE") NextState("IDLE")
) )
) )
@ -207,7 +203,7 @@ class SATACommandRX(Module):
source.read.eq(~identify), source.read.eq(~identify),
source.identify.eq(identify), source.identify.eq(identify),
source.success.eq(1), source.success.eq(1),
If(source.ack, If(source.stb & source.ack,
NextState("IDLE") NextState("IDLE")
) )
) )
@ -217,9 +213,11 @@ class SATACommandRX(Module):
] ]
class SATACommand(Module): class SATACommand(Module):
def __init__(self, transport, sector_size=512): def __init__(self, transport, sector_size=512, max_count=16):
if max_count*sector_size > 8192:
raise ValueError("sector_size x max_count must be <= 2048")
self.submodules.tx = SATACommandTX(transport, sector_size) self.submodules.tx = SATACommandTX(transport, sector_size)
self.submodules.rx = SATACommandRX(transport, sector_size) self.submodules.rx = SATACommandRX(transport, sector_size, max_count)
self.comb += [ self.comb += [
self.rx.to_tx.connect(self.tx.from_rx), self.rx.to_tx.connect(self.tx.from_rx),
self.tx.to_rx.connect(self.rx.from_tx) self.tx.to_rx.connect(self.rx.from_tx)

View file

@ -148,8 +148,8 @@ def command_tx_description(dw):
("write", 1), ("write", 1),
("read", 1), ("read", 1),
("identify", 1), ("identify", 1),
("address", 32), ("sector", 48),
("length", 32), ("count", 4),
("data", dw) ("data", dw)
] ]
return EndpointDescription(layout, packetized=True) return EndpointDescription(layout, packetized=True)

View file

@ -13,14 +13,14 @@ from lib.sata.test.hdd import *
from lib.sata.test.common import * from lib.sata.test.common import *
class CommandTXPacket(list): class CommandTXPacket(list):
def __init__(self, write=0, read=0, identify=0, address=0, length=0, data=[]): def __init__(self, write=0, read=0, identify=0, sector=0, count=0, data=[]):
self.ongoing = False self.ongoing = False
self.done = False self.done = False
self.write = write self.write = write
self.read = read self.read = read
self.identify = identify self.identify = identify
self.address = address self.sector = sector
self.length = length self.count = count
for d in data: for d in data:
self.append(d) self.append(d)
@ -47,8 +47,8 @@ class CommandStreamer(Module):
selfp.source.write = self.packet.write selfp.source.write = self.packet.write
selfp.source.read = self.packet.read selfp.source.read = self.packet.read
selfp.source.identify = self.packet.identify selfp.source.identify = self.packet.identify
selfp.source.address = self.packet.address selfp.source.sector = self.packet.sector
selfp.source.length = self.packet.length selfp.source.count = self.packet.count
if not self.packet.ongoing and not self.packet.done: if not self.packet.ongoing and not self.packet.done:
selfp.source.stb = 1 selfp.source.stb = 1
@ -105,10 +105,8 @@ class CommandLogger(Module):
class TB(Module): class TB(Module):
def __init__(self): def __init__(self):
self.submodules.hdd = HDD( self.submodules.hdd = HDD(
phy_debug=False, link_debug=False, link_random_level=50,
link_random_level=50, link_debug=False,
transport_debug=False, transport_loopback=False, transport_debug=False, transport_loopback=False,
command_debug=False,
hdd_debug=True) hdd_debug=True)
self.submodules.link = SATALink(self.hdd.phy) self.submodules.link = SATALink(self.hdd.phy)
self.submodules.transport = SATATransport(self.link) self.submodules.transport = SATATransport(self.link)
@ -128,12 +126,14 @@ class TB(Module):
] ]
def gen_simulation(self, selfp): def gen_simulation(self, selfp):
self.hdd.allocate_mem(0x00000000, 64*1024*1024) hdd = self.hdd
write_data = [i for i in range(512//4)] hdd.malloc(0, 64)
write_packet = CommandTXPacket(write=1, address=0, length=len(write_data), data=write_data) write_data = [i for i in range(hdd.sectors2dwords(2))]
write_len = hdd.dwords2sectors(len(write_data))
write_packet = CommandTXPacket(write=1, sector=2, count=write_len, data=write_data)
yield from self.streamer.send(write_packet) yield from self.streamer.send(write_packet)
yield from self.logger.receive() yield from self.logger.receive()
read_packet = CommandTXPacket(read=1, address=0, length=len(write_data)) read_packet = CommandTXPacket(read=1, sector=2, count=write_len)
yield from self.streamer.send(read_packet) yield from self.streamer.send(read_packet)
yield from self.logger.receive() yield from self.logger.receive()
read_data = self.logger.packet read_data = self.logger.packet
@ -144,4 +144,4 @@ class TB(Module):
print("shift "+ str(s) + " / length " + str(l) + " / errors " + str(e)) print("shift "+ str(s) + " / length " + str(l) + " / errors " + str(e))
if __name__ == "__main__": if __name__ == "__main__":
run_simulation(TB(), ncycles=1024, vcd_name="my.vcd", keep_files=True) run_simulation(TB(), ncycles=2048, vcd_name="my.vcd", keep_files=True)

View file

@ -6,6 +6,13 @@ from migen.fhdl.std import *
from lib.sata.common import * from lib.sata.common import *
from lib.sata.test.common import * from lib.sata.test.common import *
def print_with_prefix(s, prefix=""):
if not isinstance(s, str):
s = s.__repr__()
s = s.split("\n")
for l in s:
print(prefix + l)
# PHY Layer model # PHY Layer model
class PHYDword: class PHYDword:
def __init__(self, dat=0): def __init__(self, dat=0):
@ -49,8 +56,7 @@ class PHYSink(Module):
self.dword.dat = selfp.sink.data self.dword.dat = selfp.sink.data
class PHYLayer(Module): class PHYLayer(Module):
def __init__(self, debug=False): def __init__(self):
self.debug = debug
self.submodules.rx = PHYSink() self.submodules.rx = PHYSink()
self.submodules.tx = PHYSource() self.submodules.tx = PHYSource()
@ -63,8 +69,6 @@ class PHYLayer(Module):
self.tx.send(packet) self.tx.send(packet)
def receive(self): def receive(self):
if self.debug:
print(self)
yield from self.rx.receive() yield from self.rx.receive()
def __repr__(self): def __repr__(self):
@ -79,6 +83,9 @@ class PHYLayer(Module):
return receiving + sending return receiving + sending
# Link Layer model # Link Layer model
def print_link(s):
print_with_prefix(s, "[LNK]: ")
def import_scrambler_datas(): def import_scrambler_datas():
with subprocess.Popen(["./scrambler"], stdin=subprocess.PIPE, stdout=subprocess.PIPE) as process: with subprocess.Popen(["./scrambler"], stdin=subprocess.PIPE, stdout=subprocess.PIPE) as process:
process.stdin.write("0x10000".encode("ASCII")) process.stdin.write("0x10000".encode("ASCII"))
@ -249,6 +256,8 @@ class LinkLayer(Module):
self.phy.send(primitives["SYNC"]) self.phy.send(primitives["SYNC"])
while True: while True:
yield from self.phy.receive() yield from self.phy.receive()
if self.debug:
print_link(self.phy)
self.phy.send(primitives["SYNC"]) self.phy.send(primitives["SYNC"])
rx_dword = self.phy.rx.dword.dat rx_dword = self.phy.rx.dword.dat
rx_dword = self.remove_cont(rx_dword) rx_dword = self.remove_cont(rx_dword)
@ -264,6 +273,9 @@ class LinkLayer(Module):
self.insert_cont() self.insert_cont()
# Transport Layer model # Transport Layer model
def print_transport(s):
print_with_prefix(s, "[TRN]: ")
def get_field_data(field, packet): def get_field_data(field, packet):
return (packet[field.dword] >> field.offset) & (2**field.width-1) return (packet[field.dword] >> field.offset) & (2**field.width-1)
@ -365,7 +377,7 @@ class TransportLayer(Module):
packet = LinkTXPacket(fis.packet) packet = LinkTXPacket(fis.packet)
self.link.tx_packets.append(packet) self.link.tx_packets.append(packet)
if self.debug and not self.loopback: if self.debug and not self.loopback:
print(fis) print_transport(fis)
def callback(self, packet): def callback(self, packet):
fis_type = packet[0] & 0xff fis_type = packet[0] & 0xff
@ -380,7 +392,7 @@ class TransportLayer(Module):
else: else:
fis = FIS_UNKNOWN(packet, direction="H2D") fis = FIS_UNKNOWN(packet, direction="H2D")
if self.debug: if self.debug:
print(fis) print_transport(fis)
if self.loopback: if self.loopback:
self.send(fis) self.send(fis)
else: else:
@ -388,9 +400,8 @@ class TransportLayer(Module):
# Command Layer model # Command Layer model
class CommandLayer(Module): class CommandLayer(Module):
def __init__(self, transport, debug=False): def __init__(self, transport):
self.transport = transport self.transport = transport
self.debug = debug
self.transport.set_command_callback(self.callback) self.transport.set_command_callback(self.callback)
self.hdd = None self.hdd = None
@ -399,103 +410,100 @@ class CommandLayer(Module):
self.hdd = hdd self.hdd = hdd
def callback(self, fis): def callback(self, fis):
# XXX manage maximum of 2048 DWORDS per DMA
resp = None resp = None
if isinstance(fis, FIS_REG_H2D): if isinstance(fis, FIS_REG_H2D):
if fis.command == regs["WRITE_DMA_EXT"]: if fis.command == regs["WRITE_DMA_EXT"]:
resp = self.hdd.write_dma_cmd(fis) resp = self.hdd.write_dma_callback(fis)
elif fis.command == regs["READ_DMA_EXT"]: elif fis.command == regs["READ_DMA_EXT"]:
resp = self.hdd.read_dma_cmd(fis) resp = self.hdd.read_dma_callback(fis)
elif fis.command == regs["IDENTIFY_DEVICE_DMA"]: elif fis.command == regs["IDENTIFY_DEVICE_DMA"]:
resp = self.hdd.identify_device_dma_cmd(fis) resp = self.hdd.identify_device_dma_callback(fis)
elif isinstance(fis, FIS_DATA): elif isinstance(fis, FIS_DATA):
resp = self.hdd.data_cmd(fis) resp = self.hdd.data_callback(fis)
if resp is not None: if resp is not None:
for packet in resp: for packet in resp:
self.transport.send(packet) self.transport.send(packet)
# HDD model # HDD model
def print_hdd(s):
print_with_prefix(s, "[HDD]: ")
class HDDMemRegion: class HDDMemRegion:
def __init__(self, base, length): def __init__(self, base, count, sector_size):
self.base = base self.base = base
self.length = length self.count = count
self.data = [0]*(length//4) self.data = [0]*(count*sector_size//4)
class HDD(Module): class HDD(Module):
def __init__(self, def __init__(self,
phy_debug=False,
link_debug=False, link_random_level=0, link_debug=False, link_random_level=0,
transport_debug=False, transport_loopback=False, transport_debug=False, transport_loopback=False,
command_debug=False,
hdd_debug=False, hdd_sector_size=512, hdd_debug=False, hdd_sector_size=512,
): ):
### ###
self.submodules.phy = PHYLayer(phy_debug) self.submodules.phy = PHYLayer()
self.submodules.link = LinkLayer(self.phy, link_debug, link_random_level) self.submodules.link = LinkLayer(self.phy, link_debug, link_random_level)
self.submodules.transport = TransportLayer(self.link, transport_debug, transport_loopback) self.submodules.transport = TransportLayer(self.link, transport_debug, transport_loopback)
self.submodules.command = CommandLayer(self.transport, command_debug) self.submodules.command = CommandLayer(self.transport)
self.command.set_hdd(self) self.command.set_hdd(self)
self.hdd_debug = hdd_debug self.debug = hdd_debug
self.hdd_sector_size = hdd_sector_size self.sector_size = hdd_sector_size
self.mem = None self.mem = None
self.wr_address = 0 self.wr_sector = 0
self.wr_length = 0 self.wr_end_sector = 0
self.wr_cnt = 0
self.rd_address = 0
self.rd_length = 0
def allocate_mem(self, base, length): def dwords2sectors(self, n):
if self.hdd_debug: return math.ceil(n*4/self.sector_size)
print("[HDD] : Allocating {n} bytes at 0x{a}".format(n=length, a=base))
self.mem = HDDMemRegion(base, length)
def write_mem(self, adr, data): def sectors2dwords(self, n):
if self.hdd_debug: return n*self.sector_size//4
print("[HDD] : Writing {n} bytes at 0x{a}".format(n=len(data)*4, a=adr))
current_adr = (adr-self.mem.base)//4 def malloc(self, sector, count):
if self.debug:
s = "Allocating {n} sectors: {s} to {e}".format(n=count, s=sector, e=sector+count)
s += " ({} KB)".format(count*self.sector_size//1024)
print_hdd(s)
self.mem = HDDMemRegion(sector, count, self.sector_size)
def write(self, sector, data):
n = math.ceil(self.dwords2sectors(len(data)))
if self.debug:
print_hdd("Writing sector {s} to {e}".format(s=sector, e=sector+n-1))
for i in range(len(data)): for i in range(len(data)):
self.mem.data[current_adr+i] = data[i] offset = self.sectors2dwords(sector)
self.mem.data[offset+i] = data[i]
def read_mem(self, adr, length=1): def read(self, sector, count):
if self.hdd_debug: if self.debug:
print("[HDD] : Reading {n} bytes at 0x{a}".format(n=length, a=adr)) print_hdd("Reading sector {s} to {e}".format(s=sector, e=sector+count-1))
current_adr = (adr-self.mem.base)//4
data = [] data = []
for i in range(length//4): for i in range(self.sectors2dwords(count)):
data.append(self.mem.data[current_adr+i]) data.append(self.mem.data[self.sectors2dwords(sector)+i])
return data return data
def write_dma_cmd(self, fis): def write_dma_callback(self, fis):
self.wr_address = fis.lba_lsb self.wr_sector = fis.lba_lsb + (fis.lba_msb << 32)
self.wr_length = fis.count*self.hdd_sector_size self.wr_end_sector = self.wr_sector + fis.count
self.wr_cnt = 0
return [FIS_DMA_ACTIVATE_D2H()] return [FIS_DMA_ACTIVATE_D2H()]
def read_dma_cmd(self, fis): def read_dma_callback(self, fis):
self.rd_address = fis.lba_lsb sector = fis.lba_lsb + (fis.lba_msb << 32)
self.rd_length = fis.count*self.hdd_sector_size packet = self.read(sector, fis.count)
self.rd_cnt = 0 packet.insert(0, 0)
n = math.ceil(self.rd_length/(2048*4)) return [FIS_DATA(packet, direction="D2H"), FIS_REG_D2H()]
packets = [FIS_REG_D2H()]
for i in range(n):
length = min(self.rd_length-self.rd_cnt, 2048)
packet = self.read_mem(self.rd_address, length)
packet.insert(0, 0)
packets.insert(0, FIS_DATA(packet, direction="D2H"))
return packets
def identify_dma_cmd(self, fis): def identify_dma_callback(self, fis):
packet = [i for i in range(256)] packet = [i for i in range(256)]
packet.insert(0, 0) packet.insert(0, 0)
return [FIS_DATA(packet, direction="D2H"), FIS_REG_D2H()] return [FIS_DATA(packet, direction="D2H"), FIS_REG_D2H()]
def data_cmd(self, fis): def data_callback(self, fis):
self.write_mem(self.wr_address, fis.packet[1:]) self.write(self.wr_sector, fis.packet[1:])
self.wr_cnt += len(fis.packet[1:])*4 self.wr_sector += self.dwords2sectors(len(fis.packet[1:]))
if self.wr_length == self.wr_cnt: if self.wr_sector == self.wr_end_sector:
return [FIS_REG_D2H()] return [FIS_REG_D2H()]
else: else:
return [FIS_DMA_ACTIVATE_D2H()] return [FIS_DMA_ACTIVATE_D2H()]

View file

@ -54,7 +54,7 @@ class SATATransportTX(Module):
sink.ack.eq(1) sink.ack.eq(1)
) )
).Else( ).Else(
sink.ack.eq(sink.stb) sink.ack.eq(1)
) )
) )
fsm.act("SEND_REG_H2D_CMD", fsm.act("SEND_REG_H2D_CMD",
@ -161,7 +161,6 @@ class SATATransportRX(Module):
).Elif(test_type("DATA"), ).Elif(test_type("DATA"),
NextState("RECEIVE_DATA_CMD"), NextState("RECEIVE_DATA_CMD"),
).Else( ).Else(
# XXX: Better to ack?
link.source.ack.eq(1) link.source.ack.eq(1)
) )
).Else( ).Else(
@ -180,7 +179,7 @@ class SATATransportRX(Module):
source.sop.eq(1), source.sop.eq(1),
source.eop.eq(1), source.eop.eq(1),
_decode_cmd(encoded_cmd, fis_reg_d2h_layout, source), _decode_cmd(encoded_cmd, fis_reg_d2h_layout, source),
If(source.ack, If(source.stb & source.ack,
NextState("IDLE") NextState("IDLE")
) )
) )
@ -196,7 +195,7 @@ class SATATransportRX(Module):
source.sop.eq(1), source.sop.eq(1),
source.eop.eq(1), source.eop.eq(1),
_decode_cmd(encoded_cmd, fis_dma_activate_d2h_layout, source), _decode_cmd(encoded_cmd, fis_dma_activate_d2h_layout, source),
If(source.ack, If(source.stb & source.ack,
NextState("IDLE") NextState("IDLE")
) )
) )