From: Florent Kermarrec Date: Fri, 16 Jan 2015 22:52:41 +0000 (+0100) Subject: refactor code X-Git-Tag: 24jan2021_ls180~2572^2~51 X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=6de7e15a0c9f475db6edac7e59d5923d1f0c7661;p=litex.git refactor code --- diff --git a/Makefile b/Makefile index deb3bd82..5a3406d1 100644 --- a/Makefile +++ b/Makefile @@ -2,10 +2,9 @@ MSCDIR = ../misoc CURDIR = ../lite-sata PYTHON = python3 TOOLCHAIN = vivado -PLATFORM = kc705 PROGRAMMER = vivado -CMD = $(PYTHON) make.py -X $(CURDIR) -Op toolchain $(TOOLCHAIN) -Op programmer $(PROGRAMMER) -p $(PLATFORM) -t bist +CMD = $(PYTHON) make.py -X $(CURDIR) -Op toolchain $(TOOLCHAIN) -Op programmer $(PROGRAMMER) -t bist_kc705 csv: cd $(MSCDIR) && $(CMD) --csr_csv $(CURDIR)/test/csr.csv build-csr-csv -Ot export_mila True diff --git a/README b/README index 215829a7..df0e9b63 100644 --- a/README +++ b/README @@ -5,8 +5,35 @@ Copyright 2014-2015 / Florent Kermarrec / florent@enjoy-digital.fr - A lite open-source SATA1/2/3 controller - developed in partnership with M-Labs Ltd & HKU + A generic and configurable SATA1/2/3 core + developed in partnership with M-Labs Ltd & HKU + +[> Features +------------------ +PHY: + - OOB, COMWAKE, COMINIT. + - ALIGN inserter/remover and bytes alignment on K28.5. + - 8B/10B encoding/decoding in transceiver. + - Errors detection and reporting. + - 1.5 / 3.0 / 6.0GBPs supported speeds. + - 37.5 / 75 / 150MHz system clock. +Core: + Link: + - CONT inserter/remover. + - Scrambling/Descrambling of data. + - CRC inserter/checker. + - HOLD insertion/detection. + - Errors detection and reporting. + Transport/Command: + - Easy to use user interface (Can be used with or without CPU). + - 48 bits sector addressing. + - 3 supported commands: READ_DMA(_EXT), WRITE_DMA(_EXT), IDENTIFY_DEVICE. + - Errors detection and reporting. + +Frontend: + - Configurable crossbar (simply use core.crossbar.get_port() to add a new port!) + - Ports arbitration transparent to the user. + - Synthetizable BIST. [> Getting started ------------------ @@ -33,11 +60,11 @@ make all 7. Test design: - go to test directory + go to test directory and run: python3 bist.py [> Simulations : - Simulation are avalaible in ./lib/sata/test: + Simulations are avalaible in ./lib/sata/test: - crc_tb - scrambler_tb - phy_datapath_tb @@ -45,14 +72,14 @@ - command_tb - bist_tb hdd.py is a HDD model implementing all SATA layers. - To run a simulation, move to the simulation directory and run: + To run a simulation, move to ./lib/sata/test and run: make simulation_name [> Tests : - A synthetisable BIST is provided. It can be controled with ./test/bist.py - Using Miscope and the provided example ./test/test_link.py you are able to - visualize every event of the design and even inject your data in the HDD - model! + A synthetisable BIST is provided and can be controlled with ./test/bist.py + By using Miscope and the provided ./test/test_link.py example you are able to + visualize the internal logic of the design and even inject the captured data in + the HDD model! [> Contact E-mail: florent@enjoy-digital.fr diff --git a/lib/sata/__init__.py b/lib/sata/__init__.py deleted file mode 100644 index 0a167bdc..00000000 --- a/lib/sata/__init__.py +++ /dev/null @@ -1,21 +0,0 @@ -from lib.sata.common import * -from lib.sata.link import SATALink -from lib.sata.transport import SATATransport -from lib.sata.command import SATACommand - -from lib.sata.frontend.crossbar import SATACrossbar - -class SATACON(Module): - def __init__(self, phy): - ### - # core - self.link = SATALink(phy) - self.transport = SATATransport(self.link) - self.command = SATACommand(self.transport) - - # frontend - self.crossbar = SATACrossbar(32) - self.comb += [ - Record.connect(self.crossbar.master.source, self.command.sink), - Record.connect(self.command.source, self.crossbar.master.sink) - ] diff --git a/lib/sata/bist.py b/lib/sata/bist.py deleted file mode 100644 index 4dfba893..00000000 --- a/lib/sata/bist.py +++ /dev/null @@ -1,245 +0,0 @@ -from lib.sata.common import * -from lib.sata.link.scrambler import Scrambler - -from migen.fhdl.decorators import ModuleDecorator -from migen.bank.description import * - -class SATABISTGenerator(Module): - def __init__(self, sata_master_port): - self.start = Signal() - self.sector = Signal(48) - self.count = Signal(16) - self.random = Signal() - - self.done = Signal() - self.errors = Signal(32) # Note: Not used for writes - - ### - - source, sink = sata_master_port.source, sata_master_port.sink - - self.counter = counter = Counter(bits_sign=32) - - self.scrambler = scrambler = InsertReset(Scrambler()) - self.comb += [ - scrambler.reset.eq(counter.reset), - scrambler.ce.eq(counter.ce) - ] - - self.fsm = fsm = FSM(reset_state="IDLE") - fsm.act("IDLE", - self.done.eq(1), - counter.reset.eq(1), - If(self.start, - NextState("SEND_CMD_AND_DATA") - ) - ) - self.comb += [ - source.sop.eq(counter.value == 0), - source.eop.eq(counter.value == (logical_sector_size//4*self.count)-1), - source.write.eq(1), - source.sector.eq(self.sector), - source.count.eq(self.count), - If(self.random, - source.data.eq(scrambler.value) - ).Else( - source.data.eq(counter.value) - ) - ] - fsm.act("SEND_CMD_AND_DATA", - source.stb.eq(1), - If(source.stb & source.ack, - counter.ce.eq(1), - If(source.eop, - NextState("WAIT_ACK") - ) - ) - ) - fsm.act("WAIT_ACK", - sink.ack.eq(1), - If(sink.stb, - NextState("IDLE") - ) - ) - -class SATABISTChecker(Module): - def __init__(self, sata_master_port): - self.start = Signal() - self.sector = Signal(48) - self.count = Signal(16) - self.random = Signal() - - self.done = Signal() - self.errors = Signal(32) - - ### - - source, sink = sata_master_port.source, sata_master_port.sink - - self.counter = counter = Counter(bits_sign=32) - self.error_counter = Counter(self.errors, bits_sign=32) - - self.scrambler = scrambler = InsertReset(Scrambler()) - self.comb += [ - scrambler.reset.eq(counter.reset), - scrambler.ce.eq(counter.ce) - ] - - self.fsm = fsm = FSM(reset_state="IDLE") - fsm.act("IDLE", - self.done.eq(1), - counter.reset.eq(1), - If(self.start, - self.error_counter.reset.eq(1), - NextState("SEND_CMD") - ) - ) - self.comb += [ - source.sop.eq(1), - source.eop.eq(1), - source.read.eq(1), - source.sector.eq(self.sector), - source.count.eq(self.count), - ] - fsm.act("SEND_CMD", - source.stb.eq(1), - If(source.ack, - counter.reset.eq(1), - NextState("WAIT_ACK") - ) - ) - fsm.act("WAIT_ACK", - If(sink.stb & sink.read, - NextState("RECEIVE_DATA") - ) - ) - expected_data = Signal(32) - self.comb += \ - If(self.random, - expected_data.eq(scrambler.value) - ).Else( - expected_data.eq(counter.value) - ) - fsm.act("RECEIVE_DATA", - sink.ack.eq(1), - If(sink.stb, - counter.ce.eq(1), - If(sink.data != expected_data, - self.error_counter.ce.eq(1) - ), - If(sink.eop, - If(sink.last, - NextState("IDLE") - ).Else( - NextState("WAIT_ACK") - ) - ) - ) - ) - -class SATABISTUnitControl(Module, AutoCSR): - def __init__(self, bist_unit): - self._start = CSR() - self._sector = CSRStorage(48) - self._count = CSRStorage(16) - self._random = CSRStorage() - self._done = CSRStatus() - self._errors = CSRStatus(32) - self._cycles = CSRStatus(32) - - ### - self.bist_unit = bist_unit - self.comb += [ - bist_unit.start.eq(self._start.r & self._start.re), - bist_unit.sector.eq(self._sector.storage), - bist_unit.count.eq(self._count.storage), - bist_unit.random.eq(self._random.storage), - - self._done.status.eq(bist_unit.done), - self._errors.status.eq(bist_unit.errors) - ] - - self.cycles_counter = Counter(self._cycles.status) - self.sync += [ - self.cycles_counter.reset.eq(bist_unit.start), - self.cycles_counter.ce.eq(~bist_unit.done) - ] - -class SATABISTIdentify(Module): - def __init__(self, sata_master_port): - self.start = Signal() - self.done = Signal() - - self.fifo = fifo = SyncFIFO([("data", 32)], 512, buffered=True) - self.source = self.fifo.source - - ### - - source, sink = sata_master_port.source, sata_master_port.sink - - self.fsm = fsm = FSM(reset_state="IDLE") - fsm.act("IDLE", - self.done.eq(1), - If(self.start, - NextState("SEND_CMD") - ) - ) - self.comb += [ - source.sop.eq(1), - source.eop.eq(1), - source.identify.eq(1), - ] - fsm.act("SEND_CMD", - source.stb.eq(1), - If(source.stb & source.ack, - NextState("WAIT_ACK") - ) - ) - fsm.act("WAIT_ACK", - If(sink.stb & sink.identify, - NextState("RECEIVE_DATA") - ) - ) - self.comb += fifo.sink.data.eq(sink.data) - fsm.act("RECEIVE_DATA", - sink.ack.eq(fifo.sink.ack), - If(sink.stb, - fifo.sink.stb.eq(1), - If(sink.eop, - NextState("IDLE") - ) - ) - ) - -class SATABISTIdentifyControl(Module, AutoCSR): - def __init__(self, bist_identify): - self._start = CSR() - self._done = CSRStatus() - self._source_stb = CSRStatus() - self._source_ack = CSR() - self._source_data = CSRStatus(32) - - ### - self.bist_identify = bist_identify - self.comb += [ - bist_identify.start.eq(self._start.r & self._start.re), - self._done.status.eq(bist_identify.done), - - self._source_stb.status.eq(bist_identify.source.stb), - self._source_data.status.eq(bist_identify.source.data), - bist_identify.source.ack.eq(self._source_ack.r & self._source_ack.re) - ] - -class SATABIST(Module, AutoCSR): - def __init__(self, sata_master_ports, with_control=False): - generator = SATABISTGenerator(sata_master_ports[0]) - checker = SATABISTChecker(sata_master_ports[1]) - identify = SATABISTIdentify(sata_master_ports[2]) - if with_control: - self.generator = SATABISTUnitControl(generator) - self.checker = SATABISTUnitControl(checker) - self.identify = SATABISTIdentifyControl(identify) - else: - self.generator = generator - self.checker = checker - self.identify = identify diff --git a/lib/sata/command/__init__.py b/lib/sata/command/__init__.py deleted file mode 100644 index 810d235f..00000000 --- a/lib/sata/command/__init__.py +++ /dev/null @@ -1,314 +0,0 @@ -from lib.sata.common import * - -tx_to_rx = [ - ("write", 1), - ("read", 1), - ("identify", 1), - ("count", 16) -] - -rx_to_tx = [ - ("dma_activate", 1) -] - -class SATACommandTX(Module): - def __init__(self, transport): - 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), - transport.sink.data.eq(sink.data) - ] - - self.dwords_counter = dwords_counter = Counter(max=fis_max_dwords) - - self.fsm = fsm = FSM(reset_state="IDLE") - fsm.act("IDLE", - sink.ack.eq(0), - 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", - dwords_counter.reset.eq(1), - If(from_rx.dma_activate, - NextState("SEND_DATA") - ) - ) - fsm.act("SEND_DATA", - dwords_counter.ce.eq(sink.stb & sink.ack), - - transport.sink.stb.eq(sink.stb), - transport.sink.sop.eq(dwords_counter.value == 0), - transport.sink.eop.eq((dwords_counter.value == (fis_max_dwords-1)) | sink.eop), - - transport.sink.type.eq(fis_types["DATA"]), - sink.ack.eq(transport.sink.ack), - If(sink.stb & sink.ack, - If(sink.eop, - NextState("IDLE") - ).Elif(dwords_counter.value == (fis_max_dwords-1), - NextState("WAIT_DMA_ACTIVATE") - ) - ) - ) - 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"]), - 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): - 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_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 - - def test_type(name): - return transport.source.type == fis_types[name] - - identify = Signal() - dma_activate = Signal() - 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) - - self.fsm = fsm = FSM(reset_state="IDLE") - fsm.act("IDLE", - self.dwords_counter.reset.eq(1), - transport.source.ack.eq(1), - If(from_tx.write, - NextState("WAIT_WRITE_ACTIVATE_OR_REG_D2H") - ).Elif(from_tx.read, - NextState("WAIT_READ_DATA_OR_REG_D2H"), - ).Elif(from_tx.identify, - NextState("WAIT_PIO_SETUP_D2H"), - ) - ) - self.sync += \ - If(fsm.ongoing("IDLE"), - identify.eq(from_tx.identify) - ) - fsm.act("WAIT_WRITE_ACTIVATE_OR_REG_D2H", - transport.source.ack.eq(1), - If(transport.source.stb, - If(test_type("DMA_ACTIVATE_D2H"), - dma_activate.eq(1), - ).Elif(test_type("REG_D2H"), - # XXX: use status and error fields of REG_D2H - NextState("PRESENT_WRITE_RESPONSE") - ) - ) - ) - fsm.act("PRESENT_WRITE_RESPONSE", - 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, - NextState("IDLE") - ) - ) - fsm.act("WAIT_READ_DATA_OR_REG_D2H", - transport.source.ack.eq(1), - If(transport.source.stb, - transport.source.ack.eq(0), - If(test_type("DATA"), - NextState("PRESENT_READ_DATA") - ).Elif(test_type("REG_D2H"), - # XXX: use status and error fields of REG_D2H - NextState("PRESENT_READ_RESPONSE") - ) - ) - ) - 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") - ) - ) - - self.comb += [ - data_buffer.sink.sop.eq(transport.source.sop), - data_buffer.sink.eop.eq(transport.source.eop), - data_buffer.sink.data.eq(transport.source.data) - ] - fsm.act("PRESENT_READ_DATA", - data_buffer.sink.stb.eq(transport.source.stb), - transport.source.ack.eq(data_buffer.sink.ack), - If(data_buffer.sink.stb & data_buffer.sink.ack, - self.dwords_counter.ce.eq(~read_done), - If(data_buffer.sink.eop, - If(read_done & ~identify, - NextState("WAIT_READ_DATA_OR_REG_D2H") - ).Else( - NextState("PRESENT_READ_RESPONSE") - ) - ) - ) - ) - read_error = Signal() - self.sync += \ - If(fsm.before_entering("PRESENT_READ_DATA"), - read_error.eq(1) - ).Elif(transport.source.stb & transport.source.ack & transport.source.eop, - read_error.eq(transport.source.error) - ) - fsm.act("PRESENT_READ_RESPONSE", - cmd_buffer.sink.stb.eq(1), - cmd_buffer.sink.read.eq(~identify), - cmd_buffer.sink.identify.eq(identify), - cmd_buffer.sink.last.eq(read_done | identify), - 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) - ), - If(read_done | identify, - NextState("IDLE") - ).Else( - NextState("WAIT_READ_DATA_OR_REG_D2H") - ) - ) - ) - - self.out_fsm = out_fsm = FSM(reset_state="IDLE") - out_fsm.act("IDLE", - If(cmd_buffer.source.stb, - If((cmd_buffer.source.read | cmd_buffer.source.identify) & cmd_buffer.source.success, - NextState("PRESENT_RESPONSE_WITH_DATA"), - ).Else( - NextState("PRESENT_RESPONSE_WITHOUT_DATA"), - ) - ) - ) - - self.comb += [ - source.write.eq(cmd_buffer.source.write), - source.read.eq(cmd_buffer.source.read), - source.identify.eq(cmd_buffer.source.identify), - source.last.eq(cmd_buffer.source.last), - source.success.eq(cmd_buffer.source.success), - source.failed.eq(cmd_buffer.source.failed), - source.data.eq(data_buffer.source.data) - ] - - out_fsm.act("PRESENT_RESPONSE_WITH_DATA", - source.stb.eq(data_buffer.source.stb), - source.sop.eq(data_buffer.source.sop), - source.eop.eq(data_buffer.source.eop), - - data_buffer.source.ack.eq(source.ack), - - If(source.stb & source.eop & source.ack, - cmd_buffer.source.ack.eq(1), - NextState("IDLE") - ) - ) - out_fsm.act("PRESENT_RESPONSE_WITHOUT_DATA", - source.stb.eq(1), - source.sop.eq(1), - source.eop.eq(1), - If(source.stb & source.ack, - cmd_buffer.source.ack.eq(1), - NextState("IDLE") - ) - ) - - self.comb += [ - to_tx.dma_activate.eq(dma_activate), - ] - -class SATACommand(Module): - def __init__(self, transport): - self.tx = SATACommandTX(transport) - self.rx = SATACommandRX(transport) - 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 diff --git a/lib/sata/common.py b/lib/sata/common.py deleted file mode 100644 index bce3b572..00000000 --- a/lib/sata/common.py +++ /dev/null @@ -1,284 +0,0 @@ -import math - -from migen.fhdl.std import * -from migen.genlib.resetsync import * -from migen.genlib.fsm import * -from migen.genlib.record import * -from migen.flow.actor import * -from migen.flow.plumbing import Buffer -from migen.actorlib.fifo import * -from migen.actorlib.structuring import Pipeline - -# PHY / Link Layers -primitives = { - "ALIGN" : 0x7B4A4ABC, - "CONT" : 0X9999AA7C, - "SYNC" : 0xB5B5957C, - "R_RDY" : 0x4A4A957C, - "R_OK" : 0x3535B57C, - "R_ERR" : 0x5656B57C, - "R_IP" : 0X5555B57C, - "X_RDY" : 0x5757B57C, - "CONT" : 0x9999AA7C, - "WTRM" : 0x5858B57C, - "SOF" : 0x3737B57C, - "EOF" : 0xD5D5B57C, - "HOLD" : 0xD5D5AA7C, - "HOLDA" : 0X9595AA7C -} - -def is_primitive(dword): - for k, v in primitives.items(): - if dword == v: - return True - return False - -def decode_primitive(dword): - for k, v in primitives.items(): - if dword == v: - return k - return "" - -def phy_description(dw): - layout = [ - ("data", dw), - ("charisk", dw//8), - ] - return EndpointDescription(layout, packetized=False) - -def link_description(dw): - layout = [ - ("d", dw), - ("error", 1) - ] - return EndpointDescription(layout, packetized=True) - -# Transport Layer -fis_max_dwords = 2048 - -fis_types = { - "REG_H2D": 0x27, - "REG_D2H": 0x34, - "DMA_ACTIVATE_D2H": 0x39, - "PIO_SETUP_D2H": 0x5F, - "DATA": 0x46 -} - -class FISField(): - def __init__(self, dword, offset, width): - self.dword = dword - self.offset = offset - self.width = width - -fis_reg_h2d_cmd_len = 5 -fis_reg_h2d_layout = { - "type": FISField(0, 0, 8), - "pm_port": FISField(0, 8, 4), - "c": FISField(0, 15, 1), - "command": FISField(0, 16, 8), - "features_lsb": FISField(0, 24, 8), - - "lba_lsb": FISField(1, 0, 24), - "device": FISField(1, 24, 8), - - "lba_msb": FISField(2, 0, 24), - "features_msb": FISField(2, 24, 8), - - "count": FISField(3, 0, 16), - "icc": FISField(3, 16, 8), - "control": FISField(3, 24, 8) -} - -fis_reg_d2h_cmd_len = 5 -fis_reg_d2h_layout = { - "type": FISField(0, 0, 8), - "pm_port": FISField(0, 8, 4), - "i": FISField(0, 14, 1), - "status": FISField(0, 16, 8), - "error": FISField(0, 24, 8), - - "lba_lsb": FISField(1, 0, 24), - "device": FISField(1, 24, 8), - - "lba_msb": FISField(2, 0, 24), - - "count": FISField(3, 0, 16) -} - -fis_dma_activate_d2h_cmd_len = 1 -fis_dma_activate_d2h_layout = { - "type": FISField(0, 0, 8), - "pm_port": FISField(0, 8, 4) -} - -fis_pio_setup_d2h_cmd_len = 5 -fis_pio_setup_d2h_layout = { - "type": FISField(0, 0, 8), - "pm_port": FISField(0, 8, 4), - "d": FISField(0, 13, 1), - "i": FISField(0, 14, 1), - "status": FISField(0, 16, 8), - "error": FISField(0, 24, 8), - - "lba_lsb": FISField(1, 0, 24), - - "lba_msb": FISField(2, 0, 24), - - "count": FISField(3, 0, 16), - - "transfer_count": FISField(4, 0, 16), -} - -fis_data_cmd_len = 1 -fis_data_layout = { - "type": FISField(0, 0, 8) -} - -def transport_tx_description(dw): - layout = [ - ("type", 8), - ("pm_port", 4), - ("c", 1), - ("command", 8), - ("features", 16), - ("lba", 48), - ("device", 8), - ("count", 16), - ("icc", 8), - ("control", 8), - ("data", dw) - ] - return EndpointDescription(layout, packetized=True) - -def transport_rx_description(dw): - layout = [ - ("type", 8), - ("pm_port", 4), - ("r", 1), - ("d", 1), - ("i", 1), - ("status", 8), - ("error", 8), - ("lba", 48), - ("device", 8), - ("count", 16), - ("transfer_count", 16), - ("data", dw), - ("error", 1) - ] - return EndpointDescription(layout, packetized=True) - -# Command Layer -regs = { - "WRITE_DMA_EXT" : 0x35, - "READ_DMA_EXT" : 0x25, - "IDENTIFY_DEVICE" : 0xEC -} - -def command_tx_description(dw): - layout = [ - ("write", 1), - ("read", 1), - ("identify", 1), - ("sector", 48), - ("count", 16), - ("data", dw) - ] - return EndpointDescription(layout, packetized=True) - -def command_rx_description(dw): - layout = [ - ("write", 1), - ("read", 1), - ("identify", 1), - ("last", 1), - ("success", 1), - ("failed", 1), - ("data", dw) - ] - return EndpointDescription(layout, packetized=True) - -def command_rx_cmd_description(dw): - layout = [ - ("write", 1), - ("read", 1), - ("identify", 1), - ("last", 1), - ("success", 1), - ("failed", 1) - ] - return EndpointDescription(layout, packetized=False) - -def command_rx_data_description(dw): - layout = [ - ("data", dw) - ] - return EndpointDescription(layout, packetized=True) - -# HDD -logical_sector_size = 512 # constant since all HDDs use this - -def dwords2sectors(n): - return math.ceil(n*4/logical_sector_size) - -def sectors2dwords(n): - return n*logical_sector_size//4 - -# Generic modules -@DecorateModule(InsertReset) -@DecorateModule(InsertCE) -class Counter(Module): - def __init__(self, signal=None, **kwargs): - if signal is None: - self.value = Signal(**kwargs) - else: - self.value = signal - self.width = flen(self.value) - self.sync += self.value.eq(self.value+1) - -@DecorateModule(InsertReset) -@DecorateModule(InsertCE) -class Timeout(Module): - def __init__(self, length): - self.reached = Signal() - ### - value = Signal(max=length) - self.sync += value.eq(value+1) - self.comb += [ - self.reached.eq(value == length) - ] - -# XXX use ModuleDecorator -class BufferizeEndpoints(Module): - def __init__(self, decorated, *args): - self.decorated = decorated - - endpoints = get_endpoints(decorated) - sinks = {} - sources = {} - for name, endpoint in endpoints.items(): - if name in args or len(args) == 0: - if isinstance(endpoint, Sink): - sinks.update({name : endpoint}) - elif isinstance(endpoint, Source): - sources.update({name : endpoint}) - - # add buffer on sinks - for name, sink in sinks.items(): - buf = Buffer(sink.description) - self.submodules += buf - setattr(self, name, buf.d) - self.comb += Record.connect(buf.q, sink) - - # add buffer on sources - for name, source in sources.items(): - buf = Buffer(source.description) - self.submodules += buf - self.comb += Record.connect(source, buf.d) - setattr(self, name, buf.q) - - def __getattr__(self, name): - return getattr(self.decorated, name) - - def __dir__(self): - return dir(self.decorated) diff --git a/lib/sata/frontend/arbiter.py b/lib/sata/frontend/arbiter.py deleted file mode 100644 index 678f2d7a..00000000 --- a/lib/sata/frontend/arbiter.py +++ /dev/null @@ -1,31 +0,0 @@ -from lib.sata.common import * -from lib.sata.frontend.common import * - -from migen.genlib.roundrobin import * - -class SATAArbiter(Module): - def __init__(self, slaves, master): - if len(slaves) == 1: - self.comb += slaves[0].connect(master) - else: - self.rr = RoundRobin(len(slaves)) - self.grant = self.rr.grant - cases = {} - for i, slave in enumerate(slaves): - sink, source = slave.sink, slave.source - start = Signal() - done = Signal() - ongoing = Signal() - self.comb += [ - start.eq(sink.stb & sink.sop), - done.eq(source.stb & source.last & source.eop & source.ack) - ] - self.sync += \ - If(start, - ongoing.eq(1) - ).Elif(done, - ongoing.eq(0) - ) - self.comb += self.rr.request[i].eq((start | ongoing) & ~done) - cases[i] = [slaves[i].connect(master)] - self.comb += Case(self.grant, cases) diff --git a/lib/sata/frontend/common.py b/lib/sata/frontend/common.py deleted file mode 100644 index 114a0a83..00000000 --- a/lib/sata/frontend/common.py +++ /dev/null @@ -1,23 +0,0 @@ -from lib.sata.common import * - -class SATAMasterPort: - def __init__(self, dw): - self.source = Source(command_tx_description(dw)) - self.sink = Sink(command_rx_description(dw)) - - def connect(self, slave): - return [ - Record.connect(self.source, slave.sink), - Record.connect(slave.source, self.sink) - ] - -class SATASlavePort: - def __init__(self, dw): - self.sink = Sink(command_tx_description(dw)) - self.source = Source(command_rx_description(dw)) - - def connect(self, master): - return [ - Record.connect(self.sink, master.source), - Record.connect(master.sink, self.source) - ] diff --git a/lib/sata/frontend/crossbar.py b/lib/sata/frontend/crossbar.py deleted file mode 100644 index 41ac806e..00000000 --- a/lib/sata/frontend/crossbar.py +++ /dev/null @@ -1,25 +0,0 @@ -from lib.sata.common import * -from lib.sata.frontend.common import * -from lib.sata.frontend.arbiter import SATAArbiter - -class SATACrossbar(Module): - def __init__(self, dw): - self.dw = dw - self.slaves = [] - self.master = SATAMasterPort(dw) - - def get_port(self): - master = SATAMasterPort(self.dw) - slave = SATASlavePort(self.dw) - self.comb += master.connect(slave) - self.slaves.append(slave) - return master - - def get_ports(self, n): - masters = [] - for i in range(n): - masters.append(self.get_port()) - return masters - - def do_finalize(self): - self.arbiter = SATAArbiter(self.slaves, self.master) diff --git a/lib/sata/link/__init__.py b/lib/sata/link/__init__.py deleted file mode 100644 index b142e68c..00000000 --- a/lib/sata/link/__init__.py +++ /dev/null @@ -1,244 +0,0 @@ -from lib.sata.common import * -from lib.sata.link.crc import SATACRCInserter, SATACRCChecker -from lib.sata.link.scrambler import SATAScrambler -from lib.sata.link.cont import SATACONTInserter, SATACONTRemover - -from_rx = [ - ("idle", 1), - ("insert", 32), - ("det", 32) -] - -class SATALinkTX(Module): - def __init__(self, phy): - self.sink = Sink(link_description(32)) - self.from_rx = Sink(from_rx) - - ### - - self.fsm = fsm = FSM(reset_state="IDLE") - - # insert CRC - crc = SATACRCInserter(link_description(32)) - self.submodules += crc - - # scramble - scrambler = SATAScrambler(link_description(32)) - self.submodules += scrambler - - # connect CRC / scrambler - self.comb += [ - Record.connect(self.sink, crc.sink), - Record.connect(crc.source, scrambler.sink) - ] - - # inserter CONT and scrambled data between - # CONT and next primitive - self.cont = cont = BufferizeEndpoints(SATACONTInserter(phy_description(32)), "source") - - # datas / primitives mux - insert = Signal(32) - self.comb += [ - If(self.from_rx.insert, - cont.sink.stb.eq(1), - cont.sink.data.eq(self.from_rx.insert), - cont.sink.charisk.eq(0x0001), - ). - Elif(insert, - cont.sink.stb.eq(1), - cont.sink.data.eq(insert), - cont.sink.charisk.eq(0x0001), - ).Elif(fsm.ongoing("COPY"), - cont.sink.stb.eq(scrambler.source.stb), - cont.sink.data.eq(scrambler.source.d), - scrambler.source.ack.eq(cont.sink.ack), - cont.sink.charisk.eq(0) - ) - ] - self.comb += Record.connect(cont.source, phy.sink) - - # FSM - fsm.act("IDLE", - scrambler.reset.eq(1), - If(self.from_rx.idle, - insert.eq(primitives["SYNC"]), - If(scrambler.source.stb & scrambler.source.sop, - If(self.from_rx.det == primitives["SYNC"], - NextState("RDY") - ) - ) - ) - ) - fsm.act("RDY", - insert.eq(primitives["X_RDY"]), - If(~self.from_rx.idle, - NextState("IDLE") - ).Elif(self.from_rx.det == primitives["R_RDY"], - NextState("SOF") - ) - ) - fsm.act("SOF", - insert.eq(primitives["SOF"]), - If(phy.sink.ack, - NextState("COPY") - ) - ) - fsm.act("COPY", - If(self.from_rx.det == primitives["HOLD"], - insert.eq(primitives["HOLDA"]), - ).Elif(~scrambler.source.stb, - insert.eq(primitives["HOLD"]), - ).Elif(scrambler.source.stb & scrambler.source.eop & scrambler.source.ack, - NextState("EOF") - ) - ) - fsm.act("EOF", - insert.eq(primitives["EOF"]), - If(phy.sink.ack, - NextState("WTRM") - ) - ) - fsm.act("WTRM", - insert.eq(primitives["WTRM"]), - If(self.from_rx.det == primitives["R_OK"], - NextState("IDLE") - ).Elif(self.from_rx.det == primitives["R_ERR"], - NextState("IDLE") - ) - ) - -class SATALinkRX(Module): - def __init__(self, phy): - self.source = Source(link_description(32)) - self.to_tx = Source(from_rx) - - ### - - self.fsm = fsm = FSM(reset_state="IDLE") - - # CONT remover - self.cont = cont = BufferizeEndpoints(SATACONTRemover(phy_description(32)), "source") - self.comb += Record.connect(phy.source, cont.sink) - - # datas / primitives detection - insert = Signal(32) - det = Signal(32) - self.comb += \ - If(cont.source.stb & (cont.source.charisk == 0b0001), - det.eq(cont.source.data) - ) - - # descrambler - self.scrambler = scrambler = SATAScrambler(link_description(32)) - - # check CRC - self.crc = crc = SATACRCChecker(link_description(32)) - - sop = Signal() - eop = Signal() - self.sync += \ - If(fsm.ongoing("IDLE"), - sop.eq(1), - ).Elif(fsm.ongoing("COPY"), - If(scrambler.sink.stb & scrambler.sink.ack, - sop.eq(0) - ) - ) - self.comb += eop.eq(det == primitives["EOF"]) - - crc_error = Signal() - self.sync += \ - If(crc.source.stb & crc.source.eop & crc.source.ack, - crc_error.eq(crc.source.error) - ) - - # small fifo to manage HOLD - self.fifo = SyncFIFO(link_description(32), 32) - - # graph - self.comb += [ - cont.source.ack.eq(1), - Record.connect(scrambler.source, crc.sink), - Record.connect(crc.source, self.fifo.sink), - Record.connect(self.fifo.source, self.source) - ] - cont_source_data_d = Signal(32) - self.sync += \ - If(cont.source.stb & (det == 0), - scrambler.sink.d.eq(cont.source.data) - ) - - # FSM - fsm.act("IDLE", - scrambler.reset.eq(1), - If(det == primitives["X_RDY"], - NextState("RDY") - ) - ) - fsm.act("RDY", - insert.eq(primitives["R_RDY"]), - If(det == primitives["SOF"], - NextState("WAIT_FIRST") - ) - ) - fsm.act("WAIT_FIRST", - insert.eq(primitives["R_IP"]), - If(cont.source.stb & (det == 0), - NextState("COPY") - ) - ) - self.comb += [ - scrambler.sink.sop.eq(sop), - scrambler.sink.eop.eq(eop) - ] - fsm.act("COPY", - scrambler.sink.stb.eq(cont.source.stb & ((det == 0) | eop)), - insert.eq(primitives["R_IP"]), - If(det == primitives["HOLD"], - insert.eq(primitives["HOLDA"]) - ).Elif(det == primitives["EOF"], - NextState("WTRM") - ).Elif(self.fifo.fifo.level > 8, - insert.eq(primitives["HOLD"]) - ) - ) - fsm.act("EOF", - insert.eq(primitives["R_IP"]), - If(det == primitives["WTRM"], - NextState("WTRM") - ) - ) - fsm.act("WTRM", - insert.eq(primitives["R_IP"]), - If(~crc_error, - NextState("R_OK") - ).Else( - NextState("R_ERR") - ) - ) - fsm.act("R_OK", - insert.eq(primitives["R_OK"]), - If(det == primitives["SYNC"], - NextState("IDLE") - ) - ) - fsm.act("R_ERR", - insert.eq(primitives["R_ERR"]), - If(det == primitives["SYNC"], - NextState("IDLE") - ) - ) - - # to TX - self.comb += [ - self.to_tx.idle.eq(fsm.ongoing("IDLE")), - self.to_tx.insert.eq(insert), - self.to_tx.det.eq(det) - ] - -class SATALink(Module): - def __init__(self, phy): - self.tx = SATALinkTX(phy) - self.rx = SATALinkRX(phy) - self.comb += Record.connect(self.rx.to_tx, self.tx.from_rx) - self.sink, self.source = self.tx.sink, self.rx.source diff --git a/lib/sata/link/cont.py b/lib/sata/link/cont.py deleted file mode 100644 index ece5896c..00000000 --- a/lib/sata/link/cont.py +++ /dev/null @@ -1,116 +0,0 @@ -from lib.sata.common import * -from lib.sata.link.scrambler import Scrambler - -from migen.genlib.misc import optree - -class SATACONTInserter(Module): - def __init__(self, description): - self.sink = sink = Sink(description) - self.source = source = Source(description) - - ### - - self.counter = counter = Counter(max=4) - - is_data = Signal() - was_data = Signal() - was_hold = Signal() - change = Signal() - self.comb += is_data.eq(sink.charisk == 0) - - last_data = Signal(32) - last_primitive = Signal(32) - last_charisk = Signal(4) - self.sync += [ - If(sink.stb & source.ack, - last_data.eq(sink.data), - last_charisk.eq(sink.charisk), - If(~is_data, - last_primitive.eq(sink.data), - ), - was_data.eq(is_data), - was_hold.eq(last_primitive == primitives["HOLD"]) - ) - ] - self.comb += change.eq( - (sink.data != last_data) | - (sink.charisk != last_charisk) | - is_data - ) - - # scrambler - self.scrambler = scrambler = InsertReset(Scrambler()) - - # Datapath - self.comb += [ - Record.connect(sink, source), - If(sink.stb, - If(~change, - counter.ce.eq(sink.ack & (counter.value !=2)), - # insert CONT - If(counter.value == 1, - source.charisk.eq(0b0001), - source.data.eq(primitives["CONT"]) - # insert scrambled data for EMI - ).Elif(counter.value == 2, - scrambler.ce.eq(sink.ack), - source.charisk.eq(0b0000), - source.data.eq(scrambler.value) - ) - ).Else( - counter.reset.eq(source.ack), - If(counter.value == 2, - # Reinsert last primitive - If(is_data | (~is_data & was_hold), - source.stb.eq(1), - sink.ack.eq(0), - source.charisk.eq(0b0001), - source.data.eq(last_primitive) - ) - ) - ) - ) - ] - -class SATACONTRemover(Module): - def __init__(self, description): - self.sink = sink = Sink(description) - self.source = source = Source(description) - - ### - - is_data = Signal() - is_cont = Signal() - in_cont = Signal() - cont_ongoing = Signal() - - self.comb += [ - is_data.eq(sink.charisk == 0), - is_cont.eq(~is_data & (sink.data == primitives["CONT"])) - ] - self.sync += \ - If(sink.stb & sink.ack, - If(is_cont, - in_cont.eq(1) - ).Elif(~is_data, - in_cont.eq(0) - ) - ) - self.comb += cont_ongoing.eq(is_cont | (in_cont & is_data)) - - # Datapath - last_primitive = Signal(32) - self.sync += [ - If(sink.stb & sink.ack, - If(~is_data & ~is_cont, - last_primitive.eq(sink.data) - ) - ) - ] - self.comb += [ - Record.connect(sink, source), - If(cont_ongoing, - source.charisk.eq(0b0001), - source.data.eq(last_primitive) - ) - ] diff --git a/lib/sata/link/crc.py b/lib/sata/link/crc.py deleted file mode 100644 index 4d6c67b6..00000000 --- a/lib/sata/link/crc.py +++ /dev/null @@ -1,115 +0,0 @@ -from lib.sata.common import * - -from migen.genlib.misc import optree -from migen.actorlib.crc import CRCInserter, CRCChecker - -class CRCEngine(Module): - """Cyclic Redundancy Check Engine - - Compute next CRC value from last CRC value and data input using - an optimized asynchronous LFSR. - - Parameters - ---------- - width : int - Width of the data bus and CRC. - polynom : int - Polynom of the CRC (ex: 0x04C11DB7 for IEEE 802.3 CRC) - - Attributes - ---------- - d : in - Data input. - last : in - last CRC value. - next : - next CRC value. - """ - def __init__(self, width, polynom): - self.d = Signal(width) - self.last = Signal(width) - self.next = Signal(width) - - ### - - def _optimize_eq(l): - """ - Replace even numbers of XORs in the equation - with an equivalent XOR - """ - d = {} - for e in l: - if e in d: - d[e] += 1 - else: - d[e] = 1 - r = [] - for key, value in d.items(): - if value%2 != 0: - r.append(key) - return r - - new = Signal(32) - self.comb += new.eq(self.last ^ self.d) - - # compute and optimize CRC's LFSR - curval = [[("new", i)] for i in range(width)] - for i in range(width): - feedback = curval.pop() - for j in range(width-1): - if (polynom & (1<<(j+1))): - curval[j] += feedback - curval[j] = _optimize_eq(curval[j]) - curval.insert(0, feedback) - - # implement logic - for i in range(width): - xors = [] - for t, n in curval[i]: - if t == "new": - xors += [new[n]] - self.comb += self.next[i].eq(optree("^", xors)) - -@DecorateModule(InsertReset) -@DecorateModule(InsertCE) -class SATACRC(Module): - """SATA CRC - - Implement a SATA CRC generator/checker - - Attributes - ---------- - value : out - CRC value (used for generator). - error : out - CRC error (used for checker). - """ - width = 32 - polynom = 0x04C11DB7 - init = 0x52325032 - check = 0x00000000 - def __init__(self, dw=32): - self.d = Signal(self.width) - self.value = Signal(self.width) - self.error = Signal() - - ### - - self.engine = CRCEngine(self.width, self.polynom) - reg_i = Signal(self.width, reset=self.init) - self.sync += reg_i.eq(self.engine.next) - self.comb += [ - self.engine.d.eq(self.d), - self.engine.last.eq(reg_i), - - self.value.eq(reg_i), - self.error.eq(self.engine.next != self.check) - ] - -class SATACRCInserter(CRCInserter): - def __init__(self, description): - CRCInserter.__init__(self, SATACRC, description) - -class SATACRCChecker(CRCChecker): - def __init__(self, description): - CRCChecker.__init__(self, SATACRC, description) diff --git a/lib/sata/link/scrambler.py b/lib/sata/link/scrambler.py deleted file mode 100644 index d2b473f8..00000000 --- a/lib/sata/link/scrambler.py +++ /dev/null @@ -1,83 +0,0 @@ -from lib.sata.common import * - -from migen.fhdl.std import * -from migen.genlib.misc import optree - -@DecorateModule(InsertCE) -class Scrambler(Module): - """SATA Scrambler - - Implement a SATA Scrambler - - Attributes - ---------- - value : out - Scrambled value. - """ - def __init__(self): - self.value = Signal(32) - - ### - - context = Signal(16, reset=0xf0f6) - next_value = Signal(32) - self.sync += context.eq(next_value[16:32]) - - # XXX: from SATA specification, replace it with - # a generic implementation using polynoms. - lfsr_coefs = ( - (15, 13, 4, 0), #0 - (15, 14, 13, 5, 4, 1, 0), - (14, 13, 6, 5, 4, 2,1, 0), - (15, 14, 7, 6, 5, 3,2, 1), - (13, 8, 7, 6, 3, 2, 0), - (14, 9, 8, 7, 4, 3, 1), - (15, 10, 9, 8, 5, 4, 2), - (15, 13, 11, 10, 9, 6, 5, 4, 3, 0), - (15, 14, 13, 12, 11, 10,7, 6, 5, 1, 0), - (14, 12, 11, 8, 7, 6, 4, 2, 1, 0), - (15, 13, 12, 9, 8, 7, 5, 3, 2, 1), - (15, 14, 10, 9, 8, 6, 3, 2, 0), - (13, 11, 10, 9, 7, 3, 1, 0), - (14, 12, 11, 10, 8, 4, 2, 1), - (15, 13, 12, 11, 9, 5, 3, 2), - (15, 14, 12, 10, 6, 3, 0), - - (11, 7, 1, 0), #16 - (12, 8, 2, 1), - (13, 9, 3, 2), - (14, 10, 4, 3), - (15, 11, 5, 4), - (15, 13, 12, 6, 5, 4, 0), - (15, 14, 7, 6, 5, 4, 1, 0), - (13, 8, 7, 6, 5, 4, 2, 1, 0), - (14, 9, 8,7, 6, 5, 3, 2, 1), - (15, 10, 9, 8, 7, 6, 4, 3, 2), - (15, 13, 11, 10, 9, 8, 7, 5, 3, 0), - (15, 14, 13, 12, 11, 10, 9, 8, 6, 1, 0), - (14, 12, 11, 10, 9, 7, 4, 2, 1, 0), - (15, 13, 12, 11, 10, 8, 5, 3, 2, 1), - (15, 14, 12, 11, 9, 6, 3, 2, 0), - (12, 10, 7, 3, 1, 0), - ) - - for n, coefs in enumerate(lfsr_coefs): - eq = [context[i] for i in coefs] - self.comb += next_value[n].eq(optree("^", eq)) - - self.comb += self.value.eq(next_value) - -@DecorateModule(InsertReset) -class SATAScrambler(Module): - def __init__(self, description): - self.sink = sink = Sink(description) - self.source = source = Source(description) - - ### - - self.scrambler = Scrambler() - self.comb += [ - self.scrambler.ce.eq(sink.stb & sink.ack), - Record.connect(sink, source), - source.d.eq(sink.d ^ self.scrambler.value) - ] diff --git a/lib/sata/phy/__init__.py b/lib/sata/phy/__init__.py deleted file mode 100644 index 925d7720..00000000 --- a/lib/sata/phy/__init__.py +++ /dev/null @@ -1,25 +0,0 @@ -from lib.sata.common import * -from lib.sata.phy.ctrl import SATAPHYHostCtrl -from lib.sata.phy.datapath import SATAPHYDatapath - -class SATAPHY(Module): - def __init__(self, pads, clk_freq, host=True, device_family="k7", speed="SATA1"): - self.speed = speed - # Transceiver / Clocks - if device_family == "k7": - from lib.sata.phy.k7.trx import K7SATAPHYTRX - from lib.sata.phy.k7.crg import K7SATAPHYCRG - self.trx = K7SATAPHYTRX(pads, speed) - self.crg = K7SATAPHYCRG(pads, self.trx, clk_freq, speed) - else: - raise NotImplementedError(device_family + "device family not implemented") - - # Control - if host: - self.ctrl = SATAPHYHostCtrl(self.trx, self.crg, clk_freq) - else: - self.ctrl = SATAPHYDeviceCtrl(self.trx, self.crg, clk_freq) - - # Datapath - self.datapath = SATAPHYDatapath(self.trx, self.ctrl) - self.sink, self.source = self.datapath.sink, self.datapath.source diff --git a/lib/sata/phy/ctrl.py b/lib/sata/phy/ctrl.py deleted file mode 100644 index c574d7ec..00000000 --- a/lib/sata/phy/ctrl.py +++ /dev/null @@ -1,165 +0,0 @@ -from math import ceil - -from lib.sata.common import * - -def us(t, clk_freq): - clk_period_us = 1000000/clk_freq - return ceil(t/clk_period_us) - -class SATAPHYHostCtrlTimeout(Module): - def __init__(self, load): - self.load = Signal() - self.dec = Signal() - self.reached = Signal() - - cnt = Signal(max=load+1) - self.sync += \ - If(self.load, - cnt.eq(load) - ).Elif(self.dec & ~self.reached, - cnt.eq(cnt-1) - ) - self.comb += self.reached.eq(cnt == 0) - -class SATAPHYHostCtrl(Module): - def __init__(self, trx, crg, clk_freq): - self.ready = Signal() - self.need_reset = Signal() - self.sink = sink = Sink(phy_description(32)) - self.source = source = Source(phy_description(32)) - - ### - self.comb += [ - source.stb.eq(1), - sink.ack.eq(1) - ] - - retry_timeout = SATAPHYHostCtrlTimeout(us(10000, clk_freq)) - align_timeout = SATAPHYHostCtrlTimeout(us(873, clk_freq)) - self.submodules += align_timeout, retry_timeout - - align_detect = Signal() - non_align_cnt = Signal(4) - - self.fsm = fsm = FSM(reset_state="RESET") - fsm.act("RESET", - trx.tx_idle.eq(1), - retry_timeout.load.eq(1), - align_timeout.load.eq(1), - If(crg.ready, - NextState("COMINIT") - ), - ) - fsm.act("COMINIT", - trx.tx_idle.eq(1), - trx.tx_cominit_stb.eq(1), - If(trx.tx_cominit_ack & ~trx.rx_cominit_stb, - NextState("AWAIT_COMINIT") - ), - ) - fsm.act("AWAIT_COMINIT", - trx.tx_idle.eq(1), - retry_timeout.dec.eq(1), - If(trx.rx_cominit_stb, - NextState("AWAIT_NO_COMINIT") - ).Else( - If(retry_timeout.reached, - NextState("RESET") - ) - ), - ) - fsm.act("AWAIT_NO_COMINIT", - trx.tx_idle.eq(1), - retry_timeout.load.eq(1), - If(~trx.rx_cominit_stb, - NextState("CALIBRATE") - ), - ) - fsm.act("CALIBRATE", - trx.tx_idle.eq(1), - NextState("COMWAKE"), - ) - fsm.act("COMWAKE", - trx.tx_idle.eq(1), - trx.tx_comwake_stb.eq(1), - If(trx.tx_comwake_ack, - NextState("AWAIT_COMWAKE") - ), - ) - fsm.act("AWAIT_COMWAKE", - trx.tx_idle.eq(1), - retry_timeout.dec.eq(1), - If(trx.rx_comwake_stb, - NextState("AWAIT_NO_COMWAKE") - ).Else( - If(retry_timeout.reached, - NextState("RESET") - ) - ), - ) - fsm.act("AWAIT_NO_COMWAKE", - trx.tx_idle.eq(1), - If(~trx.rx_comwake_stb, - NextState("AWAIT_NO_RX_IDLE") - ), - ) - fsm.act("AWAIT_NO_RX_IDLE", - trx.tx_idle.eq(0), - source.data.eq(0x4A4A4A4A), #D10.2 - source.charisk.eq(0b0000), - If(~trx.rx_idle, - NextState("AWAIT_ALIGN"), - crg.reset.eq(1), - trx.pmarxreset.eq(1) - ), - ) - fsm.act("AWAIT_ALIGN", - trx.tx_idle.eq(0), - source.data.eq(0x4A4A4A4A), #D10.2 - source.charisk.eq(0b0000), - trx.rx_align.eq(1), - align_timeout.dec.eq(1), - If(align_detect & ~trx.rx_idle, - NextState("SEND_ALIGN") - ).Elif(align_timeout.reached, - NextState("RESET") - ), - ) - fsm.act("SEND_ALIGN", - trx.tx_idle.eq(0), - trx.rx_align.eq(1), - source.data.eq(primitives["ALIGN"]), - source.charisk.eq(0b0001), - If(non_align_cnt == 3, - NextState("READY") - ), - ) - fsm.act("READY", - trx.tx_idle.eq(0), - trx.rx_align.eq(1), - source.data.eq(primitives["SYNC"]), - source.charisk.eq(0b0001), - If(trx.rx_idle, - NextState("RESET") - ), - self.ready.eq(1), - ) - - self.reset_timeout = Timeout(clk_freq//16) - self.comb += [ - self.reset_timeout.ce.eq(~self.ready), - self.need_reset.eq(self.reset_timeout.reached) - ] - - self.comb += \ - align_detect.eq(self.sink.stb & (self.sink.data == primitives["ALIGN"])) - self.sync += \ - If(fsm.ongoing("SEND_ALIGN"), - If(sink.stb, - If(sink.data[0:8] == 0x7C, - non_align_cnt.eq(non_align_cnt + 1) - ).Else( - non_align_cnt.eq(0) - ) - ) - ) diff --git a/lib/sata/phy/datapath.py b/lib/sata/phy/datapath.py deleted file mode 100644 index 8930955f..00000000 --- a/lib/sata/phy/datapath.py +++ /dev/null @@ -1,161 +0,0 @@ -from lib.sata.common import * - -from migen.genlib.misc import chooser -from migen.flow.plumbing import Multiplexer, Demultiplexer -from migen.actorlib.structuring import Converter - -class SATAPHYDatapathRX(Module): - def __init__(self): - self.sink = Sink(phy_description(16)) - self.source = Source(phy_description(32)) - - ### - - # width convertion (16 to 32) and byte alignment - byte_alignment = Signal() - last_charisk = Signal(2) - last_data = Signal(16) - self.sync.sata_rx += \ - If(self.sink.stb & self.sink.ack, - If(self.sink.charisk != 0, - byte_alignment.eq(self.sink.charisk[1]) - ), - last_charisk.eq(self.sink.charisk), - last_data.eq(self.sink.data) - ) - converter = Converter(phy_description(16), phy_description(32), reverse=False) - self.converter = InsertReset(RenameClockDomains(converter, "sata_rx")) - self.comb += [ - self.converter.sink.stb.eq(self.sink.stb), - If(byte_alignment, - self.converter.sink.charisk.eq(Cat(last_charisk[1], self.sink.charisk[0])), - self.converter.sink.data.eq(Cat(last_data[8:], self.sink.data[:8])) - ).Else( - self.converter.sink.charisk.eq(self.sink.charisk), - self.converter.sink.data.eq(self.sink.data) - ), - self.sink.ack.eq(self.converter.sink.ack), - self.converter.reset.eq(self.converter.source.charisk[2:] != 0) - ] - - # clock domain crossing - # (SATA3) 300MHz sata_rx clk to sys_clk - # (SATA2) 150MHz sata_rx clk to sys_clk - # (SATA1) 75MHz sata_rx clk to sys_clk - # requirements: - # due to the convertion ratio of 2, sys_clk need to be > sata_rx/2 - # source destination is always able to accept data (ack always 1) - fifo = AsyncFIFO(phy_description(32), 4) - self.fifo = RenameClockDomains(fifo, {"write": "sata_rx", "read": "sys"}) - self.comb += [ - Record.connect(self.converter.source, fifo.sink), - Record.connect(fifo.source, self.source) - ] - -class SATAPHYDatapathTX(Module): - def __init__(self): - self.sink = Sink(phy_description(32)) - self.source = Source(phy_description(16)) - - ### - - # clock domain crossing - # (SATA3) sys_clk to 300MHz sata_tx clk - # (SATA2) sys_clk to 150MHz sata_tx clk - # (SATA1) sys_clk to 75MHz sata_tx clk - # requirements: - # source destination is always able to accept data (ack always 1) - fifo = AsyncFIFO(phy_description(32), 4) - self.fifo = RenameClockDomains(fifo, {"write": "sys", "read": "sata_tx"}) - self.comb += Record.connect(self.sink, fifo.sink) - - # width convertion (32 to 16) - converter = Converter(phy_description(32), phy_description(16), reverse=False) - self.converter = RenameClockDomains(converter, "sata_tx") - self.comb += [ - Record.connect(self.fifo.source, self.converter.sink), - Record.connect(self.converter.source, self.source) - ] - -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)) - self.source = Source(phy_description(32)) - - ### - - # TX path - self.align_inserter = SATAPHYAlignInserter(ctrl) - self.mux = Multiplexer(phy_description(32), 2) - self.tx = SATAPHYDatapathTX() - self.comb += [ - 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) - ] - - # RX path - self.rx = SATAPHYDatapathRX() - self.demux = Demultiplexer(phy_description(32), 2) - self.align_remover = SATAPHYAlignRemover() - self.comb += [ - 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) - ] diff --git a/lib/sata/phy/k7/crg.py b/lib/sata/phy/k7/crg.py deleted file mode 100644 index 7e57e608..00000000 --- a/lib/sata/phy/k7/crg.py +++ /dev/null @@ -1,157 +0,0 @@ -from math import ceil - -from lib.sata.common import * - -class K7SATAPHYCRG(Module): - def __init__(self, pads, gtx, clk_freq, speed): - self.reset = Signal() - self.ready = Signal() - - self.cd_sata_tx = ClockDomain() - self.cd_sata_rx = ClockDomain() - - # CPLL - # (SATA3) 150MHz / VCO @ 3GHz / Line rate @ 6Gbps - # (SATA2 & SATA1) VCO still @ 3 GHz, Line rate is decreased with output dividers. - refclk = Signal() - self.specials += Instance("IBUFDS_GTE2", - i_CEB=0, - i_I=pads.refclk_p, - i_IB=pads.refclk_n, - o_O=refclk - ) - self.comb += gtx.gtrefclk0.eq(refclk) - - # TX clocking - # (SATA3) 150MHz from CPLL TXOUTCLK, sata_tx clk @ 300MHz (16-bits) - # (SATA2) 150MHz from CPLL TXOUTCLK, sata_tx clk @ 150MHz (16-bits) - # (SATA1) 150MHz from CPLL TXOUTCLK, sata_tx clk @ 75MHz (16-bits) - mmcm_reset = Signal() - mmcm_locked = Signal() - mmcm_fb = Signal() - mmcm_clk_i = Signal() - mmcm_clk0_o = Signal() - mmcm_div_config = { - "SATA1" : 16.0, - "SATA2" : 8.0, - "SATA3" : 4.0 - } - mmcm_div = mmcm_div_config[speed] - self.specials += [ - Instance("BUFG", i_I=gtx.txoutclk, o_O=mmcm_clk_i), - Instance("MMCME2_ADV", - p_BANDWIDTH="HIGH", p_COMPENSATION="ZHOLD", i_RST=mmcm_reset, o_LOCKED=mmcm_locked, - - # DRP - i_DCLK=0, i_DEN=0, i_DWE=0, #o_DRDY=, - i_DADDR=0, i_DI=0, #o_DO=, - - # VCO - p_REF_JITTER1=0.01, p_CLKIN1_PERIOD=6.666, - p_CLKFBOUT_MULT_F=8.000, p_CLKFBOUT_PHASE=0.000, p_DIVCLK_DIVIDE=1, - i_CLKIN1=mmcm_clk_i, i_CLKFBIN=mmcm_fb, o_CLKFBOUT=mmcm_fb, - - # CLK0 - p_CLKOUT0_DIVIDE_F=mmcm_div, p_CLKOUT0_PHASE=0.000, o_CLKOUT0=mmcm_clk0_o, - ), - Instance("BUFG", i_I=mmcm_clk0_o, o_O=self.cd_sata_tx.clk), - ] - self.comb += [ - gtx.txusrclk.eq(self.cd_sata_tx.clk), - gtx.txusrclk2.eq(self.cd_sata_tx.clk) - ] - - # RX clocking - # (SATA3) sata_rx recovered clk @ 300MHz from GTX RXOUTCLK - # (SATA2) sata_rx recovered clk @ 150MHz from GTX RXOUTCLK - # (SATA1) sata_rx recovered clk @ 150MHz from GTX RXOUTCLK - self.specials += [ - Instance("BUFG", i_I=gtx.rxoutclk, o_O=self.cd_sata_rx.clk), - ] - self.comb += [ - gtx.rxusrclk.eq(self.cd_sata_rx.clk), - gtx.rxusrclk2.eq(self.cd_sata_rx.clk) - ] - - # Configuration Reset - # After configuration, GTX's resets have to stay low for at least 500ns - # See AR43482 - reset_en = Signal() - clk_period_ns = 1000000000/clk_freq - reset_en_cnt_max = ceil(500/clk_period_ns) - reset_en_cnt = Signal(max=reset_en_cnt_max, reset=reset_en_cnt_max-1) - self.sync += \ - If(self.reset, - reset_en_cnt.eq(reset_en_cnt.reset) - ).Elif(~reset_en, - reset_en_cnt.eq(reset_en_cnt-1) - ) - self.comb += reset_en.eq(reset_en_cnt == 0) - - # TX Reset FSM - tx_reset_fsm = InsertReset(FSM(reset_state="IDLE")) - self.submodules += tx_reset_fsm - self.comb += tx_reset_fsm.reset.eq(self.reset) - tx_reset_fsm.act("IDLE", - If(reset_en, - NextState("RESET_GTX"), - ) - ) - tx_reset_fsm.act("RESET_GTX", - gtx.gttxreset.eq(1), - If(gtx.cplllock & mmcm_locked, - NextState("RELEASE_GTX") - ) - ) - tx_reset_fsm.act("RELEASE_GTX", - gtx.txuserrdy.eq(1), - If(gtx.txresetdone, - NextState("READY") - ) - ) - tx_reset_fsm.act("READY", - gtx.txuserrdy.eq(1) - ) - - # RX Reset FSM - rx_reset_fsm = InsertReset(FSM(reset_state="IDLE")) - self.submodules += rx_reset_fsm - self.comb += rx_reset_fsm.reset.eq(self.reset) - - rx_reset_fsm.act("IDLE", - If(reset_en, - NextState("RESET_GTX"), - ) - ) - rx_reset_fsm.act("RESET_GTX", - gtx.gtrxreset.eq(1), - If(gtx.cplllock & mmcm_locked, - NextState("RELEASE_GTX") - ) - ) - rx_reset_fsm.act("RELEASE_GTX", - gtx.rxuserrdy.eq(1), - If(gtx.rxresetdone, - NextState("READY") - ) - ) - rx_reset_fsm.act("READY", - gtx.rxuserrdy.eq(1) - ) - - # Ready - self.tx_ready = tx_reset_fsm.ongoing("READY") - self.rx_ready = rx_reset_fsm.ongoing("READY") - self.comb += self.ready.eq(self.tx_ready & self.rx_ready) - - # Reset PLL - self.comb += gtx.cpllreset.eq(ResetSignal() | self.reset | ~reset_en) - - # Reset MMCM - self.comb += mmcm_reset.eq(ResetSignal() | self.reset | ~gtx.cplllock) - - # Reset for SATA TX/RX clock domains - self.specials += [ - AsyncResetSynchronizer(self.cd_sata_tx, ~self.tx_ready), - AsyncResetSynchronizer(self.cd_sata_rx, ~self.rx_ready), - ] diff --git a/lib/sata/phy/k7/trx.py b/lib/sata/phy/k7/trx.py deleted file mode 100644 index eba35f13..00000000 --- a/lib/sata/phy/k7/trx.py +++ /dev/null @@ -1,854 +0,0 @@ -from lib.sata.common import * - -from migen.genlib.cdc import * - -def ones(width): - return 2**width-1 - -class _PulseSynchronizer(PulseSynchronizer): - def __init__(self, i, idomain, o, odomain): - PulseSynchronizer.__init__(self, idomain, odomain) - self.comb += [ - self.i.eq(i), - o.eq(self.o) - ] - -class _RisingEdge(Module): - def __init__(self, i, o): - i_d = Signal() - self.sync += i_d.eq(i) - self.comb += o.eq(i & ~i_d) - -class K7SATAPHYTRX(Module): - def __init__(self, pads, speed): - # Common signals - - # control - self.tx_idle = Signal() #i - - self.tx_cominit_stb = Signal() #i - self.tx_cominit_ack = Signal() #o - self.tx_comwake_stb = Signal() #i - self.tx_comwake_ack = Signal() #o - - self.rx_idle = Signal() #o - self.rx_align = Signal() #i - - self.rx_cominit_stb = Signal() #o - self.rx_comwake_stb = Signal() #o - - # datapath - self.sink = Sink(phy_description(16)) - self.source = Source(phy_description(16)) - - # K7 specific signals - # Channel - Ref Clock Ports - self.gtrefclk0 = Signal() - - # Channel PLL - self.cplllock = Signal() - self.cpllreset = Signal() - - # Receive Ports - self.rxuserrdy = Signal() - self.rxalign = Signal() - - # Receive Ports - 8b10b Decoder - self.rxcharisk = Signal(2) - self.rxdisperr = Signal(2) - - # Receive Ports - RX Data Path interface - self.gtrxreset = Signal() - self.pmarxreset = Signal() - self.rxdata = Signal(16) - self.rxoutclk = Signal() - self.rxusrclk = Signal() - self.rxusrclk2 = Signal() - - # Receive Ports - RX Driver,OOB signalling,Coupling and Eq.,CDR - self.rxelecidle = Signal() - - # Receive Ports - RX PLL Ports - self.rxresetdone = Signal() - - # Receive Ports - RX Ports for SATA - self.rxcominitdet = Signal() - self.rxcomwakedet = Signal() - - # Transmit Ports - self.txuserrdy = Signal() - - # Transmit Ports - 8b10b Encoder Control Ports - self.txcharisk = Signal(2) - - # Transmit Ports - TX Data Path interface - self.gttxreset = Signal() - self.txdata = Signal(16) - self.txoutclk = Signal() - self.txusrclk = Signal() - self.txusrclk2 = Signal() - - # Transmit Ports - TX PLL Ports - self.txresetdone = Signal() - - # Transmit Ports - TX Ports for PCI Express - self.txelecidle = Signal(reset=1) - - # Transmit Ports - TX Ports for SATA - self.txcomfinish = Signal() - self.txcominit = Signal() - self.txcomwake = Signal() - self.txrate = Signal(3) - self.rxcdrlock = Signal() - - # Config at startup - div_config = { - "SATA1" : 4, - "SATA2" : 2, - "SATA3" : 1 - } - rxout_div = div_config[speed] - txout_div = div_config[speed] - - cdr_config = { - "SATA1" : 0x0380008BFF40100008, - "SATA2" : 0x0388008BFF40200008, - "SATA3" : 0X0380008BFF10200010 - } - rxcdr_cfg = cdr_config[speed] - - # Specific / Generic signals encoding/decoding - self.comb += [ - self.txelecidle.eq(self.tx_idle), - self.tx_cominit_ack.eq(self.tx_cominit_stb & self.txcomfinish), - self.tx_comwake_ack.eq(self.tx_comwake_stb & self.txcomfinish), - self.rx_idle.eq(self.rxelecidle), - self.rxalign.eq(self.rx_align), - self.rx_cominit_stb.eq(self.rxcominitdet), - self.rx_comwake_stb.eq(self.rxcomwakedet), - ] - self.submodules += [ - _RisingEdge(self.tx_cominit_stb, self.txcominit), - _RisingEdge(self.tx_comwake_stb, self.txcomwake), - ] - - self.comb += [ - self.txcharisk.eq(self.sink.charisk), - self.txdata.eq(self.sink.data), - self.sink.ack.eq(1), - - self.source.stb.eq(1), - self.source.charisk.eq(self.rxcharisk), - self.source.data.eq(self.rxdata) - ] - - # Internals and clock domain crossing - # sys_clk --> sata_tx clk - txuserrdy = Signal() - txelecidle = Signal(reset=1) - txcominit = Signal() - txcomwake = Signal() - txrate = Signal(3) - - self.specials += [ - MultiReg(self.txuserrdy, txuserrdy, "sata_tx"), - MultiReg(self.txelecidle, txelecidle, "sata_tx"), - MultiReg(self.txrate, txrate, "sata_tx") - ] - self.submodules += [ - _PulseSynchronizer(self.txcominit, "sys", txcominit, "sata_tx"), - _PulseSynchronizer(self.txcomwake, "sys", txcomwake, "sata_tx"), - ] - - # sata_tx clk --> sys clk - txresetdone = Signal() - txcomfinish = Signal() - - self.specials += [ - MultiReg(txresetdone, self.txresetdone, "sys"), - ] - - self.submodules += [ - _PulseSynchronizer(txcomfinish, "sata_tx", self.txcomfinish, "sys"), - ] - - # sys clk --> sata_rx clk - rxuserrdy = Signal() - - self.specials += [ - MultiReg(self.rxuserrdy, rxuserrdy, "sata_rx"), - ] - - # sata_rx clk --> sys clk - rxelecidle = Signal() - rxelecidle_i = Signal() - rxelecidle_cnt_i = Signal(9) - rxresetdone = Signal() - rxcominitdet = Signal() - rxcomwakedet = Signal() - rxratedone = Signal() - rxcdrlock = Signal() - - self.specials += [ - MultiReg(rxelecidle, rxelecidle_i, "sys"), - MultiReg(rxresetdone, self.rxresetdone, "sys"), - MultiReg(rxcominitdet, self.rxcominitdet, "sys"), - MultiReg(rxcomwakedet, self.rxcomwakedet, "sys"), - MultiReg(rxcdrlock, self.rxcdrlock, "sys"), - ] - - self.sync += [ - If(rxelecidle_i != self.rxelecidle, - If(rxelecidle_cnt_i == 0, - self.rxelecidle.eq(rxelecidle_i), - rxelecidle_cnt_i.eq(255) - ).Else( - rxelecidle_cnt_i.eq(rxelecidle_cnt_i-1) - ) - ).Else( - rxelecidle_cnt_i.eq(255) - ) - ] - - self.rxbyteisaligned = Signal() - - # QPLL input clock - self.qpllclk = Signal() - self.qpllrefclk = Signal() - - # Instance - gtxe2_channel_parameters = { - # Simulation-Only Attributes - "p_SIM_RECEIVER_DETECT_PASS":"TRUE", - "p_SIM_TX_EIDLE_DRIVE_LEVEL":"X", - "p_SIM_RESET_SPEEDUP":"TRUE", - "p_SIM_CPLLREFCLK_SEL":0b001, - "p_SIM_VERSION":"4.0", - - # RX Byte and Word Alignment Attributes - "p_ALIGN_COMMA_DOUBLE":"FALSE", - "p_ALIGN_COMMA_ENABLE":ones(10), - "p_ALIGN_COMMA_WORD":2, - "p_ALIGN_MCOMMA_DET":"TRUE", - "p_ALIGN_MCOMMA_VALUE":0b1010000011, - "p_ALIGN_PCOMMA_DET":"TRUE", - "p_ALIGN_PCOMMA_VALUE":0b0101111100, - "p_SHOW_REALIGN_COMMA":"FALSE", - "p_RXSLIDE_AUTO_WAIT":7, - "p_RXSLIDE_MODE":"PCS", - "p_RX_SIG_VALID_DLY":10, - - # RX 8B/10B Decoder Attributes - "p_RX_DISPERR_SEQ_MATCH":"TRUE", - "p_DEC_MCOMMA_DETECT":"TRUE", - "p_DEC_PCOMMA_DETECT":"TRUE", - "p_DEC_VALID_COMMA_ONLY":"FALSE", - - # RX Clock Correction Attributes - "p_CBCC_DATA_SOURCE_SEL":"DECODED", - "p_CLK_COR_SEQ_2_USE":"FALSE", - "p_CLK_COR_KEEP_IDLE":"FALSE", - "p_CLK_COR_MAX_LAT":9, - "p_CLK_COR_MIN_LAT":7, - "p_CLK_COR_PRECEDENCE":"TRUE", - "p_CLK_COR_REPEAT_WAIT":0, - "p_CLK_COR_SEQ_LEN":1, - "p_CLK_COR_SEQ_1_ENABLE":ones(4), - "p_CLK_COR_SEQ_1_1":0b0100000000, - "p_CLK_COR_SEQ_1_2":0b0000000000, - "p_CLK_COR_SEQ_1_3":0b0000000000, - "p_CLK_COR_SEQ_1_4":0b0000000000, - "p_CLK_CORRECT_USE":"FALSE", - "p_CLK_COR_SEQ_2_ENABLE":ones(4), - "p_CLK_COR_SEQ_2_1":0b0100000000, - "p_CLK_COR_SEQ_2_2":0, - "p_CLK_COR_SEQ_2_3":0, - "p_CLK_COR_SEQ_2_4":0, - - # RX Channel Bonding Attributes - "p_CHAN_BOND_KEEP_ALIGN":"FALSE", - "p_CHAN_BOND_MAX_SKEW":1, - "p_CHAN_BOND_SEQ_LEN":1, - "p_CHAN_BOND_SEQ_1_1":0, - "p_CHAN_BOND_SEQ_1_1":0, - "p_CHAN_BOND_SEQ_1_2":0, - "p_CHAN_BOND_SEQ_1_3":0, - "p_CHAN_BOND_SEQ_1_4":0, - "p_CHAN_BOND_SEQ_1_ENABLE":ones(4), - "p_CHAN_BOND_SEQ_2_1":0, - "p_CHAN_BOND_SEQ_2_2":0, - "p_CHAN_BOND_SEQ_2_3":0, - "p_CHAN_BOND_SEQ_2_4":0, - "p_CHAN_BOND_SEQ_2_ENABLE":ones(4), - "p_CHAN_BOND_SEQ_2_USE":"FALSE", - "p_FTS_DESKEW_SEQ_ENABLE":ones(4), - "p_FTS_LANE_DESKEW_CFG":ones(4), - "p_FTS_LANE_DESKEW_EN":"FALSE", - - # RX Margin Analysis Attributes - "p_ES_CONTROL":0, - "p_ES_ERRDET_EN":"FALSE", - "p_ES_EYE_SCAN_EN":"TRUE", - "p_ES_HORZ_OFFSET":0, - "p_ES_PMA_CFG":0, - "p_ES_PRESCALE":0, - "p_ES_QUALIFIER":0, - "p_ES_QUAL_MASK":0, - "p_ES_SDATA_MASK":0, - "p_ES_VERT_OFFSET":0, - - # FPGA RX Interface Attributes - "p_RX_DATA_WIDTH":20, - - # PMA Attributes - "p_OUTREFCLK_SEL_INV":0b11, - "p_PMA_RSV":0x00018480, - "p_PMA_RSV2":0x2050, - "p_PMA_RSV3":0, - "p_PMA_RSV4":0, - "p_RX_BIAS_CFG":0b100, - "p_DMONITOR_CFG":0xA00, - "p_RX_CM_SEL":0b11, - "p_RX_CM_TRIM":0b010, - "p_RX_DEBUG_CFG":0, - "p_RX_OS_CFG":0b10000000, - "p_TERM_RCAL_CFG":0, - "p_TERM_RCAL_OVRD":0, - "p_TST_RSV":0, - "p_RX_CLK25_DIV":6, - "p_TX_CLK25_DIV":6, - "p_UCODEER_CLR":0, - - # PCI Express Attributes - "p_PCS_PCIE_EN":"FALSE", - - # PCS Attributes - "p_PCS_RSVD_ATTR":0x100, - - # RX Buffer Attributes - "p_RXBUF_ADDR_MODE":"FAST", - "p_RXBUF_EIDLE_HI_CNT":0b1000, - "p_RXBUF_EIDLE_LO_CNT":0, - "p_RXBUF_EN":"TRUE", - "p_RX_BUFFER_CFG":0, - "p_RXBUF_RESET_ON_CB_CHANGE":"TRUE", - "p_RXBUF_RESET_ON_COMMAALIGN":"FALSE", - "p_RXBUF_RESET_ON_EIDLE":"FALSE", - "p_RXBUF_RESET_ON_RATE_CHANGE":"TRUE", - "p_RXBUFRESET_TIME":1, - "p_RXBUF_THRESH_OVFLW":61, - "p_RXBUF_THRESH_OVRD":"FALSE", - "p_RXBUF_THRESH_UNDFLW":4, - "p_RXDLY_CFG":0x1f, - "p_RXDLY_LCFG":0x30, - "p_RXDLY_TAP_CFG":0, - "p_RXPH_CFG":0, - "p_RXPHDLY_CFG":0x084820, - "p_RXPH_MONITOR_SEL":0, - "p_RX_XCLK_SEL":"RXUSR", - "p_RX_DDI_SEL":0, - "p_RX_DEFER_RESET_BUF_EN":"TRUE", - - #CDR Attributes - "p_RXCDR_CFG":rxcdr_cfg, - "p_RXCDR_FR_RESET_ON_EIDLE":0, - "p_RXCDR_HOLD_DURING_EIDLE":0, - "p_RXCDR_PH_RESET_ON_EIDLE":0, - "p_RXCDR_LOCK_CFG":0b010101, - - # RX Initialization and Reset Attributes - "p_RXCDRFREQRESET_TIME":1, - "p_RXCDRPHRESET_TIME":1, - "p_RXISCANRESET_TIME":1, - "p_RXPCSRESET_TIME":1, - "p_RXPMARESET_TIME":3, - - # RX OOB Signaling Attributes - "p_RXOOB_CFG":0b0000110, - - # RX Gearbox Attributes - "p_RXGEARBOX_EN":"FALSE", - "p_GEARBOX_MODE":0, - - # PRBS Detection Attribute - "p_RXPRBS_ERR_LOOPBACK":0, - - # Power-Down Attributes - "p_PD_TRANS_TIME_FROM_P2":0x03c, - "p_PD_TRANS_TIME_NONE_P2":0x3c, - "p_PD_TRANS_TIME_TO_P2":0x64, - - # RX OOB Signaling Attributes - "p_SAS_MAX_COM":64, - "p_SAS_MIN_COM":36, - "p_SATA_BURST_SEQ_LEN":0b0101, - "p_SATA_BURST_VAL":0b100, - "p_SATA_EIDLE_VAL":0b100, - "p_SATA_MAX_BURST":8, - "p_SATA_MAX_INIT":21, - "p_SATA_MAX_WAKE":7, - "p_SATA_MIN_BURST":4, - "p_SATA_MIN_INIT":12, - "p_SATA_MIN_WAKE":4, - - # RX Fabric Clock Output Control Attributes - "p_TRANS_TIME_RATE":0x0e, - - # TX Buffer Attributes - "p_TXBUF_EN":"TRUE", - "p_TXBUF_RESET_ON_RATE_CHANGE":"TRUE", - "p_TXDLY_CFG":0x1f, - "p_TXDLY_LCFG":0x030, - "p_TXDLY_TAP_CFG":0, - "p_TXPH_CFG":0x0780, - "p_TXPHDLY_CFG":0x084020, - "p_TXPH_MONITOR_SEL":0, - "p_TX_XCLK_SEL":"TXOUT", - - # FPGA TX Interface Attributes - "p_TX_DATA_WIDTH":20, - - # TX Configurable Driver Attributes - "p_TX_DEEMPH0":0, - "p_TX_DEEMPH1":0, - "p_TX_EIDLE_ASSERT_DELAY":0b110, - "p_TX_EIDLE_DEASSERT_DELAY":0b100, - "p_TX_LOOPBACK_DRIVE_HIZ":"FALSE", - "p_TX_MAINCURSOR_SEL":0, - "p_TX_DRIVE_MODE":"DIRECT", - "p_TX_MARGIN_FULL_0":0b1001110, - "p_TX_MARGIN_FULL_1":0b1001001, - "p_TX_MARGIN_FULL_2":0b1000101, - "p_TX_MARGIN_FULL_3":0b1000010, - "p_TX_MARGIN_FULL_4":0b1000000, - "p_TX_MARGIN_LOW_0":0b1000110, - "p_TX_MARGIN_LOW_1":0b1000100, - "p_TX_MARGIN_LOW_2":0b1000010, - "p_TX_MARGIN_LOW_3":0b1000000, - "p_TX_MARGIN_LOW_4":0b1000000, - - # TX Gearbox Attributes - "p_TXGEARBOX_EN":"FALSE", - - # TX Initialization and Reset Attributes - "p_TXPCSRESET_TIME":1, - "p_TXPMARESET_TIME":1, - - # TX Receiver Detection Attributes - "p_TX_RXDETECT_CFG":0x1832, - "p_TX_RXDETECT_REF":0b100, - - # CPLL Attributes - "p_CPLL_CFG":0xBC07DC, - "p_CPLL_FBDIV":4, - "p_CPLL_FBDIV_45":5, - "p_CPLL_INIT_CFG":0x00001e, - "p_CPLL_LOCK_CFG":0x01e8, - "p_CPLL_REFCLK_DIV":1, - "p_RXOUT_DIV":rxout_div, - "p_TXOUT_DIV":txout_div, - "p_SATA_CPLL_CFG":"VCO_3000MHZ", - - # RX Initialization and Reset Attributes - "p_RXDFELPMRESET_TIME":0b0001111, - - # RX Equalizer Attributes - "p_RXLPM_HF_CFG":0b00000011110000, - "p_RXLPM_LF_CFG":0b00000011110000, - "p_RX_DFE_GAIN_CFG":0x020fea, - "p_RX_DFE_H2_CFG":0b000000000000, - "p_RX_DFE_H3_CFG":0b000001000000, - "p_RX_DFE_H4_CFG":0b00011110000, - "p_RX_DFE_H5_CFG":0b00011100000, - "p_RX_DFE_KL_CFG":0b0000011111110, - "p_RX_DFE_LPM_CFG":0x0954, - "p_RX_DFE_LPM_HOLD_DURING_EIDLE":0, - "p_RX_DFE_UT_CFG":0b10001111000000000, - "p_RX_DFE_VP_CFG":0b00011111100000011, - - # Power-Down Attributes - "p_RX_CLKMUX_PD":1, - "p_TX_CLKMUX_PD":1, - - # FPGA RX Interface Attribute - "p_RX_INT_DATAWIDTH":0, - - # FPGA TX Interface Attribute - "p_TX_INT_DATAWIDTH":0, - - # TX Configurable Driver Attributes - "p_TX_QPI_STATUS_EN":0, - - # RX Equalizer Attributes - "p_RX_DFE_KL_CFG2":0b00110011000100000001100000001100, - "p_RX_DFE_XYD_CFG":0b0000000000000, - - # TX Configurable Driver Attributes - "p_TX_PREDRIVER_MODE":0, - } - - self.specials += \ - Instance("GTXE2_CHANNEL", - # CPLL Ports - #o_CPLLFBCLKLOST=, - o_CPLLLOCK=self.cplllock, - i_CPLLLOCKDETCLK=0, - i_CPLLLOCKEN=1, - i_CPLLPD=0, - #o_CPLLREFCLKLOST=0, - i_CPLLREFCLKSEL=0b001, - i_CPLLRESET=self.cpllreset, - i_GTRSVD=0, - i_PCSRSVDIN=0, - i_PCSRSVDIN2=0, - i_PMARSVDIN=0, - i_PMARSVDIN2=0, - i_TSTIN=ones(20), - #o_TSTOUT=, - - # Channel - i_CLKRSVD=0, - - # Channel - Clocking Ports - i_GTGREFCLK=0, - i_GTNORTHREFCLK0=0, - i_GTNORTHREFCLK1=0, - i_GTREFCLK0=self.gtrefclk0, - i_GTREFCLK1=0, - i_GTSOUTHREFCLK0=0, - i_GTSOUTHREFCLK1=0, - - # Channel - DRP Ports - i_DRPADDR=0, - i_DRPCLK=0, - i_DRPDI=0, - #o_DRPDO=, - i_DRPEN=0, - #o_DRPRDY=, - i_DRPWE=0, - - # Clocking Ports - #o_GTREFCLKMONITOR=, - i_QPLLCLK=self.qpllclk, - i_QPLLREFCLK=self.qpllrefclk, - i_RXSYSCLKSEL=0b00, - i_TXSYSCLKSEL=0b00, - - # Digital Monitor Ports - #o_DMONITOROUT=, - - # FPGA TX Interface Datapath Configuration - i_TX8B10BEN=1, - - # Loopback Ports - i_LOOPBACK=0, - - # PCI Express Ports - #o_PHYSTATUS=, - i_RXRATE=0, - #o_RXVALID=, - - # Power-Down Ports - i_RXPD=0b00, - i_TXPD=0b00, - - # RX 8B/10B Decoder Ports - i_SETERRSTATUS=0, - - # RX Initialization and Reset Ports - i_EYESCANRESET=0, - i_RXUSERRDY=rxuserrdy, - - # RX Margin Analysis Ports - #o_EYESCANDATAERROR=, - i_EYESCANMODE=0, - i_EYESCANTRIGGER=0, - - # Receive Ports - CDR Ports - i_RXCDRFREQRESET=0, - i_RXCDRHOLD=0, - o_RXCDRLOCK=rxcdrlock, - i_RXCDROVRDEN=0, - i_RXCDRRESET=0, - i_RXCDRRESETRSV=0, - - # Receive Ports - Clock Correction Ports - #o_RXCLKCORCNT=, - - # Receive Ports - FPGA RX Interface Datapath Configuration - i_RX8B10BEN=1, - - # Receive Ports - FPGA RX Interface Ports - i_RXUSRCLK=self.rxusrclk, - i_RXUSRCLK2=self.rxusrclk2, - - # Receive Ports - FPGA RX interface Ports - o_RXDATA=self.rxdata, - - # Receive Ports - Pattern Checker Ports - #o_RXPRBSERR=, - i_RXPRBSSEL=0, - - # Receive Ports - Pattern Checker ports - i_RXPRBSCNTRESET=0, - - # Receive Ports - RX Equalizer Ports - i_RXDFEXYDEN=0, - i_RXDFEXYDHOLD=0, - i_RXDFEXYDOVRDEN=0, - - # Receive Ports - RX 8B/10B Decoder Ports - #o_RXDISPERR=, - #o_RXNOTINTABLE=, - - # Receive Ports - RX AFE - i_GTXRXP=pads.rxp, - i_GTXRXN=pads.rxn, - - # Receive Ports - RX Buffer Bypass Ports - i_RXBUFRESET=0, - #o_RXBUFSTATUS=, - i_RXDDIEN=0, - i_RXDLYBYPASS=1, - i_RXDLYEN=0, - i_RXDLYOVRDEN=0, - i_RXDLYSRESET=0, - #o_RXDLYSRESETDONE=0, - i_RXPHALIGN=0, - #o_RXPHALIGNDONE=, - i_RXPHALIGNEN=0, - i_RXPHDLYPD=0, - i_RXPHDLYRESET=0, - #o_RXPHMONITOR=, - i_RXPHOVRDEN=0, - #o_RXPHSLIPMONITOR=, - #o_RXSTATUS=, - - # Receive Ports - RX Byte and Word Alignment Ports - o_RXBYTEISALIGNED=self.rxbyteisaligned, - #o_RXBYTEREALIGN=, - #o_RXCOMMADET=, - i_RXCOMMADETEN=1, - i_RXMCOMMAALIGNEN=1, - i_RXPCOMMAALIGNEN=1, - - # Receive Ports - RX Channel Bonding Ports - #o_RXCHANBONDSEQ=, - i_RXCHBONDEN=0, - i_RXCHBONDLEVEL=0, - i_RXCHBONDMASTER=0, - #o_RXCHBONDO=, - i_RXCHBONDSLAVE=0, - - # Receive Ports - RX Channel Bonding Ports - #o_RXCHANISALIGNED=, - #o_RXCHANREALIGN=, - - # Receive Ports - RX Equalizer Ports - i_RXDFEAGCHOLD=0, - i_RXDFEAGCOVRDEN=0, - i_RXDFECM1EN=0, - i_RXDFELFHOLD=0, - i_RXDFELFOVRDEN=1, - i_RXDFELPMRESET=0, - i_RXDFETAP2HOLD=0, - i_RXDFETAP2OVRDEN=0, - i_RXDFETAP3HOLD=0, - i_RXDFETAP3OVRDEN=0, - i_RXDFETAP4HOLD=0, - i_RXDFETAP4OVRDEN=0, - i_RXDFETAP5HOLD=0, - i_RXDFETAP5OVRDEN=0, - i_RXDFEUTHOLD=0, - i_RXDFEUTOVRDEN=0, - i_RXDFEVPHOLD=0, - i_RXDFEVPOVRDEN=0, - i_RXDFEVSEN=0, - i_RXLPMLFKLOVRDEN=0, - #o_RXMONITOROUT=, - i_RXMONITORSEL=0b00, - i_RXOSHOLD=0, - i_RXOSOVRDEN=0, - - # Receive Ports - RX Equilizer Ports - i_RXLPMHFHOLD=0, - i_RXLPMHFOVRDEN=0, - i_RXLPMLFHOLD=0, - - # Receive Ports - RX Fabric ClocK Output Control Ports - #o_RXRATEDONE=, - - # Receive Ports - RX Fabric Output Control Ports - o_RXOUTCLK=self.rxoutclk, - #o_RXOUTCLKFABRIC=, - #o_RXOUTCLKPCS=, - i_RXOUTCLKSEL=0b010, - - # Receive Ports - RX Gearbox Ports - #o_RXDATAVALID=, - #o_RXHEADER=, - #o_RXHEADERVALID=, - #o_RXSTARTOFSEQ=, - - # Receive Ports - RX Gearbox Ports - i_RXGEARBOXSLIP=0, - - # Receive Ports - RX Initialization and Reset Ports - i_GTRXRESET=self.gtrxreset, - i_RXOOBRESET=0, - i_RXPCSRESET=0, - i_RXPMARESET=self.pmarxreset, - - # Receive Ports - RX Margin Analysis ports - i_RXLPMEN=0, - - # Receive Ports - RX OOB Signaling ports - #o_RXCOMSASDET=, - o_RXCOMWAKEDET=rxcomwakedet, - - # Receive Ports - RX OOB Signaling ports - o_RXCOMINITDET=rxcominitdet, - - # Receive Ports - RX OOB signalling Ports - o_RXELECIDLE=rxelecidle, - i_RXELECIDLEMODE=0b00, - - # Receive Ports - RX Polarity Control Ports - i_RXPOLARITY=0, - - # Receive Ports - RX gearbox ports - i_RXSLIDE=0, - - # Receive Ports - RX8B/10B Decoder Ports - #o_RXCHARISCOMMA=, - o_RXCHARISK=self.rxcharisk, - - # Receive Ports - Rx Channel Bonding Ports - i_RXCHBONDI=0, - - # Receive Ports -RX Initialization and Reset Ports - o_RXRESETDONE=rxresetdone, - - # Rx AFE Ports - i_RXQPIEN=0, - #o_RXQPISENN=, - #o_RXQPISENP=, - - # TX Buffer Bypass Ports - i_TXPHDLYTSTCLK=0, - - # TX Configurable Driver Ports - i_TXPOSTCURSOR=0, - i_TXPOSTCURSORINV=0, - i_TXPRECURSOR=0, - i_TXPRECURSORINV=0, - i_TXQPIBIASEN=0, - i_TXQPISTRONGPDOWN=0, - i_TXQPIWEAKPUP=0, - - # TX Initialization and Reset Ports - i_CFGRESET=0, - i_GTTXRESET=self.gttxreset, - #o_PCSRSVDOUT=, - i_TXUSERRDY=txuserrdy, - - # Transceiver Reset Mode Operation - i_GTRESETSEL=0, - i_RESETOVRD=0, - - # Transmit Ports - 8b10b Encoder Control Ports - i_TXCHARDISPMODE=0, - i_TXCHARDISPVAL=0, - - # Transmit Ports - FPGA TX Interface Ports - i_TXUSRCLK=self.txusrclk, - i_TXUSRCLK2=self.txusrclk2, - - # Transmit Ports - PCI Express Ports - i_TXELECIDLE=txelecidle, - i_TXMARGIN=0, - i_TXRATE=txrate, - i_TXSWING=0, - - # Transmit Ports - Pattern Generator Ports - i_TXPRBSFORCEERR=0, - - # Transmit Ports - TX Buffer Bypass Ports - i_TXDLYBYPASS=1, - i_TXDLYEN=0, - i_TXDLYHOLD=0, - i_TXDLYOVRDEN=0, - i_TXDLYSRESET=0, - #o_TXDLYSRESETDONE=, - i_TXDLYUPDOWN=0, - i_TXPHALIGN=0, - #o_TXPHALIGNDONE=txphaligndone, - i_TXPHALIGNEN=0, - i_TXPHDLYPD=0, - i_TXPHDLYRESET=0, - i_TXPHINIT=0, - #o_TXPHINITDONE=, - i_TXPHOVRDEN=0, - - # Transmit Ports - TX Buffer Ports - #o_TXBUFSTATUS=, - - # Transmit Ports - TX Configurable Driver Ports - i_TXBUFDIFFCTRL=0b100, - i_TXDEEMPH=0, - i_TXDIFFCTRL=0b1000, - i_TXDIFFPD=0, - i_TXINHIBIT=0, - i_TXMAINCURSOR=0, - i_TXPISOPD=0, - - # Transmit Ports - TX Data Path interface - i_TXDATA=self.txdata, - - # Transmit Ports - TX Driver and OOB signaling - o_GTXTXP=pads.txp, - o_GTXTXN=pads.txn, - - # Transmit Ports - TX Fabric Clock Output Control Ports - o_TXOUTCLK=self.txoutclk, - #o_TXOUTCLKFABRIC=, - #o_TXOUTCLKPCS=, - i_TXOUTCLKSEL=0b11, #?? - #o_TXRATEDONE=, - # Transmit Ports - TX Gearbox Ports - i_TXCHARISK=self.txcharisk, - #o_TXGEARBOXREADY=, - i_TXHEADER=0, - i_TXSEQUENCE=0, - i_TXSTARTSEQ=0, - - # Transmit Ports - TX Initialization and Reset Ports - i_TXPCSRESET=0, - i_TXPMARESET=0, - o_TXRESETDONE=txresetdone, - - # Transmit Ports - TX OOB signalling Ports - o_TXCOMFINISH=txcomfinish, - i_TXCOMINIT=txcominit, - i_TXCOMSAS=0, - i_TXCOMWAKE=txcomwake, - i_TXPDELECIDLEMODE=0, - - # Transmit Ports - TX Polarity Control Ports - i_TXPOLARITY=0, - - # Transmit Ports - TX Receiver Detection Ports - i_TXDETECTRX=0, - - # Transmit Ports - TX8b/10b Encoder Ports - i_TX8B10BBYPASS=0, - - # Transmit Ports - pattern Generator Ports - i_TXPRBSSEL=0, - - # Tx Configurable Driver Ports - #o_TXQPISENN=, - #o_TXQPISENP=, - - **gtxe2_channel_parameters - ) diff --git a/lib/sata/test/Makefile b/lib/sata/test/Makefile deleted file mode 100644 index 02859182..00000000 --- a/lib/sata/test/Makefile +++ /dev/null @@ -1,33 +0,0 @@ -MSCDIR = ../../../ -PYTHON = python3 - -CMD = PYTHONPATH=$(MSCDIR) $(PYTHON) - -CC=gcc -CFLAGS =-Wall -O0 - -phy_datapath_tb: - $(CMD) phy_datapath_tb.py - -crc_tb: - $(CC) $(CFLAGS) $(INC) -o crc crc.c - $(CMD) crc_tb.py - -scrambler_tb: - $(CC) $(CFLAGS) $(INC) -o scrambler scrambler.c - $(CMD) scrambler_tb.py - -cont_tb: - $(CMD) cont_tb.py - -link_tb: - $(CMD) link_tb.py - -command_tb: - $(CMD) command_tb.py - -bist_tb: - $(CMD) bist_tb.py - -clean: - rm crc scrambler *.vcd diff --git a/lib/sata/test/bist_tb.py b/lib/sata/test/bist_tb.py deleted file mode 100644 index 72c99979..00000000 --- a/lib/sata/test/bist_tb.py +++ /dev/null @@ -1,45 +0,0 @@ -from lib.sata.common import * -from lib.sata import SATACON -from lib.sata.bist import SATABISTGenerator, SATABISTChecker - -from lib.sata.test.hdd import * -from lib.sata.test.common import * - -class TB(Module): - def __init__(self): - self.hdd = HDD( - link_debug=False, link_random_level=0, - transport_debug=False, transport_loopback=False, - hdd_debug=True) - self.controller = SATACON(self.hdd.phy) - self.generator = SATABISTGenerator(self.controller.crossbar.get_port()) - self.checker = SATABISTChecker(self.controller.crossbar.get_port()) - - def gen_simulation(self, selfp): - hdd = self.hdd - hdd.malloc(0, 64) - selfp.generator.sector = 0 - selfp.generator.count = 17 - selfp.checker.sector = 0 - selfp.checker.count = 17 - while True: - selfp.generator.start = 1 - yield - selfp.generator.start = 0 - yield - while selfp.generator.done == 0: - yield - selfp.checker.start = 1 - yield - selfp.checker.start = 0 - yield - while selfp.checker.done == 0: - yield - print("errors {}".format(selfp.checker.errors)) - selfp.generator.sector += 1 - selfp.generator.count = max((selfp.generator.count + 1)%8, 1) - selfp.checker.sector += 1 - selfp.checker.count = max((selfp.checker.count + 1)%8, 1) - -if __name__ == "__main__": - run_simulation(TB(), ncycles=8192*2, vcd_name="my.vcd", keep_files=True) diff --git a/lib/sata/test/command_tb.py b/lib/sata/test/command_tb.py deleted file mode 100644 index 4470dd86..00000000 --- a/lib/sata/test/command_tb.py +++ /dev/null @@ -1,100 +0,0 @@ -from lib.sata.common import * -from lib.sata.link import SATALink -from lib.sata.transport import SATATransport -from lib.sata.command import SATACommand - -from lib.sata.test.hdd import * -from lib.sata.test.common import * - -class CommandTXPacket(list): - def __init__(self, write=0, read=0, sector=0, count=0, data=[]): - self.ongoing = False - self.done = False - self.write = write - self.read = read - self.sector = sector - self.count = count - for d in data: - self.append(d) - -class CommandStreamer(PacketStreamer): - def __init__(self): - PacketStreamer.__init__(self, command_tx_description(32), CommandTXPacket) - - def do_simulation(self, selfp): - PacketStreamer.do_simulation(self, selfp) - selfp.source.write = self.packet.write - selfp.source.read = self.packet.read - selfp.source.sector = self.packet.sector - selfp.source.count = self.packet.count - -class CommandRXPacket(list): - def __init__(self): - self.ongoing = False - self.done = False - self.write = 0 - self.read = 0 - self.success = 0 - self.failed = 0 - -class CommandLogger(PacketLogger): - def __init__(self): - PacketLogger.__init__(self, command_rx_description(32), CommandRXPacket) - - def do_simulation(self, selfp): - selfp.sink.ack = 1 - if selfp.sink.stb == 1 and selfp.sink.sop == 1: - self.packet = CommandRXPacket() - self.packet.write = selfp.sink.write - self.packet.read = selfp.sink.read - self.packet.sucess = selfp.sink.success - self.packet.failed = selfp.sink.failed - self.packet.append(selfp.sink.data) - elif selfp.sink.stb: - self.packet.append(selfp.sink.data) - if selfp.sink.stb == 1 and selfp.sink.eop == 1: - self.packet.done = True - -class TB(Module): - def __init__(self): - self.hdd = HDD( - link_debug=False, link_random_level=50, - transport_debug=False, transport_loopback=False, - hdd_debug=True) - self.link = SATALink(self.hdd.phy) - self.transport = SATATransport(self.link) - self.command = SATACommand(self.transport) - - self.streamer = CommandStreamer() - self.streamer_randomizer = Randomizer(command_tx_description(32), level=50) - - self.logger = CommandLogger() - self.logger_randomizer = Randomizer(command_rx_description(32), level=50) - - self.pipeline = Pipeline( - self.streamer, - self.streamer_randomizer, - self.command, - self.logger_randomizer, - self.logger - ) - - def gen_simulation(self, selfp): - hdd = self.hdd - hdd.malloc(0, 64) - write_data = [i for i in range(sectors2dwords(2))] - write_len = 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.logger.receive() - read_packet = CommandTXPacket(read=1, sector=2, count=write_len) - yield from self.streamer.send(read_packet) - yield from self.logger.receive() - read_data = self.logger.packet - - # check results - s, l, e = check(write_data, read_data) - print("shift "+ str(s) + " / length " + str(l) + " / errors " + str(e)) - -if __name__ == "__main__": - run_simulation(TB(), ncycles=2048, vcd_name="my.vcd", keep_files=True) diff --git a/lib/sata/test/common.py b/lib/sata/test/common.py deleted file mode 100644 index e0ba6fe5..00000000 --- a/lib/sata/test/common.py +++ /dev/null @@ -1,135 +0,0 @@ -import random, copy - -from migen.sim.generic import run_simulation - -from lib.sata.common import * - -def seed_to_data(seed, random=True): - if random: - return (seed * 0x31415979 + 1) & 0xffffffff - else: - return seed - -def check(p1, p2): - p1 = copy.deepcopy(p1) - p2 = copy.deepcopy(p2) - if isinstance(p1, int): - return 0, 1, int(p1 != p2) - else: - if len(p1) >= len(p2): - ref, res = p1, p2 - else: - ref, res = p2, p1 - shift = 0 - while((ref[0] != res[0]) and (len(res)>1)): - res.pop(0) - shift += 1 - length = min(len(ref), len(res)) - errors = 0 - for i in range(length): - if ref.pop(0) != res.pop(0): - errors += 1 - return shift, length, errors - -def randn(max_n): - return random.randint(0, max_n-1) - -class PacketStreamer(Module): - def __init__(self, description, packet_class): - self.source = Source(description) - ### - self.packets = [] - self.packet = packet_class() - self.packet.done = 1 - - self.source_data = 0 - - def send(self, packet, blocking=True): - packet = copy.deepcopy(packet) - self.packets.append(packet) - if blocking: - while packet.done == 0: - yield - - def do_simulation(self, selfp): - if len(self.packets) and self.packet.done: - self.packet = self.packets.pop(0) - if not self.packet.ongoing and not self.packet.done: - selfp.source.stb = 1 - if self.source.description.packetized: - selfp.source.sop = 1 - if len(self.packet) > 0: - self.source_data = self.packet.pop(0) - if hasattr(selfp.source, "data"): - selfp.source.data = self.source_data - else: - selfp.source.d = self.source_data - self.packet.ongoing = True - elif selfp.source.stb == 1 and selfp.source.ack == 1: - if self.source.description.packetized: - selfp.source.sop = 0 - selfp.source.eop = (len(self.packet) == 1) - if len(self.packet) > 0: - selfp.source.stb = 1 - self.source_data = self.packet.pop(0) - if hasattr(selfp.source, "data"): - selfp.source.data = self.source_data - else: - selfp.source.d = self.source_data - else: - self.packet.done = 1 - selfp.source.stb = 0 - -class PacketLogger(Module): - def __init__(self, description, packet_class): - self.sink = Sink(description) - ### - self.packet_class = packet_class - self.packet = packet_class() - - def receive(self, length=None): - self.packet.done = 0 - if length is None: - while self.packet.done == 0: - yield - else: - while length > len(self.packet): - yield - - def do_simulation(self, selfp): - selfp.sink.ack = 1 - if self.sink.description.packetized: - if selfp.sink.stb == 1 and selfp.sink.sop == 1: - self.packet = self.packet_class() - if selfp.sink.stb: - if hasattr(selfp.sink, "data"): - self.packet.append(selfp.sink.data) - else: - self.packet.append(selfp.sink.d) - if self.sink.description.packetized: - if selfp.sink.stb == 1 and selfp.sink.eop == 1: - self.packet.done = True - -class Randomizer(Module): - def __init__(self, description, level=0): - self.level = level - - self.sink = Sink(description) - self.source = Source(description) - - self.run = Signal() - - self.comb += \ - If(self.run, - Record.connect(self.sink, self.source) - ).Else( - self.source.stb.eq(0), - self.sink.ack.eq(0), - ) - - def do_simulation(self, selfp): - n = randn(100) - if n < self.level: - selfp.run = 0 - else: - selfp.run = 1 diff --git a/lib/sata/test/cont_tb.py b/lib/sata/test/cont_tb.py deleted file mode 100644 index 61c800d2..00000000 --- a/lib/sata/test/cont_tb.py +++ /dev/null @@ -1,95 +0,0 @@ -from lib.sata.common import * -from lib.sata.link.cont import SATACONTInserter, SATACONTRemover - -from lib.sata.test.common import * - -class ContPacket(list): - def __init__(self, data=[]): - self.ongoing = False - self.done = False - for d in data: - self.append(d) - -class ContStreamer(PacketStreamer): - def __init__(self): - PacketStreamer.__init__(self, phy_description(32), ContPacket) - - 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 ContLogger(PacketLogger): - def __init__(self): - PacketLogger.__init__(self, phy_description(32), ContPacket) - -class TB(Module): - def __init__(self): - self.streamer = ContStreamer() - self.streamer_randomizer = Randomizer(phy_description(32), level=50) - self.inserter = SATACONTInserter(phy_description(32)) - self.remover = SATACONTRemover(phy_description(32)) - self.logger_randomizer = Randomizer(phy_description(32), level=50) - self.logger = ContLogger() - - self.pipeline = Pipeline( - self.streamer, - self.streamer_randomizer, - self.inserter, - self.remover, - self.logger_randomizer, - self.logger - ) - - def gen_simulation(self, selfp): - test_packet = ContPacket([ - primitives["SYNC"], - primitives["SYNC"], - primitives["SYNC"], - primitives["SYNC"], - primitives["SYNC"], - primitives["SYNC"], - primitives["ALIGN"], - primitives["ALIGN"], - primitives["SYNC"], - primitives["SYNC"], - #primitives["SYNC"], - 0x00000000, - 0x00000001, - 0x00000002, - 0x00000003, - 0x00000004, - 0x00000005, - 0x00000006, - 0x00000007, - primitives["SYNC"], - primitives["SYNC"], - primitives["SYNC"], - primitives["SYNC"], - primitives["ALIGN"], - primitives["ALIGN"], - primitives["SYNC"], - primitives["SYNC"], - primitives["SYNC"], - primitives["SYNC"]]*4 - ) - streamer_packet = ContPacket(test_packet) - yield from self.streamer.send(streamer_packet) - yield from self.logger.receive(len(test_packet)) - #for d in self.logger.packet: - # print("%08x" %d) - - # 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=1024, vcd_name="my.vcd", keep_files=True) diff --git a/lib/sata/test/crc.c b/lib/sata/test/crc.c deleted file mode 100644 index 75576b5c..00000000 --- a/lib/sata/test/crc.c +++ /dev/null @@ -1,149 +0,0 @@ -// Adapted from SATA specification -/****************************************************************************/ -/* */ -/* crc.c */ -/* */ -/* This sample code reads standard in for a sequence of 32 bit values */ -/* formatted in hexadecimal with a leading "0x" (e.g. 0xDEADBEEF). The */ -/* code calculates the Serial ATA CRC for the input data stream. The */ -/* generator polynomial used is: */ -/* 32 26 23 22 16 12 11 10 8 7 5 4 2 */ -/* G(x) = x + x + x + x + x + x + x + x + x + x + x + x + x + x + 1 */ -/* */ -/* This sample code uses a parallel implementation of the CRC calculation */ -/* circuit that is suitable for implementation in hardware. A block */ -/* diagram of the circuit being emulated is shown below. */ -/* */ -/* +---+ +---+ +---+ */ -/* Data_In --------->| | | | | R | */ -/* | + |--------->| * |--------->| e |----+ */ -/* +---->| | | | | g | | */ -/* | +---+ +---+ +---+ | */ -/* | | */ -/* | | */ -/* +--------------------------------------------+ */ -/* */ -/* The CRC value is initialized to 0x52325032 as defined in the Serial ATA */ -/* specification. */ -/* */ -/****************************************************************************/ - - - -#include -#include -int main(int argc, char *argv[]) -{ - int i; - unsigned int data_count; - unsigned int crc; - unsigned int data_in; - unsigned char crc_bit[32]; - unsigned char new_bit[32]; - - crc = 0x52325032; - data_count = 0; - - while((scanf(" 0x%8x", &data_in) == 1) && (!scanf("exit"))) { - data_count++; - /* Add the data_in value to the current value of the CRC held in the */ - /* "register". The addition is performed modulo two (XOR). */ - crc ^= data_in; - /* Expand the value of the CRC held in the register to 32 individual */ - /* bits for easy manipulation. */ - for (i = 0; i < 32; ++i) { - crc_bit[i] = (crc >> i) & 0x01; - } - /* The following 32 assignments perform the function of the box */ - /* labeled "*" in the block diagram above. The new_bit array is a */ - /* temporary holding place for the new CRC value being calculated. */ - /* Note that there are lots of shared terms in the assignments below. */ - new_bit[31] = crc_bit[31] ^ crc_bit[30] ^ crc_bit[29] ^ crc_bit[28] ^ crc_bit[27] ^ crc_bit[25] ^ crc_bit[24] ^ - crc_bit[23] ^ crc_bit[15] ^ crc_bit[11] ^ crc_bit[9] ^ crc_bit[8] ^ crc_bit[5]; - new_bit[30] = crc_bit[30] ^ crc_bit[29] ^ crc_bit[28] ^ crc_bit[27] ^ crc_bit[26] ^ crc_bit[24] ^ crc_bit[23] ^ - crc_bit[22] ^ crc_bit[14] ^ crc_bit[10] ^ crc_bit[8] ^ crc_bit[7] ^ crc_bit[4]; - new_bit[29] = crc_bit[31] ^ crc_bit[29] ^ crc_bit[28] ^ crc_bit[27] ^ crc_bit[26] ^ crc_bit[25] ^ crc_bit[23] ^ - crc_bit[22] ^ crc_bit[21] ^ crc_bit[13] ^ crc_bit[9] ^ crc_bit[7] ^ crc_bit[6] ^ crc_bit[3]; - new_bit[28] = crc_bit[30] ^ crc_bit[28] ^ crc_bit[27] ^ crc_bit[26] ^ crc_bit[25] ^ crc_bit[24] ^ crc_bit[22] ^ - crc_bit[21] ^ crc_bit[20] ^ crc_bit[12] ^ crc_bit[8] ^ crc_bit[6] ^ crc_bit[5] ^ crc_bit[2]; - new_bit[27] = crc_bit[29] ^ crc_bit[27] ^ crc_bit[26] ^ crc_bit[25] ^ crc_bit[24] ^ crc_bit[23] ^ crc_bit[21] ^ - crc_bit[20] ^ crc_bit[19] ^ crc_bit[11] ^ crc_bit[7] ^ crc_bit[5] ^ crc_bit[4] ^ crc_bit[1]; - new_bit[26] = crc_bit[31] ^ crc_bit[28] ^ crc_bit[26] ^ crc_bit[25] ^ crc_bit[24] ^ crc_bit[23] ^ crc_bit[22] ^ - crc_bit[20] ^ crc_bit[19] ^ crc_bit[18] ^ crc_bit[10] ^ crc_bit[6] ^ crc_bit[4] ^ crc_bit[3] ^ - crc_bit[0]; - new_bit[25] = crc_bit[31] ^ crc_bit[29] ^ crc_bit[28] ^ crc_bit[22] ^ crc_bit[21] ^ crc_bit[19] ^ crc_bit[18] ^ - crc_bit[17] ^ crc_bit[15] ^ crc_bit[11] ^ crc_bit[8] ^ crc_bit[3] ^ crc_bit[2]; - new_bit[24] = crc_bit[30] ^ crc_bit[28] ^ crc_bit[27] ^ crc_bit[21] ^ crc_bit[20] ^ crc_bit[18] ^ crc_bit[17] ^ - crc_bit[16] ^ crc_bit[14] ^ crc_bit[10] ^ crc_bit[7] ^ crc_bit[2] ^ crc_bit[1]; - new_bit[23] = crc_bit[31] ^ crc_bit[29] ^ crc_bit[27] ^ crc_bit[26] ^ crc_bit[20] ^ crc_bit[19] ^ crc_bit[17] ^ - crc_bit[16] ^ crc_bit[15] ^ crc_bit[13] ^ crc_bit[9] ^ crc_bit[6] ^ crc_bit[1] ^ crc_bit[0]; - new_bit[22] = crc_bit[31] ^ crc_bit[29] ^ crc_bit[27] ^ crc_bit[26] ^ crc_bit[24] ^ crc_bit[23] ^ crc_bit[19] ^ - crc_bit[18] ^ crc_bit[16] ^ crc_bit[14] ^ crc_bit[12] ^ crc_bit[11] ^ crc_bit[9] ^ crc_bit[0]; - new_bit[21] = crc_bit[31] ^ crc_bit[29] ^ crc_bit[27] ^ crc_bit[26] ^ crc_bit[24] ^ crc_bit[22] ^ crc_bit[18] ^ - crc_bit[17] ^ crc_bit[13] ^ crc_bit[10] ^ crc_bit[9] ^ crc_bit[5]; - new_bit[20] = crc_bit[30] ^ crc_bit[28] ^ crc_bit[26] ^ crc_bit[25] ^ crc_bit[23] ^ crc_bit[21] ^ crc_bit[17] ^ - crc_bit[16] ^ crc_bit[12] ^ crc_bit[9] ^ crc_bit[8] ^ crc_bit[4]; - new_bit[19] = crc_bit[29] ^ crc_bit[27] ^ crc_bit[25] ^ crc_bit[24] ^ crc_bit[22] ^ crc_bit[20] ^ crc_bit[16] ^ - crc_bit[15] ^ crc_bit[11] ^ crc_bit[8] ^ crc_bit[7] ^ crc_bit[3]; - new_bit[18] = crc_bit[31] ^ crc_bit[28] ^ crc_bit[26] ^ crc_bit[24] ^ crc_bit[23] ^ crc_bit[21] ^ crc_bit[19] ^ - crc_bit[15] ^ crc_bit[14] ^ crc_bit[10] ^ crc_bit[7] ^ crc_bit[6] ^ crc_bit[2]; - new_bit[17] = crc_bit[31] ^ crc_bit[30] ^ crc_bit[27] ^ crc_bit[25] ^ crc_bit[23] ^ crc_bit[22] ^ crc_bit[20] ^ - crc_bit[18] ^ crc_bit[14] ^ crc_bit[13] ^ crc_bit[9] ^ crc_bit[6] ^ crc_bit[5] ^ crc_bit[1]; - new_bit[16] = crc_bit[30] ^ crc_bit[29] ^ crc_bit[26] ^ crc_bit[24] ^ crc_bit[22] ^ crc_bit[21] ^ crc_bit[19] ^ - crc_bit[17] ^ crc_bit[13] ^ crc_bit[12] ^ crc_bit[8] ^ crc_bit[5] ^ crc_bit[4] ^ crc_bit[0]; - new_bit[15] = crc_bit[30] ^ crc_bit[27] ^ crc_bit[24] ^ crc_bit[21] ^ crc_bit[20] ^ crc_bit[18] ^ crc_bit[16] ^ - crc_bit[15] ^ crc_bit[12] ^ crc_bit[9] ^ crc_bit[8] ^ crc_bit[7] ^ crc_bit[5] ^ crc_bit[4] ^ - crc_bit[3]; - new_bit[14] = crc_bit[29] ^ crc_bit[26] ^ crc_bit[23] ^ crc_bit[20] ^ crc_bit[19] ^ crc_bit[17] ^ crc_bit[15] ^ - crc_bit[14] ^ crc_bit[11] ^ crc_bit[8] ^ crc_bit[7] ^ crc_bit[6] ^ crc_bit[4] ^ crc_bit[3] ^ - crc_bit[2]; - new_bit[13] = crc_bit[31] ^ crc_bit[28] ^ crc_bit[25] ^ crc_bit[22] ^ crc_bit[19] ^ crc_bit[18] ^ crc_bit[16] ^ - crc_bit[14] ^ crc_bit[13] ^ crc_bit[10] ^ crc_bit[7] ^ crc_bit[6] ^ crc_bit[5] ^ crc_bit[3] ^ - crc_bit[2] ^ crc_bit[1]; - new_bit[12] = crc_bit[31] ^ crc_bit[30] ^ crc_bit[27] ^ crc_bit[24] ^ crc_bit[21] ^ crc_bit[18] ^ crc_bit[17] ^ - crc_bit[15] ^ crc_bit[13] ^ crc_bit[12] ^ crc_bit[9] ^ crc_bit[6] ^ crc_bit[5] ^ crc_bit[4] ^ - crc_bit[2] ^ crc_bit[1] ^ crc_bit[0]; - new_bit[11] = crc_bit[31] ^ crc_bit[28] ^ crc_bit[27] ^ crc_bit[26] ^ crc_bit[25] ^ crc_bit[24] ^ crc_bit[20] ^ - crc_bit[17] ^ crc_bit[16] ^ crc_bit[15] ^ crc_bit[14] ^ crc_bit[12] ^ crc_bit[9] ^ crc_bit[4] ^ - crc_bit[3] ^ crc_bit[1] ^ crc_bit[0]; - new_bit[10] = crc_bit[31] ^ crc_bit[29] ^ crc_bit[28] ^ crc_bit[26] ^ crc_bit[19] ^ crc_bit[16] ^ crc_bit[14] ^ - crc_bit[13] ^ crc_bit[9] ^ crc_bit[5] ^ crc_bit[3] ^ crc_bit[2] ^ crc_bit[0]; - new_bit[9] = crc_bit[29] ^ crc_bit[24] ^ crc_bit[23] ^ crc_bit[18] ^ crc_bit[13] ^ crc_bit[12] ^ crc_bit[11] ^ - crc_bit[9] ^ crc_bit[5] ^ crc_bit[4] ^ crc_bit[2] ^ crc_bit[1]; - new_bit[8] = crc_bit[31] ^ crc_bit[28] ^ crc_bit[23] ^ crc_bit[22] ^ crc_bit[17] ^ crc_bit[12] ^ crc_bit[11] ^ - crc_bit[10] ^ crc_bit[8] ^ crc_bit[4] ^ crc_bit[3] ^ crc_bit[1] ^ crc_bit[0]; - new_bit[7] = crc_bit[29] ^ crc_bit[28] ^ crc_bit[25] ^ crc_bit[24] ^ crc_bit[23] ^ crc_bit[22] ^ crc_bit[21] ^ - crc_bit[16] ^ crc_bit[15] ^ crc_bit[10] ^ crc_bit[8] ^ crc_bit[7] ^ crc_bit[5] ^ crc_bit[3] ^ - crc_bit[2] ^ crc_bit[0]; - new_bit[6] = crc_bit[30] ^ crc_bit[29] ^ crc_bit[25] ^ crc_bit[22] ^ crc_bit[21] ^ crc_bit[20] ^ crc_bit[14] ^ - crc_bit[11] ^ crc_bit[8] ^ crc_bit[7] ^ crc_bit[6] ^ crc_bit[5] ^ crc_bit[4] ^ crc_bit[2] ^ - crc_bit[1]; - new_bit[5] = crc_bit[29] ^ crc_bit[28] ^ crc_bit[24] ^ crc_bit[21] ^ crc_bit[20] ^ crc_bit[19] ^ crc_bit[13] ^ - crc_bit[10] ^ crc_bit[7] ^ crc_bit[6] ^ crc_bit[5] ^ crc_bit[4] ^ crc_bit[3] ^ crc_bit[1] ^ - crc_bit[0]; - new_bit[4] = crc_bit[31] ^ crc_bit[30] ^ crc_bit[29] ^ crc_bit[25] ^ crc_bit[24] ^ crc_bit[20] ^ crc_bit[19] ^ - crc_bit[18] ^ crc_bit[15] ^ crc_bit[12] ^ crc_bit[11] ^ crc_bit[8] ^ crc_bit[6] ^ crc_bit[4] ^ - crc_bit[3] ^ crc_bit[2] ^ crc_bit[0]; - new_bit[3] = crc_bit[31] ^ crc_bit[27] ^ crc_bit[25] ^ crc_bit[19] ^ crc_bit[18] ^ crc_bit[17] ^ crc_bit[15] ^ - crc_bit[14] ^ crc_bit[10] ^ crc_bit[9] ^ crc_bit[8] ^ crc_bit[7] ^ crc_bit[3] ^ crc_bit[2] ^ - crc_bit[1]; - new_bit[2] = crc_bit[31] ^ crc_bit[30] ^ crc_bit[26] ^ crc_bit[24] ^ crc_bit[18] ^ crc_bit[17] ^ crc_bit[16] ^ - crc_bit[14] ^ crc_bit[13] ^ crc_bit[9] ^ crc_bit[8] ^ crc_bit[7] ^ crc_bit[6] ^ crc_bit[2] ^ - crc_bit[1] ^ crc_bit[0]; - new_bit[1] = crc_bit[28] ^ crc_bit[27] ^ crc_bit[24] ^ crc_bit[17] ^ crc_bit[16] ^ crc_bit[13] ^ crc_bit[12] ^ - crc_bit[11] ^ crc_bit[9] ^ crc_bit[7] ^ crc_bit[6] ^ crc_bit[1] ^ crc_bit[0]; - new_bit[0] = crc_bit[31] ^ crc_bit[30] ^ crc_bit[29] ^ crc_bit[28] ^ crc_bit[26] ^ crc_bit[25] ^ crc_bit[24] ^ - crc_bit[16] ^ crc_bit[12] ^ crc_bit[10] ^ crc_bit[9] ^ crc_bit[6] ^ crc_bit[0]; - - /* The new CRC value has been calculated as individual bits in the */ - /* new_bit array. Re-assembled it into a 32 bit value and "clock" it */ - /* into the "register". */ - crc = 0; - for (i = 31; i >= 0; --i) { - crc = crc << 1; - crc |= new_bit[i]; - } - } - printf("%08x\n", crc); - return 0; -} diff --git a/lib/sata/test/crc_tb.py b/lib/sata/test/crc_tb.py deleted file mode 100644 index 2b974fbb..00000000 --- a/lib/sata/test/crc_tb.py +++ /dev/null @@ -1,59 +0,0 @@ -import subprocess - -from lib.sata.common import * -from lib.sata.link.crc import * - -from lib.sata.test.common import * - -class TB(Module): - def __init__(self, length, random): - self.crc = SATACRC() - self.length = length - self.random = random - - def get_c_crc(self, datas): - stdin = "" - for data in datas: - stdin += "0x%08x " %data - stdin += "exit" - with subprocess.Popen("./crc", stdin=subprocess.PIPE, stdout=subprocess.PIPE) as process: - process.stdin.write(stdin.encode("ASCII")) - out, err = process.communicate() - return int(out.decode("ASCII"), 16) - - def gen_simulation(self, selfp): - # init CRC - selfp.crc.d = 0 - selfp.crc.ce = 1 - selfp.crc.reset = 1 - yield - selfp.crc.reset = 0 - - # feed CRC with datas - datas = [] - for i in range(self.length): - data = seed_to_data(i, self.random) - datas.append(data) - selfp.crc.d = data - yield - - # log results - yield - sim_crc = selfp.crc.value - - # stop - selfp.crc.ce = 0 - for i in range(32): - yield - - # get C core reference - c_crc = self.get_c_crc(datas) - - # check results - s, l, e = check(c_crc, sim_crc) - print("shift "+ str(s) + " / length " + str(l) + " / errors " + str(e)) - -if __name__ == "__main__": - from migen.sim.generic import run_simulation - length = 8192 - run_simulation(TB(length, True), ncycles=length+100, vcd_name="my.vcd") diff --git a/lib/sata/test/hdd.py b/lib/sata/test/hdd.py deleted file mode 100644 index e390d81b..00000000 --- a/lib/sata/test/hdd.py +++ /dev/null @@ -1,511 +0,0 @@ -import subprocess -import math - -from lib.sata.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 -class PHYDword: - def __init__(self, dat=0): - self.dat = dat - self.start = 1 - self.done = 0 - -class PHYSource(Module): - def __init__(self): - self.source = Source(phy_description(32)) - ### - self.dword = PHYDword() - - def send(self, dword): - self.dword = dword - - def do_simulation(self, selfp): - 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 - -class PHYSink(Module): - def __init__(self): - self.sink = Sink(phy_description(32)) - ### - self.dword = PHYDword() - - 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 - -class PHYLayer(Module): - def __init__(self): - - self.rx = PHYSink() - self.tx = PHYSource() - - self.source = self.tx.source - self.sink = self.rx.sink - - def send(self, dword): - packet = PHYDword(dword) - self.tx.send(packet) - - def receive(self): - yield from self.rx.receive() - - def __repr__(self): - receiving = "%08x " %self.rx.dword.dat - receiving += decode_primitive(self.rx.dword.dat) - receiving += " "*(16-len(receiving)) - - sending = "%08x " %self.tx.dword.dat - sending += decode_primitive(self.tx.dword.dat) - sending += " "*(16-len(sending)) - - return receiving + sending - -# Link Layer model -def print_link(s): - print_with_prefix(s, "[LNK]: ") - -def import_scrambler_datas(): - 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]] - -class LinkPacket(list): - def __init__(self, init=[]): - self.ongoing = False - self.done = False - self.scrambled_datas = import_scrambler_datas() - for dword in init: - self.append(dword) - -class LinkRXPacket(LinkPacket): - def descramble(self): - for i in range(len(self)): - self[i] = self[i] ^ self.scrambled_datas[i] - - 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")) - out, err = process.communicate() - crc = int(out.decode("ASCII"), 16) - r = (self[-1] == crc) - self.pop() - return r - - def decode(self): - self.descramble() - return self.check_crc() - -class LinkTXPacket(LinkPacket): - def insert_crc(self): - stdin = "" - for v in self: - 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) - self.append(crc) - - def scramble(self): - for i in range(len(self)): - self[i] = self[i] ^ self.scrambled_datas[i] - - def encode(self): - self.insert_crc() - self.scramble() - -class LinkLayer(Module): - def __init__(self, phy, debug=False, random_level=0): - self.phy = phy - self.debug = debug - self.random_level = random_level - self.tx_packets = [] - self.tx_packet = LinkTXPacket() - self.rx_packet = LinkRXPacket() - - self.rx_cont = False - self.rx_last = 0 - self.tx_cont = False - self.tx_cont_nb = -1 - self.tx_lasts = [0, 0, 0] - - self.scrambled_datas = import_scrambler_datas() - - self.transport_callback = None - - self.send_state = "" - self.send_states = ["RDY", "SOF", "DATA", "EOF", "WTRM"] - - def set_transport_callback(self, callback): - self.transport_callback = callback - - def send(self, dword): - if self.send_state == "RDY": - self.phy.send(primitives["X_RDY"]) - if dword == primitives["R_RDY"]: - self.send_state = "SOF" - elif self.send_state == "SOF": - self.phy.send(primitives["SOF"]) - self.send_state = "DATA" - elif self.send_state == "DATA": - if dword == primitives["HOLD"]: - self.phy.send(primitives["HOLDA"]) - else: - self.phy.send(self.tx_packet.pop(0)) - if len(self.tx_packet) == 0: - self.send_state = "EOF" - elif self.send_state == "EOF": - self.phy.send(primitives["EOF"]) - self.send_state = "WTRM" - elif self.send_state == "WTRM": - self.phy.send(primitives["WTRM"]) - if dword == primitives["R_OK"]: - self.tx_packet.done = True - elif dword == primitives["R_ERR"]: - self.tx_packet.done = True - if self.tx_packet.done: - self.phy.send(primitives["SYNC"]) - - def insert_cont(self): - self.tx_lasts.pop(0) - self.tx_lasts.append(self.phy.tx.dword.dat) - self.tx_cont = True - for i in range(3): - if not is_primitive(self.tx_lasts[i]): - self.tx_cont = False - if self.tx_lasts[i] != self.tx_lasts[0]: - self.tx_cont = False - if self.tx_cont: - if self.tx_cont_nb == 0: - self.phy.send(primitives["CONT"]) - else: - self.phy.send(self.scrambled_datas[self.tx_cont_nb]) - self.tx_cont_nb += 1 - else: - self.tx_cont_nb = 0 - - def remove_cont(self, dword): - if dword == primitives["HOLD"]: - if self.rx_cont: - self.tx_lasts = [0, 0, 0] - if dword == primitives["CONT"]: - self.rx_cont = True - elif is_primitive(dword): - self.rx_last = dword - self.rx_cont = False - if self.rx_cont: - dword = self.rx_last - return dword - - def callback(self, dword): - if dword == primitives["X_RDY"]: - self.phy.send(primitives["R_RDY"]) - elif dword == primitives["WTRM"]: - self.phy.send(primitives["R_OK"]) - if self.rx_packet.ongoing: - self.rx_packet.decode() - if self.transport_callback is not None: - self.transport_callback(self.rx_packet) - self.rx_packet.ongoing = False - elif dword == primitives["HOLD"]: - self.phy.send(primitives["HOLDA"]) - elif dword == primitives["EOF"]: - pass - elif self.rx_packet.ongoing: - if dword != primitives["HOLD"]: - n = randn(100) - if n < self.random_level: - self.phy.send(primitives["HOLD"]) - else: - self.phy.send(primitives["R_IP"]) - if not is_primitive(dword): - self.rx_packet.append(dword) - elif dword == primitives["SOF"]: - self.rx_packet = LinkRXPacket() - self.rx_packet.ongoing = True - - def gen_simulation(self, selfp): - self.tx_packet.done = True - self.phy.send(primitives["SYNC"]) - while True: - yield from self.phy.receive() - if self.debug: - print_link(self.phy) - self.phy.send(primitives["SYNC"]) - rx_dword = self.phy.rx.dword.dat - rx_dword = self.remove_cont(rx_dword) - if len(self.tx_packets) != 0: - if self.tx_packet.done: - self.tx_packet = self.tx_packets.pop(0) - self.tx_packet.encode() - self.send_state = "RDY" - if not self.tx_packet.done: - self.send(rx_dword) - else: - self.callback(rx_dword) - self.insert_cont() - -# Transport Layer model -def print_transport(s): - print_with_prefix(s, "[TRN]: ") - -def get_field_data(field, packet): - return (packet[field.dword] >> field.offset) & (2**field.width-1) - -class FIS: - def __init__(self, packet, description, direction="H2D"): - self.packet = packet - self.description = description - self.direction = direction - self.decode() - - def decode(self): - for k, v in self.description.items(): - setattr(self, k, get_field_data(v, self.packet)) - - def encode(self): - for k, v in self.description.items(): - self.packet[v.dword] |= (getattr(self, k) << v.offset) - - def __repr__(self): - if self.direction == "H2D": - r = ">>>>>>>>\n" - else: - r = "<<<<<<<<\n" - for k in sorted(self.description.keys()): - r += k + " : 0x%x" %getattr(self,k) + "\n" - return r - -class FIS_REG_H2D(FIS): - def __init__(self, packet=[0]*fis_reg_h2d_cmd_len): - FIS.__init__(self, packet, fis_reg_h2d_layout) - self.type = fis_types["REG_H2D"] - self.direction = "H2D" - - 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_cmd_len): - FIS.__init__(self, packet, fis_reg_d2h_layout) - self.type = fis_types["REG_D2H"] - self.direction = "D2H" - - 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_cmd_len): - FIS.__init__(self, packet, fis_dma_activate_d2h_layout) - self.type = fis_types["DMA_ACTIVATE_D2H"] - self.direction = "D2H" - - def __repr__(self): - r = "FIS_DMA_ACTIVATE_D2H\n" - r += FIS.__repr__(self) - return r - -class FIS_DATA(FIS): - def __init__(self, packet=[0], direction="H2D"): - FIS.__init__(self, packet, fis_data_layout, direction) - self.type = fis_types["DATA"] - - def __repr__(self): - r = "FIS_DATA\n" - r += FIS.__repr__(self) - for data in self.packet[1:]: - r += "%08x\n" %data - return r - -class FIS_UNKNOWN(FIS): - def __init__(self, packet=[0], direction="H2D"): - FIS.__init__(self, packet, {}, direction) - - def __repr__(self): - r = "UNKNOWN\n" - if self.direction == "H2D": - r += ">>>>>>>>\n" - else: - r += "<<<<<<<<\n" - for dword in self.packet: - r += "%08x\n" %dword - return r - -class TransportLayer(Module): - def __init__(self, link, debug=False, loopback=False): - self.link = link - self.debug = debug - self.loopback = loopback - self.link.set_transport_callback(self.callback) - - def set_command_callback(self, callback): - self.command_callback = callback - - def send(self, fis): - fis.encode() - packet = LinkTXPacket(fis.packet) - self.link.tx_packets.append(packet) - if self.debug and not self.loopback: - print_transport(fis) - - def callback(self, packet): - fis_type = packet[0] & 0xff - 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["DATA"]: - fis = FIS_DATA(packet, direction="H2D") - else: - fis = FIS_UNKNOWN(packet, direction="H2D") - if self.debug: - print_transport(fis) - if self.loopback: - self.send(fis) - else: - self.command_callback(fis) - -# Command Layer model -class CommandLayer(Module): - def __init__(self, transport): - self.transport = transport - self.transport.set_command_callback(self.callback) - - self.hdd = None - - def set_hdd(self, hdd): - self.hdd = hdd - - def callback(self, fis): - resp = None - if isinstance(fis, FIS_REG_H2D): - if fis.command == regs["WRITE_DMA_EXT"]: - resp = self.hdd.write_dma_callback(fis) - elif fis.command == regs["READ_DMA_EXT"]: - resp = self.hdd.read_dma_callback(fis) - elif isinstance(fis, FIS_DATA): - resp = self.hdd.data_callback(fis) - - if resp is not None: - for packet in resp: - self.transport.send(packet) - -# HDD model -def print_hdd(s): - print_with_prefix(s, "[HDD]: ") - -class HDDMemRegion: - def __init__(self, base, count, sector_size): - self.base = base - self.count = count - self.data = [0]*(count*sector_size//4) - -class HDD(Module): - def __init__(self, - link_debug=False, link_random_level=0, - transport_debug=False, transport_loopback=False, - hdd_debug=False, - ): - ### - self.phy = PHYLayer() - self.link = LinkLayer(self.phy, link_debug, link_random_level) - self.transport = TransportLayer(self.link, transport_debug, transport_loopback) - self.command = CommandLayer(self.transport) - - self.command.set_hdd(self) - - self.debug = hdd_debug - self.mem = None - self.wr_sector = 0 - self.wr_end_sector = 0 - self.rd_sector = 0 - self.rx_end_sector = 0 - - 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*logical_sector_size//1024) - print_hdd(s) - self.mem = HDDMemRegion(sector, count, logical_sector_size) - - def write(self, sector, data): - n = math.ceil(dwords2sectors(len(data))) - if self.debug: - if n == 1: - s = "{}".format(sector) - else: - s = "{s} to {e}".format(s=sector, e=sector+n-1) - print_hdd("Writing sector " + s) - for i in range(len(data)): - offset = sectors2dwords(sector) - self.mem.data[offset+i] = data[i] - - def read(self, sector, count): - if self.debug: - if count == 1: - s = "{}".format(sector) - else: - s = "{s} to {e}".format(s=sector, e=sector+count-1) - print_hdd("Reading sector " + s) - data = [] - for i in range(sectors2dwords(count)): - data.append(self.mem.data[sectors2dwords(sector)+i]) - return data - - def write_dma_callback(self, fis): - self.wr_sector = fis.lba_lsb + (fis.lba_msb << 32) - self.wr_end_sector = self.wr_sector + fis.count - return [FIS_DMA_ACTIVATE_D2H()] - - def read_dma_callback(self, fis): - self.rd_sector = fis.lba_lsb + (fis.lba_msb << 32) - self.rd_end_sector = self.rd_sector + fis.count - packets = [] - while self.rd_sector != self.rd_end_sector: - count = min(self.rd_end_sector-self.rd_sector, (fis_max_dwords*4)//logical_sector_size) - packet = self.read(self.rd_sector, count) - packet.insert(0, 0) - packets.append(FIS_DATA(packet, direction="D2H")) - self.rd_sector += count - packets.append(FIS_REG_D2H()) - return packets - - def data_callback(self, fis): - self.write(self.wr_sector, fis.packet[1:]) - self.wr_sector += dwords2sectors(len(fis.packet[1:])) - if self.wr_sector == self.wr_end_sector: - return [FIS_REG_D2H()] - else: - return [FIS_DMA_ACTIVATE_D2H()] diff --git a/lib/sata/test/link_tb.py b/lib/sata/test/link_tb.py deleted file mode 100644 index 4c8e689d..00000000 --- a/lib/sata/test/link_tb.py +++ /dev/null @@ -1,48 +0,0 @@ -from lib.sata.common import * -from lib.sata.link import SATALink - -from lib.sata.test.common import * -from lib.sata.test.hdd import * - -class LinkStreamer(PacketStreamer): - def __init__(self): - PacketStreamer.__init__(self, link_description(32), LinkTXPacket) - -class LinkLogger(PacketLogger): - def __init__(self): - PacketLogger.__init__(self, link_description(32), LinkRXPacket) - -class TB(Module): - def __init__(self): - self.hdd = HDD( - link_debug=False, link_random_level=50, - transport_debug=False, transport_loopback=True) - self.link = InsertReset(SATALink(self.hdd.phy)) - - self.streamer = LinkStreamer() - self.streamer_randomizer = Randomizer(link_description(32), level=50) - - self.logger_randomizer = Randomizer(link_description(32), level=50) - self.logger = LinkLogger() - - self.pipeline = Pipeline( - self.streamer, - self.streamer_randomizer, - self.link, - self.logger_randomizer, - self.logger - ) - - def gen_simulation(self, selfp): - for i in range(8): - streamer_packet = LinkTXPacket([i for i in range(64)]) - yield from self.streamer.send(streamer_packet) - yield from self.logger.receive() - - # 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=2048, vcd_name="my.vcd", keep_files=True) diff --git a/lib/sata/test/phy_datapath_tb.py b/lib/sata/test/phy_datapath_tb.py deleted file mode 100644 index d91092f1..00000000 --- a/lib/sata/test/phy_datapath_tb.py +++ /dev/null @@ -1,88 +0,0 @@ -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.cd_sata_rx = ClockDomain() - self.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=10) - self.trx = TRX() - self.ctrl = CTRL() - self.datapath = SATAPHYDatapath(self.trx, self.ctrl) - self.logger_randomizer = Randomizer(phy_description(32), level=10) - 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) diff --git a/lib/sata/test/scrambler.c b/lib/sata/test/scrambler.c deleted file mode 100644 index cee4b30a..00000000 --- a/lib/sata/test/scrambler.c +++ /dev/null @@ -1,113 +0,0 @@ -// Adapted from SATA specification -/****************************************************************************/ -/* */ -/* scramble.c */ -/* */ -/* This sample code generates the entire sequence of 65535 Dwords produced */ -/* by the scrambler defined in the Serial ATA specification. The */ -/* specification calls for an LFSR to generate a string of bits that will */ -/* be packaged into 32 bit Dwords to be XORed with the data Dwords. The */ -/* generator polynomial specified is: */ -/* 16 15 13 4 */ -/* G(x) = x + x + x + x + 1 */ -/* */ -/* Parallelized versions of the scrambler are initialized to a value */ -/* derived from the initialization value of 0xFFFF defined in the */ -/* specification. This implementation is initialized to 0xF0F6. Other */ -/* parallel implementations will have different initial values. The */ -/* important point is that the first Dword output of any implementation */ -/* must equal 0xC2D2768D. */ -/* This code does not represent an elegant solution for a C implementation, */ -/* but it does demonstrate a method of generating the sequence that can be */ -/* easily implemented in hardware. A block diagram of the circuit emulated */ -/* by this code is shown below. */ -/* */ -/* +-----------------------------------+ */ -/* | | */ -/* | | */ -/* | +---+ +---+ | */ -/* | | R | | * | | */ -/* +---->| e |----------+---->| M |----+----> Output(31 downto 16) */ -/* | g | | | 1 | */ -/* +---+ | +---+ */ -/* | */ -/* | +---+ */ -/* | | * | */ -/* +---->| M |---------> Output(15 downto 0) */ -/* | 2 | */ -/* +---+ */ -/* */ -/* The register shown in the block diagram is a 16 bit register. The two */ -/* boxes, *M1 and *M2, each represent a multiply by a 16 by 16 binary */ -/* matrix. A 16 by 16 matrix times a 16 bit vector yields a 16 bit vector. */ -/* The two vectors are the two halves of the 32 bit scrambler value. The */ -/* upper half of the scrambler value is stored back into the context */ -/* register to be used to generate the next value in the scrambler */ -/* */ -/****************************************************************************/ -#include -#include -int main(int argc, char *argv[]) -{ - int i, j; - unsigned int length; - unsigned short context; - unsigned long scrambler; - unsigned char now[16]; - unsigned char next[32]; - context = 0xF0F6; - - scanf("0x%8x", &length); - - for (i = 0; i < length; ++i) { - for (j = 0; j < 16; ++j) { - now[j] = (context >> j) & 0x01; - } - next[31] = now[12] ^ now[10] ^ now[7] ^ now[3] ^ now[1] ^ now[0]; - next[30] = now[15] ^ now[14] ^ now[12] ^ now[11] ^ now[9] ^ now[6] ^ now[3] ^ now[2] ^ now[0]; - next[29] = now[15] ^ now[13] ^ now[12] ^ now[11] ^ now[10] ^ now[8] ^ now[5] ^ now[3] ^ now[2] ^ now[1]; - next[28] = now[14] ^ now[12] ^ now[11] ^ now[10] ^ now[9] ^ now[7] ^ now[4] ^ now[2] ^ now[1] ^ now[0]; - next[27] = now[15] ^ now[14] ^ now[13] ^ now[12] ^ now[11] ^ now[10] ^ now[9] ^ now[8] ^ now[6] ^ now[1] ^ now[0]; - next[26] = now[15] ^ now[13] ^ now[11] ^ now[10] ^ now[9] ^ now[8] ^ now[7] ^ now[5] ^ now[3] ^ now[0]; - next[25] = now[15] ^ now[10] ^ now[9] ^ now[8] ^ now[7] ^ now[6] ^ now[4] ^ now[3] ^ now[2]; - next[24] = now[14] ^ now[9] ^ now[8] ^ now[7] ^ now[6] ^ now[5] ^ now[3] ^ now[2] ^ now[1]; - next[23] = now[13] ^ now[8] ^ now[7] ^ now[6] ^ now[5] ^ now[4] ^ now[2] ^ now[1] ^ now[0]; - next[22] = now[15] ^ now[14] ^ now[7] ^ now[6] ^ now[5] ^ now[4] ^ now[1] ^ now[0]; - next[21] = now[15] ^ now[13] ^ now[12] ^ now[6] ^ now[5] ^ now[4] ^ now[0]; - next[20] = now[15] ^ now[11] ^ now[5] ^ now[4]; - next[19] = now[14] ^ now[10] ^ now[4] ^ now[3]; - next[18] = now[13] ^ now[9] ^ now[3] ^ now[2]; - next[17] = now[12] ^ now[8] ^ now[2] ^ now[1]; - next[16] = now[11] ^ now[7] ^ now[1] ^ now[0]; - - - next[15] = now[15] ^ now[14] ^ now[12] ^ now[10] ^ now[6] ^ now[3] ^ now[0]; - next[14] = now[15] ^ now[13] ^ now[12] ^ now[11] ^ now[9] ^ now[5] ^ now[3] ^ now[2]; - next[13] = now[14] ^ now[12] ^ now[11] ^ now[10] ^ now[8] ^ now[4] ^ now[2] ^ now[1]; - next[12] = now[13] ^ now[11] ^ now[10] ^ now[9] ^ now[7] ^ now[3] ^ now[1] ^ now[0]; - next[11] = now[15] ^ now[14] ^ now[10] ^ now[9] ^ now[8] ^ now[6] ^ now[3] ^ now[2] ^ now[0]; - next[10] = now[15] ^ now[13] ^ now[12] ^ now[9] ^ now[8] ^ now[7] ^ now[5] ^ now[3] ^ now[2] ^ now[1]; - next[9] = now[14] ^ now[12] ^ now[11] ^ now[8] ^ now[7] ^ now[6] ^ now[4] ^ now[2] ^ now[1] ^ now[0]; - next[8] = now[15] ^ now[14] ^ now[13] ^ now[12] ^ now[11] ^ now[10] ^ now[7] ^ now[6] ^ now[5] ^ now[1] ^ now[0]; - next[7] = now[15] ^ now[13] ^ now[11] ^ now[10] ^ now[9] ^ now[6] ^ now[5] ^ now[4] ^ now[3] ^ now[0]; - next[6] = now[15] ^ now[10] ^ now[9] ^ now[8] ^ now[5] ^ now[4] ^ now[2]; - next[5] = now[14] ^ now[9] ^ now[8] ^ now[7] ^ now[4] ^ now[3] ^ now[1]; - next[4] = now[13] ^ now[8] ^ now[7] ^ now[6] ^ now[3] ^ now[2] ^ now[0]; - next[3] = now[15] ^ now[14] ^ now[7] ^ now[6] ^ now[5] ^ now[3] ^ now[2] ^ now[1]; - next[2] = now[14] ^ now[13] ^ now[6] ^ now[5] ^ now[4] ^ now[2] ^ now[1] ^ now[0]; - next[1] = now[15] ^ now[14] ^ now[13] ^ now[5] ^ now[4] ^ now[1] ^ now[0]; - next[0] = now[15] ^ now[13] ^ now[4] ^ now[0]; - - scrambler = 0; - for (j = 31; j >= 0; --j) { - scrambler = scrambler << 1; - scrambler |= next[j]; - } - context = scrambler >> 16; - printf("%08x\n", (unsigned int) scrambler); - - } - - return 0; - -} \ No newline at end of file diff --git a/lib/sata/test/scrambler_tb.py b/lib/sata/test/scrambler_tb.py deleted file mode 100644 index 69d7b63e..00000000 --- a/lib/sata/test/scrambler_tb.py +++ /dev/null @@ -1,49 +0,0 @@ -import subprocess - -from lib.sata.common import * -from lib.sata.link.scrambler import * - -from lib.sata.test.common import * - -class TB(Module): - def __init__(self, length): - self.scrambler = InsertReset(Scrambler()) - self.length = length - - def get_c_values(self, length): - stdin = "0x%08x" %length - with subprocess.Popen("./scrambler", stdin=subprocess.PIPE, stdout=subprocess.PIPE) as process: - process.stdin.write(stdin.encode("ASCII")) - out, err = process.communicate() - return [int(e, 16) for e in out.decode("ASCII").split("\n")[:-1]] - - def gen_simulation(self, selfp): - # init CRC - selfp.scrambler.ce = 1 - selfp.scrambler.reset = 1 - yield - selfp.scrambler.reset = 0 - - # log results - yield - sim_values = [] - for i in range(self.length): - sim_values.append(selfp.scrambler.value) - yield - - # stop - selfp.scrambler.ce = 0 - for i in range(32): - yield - - # get C code reference - c_values = self.get_c_values(self.length) - - # check results - s, l, e = check(c_values, sim_values) - print("shift "+ str(s) + " / length " + str(l) + " / errors " + str(e)) - -if __name__ == "__main__": - from migen.sim.generic import run_simulation - length = 8192 - run_simulation(TB(length), ncycles=length+100, vcd_name="my.vcd") diff --git a/lib/sata/transport/__init__.py b/lib/sata/transport/__init__.py deleted file mode 100644 index 6fcb3037..00000000 --- a/lib/sata/transport/__init__.py +++ /dev/null @@ -1,257 +0,0 @@ -from lib.sata.common import * - -def _get_item(obj, name, width): - if "_lsb" in name: - item = getattr(obj, name.replace("_lsb", ""))[:width] - elif "_msb" in name: - item = getattr(obj, name.replace("_msb", ""))[width:2*width] - else: - item = getattr(obj, name) - return item - -def _encode_cmd(obj, description, signal): - r = [] - for k, v in sorted(description.items()): - start = v.dword*32 + v.offset - end = start + v.width - item = _get_item(obj, k, v.width) - r.append(signal[start:end].eq(item)) - return r - -class SATATransportTX(Module): - def __init__(self, link): - self.sink = sink = Sink(transport_tx_description(32)) - - ### - - cmd_ndwords = max(fis_reg_h2d_cmd_len, fis_data_cmd_len) - encoded_cmd = Signal(cmd_ndwords*32) - - self.counter = counter = Counter(max=cmd_ndwords+1) - - cmd_len = Signal(counter.width) - cmd_with_data = Signal() - - cmd_send = Signal() - data_send = Signal() - cmd_done = Signal() - - def test_type(name): - return sink.type == fis_types[name] - - self.fsm = fsm = FSM(reset_state="IDLE") - fsm.act("IDLE", - sink.ack.eq(0), - counter.reset.eq(1), - If(sink.stb & sink.sop, - If(test_type("REG_H2D"), - NextState("SEND_REG_H2D_CMD") - ).Elif(test_type("DATA"), - NextState("SEND_DATA_CMD") - ).Else( - sink.ack.eq(1) - ) - ).Else( - sink.ack.eq(1) - ) - ) - fsm.act("SEND_REG_H2D_CMD", - _encode_cmd(sink, fis_reg_h2d_layout, encoded_cmd), - cmd_len.eq(fis_reg_h2d_cmd_len-1), - cmd_send.eq(1), - If(cmd_done, - sink.ack.eq(1), - NextState("IDLE") - ) - ) - fsm.act("SEND_DATA_CMD", - sink.ack.eq(0), - _encode_cmd(sink, fis_data_layout, encoded_cmd), - cmd_len.eq(fis_data_cmd_len-1), - cmd_with_data.eq(1), - cmd_send.eq(1), - If(cmd_done, - NextState("SEND_DATA") - ) - ) - fsm.act("SEND_DATA", - data_send.eq(1), - sink.ack.eq(link.sink.ack), - If(sink.stb & sink.eop & sink.ack, - NextState("IDLE") - ) - ) - - cmd_cases = {} - for i in range(cmd_ndwords): - cmd_cases[i] = [link.sink.d.eq(encoded_cmd[32*i:32*(i+1)])] - - self.comb += \ - If(cmd_send, - link.sink.stb.eq(sink.stb), - link.sink.sop.eq(counter.value == 0), - link.sink.eop.eq((counter.value == cmd_len) & ~cmd_with_data), - Case(counter.value, cmd_cases), - counter.ce.eq(sink.stb & link.sink.ack), - cmd_done.eq((counter.value == cmd_len) & link.sink.stb & link.sink.ack) - ).Elif(data_send, - link.sink.stb.eq(sink.stb), - link.sink.sop.eq(0), - link.sink.eop.eq(sink.eop), - link.sink.d.eq(sink.data), - ) - -def _decode_cmd(signal, description, obj): - r = [] - for k, v in sorted(description.items()): - start = v.dword*32+v.offset - end = start+v.width - item = _get_item(obj, k, v.width) - r.append(item.eq(signal[start:end])) - return r - -class SATATransportRX(Module): - def __init__(self, link): - self.source = source = Source(transport_rx_description(32)) - - ### - - cmd_ndwords = max(fis_reg_d2h_cmd_len, fis_dma_activate_d2h_cmd_len, fis_data_cmd_len) - encoded_cmd = Signal(cmd_ndwords*32) - - self.counter = counter = Counter(max=cmd_ndwords+1) - - cmd_len = Signal(counter.width) - - cmd_receive = Signal() - data_receive = Signal() - cmd_done = Signal() - data_done = Signal() - - def test_type(name): - return link.source.d[:8] == fis_types[name] - - self.fsm = fsm = FSM(reset_state="IDLE") - - data_sop = Signal() - - fsm.act("IDLE", - link.source.ack.eq(0), - counter.reset.eq(1), - If(link.source.stb & link.source.sop, - If(test_type("REG_D2H"), - NextState("RECEIVE_REG_D2H_CMD") - ).Elif(test_type("DMA_ACTIVATE_D2H"), - NextState("RECEIVE_DMA_ACTIVATE_D2H_CMD") - ).Elif(test_type("PIO_SETUP_D2H"), - NextState("RECEIVE_PIO_SETUP_D2H_CMD") - ).Elif(test_type("DATA"), - NextState("RECEIVE_DATA_CMD"), - ).Else( - link.source.ack.eq(1) - ) - ).Else( - link.source.ack.eq(1) - ) - ) - fsm.act("RECEIVE_REG_D2H_CMD", - cmd_len.eq(fis_reg_d2h_cmd_len-1), - cmd_receive.eq(1), - link.source.ack.eq(1), - If(cmd_done, - NextState("PRESENT_REG_D2H_CMD") - ) - ) - fsm.act("PRESENT_REG_D2H_CMD", - source.stb.eq(1), - source.sop.eq(1), - source.eop.eq(1), - _decode_cmd(encoded_cmd, fis_reg_d2h_layout, source), - If(source.stb & source.ack, - NextState("IDLE") - ) - ) - fsm.act("RECEIVE_DMA_ACTIVATE_D2H_CMD", - cmd_len.eq(fis_dma_activate_d2h_cmd_len-1), - cmd_receive.eq(1), - link.source.ack.eq(1), - If(cmd_done, - NextState("PRESENT_DMA_ACTIVATE_D2H_CMD") - ) - ) - fsm.act("PRESENT_DMA_ACTIVATE_D2H_CMD", - source.stb.eq(1), - source.sop.eq(1), - source.eop.eq(1), - _decode_cmd(encoded_cmd, fis_dma_activate_d2h_layout, source), - If(source.stb & source.ack, - NextState("IDLE") - ) - ) - fsm.act("RECEIVE_PIO_SETUP_D2H_CMD", - cmd_len.eq(fis_pio_setup_d2h_cmd_len-1), - cmd_receive.eq(1), - link.source.ack.eq(1), - If(cmd_done, - NextState("PRESENT_PIO_SETUP_D2H_CMD") - ) - ) - fsm.act("PRESENT_PIO_SETUP_D2H_CMD", - source.stb.eq(1), - source.sop.eq(1), - source.eop.eq(1), - _decode_cmd(encoded_cmd, fis_pio_setup_d2h_layout, source), - If(source.stb & source.ack, - NextState("IDLE") - ) - ) - fsm.act("RECEIVE_DATA_CMD", - cmd_len.eq(fis_data_cmd_len-1), - cmd_receive.eq(1), - link.source.ack.eq(1), - If(cmd_done, - NextState("PRESENT_DATA") - ) - ) - fsm.act("PRESENT_DATA", - data_receive.eq(1), - source.stb.eq(link.source.stb), - _decode_cmd(encoded_cmd, fis_data_layout, source), - source.sop.eq(data_sop), - source.eop.eq(link.source.eop), - source.error.eq(link.source.error), - source.data.eq(link.source.d), - link.source.ack.eq(source.ack), - If(source.stb & source.eop & source.ack, - NextState("IDLE") - ) - ) - - self.sync += \ - If(fsm.ongoing("RECEIVE_DATA_CMD"), - data_sop.eq(1) - ).Elif(fsm.ongoing("PRESENT_DATA"), - If(source.stb & source.ack, - data_sop.eq(0) - ) - ) - - cmd_cases = {} - for i in range(cmd_ndwords): - cmd_cases[i] = [encoded_cmd[32*i:32*(i+1)].eq(link.source.d)] - - self.comb += \ - If(cmd_receive & link.source.stb, - counter.ce.eq(1) - ) - self.sync += \ - If(cmd_receive, - Case(counter.value, cmd_cases), - ) - self.comb += cmd_done.eq((counter.value == cmd_len) & link.source.ack) - -class SATATransport(Module): - def __init__(self, link): - self.tx = SATATransportTX(link) - self.rx = SATATransportRX(link) - self.sink, self.source = self.tx.sink, self.rx.source diff --git a/litesata/__init__.py b/litesata/__init__.py new file mode 100644 index 00000000..c75fc32c --- /dev/null +++ b/litesata/__init__.py @@ -0,0 +1,23 @@ +from litesata.common import * +from litesata.phy import * +from litesata.core import * +from litesata.frontend import * + +from migen.bank.description import * + +class LiteSATA(Module, AutoCSR): + def __init__(self, phy, + with_crossbar=False, + with_bist=False, with_bist_csr=False): + # phy + self.phy = phy + + # core + self.core = LiteSATACore(self.phy) + + # frontend + if with_crossbar: + self.crossbar = LiteSATACrossbar(self.core) + if with_bist: + self.bist = LiteSATABIST(self.crossbar, with_bist_csr) + diff --git a/litesata/common.py b/litesata/common.py new file mode 100644 index 00000000..99c5d214 --- /dev/null +++ b/litesata/common.py @@ -0,0 +1,287 @@ +import math + +from migen.fhdl.std import * +from migen.genlib.resetsync import * +from migen.genlib.fsm import * +from migen.genlib.record import * +from migen.genlib.misc import chooser, optree +from migen.genlib.cdc import * +from migen.flow.actor import * +from migen.flow.plumbing import Multiplexer, Demultiplexer +from migen.flow.plumbing import Buffer +from migen.actorlib.fifo import * +from migen.actorlib.structuring import Pipeline, Converter + +# PHY / Link Layers +primitives = { + "ALIGN" : 0x7B4A4ABC, + "CONT" : 0X9999AA7C, + "SYNC" : 0xB5B5957C, + "R_RDY" : 0x4A4A957C, + "R_OK" : 0x3535B57C, + "R_ERR" : 0x5656B57C, + "R_IP" : 0X5555B57C, + "X_RDY" : 0x5757B57C, + "CONT" : 0x9999AA7C, + "WTRM" : 0x5858B57C, + "SOF" : 0x3737B57C, + "EOF" : 0xD5D5B57C, + "HOLD" : 0xD5D5AA7C, + "HOLDA" : 0X9595AA7C +} + +def is_primitive(dword): + for k, v in primitives.items(): + if dword == v: + return True + return False + +def decode_primitive(dword): + for k, v in primitives.items(): + if dword == v: + return k + return "" + +def phy_description(dw): + layout = [ + ("data", dw), + ("charisk", dw//8), + ] + return EndpointDescription(layout, packetized=False) + +def link_description(dw): + layout = [ + ("d", dw), + ("error", 1) + ] + return EndpointDescription(layout, packetized=True) + +# Transport Layer +fis_max_dwords = 2048 + +fis_types = { + "REG_H2D": 0x27, + "REG_D2H": 0x34, + "DMA_ACTIVATE_D2H": 0x39, + "PIO_SETUP_D2H": 0x5F, + "DATA": 0x46 +} + +class FISField(): + def __init__(self, dword, offset, width): + self.dword = dword + self.offset = offset + self.width = width + +fis_reg_h2d_cmd_len = 5 +fis_reg_h2d_layout = { + "type": FISField(0, 0, 8), + "pm_port": FISField(0, 8, 4), + "c": FISField(0, 15, 1), + "command": FISField(0, 16, 8), + "features_lsb": FISField(0, 24, 8), + + "lba_lsb": FISField(1, 0, 24), + "device": FISField(1, 24, 8), + + "lba_msb": FISField(2, 0, 24), + "features_msb": FISField(2, 24, 8), + + "count": FISField(3, 0, 16), + "icc": FISField(3, 16, 8), + "control": FISField(3, 24, 8) +} + +fis_reg_d2h_cmd_len = 5 +fis_reg_d2h_layout = { + "type": FISField(0, 0, 8), + "pm_port": FISField(0, 8, 4), + "i": FISField(0, 14, 1), + "status": FISField(0, 16, 8), + "error": FISField(0, 24, 8), + + "lba_lsb": FISField(1, 0, 24), + "device": FISField(1, 24, 8), + + "lba_msb": FISField(2, 0, 24), + + "count": FISField(3, 0, 16) +} + +fis_dma_activate_d2h_cmd_len = 1 +fis_dma_activate_d2h_layout = { + "type": FISField(0, 0, 8), + "pm_port": FISField(0, 8, 4) +} + +fis_pio_setup_d2h_cmd_len = 5 +fis_pio_setup_d2h_layout = { + "type": FISField(0, 0, 8), + "pm_port": FISField(0, 8, 4), + "d": FISField(0, 13, 1), + "i": FISField(0, 14, 1), + "status": FISField(0, 16, 8), + "error": FISField(0, 24, 8), + + "lba_lsb": FISField(1, 0, 24), + + "lba_msb": FISField(2, 0, 24), + + "count": FISField(3, 0, 16), + + "transfer_count": FISField(4, 0, 16), +} + +fis_data_cmd_len = 1 +fis_data_layout = { + "type": FISField(0, 0, 8) +} + +def transport_tx_description(dw): + layout = [ + ("type", 8), + ("pm_port", 4), + ("c", 1), + ("command", 8), + ("features", 16), + ("lba", 48), + ("device", 8), + ("count", 16), + ("icc", 8), + ("control", 8), + ("data", dw) + ] + return EndpointDescription(layout, packetized=True) + +def transport_rx_description(dw): + layout = [ + ("type", 8), + ("pm_port", 4), + ("r", 1), + ("d", 1), + ("i", 1), + ("status", 8), + ("error", 8), + ("lba", 48), + ("device", 8), + ("count", 16), + ("transfer_count", 16), + ("data", dw), + ("error", 1) + ] + return EndpointDescription(layout, packetized=True) + +# Command Layer +regs = { + "WRITE_DMA_EXT" : 0x35, + "READ_DMA_EXT" : 0x25, + "IDENTIFY_DEVICE" : 0xEC +} + +def command_tx_description(dw): + layout = [ + ("write", 1), + ("read", 1), + ("identify", 1), + ("sector", 48), + ("count", 16), + ("data", dw) + ] + return EndpointDescription(layout, packetized=True) + +def command_rx_description(dw): + layout = [ + ("write", 1), + ("read", 1), + ("identify", 1), + ("last", 1), + ("success", 1), + ("failed", 1), + ("data", dw) + ] + return EndpointDescription(layout, packetized=True) + +def command_rx_cmd_description(dw): + layout = [ + ("write", 1), + ("read", 1), + ("identify", 1), + ("last", 1), + ("success", 1), + ("failed", 1) + ] + return EndpointDescription(layout, packetized=False) + +def command_rx_data_description(dw): + layout = [ + ("data", dw) + ] + return EndpointDescription(layout, packetized=True) + +# HDD +logical_sector_size = 512 # constant since all HDDs use this + +def dwords2sectors(n): + return math.ceil(n*4/logical_sector_size) + +def sectors2dwords(n): + return n*logical_sector_size//4 + +# Generic modules +@DecorateModule(InsertReset) +@DecorateModule(InsertCE) +class Counter(Module): + def __init__(self, signal=None, **kwargs): + if signal is None: + self.value = Signal(**kwargs) + else: + self.value = signal + self.width = flen(self.value) + self.sync += self.value.eq(self.value+1) + +@DecorateModule(InsertReset) +@DecorateModule(InsertCE) +class Timeout(Module): + def __init__(self, length): + self.reached = Signal() + ### + value = Signal(max=length) + self.sync += value.eq(value+1) + self.comb += [ + self.reached.eq(value == length) + ] + +# XXX use ModuleDecorator +class BufferizeEndpoints(Module): + def __init__(self, decorated, *args): + self.decorated = decorated + + endpoints = get_endpoints(decorated) + sinks = {} + sources = {} + for name, endpoint in endpoints.items(): + if name in args or len(args) == 0: + if isinstance(endpoint, Sink): + sinks.update({name : endpoint}) + elif isinstance(endpoint, Source): + sources.update({name : endpoint}) + + # add buffer on sinks + for name, sink in sinks.items(): + buf = Buffer(sink.description) + self.submodules += buf + setattr(self, name, buf.d) + self.comb += Record.connect(buf.q, sink) + + # add buffer on sources + for name, source in sources.items(): + buf = Buffer(source.description) + self.submodules += buf + self.comb += Record.connect(source, buf.d) + setattr(self, name, buf.q) + + def __getattr__(self, name): + return getattr(self.decorated, name) + + def __dir__(self): + return dir(self.decorated) diff --git a/litesata/core/__init__.py b/litesata/core/__init__.py new file mode 100644 index 00000000..c32028bb --- /dev/null +++ b/litesata/core/__init__.py @@ -0,0 +1,11 @@ +from litesata.common import * +from litesata.core.link import LiteSATALink +from litesata.core.transport import LiteSATATransport +from litesata.core.command import LiteSATACommand + +class LiteSATACore(Module): + def __init__(self, phy): + self.link = LiteSATALink(phy) + self.transport = LiteSATATransport(self.link) + self.command = LiteSATACommand(self.transport) + self.sink, self.source = self.command.sink, self.command.source diff --git a/litesata/core/command/__init__.py b/litesata/core/command/__init__.py new file mode 100644 index 00000000..130b5fc3 --- /dev/null +++ b/litesata/core/command/__init__.py @@ -0,0 +1,314 @@ +from litesata.common import * + +tx_to_rx = [ + ("write", 1), + ("read", 1), + ("identify", 1), + ("count", 16) +] + +rx_to_tx = [ + ("dma_activate", 1) +] + +class LiteSATACommandTX(Module): + def __init__(self, transport): + 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), + transport.sink.data.eq(sink.data) + ] + + self.dwords_counter = dwords_counter = Counter(max=fis_max_dwords) + + self.fsm = fsm = FSM(reset_state="IDLE") + fsm.act("IDLE", + sink.ack.eq(0), + 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", + dwords_counter.reset.eq(1), + If(from_rx.dma_activate, + NextState("SEND_DATA") + ) + ) + fsm.act("SEND_DATA", + dwords_counter.ce.eq(sink.stb & sink.ack), + + transport.sink.stb.eq(sink.stb), + transport.sink.sop.eq(dwords_counter.value == 0), + transport.sink.eop.eq((dwords_counter.value == (fis_max_dwords-1)) | sink.eop), + + transport.sink.type.eq(fis_types["DATA"]), + sink.ack.eq(transport.sink.ack), + If(sink.stb & sink.ack, + If(sink.eop, + NextState("IDLE") + ).Elif(dwords_counter.value == (fis_max_dwords-1), + NextState("WAIT_DMA_ACTIVATE") + ) + ) + ) + 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"]), + 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 LiteSATACommandRX(Module): + def __init__(self, transport): + 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_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 + + def test_type(name): + return transport.source.type == fis_types[name] + + identify = Signal() + dma_activate = Signal() + 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) + + self.fsm = fsm = FSM(reset_state="IDLE") + fsm.act("IDLE", + self.dwords_counter.reset.eq(1), + transport.source.ack.eq(1), + If(from_tx.write, + NextState("WAIT_WRITE_ACTIVATE_OR_REG_D2H") + ).Elif(from_tx.read, + NextState("WAIT_READ_DATA_OR_REG_D2H"), + ).Elif(from_tx.identify, + NextState("WAIT_PIO_SETUP_D2H"), + ) + ) + self.sync += \ + If(fsm.ongoing("IDLE"), + identify.eq(from_tx.identify) + ) + fsm.act("WAIT_WRITE_ACTIVATE_OR_REG_D2H", + transport.source.ack.eq(1), + If(transport.source.stb, + If(test_type("DMA_ACTIVATE_D2H"), + dma_activate.eq(1), + ).Elif(test_type("REG_D2H"), + # XXX: use status and error fields of REG_D2H + NextState("PRESENT_WRITE_RESPONSE") + ) + ) + ) + fsm.act("PRESENT_WRITE_RESPONSE", + 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, + NextState("IDLE") + ) + ) + fsm.act("WAIT_READ_DATA_OR_REG_D2H", + transport.source.ack.eq(1), + If(transport.source.stb, + transport.source.ack.eq(0), + If(test_type("DATA"), + NextState("PRESENT_READ_DATA") + ).Elif(test_type("REG_D2H"), + # XXX: use status and error fields of REG_D2H + NextState("PRESENT_READ_RESPONSE") + ) + ) + ) + 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") + ) + ) + + self.comb += [ + data_buffer.sink.sop.eq(transport.source.sop), + data_buffer.sink.eop.eq(transport.source.eop), + data_buffer.sink.data.eq(transport.source.data) + ] + fsm.act("PRESENT_READ_DATA", + data_buffer.sink.stb.eq(transport.source.stb), + transport.source.ack.eq(data_buffer.sink.ack), + If(data_buffer.sink.stb & data_buffer.sink.ack, + self.dwords_counter.ce.eq(~read_done), + If(data_buffer.sink.eop, + If(read_done & ~identify, + NextState("WAIT_READ_DATA_OR_REG_D2H") + ).Else( + NextState("PRESENT_READ_RESPONSE") + ) + ) + ) + ) + read_error = Signal() + self.sync += \ + If(fsm.before_entering("PRESENT_READ_DATA"), + read_error.eq(1) + ).Elif(transport.source.stb & transport.source.ack & transport.source.eop, + read_error.eq(transport.source.error) + ) + fsm.act("PRESENT_READ_RESPONSE", + cmd_buffer.sink.stb.eq(1), + cmd_buffer.sink.read.eq(~identify), + cmd_buffer.sink.identify.eq(identify), + cmd_buffer.sink.last.eq(read_done | identify), + 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) + ), + If(read_done | identify, + NextState("IDLE") + ).Else( + NextState("WAIT_READ_DATA_OR_REG_D2H") + ) + ) + ) + + self.out_fsm = out_fsm = FSM(reset_state="IDLE") + out_fsm.act("IDLE", + If(cmd_buffer.source.stb, + If((cmd_buffer.source.read | cmd_buffer.source.identify) & cmd_buffer.source.success, + NextState("PRESENT_RESPONSE_WITH_DATA"), + ).Else( + NextState("PRESENT_RESPONSE_WITHOUT_DATA"), + ) + ) + ) + + self.comb += [ + source.write.eq(cmd_buffer.source.write), + source.read.eq(cmd_buffer.source.read), + source.identify.eq(cmd_buffer.source.identify), + source.last.eq(cmd_buffer.source.last), + source.success.eq(cmd_buffer.source.success), + source.failed.eq(cmd_buffer.source.failed), + source.data.eq(data_buffer.source.data) + ] + + out_fsm.act("PRESENT_RESPONSE_WITH_DATA", + source.stb.eq(data_buffer.source.stb), + source.sop.eq(data_buffer.source.sop), + source.eop.eq(data_buffer.source.eop), + + data_buffer.source.ack.eq(source.ack), + + If(source.stb & source.eop & source.ack, + cmd_buffer.source.ack.eq(1), + NextState("IDLE") + ) + ) + out_fsm.act("PRESENT_RESPONSE_WITHOUT_DATA", + source.stb.eq(1), + source.sop.eq(1), + source.eop.eq(1), + If(source.stb & source.ack, + cmd_buffer.source.ack.eq(1), + NextState("IDLE") + ) + ) + + self.comb += [ + to_tx.dma_activate.eq(dma_activate), + ] + +class LiteSATACommand(Module): + def __init__(self, transport): + self.tx = LiteSATACommandTX(transport) + self.rx = LiteSATACommandRX(transport) + 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 diff --git a/litesata/core/link/__init__.py b/litesata/core/link/__init__.py new file mode 100644 index 00000000..64884eee --- /dev/null +++ b/litesata/core/link/__init__.py @@ -0,0 +1,244 @@ +from litesata.common import * +from litesata.core.link.crc import LiteSATACRCInserter, LiteSATACRCChecker +from litesata.core.link.scrambler import LiteSATAScrambler +from litesata.core.link.cont import LiteSATACONTInserter, LiteSATACONTRemover + +from_rx = [ + ("idle", 1), + ("insert", 32), + ("det", 32) +] + +class LiteSATALinkTX(Module): + def __init__(self, phy): + self.sink = Sink(link_description(32)) + self.from_rx = Sink(from_rx) + + ### + + self.fsm = fsm = FSM(reset_state="IDLE") + + # insert CRC + crc = LiteSATACRCInserter(link_description(32)) + self.submodules += crc + + # scramble + scrambler = LiteSATAScrambler(link_description(32)) + self.submodules += scrambler + + # connect CRC / scrambler + self.comb += [ + Record.connect(self.sink, crc.sink), + Record.connect(crc.source, scrambler.sink) + ] + + # inserter CONT and scrambled data between + # CONT and next primitive + self.cont = cont = BufferizeEndpoints(LiteSATACONTInserter(phy_description(32)), "source") + + # datas / primitives mux + insert = Signal(32) + self.comb += [ + If(self.from_rx.insert, + cont.sink.stb.eq(1), + cont.sink.data.eq(self.from_rx.insert), + cont.sink.charisk.eq(0x0001), + ). + Elif(insert, + cont.sink.stb.eq(1), + cont.sink.data.eq(insert), + cont.sink.charisk.eq(0x0001), + ).Elif(fsm.ongoing("COPY"), + cont.sink.stb.eq(scrambler.source.stb), + cont.sink.data.eq(scrambler.source.d), + scrambler.source.ack.eq(cont.sink.ack), + cont.sink.charisk.eq(0) + ) + ] + self.comb += Record.connect(cont.source, phy.sink) + + # FSM + fsm.act("IDLE", + scrambler.reset.eq(1), + If(self.from_rx.idle, + insert.eq(primitives["SYNC"]), + If(scrambler.source.stb & scrambler.source.sop, + If(self.from_rx.det == primitives["SYNC"], + NextState("RDY") + ) + ) + ) + ) + fsm.act("RDY", + insert.eq(primitives["X_RDY"]), + If(~self.from_rx.idle, + NextState("IDLE") + ).Elif(self.from_rx.det == primitives["R_RDY"], + NextState("SOF") + ) + ) + fsm.act("SOF", + insert.eq(primitives["SOF"]), + If(phy.sink.ack, + NextState("COPY") + ) + ) + fsm.act("COPY", + If(self.from_rx.det == primitives["HOLD"], + insert.eq(primitives["HOLDA"]), + ).Elif(~scrambler.source.stb, + insert.eq(primitives["HOLD"]), + ).Elif(scrambler.source.stb & scrambler.source.eop & scrambler.source.ack, + NextState("EOF") + ) + ) + fsm.act("EOF", + insert.eq(primitives["EOF"]), + If(phy.sink.ack, + NextState("WTRM") + ) + ) + fsm.act("WTRM", + insert.eq(primitives["WTRM"]), + If(self.from_rx.det == primitives["R_OK"], + NextState("IDLE") + ).Elif(self.from_rx.det == primitives["R_ERR"], + NextState("IDLE") + ) + ) + +class LiteSATALinkRX(Module): + def __init__(self, phy): + self.source = Source(link_description(32)) + self.to_tx = Source(from_rx) + + ### + + self.fsm = fsm = FSM(reset_state="IDLE") + + # CONT remover + self.cont = cont = BufferizeEndpoints(LiteSATACONTRemover(phy_description(32)), "source") + self.comb += Record.connect(phy.source, cont.sink) + + # datas / primitives detection + insert = Signal(32) + det = Signal(32) + self.comb += \ + If(cont.source.stb & (cont.source.charisk == 0b0001), + det.eq(cont.source.data) + ) + + # descrambler + self.scrambler = scrambler = LiteSATAScrambler(link_description(32)) + + # check CRC + self.crc = crc = LiteSATACRCChecker(link_description(32)) + + sop = Signal() + eop = Signal() + self.sync += \ + If(fsm.ongoing("IDLE"), + sop.eq(1), + ).Elif(fsm.ongoing("COPY"), + If(scrambler.sink.stb & scrambler.sink.ack, + sop.eq(0) + ) + ) + self.comb += eop.eq(det == primitives["EOF"]) + + crc_error = Signal() + self.sync += \ + If(crc.source.stb & crc.source.eop & crc.source.ack, + crc_error.eq(crc.source.error) + ) + + # small fifo to manage HOLD + self.fifo = SyncFIFO(link_description(32), 32) + + # graph + self.comb += [ + cont.source.ack.eq(1), + Record.connect(scrambler.source, crc.sink), + Record.connect(crc.source, self.fifo.sink), + Record.connect(self.fifo.source, self.source) + ] + cont_source_data_d = Signal(32) + self.sync += \ + If(cont.source.stb & (det == 0), + scrambler.sink.d.eq(cont.source.data) + ) + + # FSM + fsm.act("IDLE", + scrambler.reset.eq(1), + If(det == primitives["X_RDY"], + NextState("RDY") + ) + ) + fsm.act("RDY", + insert.eq(primitives["R_RDY"]), + If(det == primitives["SOF"], + NextState("WAIT_FIRST") + ) + ) + fsm.act("WAIT_FIRST", + insert.eq(primitives["R_IP"]), + If(cont.source.stb & (det == 0), + NextState("COPY") + ) + ) + self.comb += [ + scrambler.sink.sop.eq(sop), + scrambler.sink.eop.eq(eop) + ] + fsm.act("COPY", + scrambler.sink.stb.eq(cont.source.stb & ((det == 0) | eop)), + insert.eq(primitives["R_IP"]), + If(det == primitives["HOLD"], + insert.eq(primitives["HOLDA"]) + ).Elif(det == primitives["EOF"], + NextState("WTRM") + ).Elif(self.fifo.fifo.level > 8, + insert.eq(primitives["HOLD"]) + ) + ) + fsm.act("EOF", + insert.eq(primitives["R_IP"]), + If(det == primitives["WTRM"], + NextState("WTRM") + ) + ) + fsm.act("WTRM", + insert.eq(primitives["R_IP"]), + If(~crc_error, + NextState("R_OK") + ).Else( + NextState("R_ERR") + ) + ) + fsm.act("R_OK", + insert.eq(primitives["R_OK"]), + If(det == primitives["SYNC"], + NextState("IDLE") + ) + ) + fsm.act("R_ERR", + insert.eq(primitives["R_ERR"]), + If(det == primitives["SYNC"], + NextState("IDLE") + ) + ) + + # to TX + self.comb += [ + self.to_tx.idle.eq(fsm.ongoing("IDLE")), + self.to_tx.insert.eq(insert), + self.to_tx.det.eq(det) + ] + +class LiteSATALink(Module): + def __init__(self, phy): + self.tx = LiteSATALinkTX(phy) + self.rx = LiteSATALinkRX(phy) + self.comb += Record.connect(self.rx.to_tx, self.tx.from_rx) + self.sink, self.source = self.tx.sink, self.rx.source diff --git a/litesata/core/link/cont.py b/litesata/core/link/cont.py new file mode 100644 index 00000000..dff6a28f --- /dev/null +++ b/litesata/core/link/cont.py @@ -0,0 +1,114 @@ +from litesata.common import * +from litesata.core.link.scrambler import Scrambler + +class LiteSATACONTInserter(Module): + def __init__(self, description): + self.sink = sink = Sink(description) + self.source = source = Source(description) + + ### + + self.counter = counter = Counter(max=4) + + is_data = Signal() + was_data = Signal() + was_hold = Signal() + change = Signal() + self.comb += is_data.eq(sink.charisk == 0) + + last_data = Signal(32) + last_primitive = Signal(32) + last_charisk = Signal(4) + self.sync += [ + If(sink.stb & source.ack, + last_data.eq(sink.data), + last_charisk.eq(sink.charisk), + If(~is_data, + last_primitive.eq(sink.data), + ), + was_data.eq(is_data), + was_hold.eq(last_primitive == primitives["HOLD"]) + ) + ] + self.comb += change.eq( + (sink.data != last_data) | + (sink.charisk != last_charisk) | + is_data + ) + + # scrambler + self.scrambler = scrambler = InsertReset(Scrambler()) + + # Datapath + self.comb += [ + Record.connect(sink, source), + If(sink.stb, + If(~change, + counter.ce.eq(sink.ack & (counter.value !=2)), + # insert CONT + If(counter.value == 1, + source.charisk.eq(0b0001), + source.data.eq(primitives["CONT"]) + # insert scrambled data for EMI + ).Elif(counter.value == 2, + scrambler.ce.eq(sink.ack), + source.charisk.eq(0b0000), + source.data.eq(scrambler.value) + ) + ).Else( + counter.reset.eq(source.ack), + If(counter.value == 2, + # Reinsert last primitive + If(is_data | (~is_data & was_hold), + source.stb.eq(1), + sink.ack.eq(0), + source.charisk.eq(0b0001), + source.data.eq(last_primitive) + ) + ) + ) + ) + ] + +class LiteSATACONTRemover(Module): + def __init__(self, description): + self.sink = sink = Sink(description) + self.source = source = Source(description) + + ### + + is_data = Signal() + is_cont = Signal() + in_cont = Signal() + cont_ongoing = Signal() + + self.comb += [ + is_data.eq(sink.charisk == 0), + is_cont.eq(~is_data & (sink.data == primitives["CONT"])) + ] + self.sync += \ + If(sink.stb & sink.ack, + If(is_cont, + in_cont.eq(1) + ).Elif(~is_data, + in_cont.eq(0) + ) + ) + self.comb += cont_ongoing.eq(is_cont | (in_cont & is_data)) + + # Datapath + last_primitive = Signal(32) + self.sync += [ + If(sink.stb & sink.ack, + If(~is_data & ~is_cont, + last_primitive.eq(sink.data) + ) + ) + ] + self.comb += [ + Record.connect(sink, source), + If(cont_ongoing, + source.charisk.eq(0b0001), + source.data.eq(last_primitive) + ) + ] diff --git a/litesata/core/link/crc.py b/litesata/core/link/crc.py new file mode 100644 index 00000000..1f5ac617 --- /dev/null +++ b/litesata/core/link/crc.py @@ -0,0 +1,114 @@ +from litesata.common import * + +from migen.actorlib.crc import CRCInserter, CRCChecker + +class CRCEngine(Module): + """Cyclic Redundancy Check Engine + + Compute next CRC value from last CRC value and data input using + an optimized asynchronous LFSR. + + Parameters + ---------- + width : int + Width of the data bus and CRC. + polynom : int + Polynom of the CRC (ex: 0x04C11DB7 for IEEE 802.3 CRC) + + Attributes + ---------- + d : in + Data input. + last : in + last CRC value. + next : + next CRC value. + """ + def __init__(self, width, polynom): + self.d = Signal(width) + self.last = Signal(width) + self.next = Signal(width) + + ### + + def _optimize_eq(l): + """ + Replace even numbers of XORs in the equation + with an equivalent XOR + """ + d = {} + for e in l: + if e in d: + d[e] += 1 + else: + d[e] = 1 + r = [] + for key, value in d.items(): + if value%2 != 0: + r.append(key) + return r + + new = Signal(32) + self.comb += new.eq(self.last ^ self.d) + + # compute and optimize CRC's LFSR + curval = [[("new", i)] for i in range(width)] + for i in range(width): + feedback = curval.pop() + for j in range(width-1): + if (polynom & (1<<(j+1))): + curval[j] += feedback + curval[j] = _optimize_eq(curval[j]) + curval.insert(0, feedback) + + # implement logic + for i in range(width): + xors = [] + for t, n in curval[i]: + if t == "new": + xors += [new[n]] + self.comb += self.next[i].eq(optree("^", xors)) + +@DecorateModule(InsertReset) +@DecorateModule(InsertCE) +class LiteSATACRC(Module): + """SATA CRC + + Implement a SATA CRC generator/checker + + Attributes + ---------- + value : out + CRC value (used for generator). + error : out + CRC error (used for checker). + """ + width = 32 + polynom = 0x04C11DB7 + init = 0x52325032 + check = 0x00000000 + def __init__(self, dw=32): + self.d = Signal(self.width) + self.value = Signal(self.width) + self.error = Signal() + + ### + + self.engine = CRCEngine(self.width, self.polynom) + reg_i = Signal(self.width, reset=self.init) + self.sync += reg_i.eq(self.engine.next) + self.comb += [ + self.engine.d.eq(self.d), + self.engine.last.eq(reg_i), + + self.value.eq(reg_i), + self.error.eq(self.engine.next != self.check) + ] + +class LiteSATACRCInserter(CRCInserter): + def __init__(self, description): + CRCInserter.__init__(self, LiteSATACRC, description) + +class LiteSATACRCChecker(CRCChecker): + def __init__(self, description): + CRCChecker.__init__(self, LiteSATACRC, description) diff --git a/litesata/core/link/scrambler.py b/litesata/core/link/scrambler.py new file mode 100644 index 00000000..4b5b9960 --- /dev/null +++ b/litesata/core/link/scrambler.py @@ -0,0 +1,80 @@ +from litesata.common import * + +@DecorateModule(InsertCE) +class Scrambler(Module): + """SATA Scrambler + + Implement a SATA Scrambler + + Attributes + ---------- + value : out + Scrambled value. + """ + def __init__(self): + self.value = Signal(32) + + ### + + context = Signal(16, reset=0xf0f6) + next_value = Signal(32) + self.sync += context.eq(next_value[16:32]) + + # XXX: from SATA specification, replace it with + # a generic implementation using polynoms. + lfsr_coefs = ( + (15, 13, 4, 0), #0 + (15, 14, 13, 5, 4, 1, 0), + (14, 13, 6, 5, 4, 2,1, 0), + (15, 14, 7, 6, 5, 3,2, 1), + (13, 8, 7, 6, 3, 2, 0), + (14, 9, 8, 7, 4, 3, 1), + (15, 10, 9, 8, 5, 4, 2), + (15, 13, 11, 10, 9, 6, 5, 4, 3, 0), + (15, 14, 13, 12, 11, 10,7, 6, 5, 1, 0), + (14, 12, 11, 8, 7, 6, 4, 2, 1, 0), + (15, 13, 12, 9, 8, 7, 5, 3, 2, 1), + (15, 14, 10, 9, 8, 6, 3, 2, 0), + (13, 11, 10, 9, 7, 3, 1, 0), + (14, 12, 11, 10, 8, 4, 2, 1), + (15, 13, 12, 11, 9, 5, 3, 2), + (15, 14, 12, 10, 6, 3, 0), + + (11, 7, 1, 0), #16 + (12, 8, 2, 1), + (13, 9, 3, 2), + (14, 10, 4, 3), + (15, 11, 5, 4), + (15, 13, 12, 6, 5, 4, 0), + (15, 14, 7, 6, 5, 4, 1, 0), + (13, 8, 7, 6, 5, 4, 2, 1, 0), + (14, 9, 8,7, 6, 5, 3, 2, 1), + (15, 10, 9, 8, 7, 6, 4, 3, 2), + (15, 13, 11, 10, 9, 8, 7, 5, 3, 0), + (15, 14, 13, 12, 11, 10, 9, 8, 6, 1, 0), + (14, 12, 11, 10, 9, 7, 4, 2, 1, 0), + (15, 13, 12, 11, 10, 8, 5, 3, 2, 1), + (15, 14, 12, 11, 9, 6, 3, 2, 0), + (12, 10, 7, 3, 1, 0), + ) + + for n, coefs in enumerate(lfsr_coefs): + eq = [context[i] for i in coefs] + self.comb += next_value[n].eq(optree("^", eq)) + + self.comb += self.value.eq(next_value) + +@DecorateModule(InsertReset) +class LiteSATAScrambler(Module): + def __init__(self, description): + self.sink = sink = Sink(description) + self.source = source = Source(description) + + ### + + self.scrambler = Scrambler() + self.comb += [ + self.scrambler.ce.eq(sink.stb & sink.ack), + Record.connect(sink, source), + source.d.eq(sink.d ^ self.scrambler.value) + ] diff --git a/litesata/core/transport/__init__.py b/litesata/core/transport/__init__.py new file mode 100644 index 00000000..e0030217 --- /dev/null +++ b/litesata/core/transport/__init__.py @@ -0,0 +1,258 @@ +from litesata.common import * + +def _get_item(obj, name, width): + if "_lsb" in name: + item = getattr(obj, name.replace("_lsb", ""))[:width] + elif "_msb" in name: + item = getattr(obj, name.replace("_msb", ""))[width:2*width] + else: + item = getattr(obj, name) + return item + +def _encode_cmd(obj, description, signal): + r = [] + for k, v in sorted(description.items()): + start = v.dword*32 + v.offset + end = start + v.width + item = _get_item(obj, k, v.width) + r.append(signal[start:end].eq(item)) + return r + +class LiteSATATransportTX(Module): + def __init__(self, link): + self.sink = sink = Sink(transport_tx_description(32)) + + ### + + cmd_ndwords = max(fis_reg_h2d_cmd_len, fis_data_cmd_len) + encoded_cmd = Signal(cmd_ndwords*32) + + self.counter = counter = Counter(max=cmd_ndwords+1) + + cmd_len = Signal(counter.width) + cmd_with_data = Signal() + + cmd_send = Signal() + data_send = Signal() + cmd_done = Signal() + + def test_type(name): + return sink.type == fis_types[name] + + self.fsm = fsm = FSM(reset_state="IDLE") + fsm.act("IDLE", + sink.ack.eq(0), + counter.reset.eq(1), + If(sink.stb & sink.sop, + If(test_type("REG_H2D"), + NextState("SEND_REG_H2D_CMD") + ).Elif(test_type("DATA"), + NextState("SEND_DATA_CMD") + ).Else( + sink.ack.eq(1) + ) + ).Else( + sink.ack.eq(1) + ) + ) + fsm.act("SEND_REG_H2D_CMD", + _encode_cmd(sink, fis_reg_h2d_layout, encoded_cmd), + cmd_len.eq(fis_reg_h2d_cmd_len-1), + cmd_send.eq(1), + If(cmd_done, + sink.ack.eq(1), + NextState("IDLE") + ) + ) + fsm.act("SEND_DATA_CMD", + sink.ack.eq(0), + _encode_cmd(sink, fis_data_layout, encoded_cmd), + cmd_len.eq(fis_data_cmd_len-1), + cmd_with_data.eq(1), + cmd_send.eq(1), + If(cmd_done, + NextState("SEND_DATA") + ) + ) + fsm.act("SEND_DATA", + data_send.eq(1), + sink.ack.eq(link.sink.ack), + If(sink.stb & sink.eop & sink.ack, + NextState("IDLE") + ) + ) + + cmd_cases = {} + for i in range(cmd_ndwords): + cmd_cases[i] = [link.sink.d.eq(encoded_cmd[32*i:32*(i+1)])] + + self.comb += \ + If(cmd_send, + link.sink.stb.eq(sink.stb), + link.sink.sop.eq(counter.value == 0), + link.sink.eop.eq((counter.value == cmd_len) & ~cmd_with_data), + Case(counter.value, cmd_cases), + counter.ce.eq(sink.stb & link.sink.ack), + cmd_done.eq((counter.value == cmd_len) & link.sink.stb & link.sink.ack) + ).Elif(data_send, + link.sink.stb.eq(sink.stb), + link.sink.sop.eq(0), + link.sink.eop.eq(sink.eop), + link.sink.d.eq(sink.data), + ) + +def _decode_cmd(signal, description, obj): + r = [] + for k, v in sorted(description.items()): + start = v.dword*32+v.offset + end = start+v.width + item = _get_item(obj, k, v.width) + r.append(item.eq(signal[start:end])) + return r + +class LiteSATATransportRX(Module): + def __init__(self, link): + self.source = source = Source(transport_rx_description(32)) + + ### + + cmd_ndwords = max(fis_reg_d2h_cmd_len, fis_dma_activate_d2h_cmd_len, + fis_pio_setup_d2h_cmd_len, fis_data_cmd_len) + encoded_cmd = Signal(cmd_ndwords*32) + + self.counter = counter = Counter(max=cmd_ndwords+1) + + cmd_len = Signal(counter.width) + + cmd_receive = Signal() + data_receive = Signal() + cmd_done = Signal() + data_done = Signal() + + def test_type(name): + return link.source.d[:8] == fis_types[name] + + self.fsm = fsm = FSM(reset_state="IDLE") + + data_sop = Signal() + + fsm.act("IDLE", + link.source.ack.eq(0), + counter.reset.eq(1), + If(link.source.stb & link.source.sop, + If(test_type("REG_D2H"), + NextState("RECEIVE_REG_D2H_CMD") + ).Elif(test_type("DMA_ACTIVATE_D2H"), + NextState("RECEIVE_DMA_ACTIVATE_D2H_CMD") + ).Elif(test_type("PIO_SETUP_D2H"), + NextState("RECEIVE_PIO_SETUP_D2H_CMD") + ).Elif(test_type("DATA"), + NextState("RECEIVE_DATA_CMD"), + ).Else( + link.source.ack.eq(1) + ) + ).Else( + link.source.ack.eq(1) + ) + ) + fsm.act("RECEIVE_REG_D2H_CMD", + cmd_len.eq(fis_reg_d2h_cmd_len-1), + cmd_receive.eq(1), + link.source.ack.eq(1), + If(cmd_done, + NextState("PRESENT_REG_D2H_CMD") + ) + ) + fsm.act("PRESENT_REG_D2H_CMD", + source.stb.eq(1), + source.sop.eq(1), + source.eop.eq(1), + _decode_cmd(encoded_cmd, fis_reg_d2h_layout, source), + If(source.stb & source.ack, + NextState("IDLE") + ) + ) + fsm.act("RECEIVE_DMA_ACTIVATE_D2H_CMD", + cmd_len.eq(fis_dma_activate_d2h_cmd_len-1), + cmd_receive.eq(1), + link.source.ack.eq(1), + If(cmd_done, + NextState("PRESENT_DMA_ACTIVATE_D2H_CMD") + ) + ) + fsm.act("PRESENT_DMA_ACTIVATE_D2H_CMD", + source.stb.eq(1), + source.sop.eq(1), + source.eop.eq(1), + _decode_cmd(encoded_cmd, fis_dma_activate_d2h_layout, source), + If(source.stb & source.ack, + NextState("IDLE") + ) + ) + fsm.act("RECEIVE_PIO_SETUP_D2H_CMD", + cmd_len.eq(fis_pio_setup_d2h_cmd_len-1), + cmd_receive.eq(1), + link.source.ack.eq(1), + If(cmd_done, + NextState("PRESENT_PIO_SETUP_D2H_CMD") + ) + ) + fsm.act("PRESENT_PIO_SETUP_D2H_CMD", + source.stb.eq(1), + source.sop.eq(1), + source.eop.eq(1), + _decode_cmd(encoded_cmd, fis_pio_setup_d2h_layout, source), + If(source.stb & source.ack, + NextState("IDLE") + ) + ) + fsm.act("RECEIVE_DATA_CMD", + cmd_len.eq(fis_data_cmd_len-1), + cmd_receive.eq(1), + link.source.ack.eq(1), + If(cmd_done, + NextState("PRESENT_DATA") + ) + ) + fsm.act("PRESENT_DATA", + data_receive.eq(1), + source.stb.eq(link.source.stb), + _decode_cmd(encoded_cmd, fis_data_layout, source), + source.sop.eq(data_sop), + source.eop.eq(link.source.eop), + source.error.eq(link.source.error), + source.data.eq(link.source.d), + link.source.ack.eq(source.ack), + If(source.stb & source.eop & source.ack, + NextState("IDLE") + ) + ) + + self.sync += \ + If(fsm.ongoing("RECEIVE_DATA_CMD"), + data_sop.eq(1) + ).Elif(fsm.ongoing("PRESENT_DATA"), + If(source.stb & source.ack, + data_sop.eq(0) + ) + ) + + cmd_cases = {} + for i in range(cmd_ndwords): + cmd_cases[i] = [encoded_cmd[32*i:32*(i+1)].eq(link.source.d)] + + self.comb += \ + If(cmd_receive & link.source.stb, + counter.ce.eq(1) + ) + self.sync += \ + If(cmd_receive, + Case(counter.value, cmd_cases), + ) + self.comb += cmd_done.eq((counter.value == cmd_len) & link.source.ack) + +class LiteSATATransport(Module): + def __init__(self, link): + self.tx = LiteSATATransportTX(link) + self.rx = LiteSATATransportRX(link) + self.sink, self.source = self.tx.sink, self.rx.source diff --git a/litesata/frontend/__init__.py b/litesata/frontend/__init__.py new file mode 100644 index 00000000..63785161 --- /dev/null +++ b/litesata/frontend/__init__.py @@ -0,0 +1,4 @@ +from litesata.common import * +from litesata.frontend.crossbar import LiteSATACrossbar +from litesata.frontend.arbiter import LiteSATAArbiter +from litesata.frontend.bist import LiteSATABIST diff --git a/litesata/frontend/arbiter.py b/litesata/frontend/arbiter.py new file mode 100644 index 00000000..049946c1 --- /dev/null +++ b/litesata/frontend/arbiter.py @@ -0,0 +1,31 @@ +from litesata.common import * +from litesata.frontend.common import * + +from migen.genlib.roundrobin import * + +class LiteSATAArbiter(Module): + def __init__(self, slaves, master): + if len(slaves) == 1: + self.comb += slaves[0].connect(master) + else: + self.rr = RoundRobin(len(slaves)) + self.grant = self.rr.grant + cases = {} + for i, slave in enumerate(slaves): + sink, source = slave.sink, slave.source + start = Signal() + done = Signal() + ongoing = Signal() + self.comb += [ + start.eq(sink.stb & sink.sop), + done.eq(source.stb & source.last & source.eop & source.ack) + ] + self.sync += \ + If(start, + ongoing.eq(1) + ).Elif(done, + ongoing.eq(0) + ) + self.comb += self.rr.request[i].eq((start | ongoing) & ~done) + cases[i] = [slaves[i].connect(master)] + self.comb += Case(self.grant, cases) diff --git a/litesata/frontend/bist.py b/litesata/frontend/bist.py new file mode 100644 index 00000000..be994cb4 --- /dev/null +++ b/litesata/frontend/bist.py @@ -0,0 +1,247 @@ +from litesata.common import * +from litesata.core.link.scrambler import Scrambler + +from migen.fhdl.decorators import ModuleDecorator +from migen.bank.description import * + +class LiteSATABISTGenerator(Module): + def __init__(self, sata_master_port): + self.start = Signal() + self.sector = Signal(48) + self.count = Signal(16) + self.random = Signal() + + self.done = Signal() + self.errors = Signal(32) # Note: Not used for writes + + ### + + source, sink = sata_master_port.source, sata_master_port.sink + + self.counter = counter = Counter(bits_sign=32) + + self.scrambler = scrambler = InsertReset(Scrambler()) + self.comb += [ + scrambler.reset.eq(counter.reset), + scrambler.ce.eq(counter.ce) + ] + + self.fsm = fsm = FSM(reset_state="IDLE") + fsm.act("IDLE", + self.done.eq(1), + counter.reset.eq(1), + If(self.start, + NextState("SEND_CMD_AND_DATA") + ) + ) + self.comb += [ + source.sop.eq(counter.value == 0), + source.eop.eq(counter.value == (logical_sector_size//4*self.count)-1), + source.write.eq(1), + source.sector.eq(self.sector), + source.count.eq(self.count), + If(self.random, + source.data.eq(scrambler.value) + ).Else( + source.data.eq(counter.value) + ) + ] + fsm.act("SEND_CMD_AND_DATA", + source.stb.eq(1), + If(source.stb & source.ack, + counter.ce.eq(1), + If(source.eop, + NextState("WAIT_ACK") + ) + ) + ) + fsm.act("WAIT_ACK", + sink.ack.eq(1), + If(sink.stb, + NextState("IDLE") + ) + ) + +class LiteSATABISTChecker(Module): + def __init__(self, sata_master_port): + self.start = Signal() + self.sector = Signal(48) + self.count = Signal(16) + self.random = Signal() + + self.done = Signal() + self.errors = Signal(32) + + ### + + source, sink = sata_master_port.source, sata_master_port.sink + + self.counter = counter = Counter(bits_sign=32) + self.error_counter = Counter(self.errors, bits_sign=32) + + self.scrambler = scrambler = InsertReset(Scrambler()) + self.comb += [ + scrambler.reset.eq(counter.reset), + scrambler.ce.eq(counter.ce) + ] + + self.fsm = fsm = FSM(reset_state="IDLE") + fsm.act("IDLE", + self.done.eq(1), + counter.reset.eq(1), + If(self.start, + self.error_counter.reset.eq(1), + NextState("SEND_CMD") + ) + ) + self.comb += [ + source.sop.eq(1), + source.eop.eq(1), + source.read.eq(1), + source.sector.eq(self.sector), + source.count.eq(self.count), + ] + fsm.act("SEND_CMD", + source.stb.eq(1), + If(source.ack, + counter.reset.eq(1), + NextState("WAIT_ACK") + ) + ) + fsm.act("WAIT_ACK", + If(sink.stb & sink.read, + NextState("RECEIVE_DATA") + ) + ) + expected_data = Signal(32) + self.comb += \ + If(self.random, + expected_data.eq(scrambler.value) + ).Else( + expected_data.eq(counter.value) + ) + fsm.act("RECEIVE_DATA", + sink.ack.eq(1), + If(sink.stb, + counter.ce.eq(1), + If(sink.data != expected_data, + self.error_counter.ce.eq(1) + ), + If(sink.eop, + If(sink.last, + NextState("IDLE") + ).Else( + NextState("WAIT_ACK") + ) + ) + ) + ) + +class LiteSATABISTUnitCSR(Module, AutoCSR): + def __init__(self, bist_unit): + self._start = CSR() + self._sector = CSRStorage(48) + self._count = CSRStorage(16) + self._random = CSRStorage() + self._done = CSRStatus() + self._errors = CSRStatus(32) + self._cycles = CSRStatus(32) + + ### + + self.bist_unit = bist_unit + self.comb += [ + bist_unit.start.eq(self._start.r & self._start.re), + bist_unit.sector.eq(self._sector.storage), + bist_unit.count.eq(self._count.storage), + bist_unit.random.eq(self._random.storage), + + self._done.status.eq(bist_unit.done), + self._errors.status.eq(bist_unit.errors) + ] + + self.cycles_counter = Counter(self._cycles.status) + self.sync += [ + self.cycles_counter.reset.eq(bist_unit.start), + self.cycles_counter.ce.eq(~bist_unit.done) + ] + +class LiteSATABISTIdentify(Module): + def __init__(self, sata_master_port): + self.start = Signal() + self.done = Signal() + + self.fifo = fifo = SyncFIFO([("data", 32)], 512, buffered=True) + self.source = self.fifo.source + + ### + + source, sink = sata_master_port.source, sata_master_port.sink + + self.fsm = fsm = FSM(reset_state="IDLE") + fsm.act("IDLE", + self.done.eq(1), + If(self.start, + NextState("SEND_CMD") + ) + ) + self.comb += [ + source.sop.eq(1), + source.eop.eq(1), + source.identify.eq(1), + ] + fsm.act("SEND_CMD", + source.stb.eq(1), + If(source.stb & source.ack, + NextState("WAIT_ACK") + ) + ) + fsm.act("WAIT_ACK", + If(sink.stb & sink.identify, + NextState("RECEIVE_DATA") + ) + ) + self.comb += fifo.sink.data.eq(sink.data) + fsm.act("RECEIVE_DATA", + sink.ack.eq(fifo.sink.ack), + If(sink.stb, + fifo.sink.stb.eq(1), + If(sink.eop, + NextState("IDLE") + ) + ) + ) + +class LiteSATABISTIdentifyCSR(Module, AutoCSR): + def __init__(self, bist_identify): + self._start = CSR() + self._done = CSRStatus() + self._source_stb = CSRStatus() + self._source_ack = CSR() + self._source_data = CSRStatus(32) + + ### + + self.bist_identify = bist_identify + self.comb += [ + bist_identify.start.eq(self._start.r & self._start.re), + self._done.status.eq(bist_identify.done), + + self._source_stb.status.eq(bist_identify.source.stb), + self._source_data.status.eq(bist_identify.source.data), + bist_identify.source.ack.eq(self._source_ack.r & self._source_ack.re) + ] + +class LiteSATABIST(Module, AutoCSR): + def __init__(self, crossbar, with_csr=False): + generator = LiteSATABISTGenerator(crossbar.get_port()) + checker = LiteSATABISTChecker(crossbar.get_port()) + identify = LiteSATABISTIdentify(crossbar.get_port()) + if with_csr: + self.generator = LiteSATABISTUnitCSR(generator) + self.checker = LiteSATABISTUnitCSR(checker) + self.identify = LiteSATABISTIdentifyCSR(identify) + else: + self.generator = generator + self.checker = checker + self.identify = identify diff --git a/litesata/frontend/common.py b/litesata/frontend/common.py new file mode 100644 index 00000000..308a6c6e --- /dev/null +++ b/litesata/frontend/common.py @@ -0,0 +1,23 @@ +from litesata.common import * + +class LiteSATAMasterPort: + def __init__(self, dw): + self.source = Source(command_tx_description(dw)) + self.sink = Sink(command_rx_description(dw)) + + def connect(self, slave): + return [ + Record.connect(self.source, slave.sink), + Record.connect(slave.source, self.sink) + ] + +class LiteSATASlavePort: + def __init__(self, dw): + self.sink = Sink(command_tx_description(dw)) + self.source = Source(command_rx_description(dw)) + + def connect(self, master): + return [ + Record.connect(self.sink, master.source), + Record.connect(master.sink, self.source) + ] diff --git a/litesata/frontend/crossbar.py b/litesata/frontend/crossbar.py new file mode 100644 index 00000000..f4ca37aa --- /dev/null +++ b/litesata/frontend/crossbar.py @@ -0,0 +1,28 @@ +from litesata.common import * +from litesata.frontend.common import * +from litesata.frontend.arbiter import LiteSATAArbiter + +class LiteSATACrossbar(Module): + def __init__(self, core): + self.slaves = [] + self.master = LiteSATAMasterPort(32) + self.comb += [ + self.master.source.connect(core.sink), + core.source.connect(self.master.sink) + ] + + def get_port(self): + master = LiteSATAMasterPort(32) + slave = LiteSATASlavePort(32) + self.comb += master.connect(slave) + self.slaves.append(slave) + return master + + def get_ports(self, n): + masters = [] + for i in range(n): + masters.append(self.get_port()) + return masters + + def do_finalize(self): + self.arbiter = LiteSATAArbiter(self.slaves, self.master) diff --git a/litesata/phy/__init__.py b/litesata/phy/__init__.py new file mode 100644 index 00000000..fcb206f3 --- /dev/null +++ b/litesata/phy/__init__.py @@ -0,0 +1,23 @@ +from litesata.common import * +from litesata.phy.ctrl import * +from litesata.phy.datapath import * + +class LiteSATAPHY(Module): + def __init__(self, device, pads, speed, clk_freq): + self.speed = speed + # Transceiver / Clocks + if device[:3] == "xc7": # Kintex 7 + from litesata.phy.k7.trx import K7LiteSATAPHYTRX + from litesata.phy.k7.crg import K7LiteSATAPHYCRG + self.trx = K7LiteSATAPHYTRX(pads, speed) + self.crg = K7LiteSATAPHYCRG(pads, self.trx, speed, clk_freq) + else: + msg = "Device" + device + "not (yet) supported." + raise NotImplementedError(msg) + + # Control + self.ctrl = LiteSATAPHYCtrl(self.trx, self.crg, clk_freq) + + # Datapath + self.datapath = LiteSATAPHYDatapath(self.trx, self.ctrl) + self.sink, self.source = self.datapath.sink, self.datapath.source diff --git a/litesata/phy/ctrl.py b/litesata/phy/ctrl.py new file mode 100644 index 00000000..23d64085 --- /dev/null +++ b/litesata/phy/ctrl.py @@ -0,0 +1,148 @@ +from litesata.common import * + +def us(t, clk_freq): + clk_period_us = 1000000/clk_freq + return math.ceil(t/clk_period_us) + +class LiteSATAPHYCtrl(Module): + def __init__(self, trx, crg, clk_freq): + self.ready = Signal() + self.need_reset = Signal() + self.sink = sink = Sink(phy_description(32)) + self.source = source = Source(phy_description(32)) + + ### + self.comb += [ + source.stb.eq(1), + sink.ack.eq(1) + ] + + retry_timeout = Timeout(us(10000, clk_freq)) + align_timeout = Timeout(us(873, clk_freq)) + self.submodules += align_timeout, retry_timeout + + align_detect = Signal() + non_align_cnt = Signal(4) + + self.fsm = fsm = FSM(reset_state="RESET") + fsm.act("RESET", + trx.tx_idle.eq(1), + retry_timeout.reset.eq(1), + align_timeout.reset.eq(1), + If(crg.ready, + NextState("COMINIT") + ), + ) + fsm.act("COMINIT", + trx.tx_idle.eq(1), + trx.tx_cominit_stb.eq(1), + If(trx.tx_cominit_ack & ~trx.rx_cominit_stb, + NextState("AWAIT_COMINIT") + ), + ) + fsm.act("AWAIT_COMINIT", + trx.tx_idle.eq(1), + retry_timeout.ce.eq(1), + If(trx.rx_cominit_stb, + NextState("AWAIT_NO_COMINIT") + ).Else( + If(retry_timeout.reached, + NextState("RESET") + ) + ), + ) + fsm.act("AWAIT_NO_COMINIT", + trx.tx_idle.eq(1), + retry_timeout.reset.eq(1), + If(~trx.rx_cominit_stb, + NextState("CALIBRATE") + ), + ) + fsm.act("CALIBRATE", + trx.tx_idle.eq(1), + NextState("COMWAKE"), + ) + fsm.act("COMWAKE", + trx.tx_idle.eq(1), + trx.tx_comwake_stb.eq(1), + If(trx.tx_comwake_ack, + NextState("AWAIT_COMWAKE") + ), + ) + fsm.act("AWAIT_COMWAKE", + trx.tx_idle.eq(1), + retry_timeout.ce.eq(1), + If(trx.rx_comwake_stb, + NextState("AWAIT_NO_COMWAKE") + ).Else( + If(retry_timeout.reached, + NextState("RESET") + ) + ), + ) + fsm.act("AWAIT_NO_COMWAKE", + trx.tx_idle.eq(1), + If(~trx.rx_comwake_stb, + NextState("AWAIT_NO_RX_IDLE") + ), + ) + fsm.act("AWAIT_NO_RX_IDLE", + trx.tx_idle.eq(0), + source.data.eq(0x4A4A4A4A), #D10.2 + source.charisk.eq(0b0000), + If(~trx.rx_idle, + NextState("AWAIT_ALIGN"), + crg.reset.eq(1), + trx.pmarxreset.eq(1) + ), + ) + fsm.act("AWAIT_ALIGN", + trx.tx_idle.eq(0), + source.data.eq(0x4A4A4A4A), #D10.2 + source.charisk.eq(0b0000), + trx.rx_align.eq(1), + align_timeout.ce.eq(1), + If(align_detect & ~trx.rx_idle, + NextState("SEND_ALIGN") + ).Elif(align_timeout.reached, + NextState("RESET") + ), + ) + fsm.act("SEND_ALIGN", + trx.tx_idle.eq(0), + trx.rx_align.eq(1), + source.data.eq(primitives["ALIGN"]), + source.charisk.eq(0b0001), + If(non_align_cnt == 3, + NextState("READY") + ), + ) + fsm.act("READY", + trx.tx_idle.eq(0), + trx.rx_align.eq(1), + source.data.eq(primitives["SYNC"]), + source.charisk.eq(0b0001), + If(trx.rx_idle, + NextState("RESET") + ), + self.ready.eq(1), + ) + + self.reset_timeout = Timeout(clk_freq//16) + self.comb += [ + self.reset_timeout.ce.eq(~self.ready), + self.need_reset.eq(self.reset_timeout.reached) + ] + + self.comb += \ + align_detect.eq(self.sink.stb & (self.sink.data == primitives["ALIGN"])) + self.sync += \ + If(fsm.ongoing("SEND_ALIGN"), + If(sink.stb, + If(sink.data[0:8] == 0x7C, + non_align_cnt.eq(non_align_cnt + 1) + ).Else( + non_align_cnt.eq(0) + ) + ) + ) diff --git a/litesata/phy/datapath.py b/litesata/phy/datapath.py new file mode 100644 index 00000000..c47e7529 --- /dev/null +++ b/litesata/phy/datapath.py @@ -0,0 +1,157 @@ +from litesata.common import * + +class LiteSATAPHYDatapathRX(Module): + def __init__(self): + self.sink = Sink(phy_description(16)) + self.source = Source(phy_description(32)) + + ### + + # width convertion (16 to 32) and byte alignment + byte_alignment = Signal() + last_charisk = Signal(2) + last_data = Signal(16) + self.sync.sata_rx += \ + If(self.sink.stb & self.sink.ack, + If(self.sink.charisk != 0, + byte_alignment.eq(self.sink.charisk[1]) + ), + last_charisk.eq(self.sink.charisk), + last_data.eq(self.sink.data) + ) + converter = Converter(phy_description(16), phy_description(32), reverse=False) + self.converter = InsertReset(RenameClockDomains(converter, "sata_rx")) + self.comb += [ + self.converter.sink.stb.eq(self.sink.stb), + If(byte_alignment, + self.converter.sink.charisk.eq(Cat(last_charisk[1], self.sink.charisk[0])), + self.converter.sink.data.eq(Cat(last_data[8:], self.sink.data[:8])) + ).Else( + self.converter.sink.charisk.eq(self.sink.charisk), + self.converter.sink.data.eq(self.sink.data) + ), + self.sink.ack.eq(self.converter.sink.ack), + self.converter.reset.eq(self.converter.source.charisk[2:] != 0) + ] + + # clock domain crossing + # (SATA3) 300MHz sata_rx clk to sys_clk + # (SATA2) 150MHz sata_rx clk to sys_clk + # (SATA1) 75MHz sata_rx clk to sys_clk + # requirements: + # due to the convertion ratio of 2, sys_clk need to be > sata_rx/2 + # source destination is always able to accept data (ack always 1) + fifo = AsyncFIFO(phy_description(32), 4) + self.fifo = RenameClockDomains(fifo, {"write": "sata_rx", "read": "sys"}) + self.comb += [ + Record.connect(self.converter.source, fifo.sink), + Record.connect(fifo.source, self.source) + ] + +class LiteSATAPHYDatapathTX(Module): + def __init__(self): + self.sink = Sink(phy_description(32)) + self.source = Source(phy_description(16)) + + ### + + # clock domain crossing + # (SATA3) sys_clk to 300MHz sata_tx clk + # (SATA2) sys_clk to 150MHz sata_tx clk + # (SATA1) sys_clk to 75MHz sata_tx clk + # requirements: + # source destination is always able to accept data (ack always 1) + fifo = AsyncFIFO(phy_description(32), 4) + self.fifo = RenameClockDomains(fifo, {"write": "sys", "read": "sata_tx"}) + self.comb += Record.connect(self.sink, fifo.sink) + + # width convertion (32 to 16) + converter = Converter(phy_description(32), phy_description(16), reverse=False) + self.converter = RenameClockDomains(converter, "sata_tx") + self.comb += [ + Record.connect(self.fifo.source, self.converter.sink), + Record.connect(self.converter.source, self.source) + ] + +class LiteSATAPHYAlignInserter(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 LiteSATAPHYAlignRemover(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 LiteSATAPHYDatapath(Module): + def __init__(self, trx, ctrl): + self.sink = Sink(phy_description(32)) + self.source = Source(phy_description(32)) + + ### + + # TX path + self.align_inserter = LiteSATAPHYAlignInserter(ctrl) + self.mux = Multiplexer(phy_description(32), 2) + self.tx = LiteSATAPHYDatapathTX() + self.comb += [ + 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) + ] + + # RX path + self.rx = LiteSATAPHYDatapathRX() + self.demux = Demultiplexer(phy_description(32), 2) + self.align_remover = LiteSATAPHYAlignRemover() + self.comb += [ + 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) + ] diff --git a/litesata/phy/k7/crg.py b/litesata/phy/k7/crg.py new file mode 100644 index 00000000..9bfc67eb --- /dev/null +++ b/litesata/phy/k7/crg.py @@ -0,0 +1,155 @@ +from litesata.common import * + +class K7LiteSATAPHYCRG(Module): + def __init__(self, pads, gtx, speed, clk_freq): + self.reset = Signal() + self.ready = Signal() + + self.cd_sata_tx = ClockDomain() + self.cd_sata_rx = ClockDomain() + + # CPLL + # (SATA3) 150MHz / VCO @ 3GHz / Line rate @ 6Gbps + # (SATA2 & SATA1) VCO still @ 3 GHz, Line rate is decreased with output dividers. + refclk = Signal() + self.specials += Instance("IBUFDS_GTE2", + i_CEB=0, + i_I=pads.refclk_p, + i_IB=pads.refclk_n, + o_O=refclk + ) + self.comb += gtx.gtrefclk0.eq(refclk) + + # TX clocking + # (SATA3) 150MHz from CPLL TXOUTCLK, sata_tx clk @ 300MHz (16-bits) + # (SATA2) 150MHz from CPLL TXOUTCLK, sata_tx clk @ 150MHz (16-bits) + # (SATA1) 150MHz from CPLL TXOUTCLK, sata_tx clk @ 75MHz (16-bits) + mmcm_reset = Signal() + mmcm_locked = Signal() + mmcm_fb = Signal() + mmcm_clk_i = Signal() + mmcm_clk0_o = Signal() + mmcm_div_config = { + "SATA1" : 16.0, + "SATA2" : 8.0, + "SATA3" : 4.0 + } + mmcm_div = mmcm_div_config[speed] + self.specials += [ + Instance("BUFG", i_I=gtx.txoutclk, o_O=mmcm_clk_i), + Instance("MMCME2_ADV", + p_BANDWIDTH="HIGH", p_COMPENSATION="ZHOLD", i_RST=mmcm_reset, o_LOCKED=mmcm_locked, + + # DRP + i_DCLK=0, i_DEN=0, i_DWE=0, #o_DRDY=, + i_DADDR=0, i_DI=0, #o_DO=, + + # VCO + p_REF_JITTER1=0.01, p_CLKIN1_PERIOD=6.666, + p_CLKFBOUT_MULT_F=8.000, p_CLKFBOUT_PHASE=0.000, p_DIVCLK_DIVIDE=1, + i_CLKIN1=mmcm_clk_i, i_CLKFBIN=mmcm_fb, o_CLKFBOUT=mmcm_fb, + + # CLK0 + p_CLKOUT0_DIVIDE_F=mmcm_div, p_CLKOUT0_PHASE=0.000, o_CLKOUT0=mmcm_clk0_o, + ), + Instance("BUFG", i_I=mmcm_clk0_o, o_O=self.cd_sata_tx.clk), + ] + self.comb += [ + gtx.txusrclk.eq(self.cd_sata_tx.clk), + gtx.txusrclk2.eq(self.cd_sata_tx.clk) + ] + + # RX clocking + # (SATA3) sata_rx recovered clk @ 300MHz from GTX RXOUTCLK + # (SATA2) sata_rx recovered clk @ 150MHz from GTX RXOUTCLK + # (SATA1) sata_rx recovered clk @ 150MHz from GTX RXOUTCLK + self.specials += [ + Instance("BUFG", i_I=gtx.rxoutclk, o_O=self.cd_sata_rx.clk), + ] + self.comb += [ + gtx.rxusrclk.eq(self.cd_sata_rx.clk), + gtx.rxusrclk2.eq(self.cd_sata_rx.clk) + ] + + # Configuration Reset + # After configuration, GTX's resets have to stay low for at least 500ns + # See AR43482 + reset_en = Signal() + clk_period_ns = 1000000000/clk_freq + reset_en_cnt_max = math.ceil(500/clk_period_ns) + reset_en_cnt = Signal(max=reset_en_cnt_max, reset=reset_en_cnt_max-1) + self.sync += \ + If(self.reset, + reset_en_cnt.eq(reset_en_cnt.reset) + ).Elif(~reset_en, + reset_en_cnt.eq(reset_en_cnt-1) + ) + self.comb += reset_en.eq(reset_en_cnt == 0) + + # TX Reset FSM + tx_reset_fsm = InsertReset(FSM(reset_state="IDLE")) + self.submodules += tx_reset_fsm + self.comb += tx_reset_fsm.reset.eq(self.reset) + tx_reset_fsm.act("IDLE", + If(reset_en, + NextState("RESET_GTX"), + ) + ) + tx_reset_fsm.act("RESET_GTX", + gtx.gttxreset.eq(1), + If(gtx.cplllock & mmcm_locked, + NextState("RELEASE_GTX") + ) + ) + tx_reset_fsm.act("RELEASE_GTX", + gtx.txuserrdy.eq(1), + If(gtx.txresetdone, + NextState("READY") + ) + ) + tx_reset_fsm.act("READY", + gtx.txuserrdy.eq(1) + ) + + # RX Reset FSM + rx_reset_fsm = InsertReset(FSM(reset_state="IDLE")) + self.submodules += rx_reset_fsm + self.comb += rx_reset_fsm.reset.eq(self.reset) + + rx_reset_fsm.act("IDLE", + If(reset_en, + NextState("RESET_GTX"), + ) + ) + rx_reset_fsm.act("RESET_GTX", + gtx.gtrxreset.eq(1), + If(gtx.cplllock & mmcm_locked, + NextState("RELEASE_GTX") + ) + ) + rx_reset_fsm.act("RELEASE_GTX", + gtx.rxuserrdy.eq(1), + If(gtx.rxresetdone, + NextState("READY") + ) + ) + rx_reset_fsm.act("READY", + gtx.rxuserrdy.eq(1) + ) + + # Ready + self.tx_ready = tx_reset_fsm.ongoing("READY") + self.rx_ready = rx_reset_fsm.ongoing("READY") + self.comb += self.ready.eq(self.tx_ready & self.rx_ready) + + # Reset PLL + self.comb += gtx.cpllreset.eq(ResetSignal() | self.reset | ~reset_en) + + # Reset MMCM + self.comb += mmcm_reset.eq(ResetSignal() | self.reset | ~gtx.cplllock) + + # Reset for SATA TX/RX clock domains + self.specials += [ + AsyncResetSynchronizer(self.cd_sata_tx, ~self.tx_ready), + AsyncResetSynchronizer(self.cd_sata_rx, ~self.rx_ready), + ] diff --git a/litesata/phy/k7/trx.py b/litesata/phy/k7/trx.py new file mode 100644 index 00000000..ad9a9c83 --- /dev/null +++ b/litesata/phy/k7/trx.py @@ -0,0 +1,852 @@ +from litesata.common import * + +def ones(width): + return 2**width-1 + +class _PulseSynchronizer(PulseSynchronizer): + def __init__(self, i, idomain, o, odomain): + PulseSynchronizer.__init__(self, idomain, odomain) + self.comb += [ + self.i.eq(i), + o.eq(self.o) + ] + +class _RisingEdge(Module): + def __init__(self, i, o): + i_d = Signal() + self.sync += i_d.eq(i) + self.comb += o.eq(i & ~i_d) + +class K7LiteSATAPHYTRX(Module): + def __init__(self, pads, speed): + # Common signals + + # control + self.tx_idle = Signal() #i + + self.tx_cominit_stb = Signal() #i + self.tx_cominit_ack = Signal() #o + self.tx_comwake_stb = Signal() #i + self.tx_comwake_ack = Signal() #o + + self.rx_idle = Signal() #o + self.rx_align = Signal() #i + + self.rx_cominit_stb = Signal() #o + self.rx_comwake_stb = Signal() #o + + # datapath + self.sink = Sink(phy_description(16)) + self.source = Source(phy_description(16)) + + # K7 specific signals + # Channel - Ref Clock Ports + self.gtrefclk0 = Signal() + + # Channel PLL + self.cplllock = Signal() + self.cpllreset = Signal() + + # Receive Ports + self.rxuserrdy = Signal() + self.rxalign = Signal() + + # Receive Ports - 8b10b Decoder + self.rxcharisk = Signal(2) + self.rxdisperr = Signal(2) + + # Receive Ports - RX Data Path interface + self.gtrxreset = Signal() + self.pmarxreset = Signal() + self.rxdata = Signal(16) + self.rxoutclk = Signal() + self.rxusrclk = Signal() + self.rxusrclk2 = Signal() + + # Receive Ports - RX Driver,OOB signalling,Coupling and Eq.,CDR + self.rxelecidle = Signal() + + # Receive Ports - RX PLL Ports + self.rxresetdone = Signal() + + # Receive Ports - RX Ports for SATA + self.rxcominitdet = Signal() + self.rxcomwakedet = Signal() + + # Transmit Ports + self.txuserrdy = Signal() + + # Transmit Ports - 8b10b Encoder Control Ports + self.txcharisk = Signal(2) + + # Transmit Ports - TX Data Path interface + self.gttxreset = Signal() + self.txdata = Signal(16) + self.txoutclk = Signal() + self.txusrclk = Signal() + self.txusrclk2 = Signal() + + # Transmit Ports - TX PLL Ports + self.txresetdone = Signal() + + # Transmit Ports - TX Ports for PCI Express + self.txelecidle = Signal(reset=1) + + # Transmit Ports - TX Ports for SATA + self.txcomfinish = Signal() + self.txcominit = Signal() + self.txcomwake = Signal() + self.txrate = Signal(3) + self.rxcdrlock = Signal() + + # Config at startup + div_config = { + "SATA1" : 4, + "SATA2" : 2, + "SATA3" : 1 + } + rxout_div = div_config[speed] + txout_div = div_config[speed] + + cdr_config = { + "SATA1" : 0x0380008BFF40100008, + "SATA2" : 0x0388008BFF40200008, + "SATA3" : 0X0380008BFF10200010 + } + rxcdr_cfg = cdr_config[speed] + + # Specific / Generic signals encoding/decoding + self.comb += [ + self.txelecidle.eq(self.tx_idle), + self.tx_cominit_ack.eq(self.tx_cominit_stb & self.txcomfinish), + self.tx_comwake_ack.eq(self.tx_comwake_stb & self.txcomfinish), + self.rx_idle.eq(self.rxelecidle), + self.rxalign.eq(self.rx_align), + self.rx_cominit_stb.eq(self.rxcominitdet), + self.rx_comwake_stb.eq(self.rxcomwakedet), + ] + self.submodules += [ + _RisingEdge(self.tx_cominit_stb, self.txcominit), + _RisingEdge(self.tx_comwake_stb, self.txcomwake), + ] + + self.comb += [ + self.txcharisk.eq(self.sink.charisk), + self.txdata.eq(self.sink.data), + self.sink.ack.eq(1), + + self.source.stb.eq(1), + self.source.charisk.eq(self.rxcharisk), + self.source.data.eq(self.rxdata) + ] + + # Internals and clock domain crossing + # sys_clk --> sata_tx clk + txuserrdy = Signal() + txelecidle = Signal(reset=1) + txcominit = Signal() + txcomwake = Signal() + txrate = Signal(3) + + self.specials += [ + MultiReg(self.txuserrdy, txuserrdy, "sata_tx"), + MultiReg(self.txelecidle, txelecidle, "sata_tx"), + MultiReg(self.txrate, txrate, "sata_tx") + ] + self.submodules += [ + _PulseSynchronizer(self.txcominit, "sys", txcominit, "sata_tx"), + _PulseSynchronizer(self.txcomwake, "sys", txcomwake, "sata_tx"), + ] + + # sata_tx clk --> sys clk + txresetdone = Signal() + txcomfinish = Signal() + + self.specials += [ + MultiReg(txresetdone, self.txresetdone, "sys"), + ] + + self.submodules += [ + _PulseSynchronizer(txcomfinish, "sata_tx", self.txcomfinish, "sys"), + ] + + # sys clk --> sata_rx clk + rxuserrdy = Signal() + + self.specials += [ + MultiReg(self.rxuserrdy, rxuserrdy, "sata_rx"), + ] + + # sata_rx clk --> sys clk + rxelecidle = Signal() + rxelecidle_i = Signal() + rxelecidle_cnt_i = Signal(9) + rxresetdone = Signal() + rxcominitdet = Signal() + rxcomwakedet = Signal() + rxratedone = Signal() + rxcdrlock = Signal() + + self.specials += [ + MultiReg(rxelecidle, rxelecidle_i, "sys"), + MultiReg(rxresetdone, self.rxresetdone, "sys"), + MultiReg(rxcominitdet, self.rxcominitdet, "sys"), + MultiReg(rxcomwakedet, self.rxcomwakedet, "sys"), + MultiReg(rxcdrlock, self.rxcdrlock, "sys"), + ] + + self.sync += [ + If(rxelecidle_i != self.rxelecidle, + If(rxelecidle_cnt_i == 0, + self.rxelecidle.eq(rxelecidle_i), + rxelecidle_cnt_i.eq(255) + ).Else( + rxelecidle_cnt_i.eq(rxelecidle_cnt_i-1) + ) + ).Else( + rxelecidle_cnt_i.eq(255) + ) + ] + + self.rxbyteisaligned = Signal() + + # QPLL input clock + self.qpllclk = Signal() + self.qpllrefclk = Signal() + + # Instance + gtxe2_channel_parameters = { + # Simulation-Only Attributes + "p_SIM_RECEIVER_DETECT_PASS":"TRUE", + "p_SIM_TX_EIDLE_DRIVE_LEVEL":"X", + "p_SIM_RESET_SPEEDUP":"TRUE", + "p_SIM_CPLLREFCLK_SEL":0b001, + "p_SIM_VERSION":"4.0", + + # RX Byte and Word Alignment Attributes + "p_ALIGN_COMMA_DOUBLE":"FALSE", + "p_ALIGN_COMMA_ENABLE":ones(10), + "p_ALIGN_COMMA_WORD":2, + "p_ALIGN_MCOMMA_DET":"TRUE", + "p_ALIGN_MCOMMA_VALUE":0b1010000011, + "p_ALIGN_PCOMMA_DET":"TRUE", + "p_ALIGN_PCOMMA_VALUE":0b0101111100, + "p_SHOW_REALIGN_COMMA":"FALSE", + "p_RXSLIDE_AUTO_WAIT":7, + "p_RXSLIDE_MODE":"PCS", + "p_RX_SIG_VALID_DLY":10, + + # RX 8B/10B Decoder Attributes + "p_RX_DISPERR_SEQ_MATCH":"TRUE", + "p_DEC_MCOMMA_DETECT":"TRUE", + "p_DEC_PCOMMA_DETECT":"TRUE", + "p_DEC_VALID_COMMA_ONLY":"FALSE", + + # RX Clock Correction Attributes + "p_CBCC_DATA_SOURCE_SEL":"DECODED", + "p_CLK_COR_SEQ_2_USE":"FALSE", + "p_CLK_COR_KEEP_IDLE":"FALSE", + "p_CLK_COR_MAX_LAT":9, + "p_CLK_COR_MIN_LAT":7, + "p_CLK_COR_PRECEDENCE":"TRUE", + "p_CLK_COR_REPEAT_WAIT":0, + "p_CLK_COR_SEQ_LEN":1, + "p_CLK_COR_SEQ_1_ENABLE":ones(4), + "p_CLK_COR_SEQ_1_1":0b0100000000, + "p_CLK_COR_SEQ_1_2":0b0000000000, + "p_CLK_COR_SEQ_1_3":0b0000000000, + "p_CLK_COR_SEQ_1_4":0b0000000000, + "p_CLK_CORRECT_USE":"FALSE", + "p_CLK_COR_SEQ_2_ENABLE":ones(4), + "p_CLK_COR_SEQ_2_1":0b0100000000, + "p_CLK_COR_SEQ_2_2":0, + "p_CLK_COR_SEQ_2_3":0, + "p_CLK_COR_SEQ_2_4":0, + + # RX Channel Bonding Attributes + "p_CHAN_BOND_KEEP_ALIGN":"FALSE", + "p_CHAN_BOND_MAX_SKEW":1, + "p_CHAN_BOND_SEQ_LEN":1, + "p_CHAN_BOND_SEQ_1_1":0, + "p_CHAN_BOND_SEQ_1_1":0, + "p_CHAN_BOND_SEQ_1_2":0, + "p_CHAN_BOND_SEQ_1_3":0, + "p_CHAN_BOND_SEQ_1_4":0, + "p_CHAN_BOND_SEQ_1_ENABLE":ones(4), + "p_CHAN_BOND_SEQ_2_1":0, + "p_CHAN_BOND_SEQ_2_2":0, + "p_CHAN_BOND_SEQ_2_3":0, + "p_CHAN_BOND_SEQ_2_4":0, + "p_CHAN_BOND_SEQ_2_ENABLE":ones(4), + "p_CHAN_BOND_SEQ_2_USE":"FALSE", + "p_FTS_DESKEW_SEQ_ENABLE":ones(4), + "p_FTS_LANE_DESKEW_CFG":ones(4), + "p_FTS_LANE_DESKEW_EN":"FALSE", + + # RX Margin Analysis Attributes + "p_ES_CONTROL":0, + "p_ES_ERRDET_EN":"FALSE", + "p_ES_EYE_SCAN_EN":"TRUE", + "p_ES_HORZ_OFFSET":0, + "p_ES_PMA_CFG":0, + "p_ES_PRESCALE":0, + "p_ES_QUALIFIER":0, + "p_ES_QUAL_MASK":0, + "p_ES_SDATA_MASK":0, + "p_ES_VERT_OFFSET":0, + + # FPGA RX Interface Attributes + "p_RX_DATA_WIDTH":20, + + # PMA Attributes + "p_OUTREFCLK_SEL_INV":0b11, + "p_PMA_RSV":0x00018480, + "p_PMA_RSV2":0x2050, + "p_PMA_RSV3":0, + "p_PMA_RSV4":0, + "p_RX_BIAS_CFG":0b100, + "p_DMONITOR_CFG":0xA00, + "p_RX_CM_SEL":0b11, + "p_RX_CM_TRIM":0b010, + "p_RX_DEBUG_CFG":0, + "p_RX_OS_CFG":0b10000000, + "p_TERM_RCAL_CFG":0, + "p_TERM_RCAL_OVRD":0, + "p_TST_RSV":0, + "p_RX_CLK25_DIV":6, + "p_TX_CLK25_DIV":6, + "p_UCODEER_CLR":0, + + # PCI Express Attributes + "p_PCS_PCIE_EN":"FALSE", + + # PCS Attributes + "p_PCS_RSVD_ATTR":0x100, + + # RX Buffer Attributes + "p_RXBUF_ADDR_MODE":"FAST", + "p_RXBUF_EIDLE_HI_CNT":0b1000, + "p_RXBUF_EIDLE_LO_CNT":0, + "p_RXBUF_EN":"TRUE", + "p_RX_BUFFER_CFG":0, + "p_RXBUF_RESET_ON_CB_CHANGE":"TRUE", + "p_RXBUF_RESET_ON_COMMAALIGN":"FALSE", + "p_RXBUF_RESET_ON_EIDLE":"FALSE", + "p_RXBUF_RESET_ON_RATE_CHANGE":"TRUE", + "p_RXBUFRESET_TIME":1, + "p_RXBUF_THRESH_OVFLW":61, + "p_RXBUF_THRESH_OVRD":"FALSE", + "p_RXBUF_THRESH_UNDFLW":4, + "p_RXDLY_CFG":0x1f, + "p_RXDLY_LCFG":0x30, + "p_RXDLY_TAP_CFG":0, + "p_RXPH_CFG":0, + "p_RXPHDLY_CFG":0x084820, + "p_RXPH_MONITOR_SEL":0, + "p_RX_XCLK_SEL":"RXUSR", + "p_RX_DDI_SEL":0, + "p_RX_DEFER_RESET_BUF_EN":"TRUE", + + #CDR Attributes + "p_RXCDR_CFG":rxcdr_cfg, + "p_RXCDR_FR_RESET_ON_EIDLE":0, + "p_RXCDR_HOLD_DURING_EIDLE":0, + "p_RXCDR_PH_RESET_ON_EIDLE":0, + "p_RXCDR_LOCK_CFG":0b010101, + + # RX Initialization and Reset Attributes + "p_RXCDRFREQRESET_TIME":1, + "p_RXCDRPHRESET_TIME":1, + "p_RXISCANRESET_TIME":1, + "p_RXPCSRESET_TIME":1, + "p_RXPMARESET_TIME":3, + + # RX OOB Signaling Attributes + "p_RXOOB_CFG":0b0000110, + + # RX Gearbox Attributes + "p_RXGEARBOX_EN":"FALSE", + "p_GEARBOX_MODE":0, + + # PRBS Detection Attribute + "p_RXPRBS_ERR_LOOPBACK":0, + + # Power-Down Attributes + "p_PD_TRANS_TIME_FROM_P2":0x03c, + "p_PD_TRANS_TIME_NONE_P2":0x3c, + "p_PD_TRANS_TIME_TO_P2":0x64, + + # RX OOB Signaling Attributes + "p_SAS_MAX_COM":64, + "p_SAS_MIN_COM":36, + "p_SATA_BURST_SEQ_LEN":0b0101, + "p_SATA_BURST_VAL":0b100, + "p_SATA_EIDLE_VAL":0b100, + "p_SATA_MAX_BURST":8, + "p_SATA_MAX_INIT":21, + "p_SATA_MAX_WAKE":7, + "p_SATA_MIN_BURST":4, + "p_SATA_MIN_INIT":12, + "p_SATA_MIN_WAKE":4, + + # RX Fabric Clock Output Control Attributes + "p_TRANS_TIME_RATE":0x0e, + + # TX Buffer Attributes + "p_TXBUF_EN":"TRUE", + "p_TXBUF_RESET_ON_RATE_CHANGE":"TRUE", + "p_TXDLY_CFG":0x1f, + "p_TXDLY_LCFG":0x030, + "p_TXDLY_TAP_CFG":0, + "p_TXPH_CFG":0x0780, + "p_TXPHDLY_CFG":0x084020, + "p_TXPH_MONITOR_SEL":0, + "p_TX_XCLK_SEL":"TXOUT", + + # FPGA TX Interface Attributes + "p_TX_DATA_WIDTH":20, + + # TX Configurable Driver Attributes + "p_TX_DEEMPH0":0, + "p_TX_DEEMPH1":0, + "p_TX_EIDLE_ASSERT_DELAY":0b110, + "p_TX_EIDLE_DEASSERT_DELAY":0b100, + "p_TX_LOOPBACK_DRIVE_HIZ":"FALSE", + "p_TX_MAINCURSOR_SEL":0, + "p_TX_DRIVE_MODE":"DIRECT", + "p_TX_MARGIN_FULL_0":0b1001110, + "p_TX_MARGIN_FULL_1":0b1001001, + "p_TX_MARGIN_FULL_2":0b1000101, + "p_TX_MARGIN_FULL_3":0b1000010, + "p_TX_MARGIN_FULL_4":0b1000000, + "p_TX_MARGIN_LOW_0":0b1000110, + "p_TX_MARGIN_LOW_1":0b1000100, + "p_TX_MARGIN_LOW_2":0b1000010, + "p_TX_MARGIN_LOW_3":0b1000000, + "p_TX_MARGIN_LOW_4":0b1000000, + + # TX Gearbox Attributes + "p_TXGEARBOX_EN":"FALSE", + + # TX Initialization and Reset Attributes + "p_TXPCSRESET_TIME":1, + "p_TXPMARESET_TIME":1, + + # TX Receiver Detection Attributes + "p_TX_RXDETECT_CFG":0x1832, + "p_TX_RXDETECT_REF":0b100, + + # CPLL Attributes + "p_CPLL_CFG":0xBC07DC, + "p_CPLL_FBDIV":4, + "p_CPLL_FBDIV_45":5, + "p_CPLL_INIT_CFG":0x00001e, + "p_CPLL_LOCK_CFG":0x01e8, + "p_CPLL_REFCLK_DIV":1, + "p_RXOUT_DIV":rxout_div, + "p_TXOUT_DIV":txout_div, + "p_SATA_CPLL_CFG":"VCO_3000MHZ", + + # RX Initialization and Reset Attributes + "p_RXDFELPMRESET_TIME":0b0001111, + + # RX Equalizer Attributes + "p_RXLPM_HF_CFG":0b00000011110000, + "p_RXLPM_LF_CFG":0b00000011110000, + "p_RX_DFE_GAIN_CFG":0x020fea, + "p_RX_DFE_H2_CFG":0b000000000000, + "p_RX_DFE_H3_CFG":0b000001000000, + "p_RX_DFE_H4_CFG":0b00011110000, + "p_RX_DFE_H5_CFG":0b00011100000, + "p_RX_DFE_KL_CFG":0b0000011111110, + "p_RX_DFE_LPM_CFG":0x0954, + "p_RX_DFE_LPM_HOLD_DURING_EIDLE":0, + "p_RX_DFE_UT_CFG":0b10001111000000000, + "p_RX_DFE_VP_CFG":0b00011111100000011, + + # Power-Down Attributes + "p_RX_CLKMUX_PD":1, + "p_TX_CLKMUX_PD":1, + + # FPGA RX Interface Attribute + "p_RX_INT_DATAWIDTH":0, + + # FPGA TX Interface Attribute + "p_TX_INT_DATAWIDTH":0, + + # TX Configurable Driver Attributes + "p_TX_QPI_STATUS_EN":0, + + # RX Equalizer Attributes + "p_RX_DFE_KL_CFG2":0b00110011000100000001100000001100, + "p_RX_DFE_XYD_CFG":0b0000000000000, + + # TX Configurable Driver Attributes + "p_TX_PREDRIVER_MODE":0, + } + + self.specials += \ + Instance("GTXE2_CHANNEL", + # CPLL Ports + #o_CPLLFBCLKLOST=, + o_CPLLLOCK=self.cplllock, + i_CPLLLOCKDETCLK=0, + i_CPLLLOCKEN=1, + i_CPLLPD=0, + #o_CPLLREFCLKLOST=0, + i_CPLLREFCLKSEL=0b001, + i_CPLLRESET=self.cpllreset, + i_GTRSVD=0, + i_PCSRSVDIN=0, + i_PCSRSVDIN2=0, + i_PMARSVDIN=0, + i_PMARSVDIN2=0, + i_TSTIN=ones(20), + #o_TSTOUT=, + + # Channel + i_CLKRSVD=0, + + # Channel - Clocking Ports + i_GTGREFCLK=0, + i_GTNORTHREFCLK0=0, + i_GTNORTHREFCLK1=0, + i_GTREFCLK0=self.gtrefclk0, + i_GTREFCLK1=0, + i_GTSOUTHREFCLK0=0, + i_GTSOUTHREFCLK1=0, + + # Channel - DRP Ports + i_DRPADDR=0, + i_DRPCLK=0, + i_DRPDI=0, + #o_DRPDO=, + i_DRPEN=0, + #o_DRPRDY=, + i_DRPWE=0, + + # Clocking Ports + #o_GTREFCLKMONITOR=, + i_QPLLCLK=self.qpllclk, + i_QPLLREFCLK=self.qpllrefclk, + i_RXSYSCLKSEL=0b00, + i_TXSYSCLKSEL=0b00, + + # Digital Monitor Ports + #o_DMONITOROUT=, + + # FPGA TX Interface Datapath Configuration + i_TX8B10BEN=1, + + # Loopback Ports + i_LOOPBACK=0, + + # PCI Express Ports + #o_PHYSTATUS=, + i_RXRATE=0, + #o_RXVALID=, + + # Power-Down Ports + i_RXPD=0b00, + i_TXPD=0b00, + + # RX 8B/10B Decoder Ports + i_SETERRSTATUS=0, + + # RX Initialization and Reset Ports + i_EYESCANRESET=0, + i_RXUSERRDY=rxuserrdy, + + # RX Margin Analysis Ports + #o_EYESCANDATAERROR=, + i_EYESCANMODE=0, + i_EYESCANTRIGGER=0, + + # Receive Ports - CDR Ports + i_RXCDRFREQRESET=0, + i_RXCDRHOLD=0, + o_RXCDRLOCK=rxcdrlock, + i_RXCDROVRDEN=0, + i_RXCDRRESET=0, + i_RXCDRRESETRSV=0, + + # Receive Ports - Clock Correction Ports + #o_RXCLKCORCNT=, + + # Receive Ports - FPGA RX Interface Datapath Configuration + i_RX8B10BEN=1, + + # Receive Ports - FPGA RX Interface Ports + i_RXUSRCLK=self.rxusrclk, + i_RXUSRCLK2=self.rxusrclk2, + + # Receive Ports - FPGA RX interface Ports + o_RXDATA=self.rxdata, + + # Receive Ports - Pattern Checker Ports + #o_RXPRBSERR=, + i_RXPRBSSEL=0, + + # Receive Ports - Pattern Checker ports + i_RXPRBSCNTRESET=0, + + # Receive Ports - RX Equalizer Ports + i_RXDFEXYDEN=0, + i_RXDFEXYDHOLD=0, + i_RXDFEXYDOVRDEN=0, + + # Receive Ports - RX 8B/10B Decoder Ports + #o_RXDISPERR=, + #o_RXNOTINTABLE=, + + # Receive Ports - RX AFE + i_GTXRXP=pads.rxp, + i_GTXRXN=pads.rxn, + + # Receive Ports - RX Buffer Bypass Ports + i_RXBUFRESET=0, + #o_RXBUFSTATUS=, + i_RXDDIEN=0, + i_RXDLYBYPASS=1, + i_RXDLYEN=0, + i_RXDLYOVRDEN=0, + i_RXDLYSRESET=0, + #o_RXDLYSRESETDONE=0, + i_RXPHALIGN=0, + #o_RXPHALIGNDONE=, + i_RXPHALIGNEN=0, + i_RXPHDLYPD=0, + i_RXPHDLYRESET=0, + #o_RXPHMONITOR=, + i_RXPHOVRDEN=0, + #o_RXPHSLIPMONITOR=, + #o_RXSTATUS=, + + # Receive Ports - RX Byte and Word Alignment Ports + o_RXBYTEISALIGNED=self.rxbyteisaligned, + #o_RXBYTEREALIGN=, + #o_RXCOMMADET=, + i_RXCOMMADETEN=1, + i_RXMCOMMAALIGNEN=1, + i_RXPCOMMAALIGNEN=1, + + # Receive Ports - RX Channel Bonding Ports + #o_RXCHANBONDSEQ=, + i_RXCHBONDEN=0, + i_RXCHBONDLEVEL=0, + i_RXCHBONDMASTER=0, + #o_RXCHBONDO=, + i_RXCHBONDSLAVE=0, + + # Receive Ports - RX Channel Bonding Ports + #o_RXCHANISALIGNED=, + #o_RXCHANREALIGN=, + + # Receive Ports - RX Equalizer Ports + i_RXDFEAGCHOLD=0, + i_RXDFEAGCOVRDEN=0, + i_RXDFECM1EN=0, + i_RXDFELFHOLD=0, + i_RXDFELFOVRDEN=1, + i_RXDFELPMRESET=0, + i_RXDFETAP2HOLD=0, + i_RXDFETAP2OVRDEN=0, + i_RXDFETAP3HOLD=0, + i_RXDFETAP3OVRDEN=0, + i_RXDFETAP4HOLD=0, + i_RXDFETAP4OVRDEN=0, + i_RXDFETAP5HOLD=0, + i_RXDFETAP5OVRDEN=0, + i_RXDFEUTHOLD=0, + i_RXDFEUTOVRDEN=0, + i_RXDFEVPHOLD=0, + i_RXDFEVPOVRDEN=0, + i_RXDFEVSEN=0, + i_RXLPMLFKLOVRDEN=0, + #o_RXMONITOROUT=, + i_RXMONITORSEL=0b00, + i_RXOSHOLD=0, + i_RXOSOVRDEN=0, + + # Receive Ports - RX Equilizer Ports + i_RXLPMHFHOLD=0, + i_RXLPMHFOVRDEN=0, + i_RXLPMLFHOLD=0, + + # Receive Ports - RX Fabric ClocK Output Control Ports + #o_RXRATEDONE=, + + # Receive Ports - RX Fabric Output Control Ports + o_RXOUTCLK=self.rxoutclk, + #o_RXOUTCLKFABRIC=, + #o_RXOUTCLKPCS=, + i_RXOUTCLKSEL=0b010, + + # Receive Ports - RX Gearbox Ports + #o_RXDATAVALID=, + #o_RXHEADER=, + #o_RXHEADERVALID=, + #o_RXSTARTOFSEQ=, + + # Receive Ports - RX Gearbox Ports + i_RXGEARBOXSLIP=0, + + # Receive Ports - RX Initialization and Reset Ports + i_GTRXRESET=self.gtrxreset, + i_RXOOBRESET=0, + i_RXPCSRESET=0, + i_RXPMARESET=self.pmarxreset, + + # Receive Ports - RX Margin Analysis ports + i_RXLPMEN=0, + + # Receive Ports - RX OOB Signaling ports + #o_RXCOMSASDET=, + o_RXCOMWAKEDET=rxcomwakedet, + + # Receive Ports - RX OOB Signaling ports + o_RXCOMINITDET=rxcominitdet, + + # Receive Ports - RX OOB signalling Ports + o_RXELECIDLE=rxelecidle, + i_RXELECIDLEMODE=0b00, + + # Receive Ports - RX Polarity Control Ports + i_RXPOLARITY=0, + + # Receive Ports - RX gearbox ports + i_RXSLIDE=0, + + # Receive Ports - RX8B/10B Decoder Ports + #o_RXCHARISCOMMA=, + o_RXCHARISK=self.rxcharisk, + + # Receive Ports - Rx Channel Bonding Ports + i_RXCHBONDI=0, + + # Receive Ports -RX Initialization and Reset Ports + o_RXRESETDONE=rxresetdone, + + # Rx AFE Ports + i_RXQPIEN=0, + #o_RXQPISENN=, + #o_RXQPISENP=, + + # TX Buffer Bypass Ports + i_TXPHDLYTSTCLK=0, + + # TX Configurable Driver Ports + i_TXPOSTCURSOR=0, + i_TXPOSTCURSORINV=0, + i_TXPRECURSOR=0, + i_TXPRECURSORINV=0, + i_TXQPIBIASEN=0, + i_TXQPISTRONGPDOWN=0, + i_TXQPIWEAKPUP=0, + + # TX Initialization and Reset Ports + i_CFGRESET=0, + i_GTTXRESET=self.gttxreset, + #o_PCSRSVDOUT=, + i_TXUSERRDY=txuserrdy, + + # Transceiver Reset Mode Operation + i_GTRESETSEL=0, + i_RESETOVRD=0, + + # Transmit Ports - 8b10b Encoder Control Ports + i_TXCHARDISPMODE=0, + i_TXCHARDISPVAL=0, + + # Transmit Ports - FPGA TX Interface Ports + i_TXUSRCLK=self.txusrclk, + i_TXUSRCLK2=self.txusrclk2, + + # Transmit Ports - PCI Express Ports + i_TXELECIDLE=txelecidle, + i_TXMARGIN=0, + i_TXRATE=txrate, + i_TXSWING=0, + + # Transmit Ports - Pattern Generator Ports + i_TXPRBSFORCEERR=0, + + # Transmit Ports - TX Buffer Bypass Ports + i_TXDLYBYPASS=1, + i_TXDLYEN=0, + i_TXDLYHOLD=0, + i_TXDLYOVRDEN=0, + i_TXDLYSRESET=0, + #o_TXDLYSRESETDONE=, + i_TXDLYUPDOWN=0, + i_TXPHALIGN=0, + #o_TXPHALIGNDONE=txphaligndone, + i_TXPHALIGNEN=0, + i_TXPHDLYPD=0, + i_TXPHDLYRESET=0, + i_TXPHINIT=0, + #o_TXPHINITDONE=, + i_TXPHOVRDEN=0, + + # Transmit Ports - TX Buffer Ports + #o_TXBUFSTATUS=, + + # Transmit Ports - TX Configurable Driver Ports + i_TXBUFDIFFCTRL=0b100, + i_TXDEEMPH=0, + i_TXDIFFCTRL=0b1000, + i_TXDIFFPD=0, + i_TXINHIBIT=0, + i_TXMAINCURSOR=0, + i_TXPISOPD=0, + + # Transmit Ports - TX Data Path interface + i_TXDATA=self.txdata, + + # Transmit Ports - TX Driver and OOB signaling + o_GTXTXP=pads.txp, + o_GTXTXN=pads.txn, + + # Transmit Ports - TX Fabric Clock Output Control Ports + o_TXOUTCLK=self.txoutclk, + #o_TXOUTCLKFABRIC=, + #o_TXOUTCLKPCS=, + i_TXOUTCLKSEL=0b11, #?? + #o_TXRATEDONE=, + # Transmit Ports - TX Gearbox Ports + i_TXCHARISK=self.txcharisk, + #o_TXGEARBOXREADY=, + i_TXHEADER=0, + i_TXSEQUENCE=0, + i_TXSTARTSEQ=0, + + # Transmit Ports - TX Initialization and Reset Ports + i_TXPCSRESET=0, + i_TXPMARESET=0, + o_TXRESETDONE=txresetdone, + + # Transmit Ports - TX OOB signalling Ports + o_TXCOMFINISH=txcomfinish, + i_TXCOMINIT=txcominit, + i_TXCOMSAS=0, + i_TXCOMWAKE=txcomwake, + i_TXPDELECIDLEMODE=0, + + # Transmit Ports - TX Polarity Control Ports + i_TXPOLARITY=0, + + # Transmit Ports - TX Receiver Detection Ports + i_TXDETECTRX=0, + + # Transmit Ports - TX8b/10b Encoder Ports + i_TX8B10BBYPASS=0, + + # Transmit Ports - pattern Generator Ports + i_TXPRBSSEL=0, + + # Tx Configurable Driver Ports + #o_TXQPISENN=, + #o_TXQPISENP=, + + **gtxe2_channel_parameters + ) diff --git a/litesata/test/Makefile b/litesata/test/Makefile new file mode 100644 index 00000000..6b2723bf --- /dev/null +++ b/litesata/test/Makefile @@ -0,0 +1,33 @@ +MSCDIR = ../../ +PYTHON = python3 + +CMD = PYTHONPATH=$(MSCDIR) $(PYTHON) + +CC=gcc +CFLAGS =-Wall -O0 + +phy_datapath_tb: + $(CMD) phy_datapath_tb.py + +crc_tb: + $(CC) $(CFLAGS) $(INC) -o crc crc.c + $(CMD) crc_tb.py + +scrambler_tb: + $(CC) $(CFLAGS) $(INC) -o scrambler scrambler.c + $(CMD) scrambler_tb.py + +cont_tb: + $(CMD) cont_tb.py + +link_tb: + $(CMD) link_tb.py + +command_tb: + $(CMD) command_tb.py + +bist_tb: + $(CMD) bist_tb.py + +clean: + rm crc scrambler *.vcd diff --git a/litesata/test/bist_tb.py b/litesata/test/bist_tb.py new file mode 100644 index 00000000..ed837504 --- /dev/null +++ b/litesata/test/bist_tb.py @@ -0,0 +1,45 @@ +from litesata.common import * +from litesata import LiteSATA +from litesata.frontend.bist import LiteSATABISTGenerator, LiteSATABISTChecker + +from litesata.test.hdd import * +from litesata.test.common import * + +class TB(Module): + def __init__(self): + self.hdd = HDD( + link_debug=False, link_random_level=0, + transport_debug=False, transport_loopback=False, + hdd_debug=True) + self.controller = LiteSATA(self.hdd.phy, with_crossbar=True) + self.generator = LiteSATABISTGenerator(self.controller.crossbar.get_port()) + self.checker = LiteSATABISTChecker(self.controller.crossbar.get_port()) + + def gen_simulation(self, selfp): + hdd = self.hdd + hdd.malloc(0, 64) + selfp.generator.sector = 0 + selfp.generator.count = 17 + selfp.checker.sector = 0 + selfp.checker.count = 17 + while True: + selfp.generator.start = 1 + yield + selfp.generator.start = 0 + yield + while selfp.generator.done == 0: + yield + selfp.checker.start = 1 + yield + selfp.checker.start = 0 + yield + while selfp.checker.done == 0: + yield + print("errors {}".format(selfp.checker.errors)) + selfp.generator.sector += 1 + selfp.generator.count = max((selfp.generator.count + 1)%8, 1) + selfp.checker.sector += 1 + selfp.checker.count = max((selfp.checker.count + 1)%8, 1) + +if __name__ == "__main__": + run_simulation(TB(), ncycles=8192*2, vcd_name="my.vcd", keep_files=True) diff --git a/litesata/test/command_tb.py b/litesata/test/command_tb.py new file mode 100644 index 00000000..b5c9dc49 --- /dev/null +++ b/litesata/test/command_tb.py @@ -0,0 +1,96 @@ +from litesata.common import * +from litesata.core import LiteSATACore + +from litesata.test.hdd import * +from litesata.test.common import * + +class CommandTXPacket(list): + def __init__(self, write=0, read=0, sector=0, count=0, data=[]): + self.ongoing = False + self.done = False + self.write = write + self.read = read + self.sector = sector + self.count = count + for d in data: + self.append(d) + +class CommandStreamer(PacketStreamer): + def __init__(self): + PacketStreamer.__init__(self, command_tx_description(32), CommandTXPacket) + + def do_simulation(self, selfp): + PacketStreamer.do_simulation(self, selfp) + selfp.source.write = self.packet.write + selfp.source.read = self.packet.read + selfp.source.sector = self.packet.sector + selfp.source.count = self.packet.count + +class CommandRXPacket(list): + def __init__(self): + self.ongoing = False + self.done = False + self.write = 0 + self.read = 0 + self.success = 0 + self.failed = 0 + +class CommandLogger(PacketLogger): + def __init__(self): + PacketLogger.__init__(self, command_rx_description(32), CommandRXPacket) + + def do_simulation(self, selfp): + selfp.sink.ack = 1 + if selfp.sink.stb == 1 and selfp.sink.sop == 1: + self.packet = CommandRXPacket() + self.packet.write = selfp.sink.write + self.packet.read = selfp.sink.read + self.packet.sucess = selfp.sink.success + self.packet.failed = selfp.sink.failed + self.packet.append(selfp.sink.data) + elif selfp.sink.stb: + self.packet.append(selfp.sink.data) + if selfp.sink.stb == 1 and selfp.sink.eop == 1: + self.packet.done = True + +class TB(Module): + def __init__(self): + self.hdd = HDD( + link_debug=False, link_random_level=50, + transport_debug=False, transport_loopback=False, + hdd_debug=True) + self.core = LiteSATACore(self.hdd.phy) + + self.streamer = CommandStreamer() + self.streamer_randomizer = Randomizer(command_tx_description(32), level=50) + + self.logger = CommandLogger() + self.logger_randomizer = Randomizer(command_rx_description(32), level=50) + + self.pipeline = Pipeline( + self.streamer, + self.streamer_randomizer, + self.core, + self.logger_randomizer, + self.logger + ) + + def gen_simulation(self, selfp): + hdd = self.hdd + hdd.malloc(0, 64) + write_data = [i for i in range(sectors2dwords(2))] + write_len = 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.logger.receive() + read_packet = CommandTXPacket(read=1, sector=2, count=write_len) + yield from self.streamer.send(read_packet) + yield from self.logger.receive() + read_data = self.logger.packet + + # check results + s, l, e = check(write_data, read_data) + print("shift "+ str(s) + " / length " + str(l) + " / errors " + str(e)) + +if __name__ == "__main__": + run_simulation(TB(), ncycles=2048, vcd_name="my.vcd", keep_files=True) diff --git a/litesata/test/common.py b/litesata/test/common.py new file mode 100644 index 00000000..ec8da980 --- /dev/null +++ b/litesata/test/common.py @@ -0,0 +1,135 @@ +import random, copy + +from migen.sim.generic import run_simulation + +from litesata.common import * + +def seed_to_data(seed, random=True): + if random: + return (seed * 0x31415979 + 1) & 0xffffffff + else: + return seed + +def check(p1, p2): + p1 = copy.deepcopy(p1) + p2 = copy.deepcopy(p2) + if isinstance(p1, int): + return 0, 1, int(p1 != p2) + else: + if len(p1) >= len(p2): + ref, res = p1, p2 + else: + ref, res = p2, p1 + shift = 0 + while((ref[0] != res[0]) and (len(res)>1)): + res.pop(0) + shift += 1 + length = min(len(ref), len(res)) + errors = 0 + for i in range(length): + if ref.pop(0) != res.pop(0): + errors += 1 + return shift, length, errors + +def randn(max_n): + return random.randint(0, max_n-1) + +class PacketStreamer(Module): + def __init__(self, description, packet_class): + self.source = Source(description) + ### + self.packets = [] + self.packet = packet_class() + self.packet.done = 1 + + self.source_data = 0 + + def send(self, packet, blocking=True): + packet = copy.deepcopy(packet) + self.packets.append(packet) + if blocking: + while packet.done == 0: + yield + + def do_simulation(self, selfp): + if len(self.packets) and self.packet.done: + self.packet = self.packets.pop(0) + if not self.packet.ongoing and not self.packet.done: + selfp.source.stb = 1 + if self.source.description.packetized: + selfp.source.sop = 1 + if len(self.packet) > 0: + self.source_data = self.packet.pop(0) + if hasattr(selfp.source, "data"): + selfp.source.data = self.source_data + else: + selfp.source.d = self.source_data + self.packet.ongoing = True + elif selfp.source.stb == 1 and selfp.source.ack == 1: + if self.source.description.packetized: + selfp.source.sop = 0 + selfp.source.eop = (len(self.packet) == 1) + if len(self.packet) > 0: + selfp.source.stb = 1 + self.source_data = self.packet.pop(0) + if hasattr(selfp.source, "data"): + selfp.source.data = self.source_data + else: + selfp.source.d = self.source_data + else: + self.packet.done = 1 + selfp.source.stb = 0 + +class PacketLogger(Module): + def __init__(self, description, packet_class): + self.sink = Sink(description) + ### + self.packet_class = packet_class + self.packet = packet_class() + + def receive(self, length=None): + self.packet.done = 0 + if length is None: + while self.packet.done == 0: + yield + else: + while length > len(self.packet): + yield + + def do_simulation(self, selfp): + selfp.sink.ack = 1 + if self.sink.description.packetized: + if selfp.sink.stb == 1 and selfp.sink.sop == 1: + self.packet = self.packet_class() + if selfp.sink.stb: + if hasattr(selfp.sink, "data"): + self.packet.append(selfp.sink.data) + else: + self.packet.append(selfp.sink.d) + if self.sink.description.packetized: + if selfp.sink.stb == 1 and selfp.sink.eop == 1: + self.packet.done = True + +class Randomizer(Module): + def __init__(self, description, level=0): + self.level = level + + self.sink = Sink(description) + self.source = Source(description) + + self.run = Signal() + + self.comb += \ + If(self.run, + Record.connect(self.sink, self.source) + ).Else( + self.source.stb.eq(0), + self.sink.ack.eq(0), + ) + + def do_simulation(self, selfp): + n = randn(100) + if n < self.level: + selfp.run = 0 + else: + selfp.run = 1 diff --git a/litesata/test/cont_tb.py b/litesata/test/cont_tb.py new file mode 100644 index 00000000..bbd69011 --- /dev/null +++ b/litesata/test/cont_tb.py @@ -0,0 +1,95 @@ +from litesata.common import * +from litesata.core.link.cont import LiteSATACONTInserter, LiteSATACONTRemover + +from litesata.test.common import * + +class ContPacket(list): + def __init__(self, data=[]): + self.ongoing = False + self.done = False + for d in data: + self.append(d) + +class ContStreamer(PacketStreamer): + def __init__(self): + PacketStreamer.__init__(self, phy_description(32), ContPacket) + + 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 ContLogger(PacketLogger): + def __init__(self): + PacketLogger.__init__(self, phy_description(32), ContPacket) + +class TB(Module): + def __init__(self): + self.streamer = ContStreamer() + self.streamer_randomizer = Randomizer(phy_description(32), level=50) + self.inserter = LiteSATACONTInserter(phy_description(32)) + self.remover = LiteSATACONTRemover(phy_description(32)) + self.logger_randomizer = Randomizer(phy_description(32), level=50) + self.logger = ContLogger() + + self.pipeline = Pipeline( + self.streamer, + self.streamer_randomizer, + self.inserter, + self.remover, + self.logger_randomizer, + self.logger + ) + + def gen_simulation(self, selfp): + test_packet = ContPacket([ + primitives["SYNC"], + primitives["SYNC"], + primitives["SYNC"], + primitives["SYNC"], + primitives["SYNC"], + primitives["SYNC"], + primitives["ALIGN"], + primitives["ALIGN"], + primitives["SYNC"], + primitives["SYNC"], + #primitives["SYNC"], + 0x00000000, + 0x00000001, + 0x00000002, + 0x00000003, + 0x00000004, + 0x00000005, + 0x00000006, + 0x00000007, + primitives["SYNC"], + primitives["SYNC"], + primitives["SYNC"], + primitives["SYNC"], + primitives["ALIGN"], + primitives["ALIGN"], + primitives["SYNC"], + primitives["SYNC"], + primitives["SYNC"], + primitives["SYNC"]]*4 + ) + streamer_packet = ContPacket(test_packet) + yield from self.streamer.send(streamer_packet) + yield from self.logger.receive(len(test_packet)) + #for d in self.logger.packet: + # print("%08x" %d) + + # 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=1024, vcd_name="my.vcd", keep_files=True) diff --git a/litesata/test/crc.c b/litesata/test/crc.c new file mode 100644 index 00000000..75576b5c --- /dev/null +++ b/litesata/test/crc.c @@ -0,0 +1,149 @@ +// Adapted from SATA specification +/****************************************************************************/ +/* */ +/* crc.c */ +/* */ +/* This sample code reads standard in for a sequence of 32 bit values */ +/* formatted in hexadecimal with a leading "0x" (e.g. 0xDEADBEEF). The */ +/* code calculates the Serial ATA CRC for the input data stream. The */ +/* generator polynomial used is: */ +/* 32 26 23 22 16 12 11 10 8 7 5 4 2 */ +/* G(x) = x + x + x + x + x + x + x + x + x + x + x + x + x + x + 1 */ +/* */ +/* This sample code uses a parallel implementation of the CRC calculation */ +/* circuit that is suitable for implementation in hardware. A block */ +/* diagram of the circuit being emulated is shown below. */ +/* */ +/* +---+ +---+ +---+ */ +/* Data_In --------->| | | | | R | */ +/* | + |--------->| * |--------->| e |----+ */ +/* +---->| | | | | g | | */ +/* | +---+ +---+ +---+ | */ +/* | | */ +/* | | */ +/* +--------------------------------------------+ */ +/* */ +/* The CRC value is initialized to 0x52325032 as defined in the Serial ATA */ +/* specification. */ +/* */ +/****************************************************************************/ + + + +#include +#include +int main(int argc, char *argv[]) +{ + int i; + unsigned int data_count; + unsigned int crc; + unsigned int data_in; + unsigned char crc_bit[32]; + unsigned char new_bit[32]; + + crc = 0x52325032; + data_count = 0; + + while((scanf(" 0x%8x", &data_in) == 1) && (!scanf("exit"))) { + data_count++; + /* Add the data_in value to the current value of the CRC held in the */ + /* "register". The addition is performed modulo two (XOR). */ + crc ^= data_in; + /* Expand the value of the CRC held in the register to 32 individual */ + /* bits for easy manipulation. */ + for (i = 0; i < 32; ++i) { + crc_bit[i] = (crc >> i) & 0x01; + } + /* The following 32 assignments perform the function of the box */ + /* labeled "*" in the block diagram above. The new_bit array is a */ + /* temporary holding place for the new CRC value being calculated. */ + /* Note that there are lots of shared terms in the assignments below. */ + new_bit[31] = crc_bit[31] ^ crc_bit[30] ^ crc_bit[29] ^ crc_bit[28] ^ crc_bit[27] ^ crc_bit[25] ^ crc_bit[24] ^ + crc_bit[23] ^ crc_bit[15] ^ crc_bit[11] ^ crc_bit[9] ^ crc_bit[8] ^ crc_bit[5]; + new_bit[30] = crc_bit[30] ^ crc_bit[29] ^ crc_bit[28] ^ crc_bit[27] ^ crc_bit[26] ^ crc_bit[24] ^ crc_bit[23] ^ + crc_bit[22] ^ crc_bit[14] ^ crc_bit[10] ^ crc_bit[8] ^ crc_bit[7] ^ crc_bit[4]; + new_bit[29] = crc_bit[31] ^ crc_bit[29] ^ crc_bit[28] ^ crc_bit[27] ^ crc_bit[26] ^ crc_bit[25] ^ crc_bit[23] ^ + crc_bit[22] ^ crc_bit[21] ^ crc_bit[13] ^ crc_bit[9] ^ crc_bit[7] ^ crc_bit[6] ^ crc_bit[3]; + new_bit[28] = crc_bit[30] ^ crc_bit[28] ^ crc_bit[27] ^ crc_bit[26] ^ crc_bit[25] ^ crc_bit[24] ^ crc_bit[22] ^ + crc_bit[21] ^ crc_bit[20] ^ crc_bit[12] ^ crc_bit[8] ^ crc_bit[6] ^ crc_bit[5] ^ crc_bit[2]; + new_bit[27] = crc_bit[29] ^ crc_bit[27] ^ crc_bit[26] ^ crc_bit[25] ^ crc_bit[24] ^ crc_bit[23] ^ crc_bit[21] ^ + crc_bit[20] ^ crc_bit[19] ^ crc_bit[11] ^ crc_bit[7] ^ crc_bit[5] ^ crc_bit[4] ^ crc_bit[1]; + new_bit[26] = crc_bit[31] ^ crc_bit[28] ^ crc_bit[26] ^ crc_bit[25] ^ crc_bit[24] ^ crc_bit[23] ^ crc_bit[22] ^ + crc_bit[20] ^ crc_bit[19] ^ crc_bit[18] ^ crc_bit[10] ^ crc_bit[6] ^ crc_bit[4] ^ crc_bit[3] ^ + crc_bit[0]; + new_bit[25] = crc_bit[31] ^ crc_bit[29] ^ crc_bit[28] ^ crc_bit[22] ^ crc_bit[21] ^ crc_bit[19] ^ crc_bit[18] ^ + crc_bit[17] ^ crc_bit[15] ^ crc_bit[11] ^ crc_bit[8] ^ crc_bit[3] ^ crc_bit[2]; + new_bit[24] = crc_bit[30] ^ crc_bit[28] ^ crc_bit[27] ^ crc_bit[21] ^ crc_bit[20] ^ crc_bit[18] ^ crc_bit[17] ^ + crc_bit[16] ^ crc_bit[14] ^ crc_bit[10] ^ crc_bit[7] ^ crc_bit[2] ^ crc_bit[1]; + new_bit[23] = crc_bit[31] ^ crc_bit[29] ^ crc_bit[27] ^ crc_bit[26] ^ crc_bit[20] ^ crc_bit[19] ^ crc_bit[17] ^ + crc_bit[16] ^ crc_bit[15] ^ crc_bit[13] ^ crc_bit[9] ^ crc_bit[6] ^ crc_bit[1] ^ crc_bit[0]; + new_bit[22] = crc_bit[31] ^ crc_bit[29] ^ crc_bit[27] ^ crc_bit[26] ^ crc_bit[24] ^ crc_bit[23] ^ crc_bit[19] ^ + crc_bit[18] ^ crc_bit[16] ^ crc_bit[14] ^ crc_bit[12] ^ crc_bit[11] ^ crc_bit[9] ^ crc_bit[0]; + new_bit[21] = crc_bit[31] ^ crc_bit[29] ^ crc_bit[27] ^ crc_bit[26] ^ crc_bit[24] ^ crc_bit[22] ^ crc_bit[18] ^ + crc_bit[17] ^ crc_bit[13] ^ crc_bit[10] ^ crc_bit[9] ^ crc_bit[5]; + new_bit[20] = crc_bit[30] ^ crc_bit[28] ^ crc_bit[26] ^ crc_bit[25] ^ crc_bit[23] ^ crc_bit[21] ^ crc_bit[17] ^ + crc_bit[16] ^ crc_bit[12] ^ crc_bit[9] ^ crc_bit[8] ^ crc_bit[4]; + new_bit[19] = crc_bit[29] ^ crc_bit[27] ^ crc_bit[25] ^ crc_bit[24] ^ crc_bit[22] ^ crc_bit[20] ^ crc_bit[16] ^ + crc_bit[15] ^ crc_bit[11] ^ crc_bit[8] ^ crc_bit[7] ^ crc_bit[3]; + new_bit[18] = crc_bit[31] ^ crc_bit[28] ^ crc_bit[26] ^ crc_bit[24] ^ crc_bit[23] ^ crc_bit[21] ^ crc_bit[19] ^ + crc_bit[15] ^ crc_bit[14] ^ crc_bit[10] ^ crc_bit[7] ^ crc_bit[6] ^ crc_bit[2]; + new_bit[17] = crc_bit[31] ^ crc_bit[30] ^ crc_bit[27] ^ crc_bit[25] ^ crc_bit[23] ^ crc_bit[22] ^ crc_bit[20] ^ + crc_bit[18] ^ crc_bit[14] ^ crc_bit[13] ^ crc_bit[9] ^ crc_bit[6] ^ crc_bit[5] ^ crc_bit[1]; + new_bit[16] = crc_bit[30] ^ crc_bit[29] ^ crc_bit[26] ^ crc_bit[24] ^ crc_bit[22] ^ crc_bit[21] ^ crc_bit[19] ^ + crc_bit[17] ^ crc_bit[13] ^ crc_bit[12] ^ crc_bit[8] ^ crc_bit[5] ^ crc_bit[4] ^ crc_bit[0]; + new_bit[15] = crc_bit[30] ^ crc_bit[27] ^ crc_bit[24] ^ crc_bit[21] ^ crc_bit[20] ^ crc_bit[18] ^ crc_bit[16] ^ + crc_bit[15] ^ crc_bit[12] ^ crc_bit[9] ^ crc_bit[8] ^ crc_bit[7] ^ crc_bit[5] ^ crc_bit[4] ^ + crc_bit[3]; + new_bit[14] = crc_bit[29] ^ crc_bit[26] ^ crc_bit[23] ^ crc_bit[20] ^ crc_bit[19] ^ crc_bit[17] ^ crc_bit[15] ^ + crc_bit[14] ^ crc_bit[11] ^ crc_bit[8] ^ crc_bit[7] ^ crc_bit[6] ^ crc_bit[4] ^ crc_bit[3] ^ + crc_bit[2]; + new_bit[13] = crc_bit[31] ^ crc_bit[28] ^ crc_bit[25] ^ crc_bit[22] ^ crc_bit[19] ^ crc_bit[18] ^ crc_bit[16] ^ + crc_bit[14] ^ crc_bit[13] ^ crc_bit[10] ^ crc_bit[7] ^ crc_bit[6] ^ crc_bit[5] ^ crc_bit[3] ^ + crc_bit[2] ^ crc_bit[1]; + new_bit[12] = crc_bit[31] ^ crc_bit[30] ^ crc_bit[27] ^ crc_bit[24] ^ crc_bit[21] ^ crc_bit[18] ^ crc_bit[17] ^ + crc_bit[15] ^ crc_bit[13] ^ crc_bit[12] ^ crc_bit[9] ^ crc_bit[6] ^ crc_bit[5] ^ crc_bit[4] ^ + crc_bit[2] ^ crc_bit[1] ^ crc_bit[0]; + new_bit[11] = crc_bit[31] ^ crc_bit[28] ^ crc_bit[27] ^ crc_bit[26] ^ crc_bit[25] ^ crc_bit[24] ^ crc_bit[20] ^ + crc_bit[17] ^ crc_bit[16] ^ crc_bit[15] ^ crc_bit[14] ^ crc_bit[12] ^ crc_bit[9] ^ crc_bit[4] ^ + crc_bit[3] ^ crc_bit[1] ^ crc_bit[0]; + new_bit[10] = crc_bit[31] ^ crc_bit[29] ^ crc_bit[28] ^ crc_bit[26] ^ crc_bit[19] ^ crc_bit[16] ^ crc_bit[14] ^ + crc_bit[13] ^ crc_bit[9] ^ crc_bit[5] ^ crc_bit[3] ^ crc_bit[2] ^ crc_bit[0]; + new_bit[9] = crc_bit[29] ^ crc_bit[24] ^ crc_bit[23] ^ crc_bit[18] ^ crc_bit[13] ^ crc_bit[12] ^ crc_bit[11] ^ + crc_bit[9] ^ crc_bit[5] ^ crc_bit[4] ^ crc_bit[2] ^ crc_bit[1]; + new_bit[8] = crc_bit[31] ^ crc_bit[28] ^ crc_bit[23] ^ crc_bit[22] ^ crc_bit[17] ^ crc_bit[12] ^ crc_bit[11] ^ + crc_bit[10] ^ crc_bit[8] ^ crc_bit[4] ^ crc_bit[3] ^ crc_bit[1] ^ crc_bit[0]; + new_bit[7] = crc_bit[29] ^ crc_bit[28] ^ crc_bit[25] ^ crc_bit[24] ^ crc_bit[23] ^ crc_bit[22] ^ crc_bit[21] ^ + crc_bit[16] ^ crc_bit[15] ^ crc_bit[10] ^ crc_bit[8] ^ crc_bit[7] ^ crc_bit[5] ^ crc_bit[3] ^ + crc_bit[2] ^ crc_bit[0]; + new_bit[6] = crc_bit[30] ^ crc_bit[29] ^ crc_bit[25] ^ crc_bit[22] ^ crc_bit[21] ^ crc_bit[20] ^ crc_bit[14] ^ + crc_bit[11] ^ crc_bit[8] ^ crc_bit[7] ^ crc_bit[6] ^ crc_bit[5] ^ crc_bit[4] ^ crc_bit[2] ^ + crc_bit[1]; + new_bit[5] = crc_bit[29] ^ crc_bit[28] ^ crc_bit[24] ^ crc_bit[21] ^ crc_bit[20] ^ crc_bit[19] ^ crc_bit[13] ^ + crc_bit[10] ^ crc_bit[7] ^ crc_bit[6] ^ crc_bit[5] ^ crc_bit[4] ^ crc_bit[3] ^ crc_bit[1] ^ + crc_bit[0]; + new_bit[4] = crc_bit[31] ^ crc_bit[30] ^ crc_bit[29] ^ crc_bit[25] ^ crc_bit[24] ^ crc_bit[20] ^ crc_bit[19] ^ + crc_bit[18] ^ crc_bit[15] ^ crc_bit[12] ^ crc_bit[11] ^ crc_bit[8] ^ crc_bit[6] ^ crc_bit[4] ^ + crc_bit[3] ^ crc_bit[2] ^ crc_bit[0]; + new_bit[3] = crc_bit[31] ^ crc_bit[27] ^ crc_bit[25] ^ crc_bit[19] ^ crc_bit[18] ^ crc_bit[17] ^ crc_bit[15] ^ + crc_bit[14] ^ crc_bit[10] ^ crc_bit[9] ^ crc_bit[8] ^ crc_bit[7] ^ crc_bit[3] ^ crc_bit[2] ^ + crc_bit[1]; + new_bit[2] = crc_bit[31] ^ crc_bit[30] ^ crc_bit[26] ^ crc_bit[24] ^ crc_bit[18] ^ crc_bit[17] ^ crc_bit[16] ^ + crc_bit[14] ^ crc_bit[13] ^ crc_bit[9] ^ crc_bit[8] ^ crc_bit[7] ^ crc_bit[6] ^ crc_bit[2] ^ + crc_bit[1] ^ crc_bit[0]; + new_bit[1] = crc_bit[28] ^ crc_bit[27] ^ crc_bit[24] ^ crc_bit[17] ^ crc_bit[16] ^ crc_bit[13] ^ crc_bit[12] ^ + crc_bit[11] ^ crc_bit[9] ^ crc_bit[7] ^ crc_bit[6] ^ crc_bit[1] ^ crc_bit[0]; + new_bit[0] = crc_bit[31] ^ crc_bit[30] ^ crc_bit[29] ^ crc_bit[28] ^ crc_bit[26] ^ crc_bit[25] ^ crc_bit[24] ^ + crc_bit[16] ^ crc_bit[12] ^ crc_bit[10] ^ crc_bit[9] ^ crc_bit[6] ^ crc_bit[0]; + + /* The new CRC value has been calculated as individual bits in the */ + /* new_bit array. Re-assembled it into a 32 bit value and "clock" it */ + /* into the "register". */ + crc = 0; + for (i = 31; i >= 0; --i) { + crc = crc << 1; + crc |= new_bit[i]; + } + } + printf("%08x\n", crc); + return 0; +} diff --git a/litesata/test/crc_tb.py b/litesata/test/crc_tb.py new file mode 100644 index 00000000..a36ec839 --- /dev/null +++ b/litesata/test/crc_tb.py @@ -0,0 +1,59 @@ +import subprocess + +from litesata.common import * +from litesata.core.link.crc import * + +from litesata.test.common import * + +class TB(Module): + def __init__(self, length, random): + self.crc = LiteSATACRC() + self.length = length + self.random = random + + def get_c_crc(self, datas): + stdin = "" + for data in datas: + stdin += "0x%08x " %data + stdin += "exit" + with subprocess.Popen("./crc", stdin=subprocess.PIPE, stdout=subprocess.PIPE) as process: + process.stdin.write(stdin.encode("ASCII")) + out, err = process.communicate() + return int(out.decode("ASCII"), 16) + + def gen_simulation(self, selfp): + # init CRC + selfp.crc.d = 0 + selfp.crc.ce = 1 + selfp.crc.reset = 1 + yield + selfp.crc.reset = 0 + + # feed CRC with datas + datas = [] + for i in range(self.length): + data = seed_to_data(i, self.random) + datas.append(data) + selfp.crc.d = data + yield + + # log results + yield + sim_crc = selfp.crc.value + + # stop + selfp.crc.ce = 0 + for i in range(32): + yield + + # get C core reference + c_crc = self.get_c_crc(datas) + + # check results + s, l, e = check(c_crc, sim_crc) + print("shift "+ str(s) + " / length " + str(l) + " / errors " + str(e)) + +if __name__ == "__main__": + from migen.sim.generic import run_simulation + length = 8192 + run_simulation(TB(length, True), ncycles=length+100, vcd_name="my.vcd") diff --git a/litesata/test/hdd.py b/litesata/test/hdd.py new file mode 100644 index 00000000..6e897f8e --- /dev/null +++ b/litesata/test/hdd.py @@ -0,0 +1,511 @@ +import subprocess +import math + +from litesata.common import * +from litesata.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 +class PHYDword: + def __init__(self, dat=0): + self.dat = dat + self.start = 1 + self.done = 0 + +class PHYSource(Module): + def __init__(self): + self.source = Source(phy_description(32)) + ### + self.dword = PHYDword() + + def send(self, dword): + self.dword = dword + + def do_simulation(self, selfp): + 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 + +class PHYSink(Module): + def __init__(self): + self.sink = Sink(phy_description(32)) + ### + self.dword = PHYDword() + + 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 + +class PHYLayer(Module): + def __init__(self): + + self.rx = PHYSink() + self.tx = PHYSource() + + self.source = self.tx.source + self.sink = self.rx.sink + + def send(self, dword): + packet = PHYDword(dword) + self.tx.send(packet) + + def receive(self): + yield from self.rx.receive() + + def __repr__(self): + receiving = "%08x " %self.rx.dword.dat + receiving += decode_primitive(self.rx.dword.dat) + receiving += " "*(16-len(receiving)) + + sending = "%08x " %self.tx.dword.dat + sending += decode_primitive(self.tx.dword.dat) + sending += " "*(16-len(sending)) + + return receiving + sending + +# Link Layer model +def print_link(s): + print_with_prefix(s, "[LNK]: ") + +def import_scrambler_datas(): + 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]] + +class LinkPacket(list): + def __init__(self, init=[]): + self.ongoing = False + self.done = False + self.scrambled_datas = import_scrambler_datas() + for dword in init: + self.append(dword) + +class LinkRXPacket(LinkPacket): + def descramble(self): + for i in range(len(self)): + self[i] = self[i] ^ self.scrambled_datas[i] + + 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")) + out, err = process.communicate() + crc = int(out.decode("ASCII"), 16) + r = (self[-1] == crc) + self.pop() + return r + + def decode(self): + self.descramble() + return self.check_crc() + +class LinkTXPacket(LinkPacket): + def insert_crc(self): + stdin = "" + for v in self: + 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) + self.append(crc) + + def scramble(self): + for i in range(len(self)): + self[i] = self[i] ^ self.scrambled_datas[i] + + def encode(self): + self.insert_crc() + self.scramble() + +class LinkLayer(Module): + def __init__(self, phy, debug=False, random_level=0): + self.phy = phy + self.debug = debug + self.random_level = random_level + self.tx_packets = [] + self.tx_packet = LinkTXPacket() + self.rx_packet = LinkRXPacket() + + self.rx_cont = False + self.rx_last = 0 + self.tx_cont = False + self.tx_cont_nb = -1 + self.tx_lasts = [0, 0, 0] + + self.scrambled_datas = import_scrambler_datas() + + self.transport_callback = None + + self.send_state = "" + self.send_states = ["RDY", "SOF", "DATA", "EOF", "WTRM"] + + def set_transport_callback(self, callback): + self.transport_callback = callback + + def send(self, dword): + if self.send_state == "RDY": + self.phy.send(primitives["X_RDY"]) + if dword == primitives["R_RDY"]: + self.send_state = "SOF" + elif self.send_state == "SOF": + self.phy.send(primitives["SOF"]) + self.send_state = "DATA" + elif self.send_state == "DATA": + if dword == primitives["HOLD"]: + self.phy.send(primitives["HOLDA"]) + else: + self.phy.send(self.tx_packet.pop(0)) + if len(self.tx_packet) == 0: + self.send_state = "EOF" + elif self.send_state == "EOF": + self.phy.send(primitives["EOF"]) + self.send_state = "WTRM" + elif self.send_state == "WTRM": + self.phy.send(primitives["WTRM"]) + if dword == primitives["R_OK"]: + self.tx_packet.done = True + elif dword == primitives["R_ERR"]: + self.tx_packet.done = True + if self.tx_packet.done: + self.phy.send(primitives["SYNC"]) + + def insert_cont(self): + self.tx_lasts.pop(0) + self.tx_lasts.append(self.phy.tx.dword.dat) + self.tx_cont = True + for i in range(3): + if not is_primitive(self.tx_lasts[i]): + self.tx_cont = False + if self.tx_lasts[i] != self.tx_lasts[0]: + self.tx_cont = False + if self.tx_cont: + if self.tx_cont_nb == 0: + self.phy.send(primitives["CONT"]) + else: + self.phy.send(self.scrambled_datas[self.tx_cont_nb]) + self.tx_cont_nb += 1 + else: + self.tx_cont_nb = 0 + + def remove_cont(self, dword): + if dword == primitives["HOLD"]: + if self.rx_cont: + self.tx_lasts = [0, 0, 0] + if dword == primitives["CONT"]: + self.rx_cont = True + elif is_primitive(dword): + self.rx_last = dword + self.rx_cont = False + if self.rx_cont: + dword = self.rx_last + return dword + + def callback(self, dword): + if dword == primitives["X_RDY"]: + self.phy.send(primitives["R_RDY"]) + elif dword == primitives["WTRM"]: + self.phy.send(primitives["R_OK"]) + if self.rx_packet.ongoing: + self.rx_packet.decode() + if self.transport_callback is not None: + self.transport_callback(self.rx_packet) + self.rx_packet.ongoing = False + elif dword == primitives["HOLD"]: + self.phy.send(primitives["HOLDA"]) + elif dword == primitives["EOF"]: + pass + elif self.rx_packet.ongoing: + if dword != primitives["HOLD"]: + n = randn(100) + if n < self.random_level: + self.phy.send(primitives["HOLD"]) + else: + self.phy.send(primitives["R_IP"]) + if not is_primitive(dword): + self.rx_packet.append(dword) + elif dword == primitives["SOF"]: + self.rx_packet = LinkRXPacket() + self.rx_packet.ongoing = True + + def gen_simulation(self, selfp): + self.tx_packet.done = True + self.phy.send(primitives["SYNC"]) + while True: + yield from self.phy.receive() + if self.debug: + print_link(self.phy) + self.phy.send(primitives["SYNC"]) + rx_dword = self.phy.rx.dword.dat + rx_dword = self.remove_cont(rx_dword) + if len(self.tx_packets) != 0: + if self.tx_packet.done: + self.tx_packet = self.tx_packets.pop(0) + self.tx_packet.encode() + self.send_state = "RDY" + if not self.tx_packet.done: + self.send(rx_dword) + else: + self.callback(rx_dword) + self.insert_cont() + +# Transport Layer model +def print_transport(s): + print_with_prefix(s, "[TRN]: ") + +def get_field_data(field, packet): + return (packet[field.dword] >> field.offset) & (2**field.width-1) + +class FIS: + def __init__(self, packet, description, direction="H2D"): + self.packet = packet + self.description = description + self.direction = direction + self.decode() + + def decode(self): + for k, v in self.description.items(): + setattr(self, k, get_field_data(v, self.packet)) + + def encode(self): + for k, v in self.description.items(): + self.packet[v.dword] |= (getattr(self, k) << v.offset) + + def __repr__(self): + if self.direction == "H2D": + r = ">>>>>>>>\n" + else: + r = "<<<<<<<<\n" + for k in sorted(self.description.keys()): + r += k + " : 0x%x" %getattr(self,k) + "\n" + return r + +class FIS_REG_H2D(FIS): + def __init__(self, packet=[0]*fis_reg_h2d_cmd_len): + FIS.__init__(self, packet, fis_reg_h2d_layout) + self.type = fis_types["REG_H2D"] + self.direction = "H2D" + + 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_cmd_len): + FIS.__init__(self, packet, fis_reg_d2h_layout) + self.type = fis_types["REG_D2H"] + self.direction = "D2H" + + 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_cmd_len): + FIS.__init__(self, packet, fis_dma_activate_d2h_layout) + self.type = fis_types["DMA_ACTIVATE_D2H"] + self.direction = "D2H" + + def __repr__(self): + r = "FIS_DMA_ACTIVATE_D2H\n" + r += FIS.__repr__(self) + return r + +class FIS_DATA(FIS): + def __init__(self, packet=[0], direction="H2D"): + FIS.__init__(self, packet, fis_data_layout, direction) + self.type = fis_types["DATA"] + + def __repr__(self): + r = "FIS_DATA\n" + r += FIS.__repr__(self) + for data in self.packet[1:]: + r += "%08x\n" %data + return r + +class FIS_UNKNOWN(FIS): + def __init__(self, packet=[0], direction="H2D"): + FIS.__init__(self, packet, {}, direction) + + def __repr__(self): + r = "UNKNOWN\n" + if self.direction == "H2D": + r += ">>>>>>>>\n" + else: + r += "<<<<<<<<\n" + for dword in self.packet: + r += "%08x\n" %dword + return r + +class TransportLayer(Module): + def __init__(self, link, debug=False, loopback=False): + self.link = link + self.debug = debug + self.loopback = loopback + self.link.set_transport_callback(self.callback) + + def set_command_callback(self, callback): + self.command_callback = callback + + def send(self, fis): + fis.encode() + packet = LinkTXPacket(fis.packet) + self.link.tx_packets.append(packet) + if self.debug and not self.loopback: + print_transport(fis) + + def callback(self, packet): + fis_type = packet[0] & 0xff + 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["DATA"]: + fis = FIS_DATA(packet, direction="H2D") + else: + fis = FIS_UNKNOWN(packet, direction="H2D") + if self.debug: + print_transport(fis) + if self.loopback: + self.send(fis) + else: + self.command_callback(fis) + +# Command Layer model +class CommandLayer(Module): + def __init__(self, transport): + self.transport = transport + self.transport.set_command_callback(self.callback) + + self.hdd = None + + def set_hdd(self, hdd): + self.hdd = hdd + + def callback(self, fis): + resp = None + if isinstance(fis, FIS_REG_H2D): + if fis.command == regs["WRITE_DMA_EXT"]: + resp = self.hdd.write_dma_callback(fis) + elif fis.command == regs["READ_DMA_EXT"]: + resp = self.hdd.read_dma_callback(fis) + elif isinstance(fis, FIS_DATA): + resp = self.hdd.data_callback(fis) + + if resp is not None: + for packet in resp: + self.transport.send(packet) + +# HDD model +def print_hdd(s): + print_with_prefix(s, "[HDD]: ") + +class HDDMemRegion: + def __init__(self, base, count, sector_size): + self.base = base + self.count = count + self.data = [0]*(count*sector_size//4) + +class HDD(Module): + def __init__(self, + link_debug=False, link_random_level=0, + transport_debug=False, transport_loopback=False, + hdd_debug=False, + ): + ### + self.phy = PHYLayer() + self.link = LinkLayer(self.phy, link_debug, link_random_level) + self.transport = TransportLayer(self.link, transport_debug, transport_loopback) + self.command = CommandLayer(self.transport) + + self.command.set_hdd(self) + + self.debug = hdd_debug + self.mem = None + self.wr_sector = 0 + self.wr_end_sector = 0 + self.rd_sector = 0 + self.rx_end_sector = 0 + + 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*logical_sector_size//1024) + print_hdd(s) + self.mem = HDDMemRegion(sector, count, logical_sector_size) + + def write(self, sector, data): + n = math.ceil(dwords2sectors(len(data))) + if self.debug: + if n == 1: + s = "{}".format(sector) + else: + s = "{s} to {e}".format(s=sector, e=sector+n-1) + print_hdd("Writing sector " + s) + for i in range(len(data)): + offset = sectors2dwords(sector) + self.mem.data[offset+i] = data[i] + + def read(self, sector, count): + if self.debug: + if count == 1: + s = "{}".format(sector) + else: + s = "{s} to {e}".format(s=sector, e=sector+count-1) + print_hdd("Reading sector " + s) + data = [] + for i in range(sectors2dwords(count)): + data.append(self.mem.data[sectors2dwords(sector)+i]) + return data + + def write_dma_callback(self, fis): + self.wr_sector = fis.lba_lsb + (fis.lba_msb << 32) + self.wr_end_sector = self.wr_sector + fis.count + return [FIS_DMA_ACTIVATE_D2H()] + + def read_dma_callback(self, fis): + self.rd_sector = fis.lba_lsb + (fis.lba_msb << 32) + self.rd_end_sector = self.rd_sector + fis.count + packets = [] + while self.rd_sector != self.rd_end_sector: + count = min(self.rd_end_sector-self.rd_sector, (fis_max_dwords*4)//logical_sector_size) + packet = self.read(self.rd_sector, count) + packet.insert(0, 0) + packets.append(FIS_DATA(packet, direction="D2H")) + self.rd_sector += count + packets.append(FIS_REG_D2H()) + return packets + + def data_callback(self, fis): + self.write(self.wr_sector, fis.packet[1:]) + self.wr_sector += dwords2sectors(len(fis.packet[1:])) + if self.wr_sector == self.wr_end_sector: + return [FIS_REG_D2H()] + else: + return [FIS_DMA_ACTIVATE_D2H()] diff --git a/litesata/test/link_tb.py b/litesata/test/link_tb.py new file mode 100644 index 00000000..daca230d --- /dev/null +++ b/litesata/test/link_tb.py @@ -0,0 +1,48 @@ +from litesata.common import * +from litesata.core.link import LiteSATALink + +from litesata.test.common import * +from litesata.test.hdd import * + +class LinkStreamer(PacketStreamer): + def __init__(self): + PacketStreamer.__init__(self, link_description(32), LinkTXPacket) + +class LinkLogger(PacketLogger): + def __init__(self): + PacketLogger.__init__(self, link_description(32), LinkRXPacket) + +class TB(Module): + def __init__(self): + self.hdd = HDD( + link_debug=False, link_random_level=50, + transport_debug=False, transport_loopback=True) + self.link = InsertReset(LiteSATALink(self.hdd.phy)) + + self.streamer = LinkStreamer() + self.streamer_randomizer = Randomizer(link_description(32), level=50) + + self.logger_randomizer = Randomizer(link_description(32), level=50) + self.logger = LinkLogger() + + self.pipeline = Pipeline( + self.streamer, + self.streamer_randomizer, + self.link, + self.logger_randomizer, + self.logger + ) + + def gen_simulation(self, selfp): + for i in range(8): + streamer_packet = LinkTXPacket([i for i in range(64)]) + yield from self.streamer.send(streamer_packet) + yield from self.logger.receive() + + # 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=2048, vcd_name="my.vcd", keep_files=True) diff --git a/litesata/test/phy_datapath_tb.py b/litesata/test/phy_datapath_tb.py new file mode 100644 index 00000000..71071035 --- /dev/null +++ b/litesata/test/phy_datapath_tb.py @@ -0,0 +1,88 @@ +from litesata.common import * +from litesata.phy.datapath import liteSATAPHYDatapath + +from litesata.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.cd_sata_rx = ClockDomain() + self.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=10) + self.trx = TRX() + self.ctrl = CTRL() + self.datapath = LiteSATAPHYDatapath(self.trx, self.ctrl) + self.logger_randomizer = Randomizer(phy_description(32), level=10) + 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) diff --git a/litesata/test/scrambler.c b/litesata/test/scrambler.c new file mode 100644 index 00000000..cee4b30a --- /dev/null +++ b/litesata/test/scrambler.c @@ -0,0 +1,113 @@ +// Adapted from SATA specification +/****************************************************************************/ +/* */ +/* scramble.c */ +/* */ +/* This sample code generates the entire sequence of 65535 Dwords produced */ +/* by the scrambler defined in the Serial ATA specification. The */ +/* specification calls for an LFSR to generate a string of bits that will */ +/* be packaged into 32 bit Dwords to be XORed with the data Dwords. The */ +/* generator polynomial specified is: */ +/* 16 15 13 4 */ +/* G(x) = x + x + x + x + 1 */ +/* */ +/* Parallelized versions of the scrambler are initialized to a value */ +/* derived from the initialization value of 0xFFFF defined in the */ +/* specification. This implementation is initialized to 0xF0F6. Other */ +/* parallel implementations will have different initial values. The */ +/* important point is that the first Dword output of any implementation */ +/* must equal 0xC2D2768D. */ +/* This code does not represent an elegant solution for a C implementation, */ +/* but it does demonstrate a method of generating the sequence that can be */ +/* easily implemented in hardware. A block diagram of the circuit emulated */ +/* by this code is shown below. */ +/* */ +/* +-----------------------------------+ */ +/* | | */ +/* | | */ +/* | +---+ +---+ | */ +/* | | R | | * | | */ +/* +---->| e |----------+---->| M |----+----> Output(31 downto 16) */ +/* | g | | | 1 | */ +/* +---+ | +---+ */ +/* | */ +/* | +---+ */ +/* | | * | */ +/* +---->| M |---------> Output(15 downto 0) */ +/* | 2 | */ +/* +---+ */ +/* */ +/* The register shown in the block diagram is a 16 bit register. The two */ +/* boxes, *M1 and *M2, each represent a multiply by a 16 by 16 binary */ +/* matrix. A 16 by 16 matrix times a 16 bit vector yields a 16 bit vector. */ +/* The two vectors are the two halves of the 32 bit scrambler value. The */ +/* upper half of the scrambler value is stored back into the context */ +/* register to be used to generate the next value in the scrambler */ +/* */ +/****************************************************************************/ +#include +#include +int main(int argc, char *argv[]) +{ + int i, j; + unsigned int length; + unsigned short context; + unsigned long scrambler; + unsigned char now[16]; + unsigned char next[32]; + context = 0xF0F6; + + scanf("0x%8x", &length); + + for (i = 0; i < length; ++i) { + for (j = 0; j < 16; ++j) { + now[j] = (context >> j) & 0x01; + } + next[31] = now[12] ^ now[10] ^ now[7] ^ now[3] ^ now[1] ^ now[0]; + next[30] = now[15] ^ now[14] ^ now[12] ^ now[11] ^ now[9] ^ now[6] ^ now[3] ^ now[2] ^ now[0]; + next[29] = now[15] ^ now[13] ^ now[12] ^ now[11] ^ now[10] ^ now[8] ^ now[5] ^ now[3] ^ now[2] ^ now[1]; + next[28] = now[14] ^ now[12] ^ now[11] ^ now[10] ^ now[9] ^ now[7] ^ now[4] ^ now[2] ^ now[1] ^ now[0]; + next[27] = now[15] ^ now[14] ^ now[13] ^ now[12] ^ now[11] ^ now[10] ^ now[9] ^ now[8] ^ now[6] ^ now[1] ^ now[0]; + next[26] = now[15] ^ now[13] ^ now[11] ^ now[10] ^ now[9] ^ now[8] ^ now[7] ^ now[5] ^ now[3] ^ now[0]; + next[25] = now[15] ^ now[10] ^ now[9] ^ now[8] ^ now[7] ^ now[6] ^ now[4] ^ now[3] ^ now[2]; + next[24] = now[14] ^ now[9] ^ now[8] ^ now[7] ^ now[6] ^ now[5] ^ now[3] ^ now[2] ^ now[1]; + next[23] = now[13] ^ now[8] ^ now[7] ^ now[6] ^ now[5] ^ now[4] ^ now[2] ^ now[1] ^ now[0]; + next[22] = now[15] ^ now[14] ^ now[7] ^ now[6] ^ now[5] ^ now[4] ^ now[1] ^ now[0]; + next[21] = now[15] ^ now[13] ^ now[12] ^ now[6] ^ now[5] ^ now[4] ^ now[0]; + next[20] = now[15] ^ now[11] ^ now[5] ^ now[4]; + next[19] = now[14] ^ now[10] ^ now[4] ^ now[3]; + next[18] = now[13] ^ now[9] ^ now[3] ^ now[2]; + next[17] = now[12] ^ now[8] ^ now[2] ^ now[1]; + next[16] = now[11] ^ now[7] ^ now[1] ^ now[0]; + + + next[15] = now[15] ^ now[14] ^ now[12] ^ now[10] ^ now[6] ^ now[3] ^ now[0]; + next[14] = now[15] ^ now[13] ^ now[12] ^ now[11] ^ now[9] ^ now[5] ^ now[3] ^ now[2]; + next[13] = now[14] ^ now[12] ^ now[11] ^ now[10] ^ now[8] ^ now[4] ^ now[2] ^ now[1]; + next[12] = now[13] ^ now[11] ^ now[10] ^ now[9] ^ now[7] ^ now[3] ^ now[1] ^ now[0]; + next[11] = now[15] ^ now[14] ^ now[10] ^ now[9] ^ now[8] ^ now[6] ^ now[3] ^ now[2] ^ now[0]; + next[10] = now[15] ^ now[13] ^ now[12] ^ now[9] ^ now[8] ^ now[7] ^ now[5] ^ now[3] ^ now[2] ^ now[1]; + next[9] = now[14] ^ now[12] ^ now[11] ^ now[8] ^ now[7] ^ now[6] ^ now[4] ^ now[2] ^ now[1] ^ now[0]; + next[8] = now[15] ^ now[14] ^ now[13] ^ now[12] ^ now[11] ^ now[10] ^ now[7] ^ now[6] ^ now[5] ^ now[1] ^ now[0]; + next[7] = now[15] ^ now[13] ^ now[11] ^ now[10] ^ now[9] ^ now[6] ^ now[5] ^ now[4] ^ now[3] ^ now[0]; + next[6] = now[15] ^ now[10] ^ now[9] ^ now[8] ^ now[5] ^ now[4] ^ now[2]; + next[5] = now[14] ^ now[9] ^ now[8] ^ now[7] ^ now[4] ^ now[3] ^ now[1]; + next[4] = now[13] ^ now[8] ^ now[7] ^ now[6] ^ now[3] ^ now[2] ^ now[0]; + next[3] = now[15] ^ now[14] ^ now[7] ^ now[6] ^ now[5] ^ now[3] ^ now[2] ^ now[1]; + next[2] = now[14] ^ now[13] ^ now[6] ^ now[5] ^ now[4] ^ now[2] ^ now[1] ^ now[0]; + next[1] = now[15] ^ now[14] ^ now[13] ^ now[5] ^ now[4] ^ now[1] ^ now[0]; + next[0] = now[15] ^ now[13] ^ now[4] ^ now[0]; + + scrambler = 0; + for (j = 31; j >= 0; --j) { + scrambler = scrambler << 1; + scrambler |= next[j]; + } + context = scrambler >> 16; + printf("%08x\n", (unsigned int) scrambler); + + } + + return 0; + +} \ No newline at end of file diff --git a/litesata/test/scrambler_tb.py b/litesata/test/scrambler_tb.py new file mode 100644 index 00000000..bf29e50c --- /dev/null +++ b/litesata/test/scrambler_tb.py @@ -0,0 +1,49 @@ +import subprocess + +from litesata.common import * +from litesata.core.link.scrambler import * + +from litesata.test.common import * + +class TB(Module): + def __init__(self, length): + self.scrambler = InsertReset(Scrambler()) + self.length = length + + def get_c_values(self, length): + stdin = "0x%08x" %length + with subprocess.Popen("./scrambler", stdin=subprocess.PIPE, stdout=subprocess.PIPE) as process: + process.stdin.write(stdin.encode("ASCII")) + out, err = process.communicate() + return [int(e, 16) for e in out.decode("ASCII").split("\n")[:-1]] + + def gen_simulation(self, selfp): + # init CRC + selfp.scrambler.ce = 1 + selfp.scrambler.reset = 1 + yield + selfp.scrambler.reset = 0 + + # log results + yield + sim_values = [] + for i in range(self.length): + sim_values.append(selfp.scrambler.value) + yield + + # stop + selfp.scrambler.ce = 0 + for i in range(32): + yield + + # get C code reference + c_values = self.get_c_values(self.length) + + # check results + s, l, e = check(c_values, sim_values) + print("shift "+ str(s) + " / length " + str(l) + " / errors " + str(e)) + +if __name__ == "__main__": + from migen.sim.generic import run_simulation + length = 8192 + run_simulation(TB(length), ncycles=length+100, vcd_name="my.vcd") diff --git a/platforms/kc705.py b/platforms/kc705.py index 479f7347..14cd8915 100644 --- a/platforms/kc705.py +++ b/platforms/kc705.py @@ -73,7 +73,7 @@ _io = [ IOStandard("LVCMOS25") ), - ("sata_host", 0, + ("sata0", 0, Subsignal("refclk_p", Pins("C8")), Subsignal("refclk_n", Pins("C7")), Subsignal("txp", Pins("D2")), @@ -81,15 +81,6 @@ _io = [ Subsignal("rxp", Pins("E4")), Subsignal("rxn", Pins("E3")), ), - - ("sata_device", 0, - Subsignal("refclk_p", Pins("G8")), # 125MHz SGMII - Subsignal("refclk_n", Pins("G7")), # 125MHz SGMII - Subsignal("txp", Pins("H2")), # SFP - Subsignal("txn", Pins("H1")), # SFP - Subsignal("rxp", Pins("G4")), # SFP - Subsignal("rxn", Pins("G3")), # SFP - ), ] def Platform(*args, toolchain="vivado", programmer="xc3sprog", **kwargs): diff --git a/setup.py b/setup.py new file mode 100644 index 00000000..ec8de134 --- /dev/null +++ b/setup.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python3 + +import sys, os +from setuptools import setup +from setuptools import find_packages + +here = os.path.abspath(os.path.dirname(__file__)) +README = open(os.path.join(here, "README")).read() + +required_version = (3, 3) +if sys.version_info < required_version: + raise SystemExit("LiteSATA requires python {0} or greater".format( + ".".join(map(str, required_version)))) + +setup( + name="litesata", + version="unknown", + description="Generic open-source SATA1/2/3 controller", + long_description=README, + author="Florent Kermarrec", + author_email="florent@enjoy-digital.fr", + url="http://enjoy-digital.fr", + download_url="https://github.com/Florent-Kermarrec/litesata", + packages=find_packages(here), + license="GPL", + platforms=["Any"], + keywords="HDL ASIC FPGA hardware design", + classifiers=[ + "Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)", + "Environment :: Console", + "Development Status :: Alpha", + "Intended Audience :: Developers", + "License :: OSI Approved :: GNU General Public License (GPL)", + "Operating System :: OS Independent", + "Programming Language :: Python", + ], +) diff --git a/targets/bist.py b/targets/bist.py deleted file mode 100644 index d0ee9b6b..00000000 --- a/targets/bist.py +++ /dev/null @@ -1,227 +0,0 @@ -import os - -from migen.fhdl.std import * -from migen.bank import csrgen -from migen.bus import wishbone, csr -from migen.bus import wishbone2csr -from migen.genlib.cdc import * -from migen.genlib.resetsync import AsyncResetSynchronizer -from migen.bank.description import * - -from miscope import MiLa, Term, UART2Wishbone - -from misoclib import identifier -from lib.sata.common import * -from lib.sata.phy import SATAPHY -from lib.sata import SATACON -from lib.sata.bist import SATABIST - -class _CRG(Module): - def __init__(self, platform): - self.cd_sys = ClockDomain() - self.sata_reset = Signal() - - clk200 = platform.request("clk200") - clk200_se = Signal() - self.specials += Instance("IBUFDS", i_I=clk200.p, i_IB=clk200.n, o_O=clk200_se) - - pll_locked = Signal() - pll_fb = Signal() - pll_sys = Signal() - self.specials += [ - Instance("PLLE2_BASE", - p_STARTUP_WAIT="FALSE", o_LOCKED=pll_locked, - - # VCO @ 1GHz - p_REF_JITTER1=0.01, p_CLKIN1_PERIOD=5.0, - p_CLKFBOUT_MULT=5, p_DIVCLK_DIVIDE=1, - i_CLKIN1=clk200_se, i_CLKFBIN=pll_fb, o_CLKFBOUT=pll_fb, - - # 166MHz - p_CLKOUT0_DIVIDE=6, p_CLKOUT0_PHASE=0.0, o_CLKOUT0=pll_sys, - - p_CLKOUT1_DIVIDE=2, p_CLKOUT1_PHASE=0.0, #o_CLKOUT1=, - - p_CLKOUT2_DIVIDE=2, p_CLKOUT2_PHASE=0.0, #o_CLKOUT2=, - - p_CLKOUT3_DIVIDE=2, p_CLKOUT3_PHASE=0.0, #o_CLKOUT3=, - - p_CLKOUT4_DIVIDE=2, p_CLKOUT4_PHASE=0.0, #o_CLKOUT4= - ), - Instance("BUFG", i_I=pll_sys, o_O=self.cd_sys.clk), - AsyncResetSynchronizer(self.cd_sys, ~pll_locked | platform.request("cpu_reset") | self.sata_reset), - ] - -class GenSoC(Module): - csr_base = 0x00000000 - csr_data_width = 32 - csr_map = { - "uart2wb": 0, - "identifier": 2, - } - interrupt_map = {} - cpu_type = None - def __init__(self, platform, clk_freq): - # UART <--> Wishbone bridge - self.uart2wb = UART2Wishbone(platform.request("serial"), clk_freq, baud=921600) - - # CSR bridge 0x00000000 (shadow @0x00000000) - self.wishbone2csr = wishbone2csr.WB2CSR(bus_csr=csr.Interface(self.csr_data_width)) - self._wb_masters = [self.uart2wb.wishbone] - self._wb_slaves = [(lambda a: a[23:25] == 0, self.wishbone2csr.wishbone)] - self.cpu_csr_regions = [] # list of (name, origin, busword, csr_list/Memory) - - # CSR - self.identifier = identifier.Identifier(0, int(clk_freq), 0) - - def add_cpu_memory_region(self, name, origin, length): - self.cpu_memory_regions.append((name, origin, length)) - - def add_cpu_csr_region(self, name, origin, busword, obj): - self.cpu_csr_regions.append((name, origin, busword, obj)) - - def do_finalize(self): - # Wishbone - self.wishbonecon = wishbone.InterconnectShared(self._wb_masters, - self._wb_slaves, register=True) - - # CSR - self.csrbankarray = csrgen.BankArray(self, - lambda name, memory: self.csr_map[name if memory is None else name + "_" + memory.name_override], - data_width=self.csr_data_width) - self.csrcon = csr.Interconnect(self.wishbone2csr.csr, self.csrbankarray.get_buses()) - for name, csrs, mapaddr, rmap in self.csrbankarray.banks: - self.add_cpu_csr_region(name, 0xe0000000+0x800*mapaddr, flen(rmap.bus.dat_w), csrs) - for name, memory, mapaddr, mmap in self.csrbankarray.srams: - self.add_cpu_csr_region(name, 0xe0000000+0x800*mapaddr, flen(rmap.bus.dat_w), memory) - -class BISTLeds(Module): - def __init__(self, platform, sata_phy): - # 1Hz blinking leds (sata_rx and sata_tx clocks) - sata_rx_led = platform.request("user_led", 0) - sata_tx_led = platform.request("user_led", 1) - - sata_rx_cnt = Signal(32) - sata_tx_cnt = Signal(32) - - sata_freqs_mhz = { - "SATA3" : 150.0, - "SATA2" : 75.0, - "SATA1" : 37.5, - } - sata_freq = int(sata_freqs_mhz[sata_phy.speed]*1000*1000) - - self.sync.sata_rx += \ - If(sata_rx_cnt == 0, - sata_rx_led.eq(~sata_rx_led), - sata_rx_cnt.eq(sata_freq//2) - ).Else( - sata_rx_cnt.eq(sata_rx_cnt-1) - ) - - self.sync.sata_tx += \ - If(sata_tx_cnt == 0, - sata_tx_led.eq(~sata_tx_led), - sata_tx_cnt.eq(sata_freq//2) - ).Else( - sata_tx_cnt.eq(sata_tx_cnt-1) - ) - - # ready leds (crg and ctrl) - self.comb += platform.request("user_led", 2).eq(sata_phy.crg.ready) - self.comb += platform.request("user_led", 3).eq(sata_phy.ctrl.ready) - -class BISTSoC(GenSoC, AutoCSR): - default_platform = "kc705" - csr_map = { - "sata_bist": 10, - } - csr_map.update(GenSoC.csr_map) - - def __init__(self, platform, export_mila=False): - clk_freq = 166*1000000 - GenSoC.__init__(self, platform, clk_freq) - self.crg = _CRG(platform) - - # SATA PHY and controller - self.sata_phy = SATAPHY(platform.request("sata_host"), clk_freq, speed="SATA2") - self.comb += self.crg.sata_reset.eq(self.sata_phy.ctrl.need_reset) - self.sata_con = SATACON(self.sata_phy) - - # SATA BIST generator and checker - self.sata_bist = SATABIST(self.sata_con.crossbar.get_ports(3), with_control=True) - - # Status Leds - self.leds = BISTLeds(platform, self.sata_phy) - -class BISTSoCDevel(BISTSoC, AutoCSR): - csr_map = { - "mila": 11 - } - csr_map.update(BISTSoC.csr_map) - def __init__(self, platform, export_mila=False): - BISTSoC.__init__(self, platform, export_mila) - - self.sata_con_link_rx_fsm_state = Signal(4) - self.sata_con_link_tx_fsm_state = Signal(4) - self.sata_con_transport_rx_fsm_state = Signal(4) - self.sata_con_transport_tx_fsm_state = Signal(4) - self.sata_con_command_rx_fsm_state = Signal(4) - self.sata_con_command_tx_fsm_state = Signal(4) - - debug = ( - self.sata_phy.ctrl.ready, - - self.sata_phy.source.stb, - self.sata_phy.source.data, - self.sata_phy.source.charisk, - - self.sata_phy.sink.stb, - self.sata_phy.sink.data, - self.sata_phy.sink.charisk, - - self.sata_con.command.sink.stb, - self.sata_con.command.sink.sop, - self.sata_con.command.sink.eop, - self.sata_con.command.sink.ack, - self.sata_con.command.sink.write, - self.sata_con.command.sink.read, - self.sata_con.command.sink.identify, - - self.sata_con.command.source.stb, - self.sata_con.command.source.sop, - self.sata_con.command.source.eop, - self.sata_con.command.source.ack, - self.sata_con.command.source.write, - self.sata_con.command.source.read, - self.sata_con.command.source.identify, - self.sata_con.command.source.success, - self.sata_con.command.source.failed, - self.sata_con.command.source.data, - - self.sata_con_link_rx_fsm_state, - self.sata_con_link_tx_fsm_state, - self.sata_con_transport_rx_fsm_state, - self.sata_con_transport_tx_fsm_state, - self.sata_con_command_rx_fsm_state, - self.sata_con_command_tx_fsm_state, - ) - - self.mila = MiLa(depth=2048, dat=Cat(*debug)) - self.mila.add_port(Term) - if export_mila: - mila_filename = os.path.join(platform.soc_ext_path, "test", "mila.csv") - self.mila.export(self, debug, mila_filename) - def do_finalize(self): - BISTSoC.do_finalize(self) - self.comb += [ - self.sata_con_link_rx_fsm_state.eq(self.sata_con.link.rx.fsm.state), - self.sata_con_link_tx_fsm_state.eq(self.sata_con.link.tx.fsm.state), - self.sata_con_transport_rx_fsm_state.eq(self.sata_con.transport.rx.fsm.state), - self.sata_con_transport_tx_fsm_state.eq(self.sata_con.transport.tx.fsm.state), - self.sata_con_command_rx_fsm_state.eq(self.sata_con.command.rx.fsm.state), - self.sata_con_command_tx_fsm_state.eq(self.sata_con.command.tx.fsm.state) - ] - -default_subtarget = BISTSoC -#default_subtarget = BISTSoCDevel diff --git a/targets/bist_kc705.py b/targets/bist_kc705.py new file mode 100644 index 00000000..ff752d19 --- /dev/null +++ b/targets/bist_kc705.py @@ -0,0 +1,225 @@ +import os + +from litesata.common import * +from migen.bank import csrgen +from migen.bus import wishbone, csr +from migen.bus import wishbone2csr +from migen.genlib.cdc import * +from migen.genlib.resetsync import AsyncResetSynchronizer +from migen.bank.description import * + +from misoclib import identifier + +from miscope import MiLa, Term, UART2Wishbone + +from litesata.common import * +from litesata.phy import LiteSATAPHY +from litesata import LiteSATA + +class _CRG(Module): + def __init__(self, platform): + self.cd_sys = ClockDomain() + self.reset = Signal() + + clk200 = platform.request("clk200") + clk200_se = Signal() + self.specials += Instance("IBUFDS", i_I=clk200.p, i_IB=clk200.n, o_O=clk200_se) + + pll_locked = Signal() + pll_fb = Signal() + pll_sys = Signal() + self.specials += [ + Instance("PLLE2_BASE", + p_STARTUP_WAIT="FALSE", o_LOCKED=pll_locked, + + # VCO @ 1GHz + p_REF_JITTER1=0.01, p_CLKIN1_PERIOD=5.0, + p_CLKFBOUT_MULT=5, p_DIVCLK_DIVIDE=1, + i_CLKIN1=clk200_se, i_CLKFBIN=pll_fb, o_CLKFBOUT=pll_fb, + + # 166MHz + p_CLKOUT0_DIVIDE=6, p_CLKOUT0_PHASE=0.0, o_CLKOUT0=pll_sys, + + p_CLKOUT1_DIVIDE=2, p_CLKOUT1_PHASE=0.0, #o_CLKOUT1=, + + p_CLKOUT2_DIVIDE=2, p_CLKOUT2_PHASE=0.0, #o_CLKOUT2=, + + p_CLKOUT3_DIVIDE=2, p_CLKOUT3_PHASE=0.0, #o_CLKOUT3=, + + p_CLKOUT4_DIVIDE=2, p_CLKOUT4_PHASE=0.0, #o_CLKOUT4= + ), + Instance("BUFG", i_I=pll_sys, o_O=self.cd_sys.clk), + AsyncResetSynchronizer(self.cd_sys, ~pll_locked | platform.request("cpu_reset") | self.reset), + ] + +class GenSoC(Module): + csr_base = 0x00000000 + csr_data_width = 32 + csr_map = { + "uart2wb": 0, + "identifier": 2, + } + interrupt_map = {} + cpu_type = None + def __init__(self, platform, clk_freq): + # UART <--> Wishbone bridge + self.uart2wb = UART2Wishbone(platform.request("serial"), clk_freq, baud=921600) + + # CSR bridge 0x00000000 (shadow @0x00000000) + self.wishbone2csr = wishbone2csr.WB2CSR(bus_csr=csr.Interface(self.csr_data_width)) + self._wb_masters = [self.uart2wb.wishbone] + self._wb_slaves = [(lambda a: a[23:25] == 0, self.wishbone2csr.wishbone)] + self.cpu_csr_regions = [] # list of (name, origin, busword, csr_list/Memory) + + # CSR + self.identifier = identifier.Identifier(0, int(clk_freq), 0) + + def add_cpu_memory_region(self, name, origin, length): + self.cpu_memory_regions.append((name, origin, length)) + + def add_cpu_csr_region(self, name, origin, busword, obj): + self.cpu_csr_regions.append((name, origin, busword, obj)) + + def do_finalize(self): + # Wishbone + self.wishbonecon = wishbone.InterconnectShared(self._wb_masters, + self._wb_slaves, register=True) + + # CSR + self.csrbankarray = csrgen.BankArray(self, + lambda name, memory: self.csr_map[name if memory is None else name + "_" + memory.name_override], + data_width=self.csr_data_width) + self.csrcon = csr.Interconnect(self.wishbone2csr.csr, self.csrbankarray.get_buses()) + for name, csrs, mapaddr, rmap in self.csrbankarray.banks: + self.add_cpu_csr_region(name, 0xe0000000+0x800*mapaddr, flen(rmap.bus.dat_w), csrs) + for name, memory, mapaddr, mmap in self.csrbankarray.srams: + self.add_cpu_csr_region(name, 0xe0000000+0x800*mapaddr, flen(rmap.bus.dat_w), memory) + +class BISTLeds(Module): + def __init__(self, platform, sata_phy): + # 1Hz blinking leds (sata_rx and sata_tx clocks) + sata_rx_led = platform.request("user_led", 0) + sata_tx_led = platform.request("user_led", 1) + + sata_rx_cnt = Signal(32) + sata_tx_cnt = Signal(32) + + sata_freqs_mhz = { + "SATA3" : 150.0, + "SATA2" : 75.0, + "SATA1" : 37.5, + } + sata_freq = int(sata_freqs_mhz[sata_phy.speed]*1000*1000) + + self.sync.sata_rx += \ + If(sata_rx_cnt == 0, + sata_rx_led.eq(~sata_rx_led), + sata_rx_cnt.eq(sata_freq//2) + ).Else( + sata_rx_cnt.eq(sata_rx_cnt-1) + ) + + self.sync.sata_tx += \ + If(sata_tx_cnt == 0, + sata_tx_led.eq(~sata_tx_led), + sata_tx_cnt.eq(sata_freq//2) + ).Else( + sata_tx_cnt.eq(sata_tx_cnt-1) + ) + + # ready leds (crg and ctrl) + self.comb += platform.request("user_led", 2).eq(sata_phy.crg.ready) + self.comb += platform.request("user_led", 3).eq(sata_phy.ctrl.ready) + +class BISTSoC(GenSoC, AutoCSR): + default_platform = "kc705" + csr_map = { + "sata": 10, + } + csr_map.update(GenSoC.csr_map) + + def __init__(self, platform, export_mila=False): + clk_freq = 166*1000000 + GenSoC.__init__(self, platform, clk_freq) + self.crg = _CRG(platform) + + # SATA PHY/Core/Frontend + self.sata_phy = LiteSATAPHY(platform.device, platform.request("sata0"), "SATA2", clk_freq) + self.comb += self.crg.reset.eq(self.sata_phy.ctrl.need_reset) # XXX FIXME + self.sata = LiteSATA(self.sata_phy, with_crossbar=True, with_bist=True, with_bist_csr=True) + + # Status Leds + self.leds = BISTLeds(platform, self.sata_phy) + +class BISTSoCDevel(BISTSoC, AutoCSR): + csr_map = { + "mila": 11 + } + csr_map.update(BISTSoC.csr_map) + def __init__(self, platform, export_mila=False): + BISTSoC.__init__(self, platform, export_mila) + + self.sata_core_link_rx_fsm_state = Signal(4) + self.sata_core_link_tx_fsm_state = Signal(4) + self.sata_core_transport_rx_fsm_state = Signal(4) + self.sata_core_transport_tx_fsm_state = Signal(4) + self.sata_core_command_rx_fsm_state = Signal(4) + self.sata_core_command_tx_fsm_state = Signal(4) + + debug = ( + self.sata_phy.ctrl.ready, + + self.sata_phy.source.stb, + self.sata_phy.source.data, + self.sata_phy.source.charisk, + + self.sata_phy.sink.stb, + self.sata_phy.sink.data, + self.sata_phy.sink.charisk, + + self.sata.core.command.sink.stb, + self.sata.core.command.sink.sop, + self.sata.core.command.sink.eop, + self.sata.core.command.sink.ack, + self.sata.core.command.sink.write, + self.sata.core.command.sink.read, + self.sata.core.command.sink.identify, + + self.sata.core.command.source.stb, + self.sata.core.command.source.sop, + self.sata.core.command.source.eop, + self.sata.core.command.source.ack, + self.sata.core.command.source.write, + self.sata.core.command.source.read, + self.sata.core.command.source.identify, + self.sata.core.command.source.success, + self.sata.core.command.source.failed, + self.sata.core.command.source.data, + + self.sata_core_link_rx_fsm_state, + self.sata_core_link_tx_fsm_state, + self.sata_core_transport_rx_fsm_state, + self.sata_core_transport_tx_fsm_state, + self.sata_core_command_rx_fsm_state, + self.sata_core_command_tx_fsm_state, + ) + + self.mila = MiLa(depth=2048, dat=Cat(*debug)) + self.mila.add_port(Term) + if export_mila: + mila_filename = os.path.join(platform.soc_ext_path, "test", "mila.csv") + self.mila.export(self, debug, mila_filename) + + def do_finalize(self): + BISTSoC.do_finalize(self) + self.comb += [ + self.sata_core_link_rx_fsm_state.eq(self.sata.core.link.rx.fsm.state), + self.sata_core_link_tx_fsm_state.eq(self.sata.core.link.tx.fsm.state), + self.sata_core_transport_rx_fsm_state.eq(self.sata.core.transport.rx.fsm.state), + self.sata_core_transport_tx_fsm_state.eq(self.sata.core.transport.tx.fsm.state), + self.sata_core_command_rx_fsm_state.eq(self.sata.core.command.rx.fsm.state), + self.sata_core_command_tx_fsm_state.eq(self.sata.core.command.tx.fsm.state) + ] + +#default_subtarget = BISTSoC +default_subtarget = BISTSoCDevel diff --git a/test/bist.py b/test/bist.py index c0c577fa..69272ffe 100644 --- a/test/bist.py +++ b/test/bist.py @@ -4,7 +4,7 @@ from config import * logical_sector_size = 512 -class SATABISTUnitDriver: +class LiteSATABISTUnitDriver: def __init__(self, regs, name): self.regs = regs self.name = name @@ -25,15 +25,15 @@ class SATABISTUnitDriver: errors = self.errors.read() return (speed, errors) -class SATABISTGeneratorDriver(SATABISTUnitDriver): +class LiteSATABISTGeneratorDriver(LiteSATABISTUnitDriver): def __init__(self, regs, name): - SATABISTUnitDriver.__init__(self, regs, name + "_generator") + LiteSATABISTUnitDriver.__init__(self, regs, name + "_generator") -class SATABISTCheckerDriver(SATABISTUnitDriver): +class LiteSATABISTCheckerDriver(LiteSATABISTUnitDriver): def __init__(self, regs, name): - SATABISTUnitDriver.__init__(self, regs, name + "_checker") + LiteSATABISTUnitDriver.__init__(self, regs, name + "_checker") -class SATABISTIdentifyDriver: +class LiteSATABISTIdentifyDriver: def __init__(self, regs, name): self.regs = regs self.name = name @@ -58,13 +58,16 @@ class SATABISTIdentifyDriver: def decode(self): self.serial_number = "" for i, dword in enumerate(self.data[10:20]): - s = dword.to_bytes(4, byteorder='big').decode("utf-8") - self.serial_number += s[2:] + s[:2] - - def __repr__(self): - r = "Serial Number: " + self.serial_number + try: + s = dword.to_bytes(4, byteorder='big').decode("utf-8") + self.serial_number += s[2:] + s[:2] + except: + self.serial_number += " " + + def hdd_info(self): + info = "Serial Number: " + self.serial_number # XXX: enhance decode function - return r + print(info) KB = 1024 MB = 1024*KB @@ -88,12 +91,12 @@ if __name__ == "__main__": args = _get_args() wb.open() ### - identify = SATABISTIdentifyDriver(wb.regs, "sata_bist") - generator = SATABISTGeneratorDriver(wb.regs, "sata_bist") - checker = SATABISTCheckerDriver(wb.regs, "sata_bist") + identify = LiteSATABISTIdentifyDriver(wb.regs, "sata_bist") + generator = LiteSATABISTGeneratorDriver(wb.regs, "sata_bist") + checker = LiteSATABISTCheckerDriver(wb.regs, "sata_bist") identify.run() - print(identify) + identify.hdd_info() sector = 0 count = int(args.transfer_size)*MB//logical_sector_size diff --git a/test/test_link.py b/test/test_link.py index 3849dc0a..46e41411 100644 --- a/test/test_link.py +++ b/test/test_link.py @@ -5,9 +5,9 @@ from bist import * from miscope.host.drivers import MiLaDriver mila = MiLaDriver(wb.regs, "mila") -identify = SATABISTIdentifyDriver(wb.regs, "sata_bist") -generator = SATABISTGeneratorDriver(wb.regs, "sata_bist") -checker = SATABISTCheckerDriver(wb.regs, "sata_bist") +identify = LiteSATABISTIdentifyDriver(wb.regs, "sata_bist") +generator = LiteSATABISTGeneratorDriver(wb.regs, "sata_bist") +checker = LiteSATABISTCheckerDriver(wb.regs, "sata_bist") wb.open() regs = wb.regs ### @@ -18,27 +18,27 @@ if len(sys.argv) < 2: conditions = {} conditions["wr_cmd"] = { - "bistsocdevel_sata_con_command_sink_stb" : 1, - "bistsocdevel_sata_con_command_sink_payload_write" : 1, + "bistsocdevel_core_sink_stb" : 1, + "bistsocdevel_core_sink_payload_write" : 1, } conditions["wr_dma_activate"] = { - "bistsocdevel_sata_con_command_source_source_stb" : 1, - "bistsocdevel_sata_con_command_source_source_payload_write" : 1, + "bistsocdevel_core_source_source_stb" : 1, + "bistsocdevel_core_source_source_payload_write" : 1, } conditions["rd_cmd"] = { - "bistsocdevel_sata_con_command_sink_stb" : 1, - "bistsocdevel_sata_con_command_sink_payload_read" : 1, + "bistsocdevel_core_sink_stb" : 1, + "bistsocdevel_core_sink_payload_read" : 1, } conditions["rd_data"] = { - "bistsocdevel_sata_con_command_source_source_stb" : 1, - "bistsocdevel_sata_con_command_source_source_payload_read" : 1, + "bistsocdevel_core_source_source_stb" : 1, + "bistsocdevel_core_source_source_payload_read" : 1, } conditions["id_cmd"] = { - "bistsocdevel_sata_con_command_sink_stb" : 1, - "bistsocdevel_sata_con_command_sink_payload_identify" : 1, + "bistsocdevel_core_sink_stb" : 1, + "bistsocdevel_core_sink_payload_identify" : 1, } conditions["id_pio_setup"] = { - "bistsocdevel_sata_phy_source_source_payload_data" : primitives["X_RDY"], + "bistsocdevel_source_source_payload_data" : primitives["X_RDY"], } mila.prog_term(port=0, cond=conditions[sys.argv[1]]) @@ -58,6 +58,6 @@ mila.export("dump.vcd") wb.close() print_link_trace(mila, - tx_data_name="bistsocdevel_sata_phy_sink_sink_payload_data", - rx_data_name="bistsocdevel_sata_phy_source_source_payload_data" + tx_data_name="bistsocdevel_sink_sink_payload_data", + rx_data_name="bistsocdevel_source_source_payload_data" )