Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: whitequark/Yumewatari
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 7e0cf330e1de
Choose a base ref
...
head repository: whitequark/Yumewatari
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: a2065818a5ab
Choose a head ref
  • 1 commit
  • 3 files changed
  • 1 contributor

Commits on Nov 12, 2018

  1. Copy the full SHA
    a206581 View commit details
Showing with 230 additions and 110 deletions.
  1. +92 −0 yumewatari/gateware/parser.py
  2. +129 −102 yumewatari/gateware/phy.py
  3. +9 −8 yumewatari/test/phy.py
92 changes: 92 additions & 0 deletions yumewatari/gateware/parser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
from collections import defaultdict, namedtuple
from migen import *
from migen.fhdl.structure import _Value, _Statement
from migen.genlib.fsm import _LowerNext, FSM


__all__ = ["Parser", "Memory", "NextMemory"]


class Memory(_Value):
def __init__(self, target):
self.target = target


class NextMemory(_Statement):
def __init__(self, target, value):
self.target = target
self.value = value


class _LowerMemory(_LowerNext):
def __init__(self, *args):
super().__init__(*args)
# (target, next_value_ce, next_value)
self.memories = []

def _get_memory_control(self, memory):
for target, next_value_ce, next_value in self.memories:
if target is memory:
break
else:
next_value_ce = Signal(related=memory)
next_value = Signal.like(memory)
self.memories.append((memory, next_value_ce, next_value))
return next_value_ce, next_value

def visit_unknown(self, node):
if isinstance(node, Memory):
next_value_ce, next_value = self._get_memory_control(node.target)
return Mux(next_value_ce, next_value, node.target)
elif isinstance(node, NextMemory):
next_value_ce, next_value = self._get_memory_control(node.target)
return next_value_ce.eq(1), next_value.eq(node.value)
else:
return super().visit_unknown(node)


class _ParserFSM(FSM):
def _lower_controls(self):
return _LowerMemory(self.next_state, self.encoding, self.state_aliases)

def _finalize_sync(self, ls):
super()._finalize_sync(ls)
for memory, next_value_ce, next_value in ls.memories:
self.sync += If(next_value_ce, memory.eq(next_value))


_Rule = namedtuple("_Rule", ("cond", "succ", "action"))


class Parser(Module):
def __init__(self, symbol_width, reset_rule):
self.reset = Signal()
self.error = Signal()
self.i = Signal(symbol_width)

###

self._reset_rule = reset_rule
# name -> [(cond, succ, action)]
self._grammar = defaultdict(lambda: [])

def rule(self, name, cond, succ, action=lambda symbol: []):
self._grammar[name].append(_Rule(cond, succ, action))

def do_finalize(self):
self.submodules.fsm = ResetInserter()(_ParserFSM(reset_state=self._reset_rule))
self.comb += self.fsm.reset.eq(self.reset | self.error)

for (name, rules) in self._grammar.items():
conds = Cat(rule.cond(self.i) for rule in rules)
self.fsm.act(name, [
self.error.eq(1),
Case(conds, {
(1 << n): [
NextState(rule.succ),
*rule.action(self.i),
self.error.eq(0),
]
for n, rule in enumerate(rules)
})
])
231 changes: 129 additions & 102 deletions yumewatari/gateware/phy.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from migen import *
from migen.genlib.fsm import *

from .parser import *


__all__ = ["PCIeRXPHY", "PCIeTXPHY"]

@@ -47,109 +49,139 @@ def __init__(self, lane):
self._tsZ = Record(_ts_layout) # TS being received
self.sync += If(self.error, self._tsZ.valid.eq(0))

id_ctr = Signal(max=10)
ts_idx = Signal(2) # bit 0: TS1/TS2, bit 1: noninverted/inverted
ts_id = Signal(9)

self.submodules.fsm = ResetInserter()(FSM())
self.comb += self.fsm.reset.eq(~lane.rx_valid | self.error)
self.fsm.act("COMMA",
If(lane.rx_symbol == K(28,5),
ts_inv = Signal()

self.submodules.parser = Parser(symbol_width=9, reset_rule="COMMA")
self.comb += [
self.parser.reset.eq(~lane.rx_valid),
self.parser.i.eq(lane.rx_symbol),
self.error.eq(self.parser.error)
]
self.parser.rule(
name="COMMA",
cond=lambda symbol: symbol == D(0,0),
succ="COMMA"
)
self.parser.rule(
name="COMMA",
cond=lambda symbol: symbol == K(28,5),
succ="TSn-LINK/SKP-0",
action=lambda symbol: [
NextValue(self._tsZ.valid, 1),
NextValue(self._tsY.raw_bits(), self._tsZ.raw_bits()),
NextState("TSn-LINK/SKP-0")
)
]
)
self.fsm.act("TSn-LINK/SKP-0",
If(lane.rx_symbol == K(28,0),
NextState("SKP-1")
).Else(
If(lane.rx_symbol == K(23,7),
NextValue(self._tsZ.link.valid, 0),
).Elif(~lane.rx_symbol[8],
NextValue(self._tsZ.link.valid, 1),
).Else(
self.error.eq(1)
),
NextValue(self._tsZ.link.number, lane.rx_symbol),
NextState("TSn-LANE")
)
self.parser.rule(
name="TSn-LINK/SKP-0",
cond=lambda symbol: symbol == K(28,0),
succ="SKP-1"
)
self.parser.rule(
name="TSn-LINK/SKP-0",
cond=lambda symbol: symbol == K(23,7),
succ="TSn-LANE",
action=lambda symbol: [
NextValue(self._tsZ.link.valid, 0)
]
)
self.parser.rule(
name="TSn-LINK/SKP-0",
cond=lambda symbol: ~symbol[8],
succ="TSn-LANE",
action=lambda symbol: [
NextValue(self._tsZ.link.number, symbol),
NextValue(self._tsZ.link.valid, 1)
]
)
for n in range(1, 3):
self.fsm.act("SKP-%d" % n,
If(lane.rx_symbol != K(28,0),
self.error.eq(1)
),
NextState("SKP-%d" % (n + 1) if n < 2 else "COMMA")
self.parser.rule(
name="SKP-%d" % n,
cond=lambda symbol: symbol == K(28,0),
succ="COMMA" if n == 2 else "SKP-%d" % (n + 1),
)
self.fsm.act("TSn-LANE",
If(lane.rx_symbol == K(23,7),
NextValue(self._tsZ.lane.valid, 0),
).Elif(~lane.rx_symbol[8],
NextValue(self._tsZ.lane.valid, 1),
).Else(
self.error.eq(1)
),
NextValue(self._tsZ.lane.number, lane.rx_symbol),
NextState("TSn-FTS")
self.parser.rule(
name="TSn-LANE",
cond=lambda symbol: symbol == K(23,7),
succ="TSn-FTS",
action=lambda symbol: [
NextValue(self._tsZ.lane.valid, 0)
]
)
self.fsm.act("TSn-FTS",
If(lane.rx_symbol[8],
self.error.eq(1)
),
NextValue(self._tsZ.n_fts, lane.rx_symbol),
NextState("TSn-RATE")
self.parser.rule(
name="TSn-LANE",
cond=lambda symbol: ~symbol[8],
succ="TSn-FTS",
action=lambda symbol: [
NextValue(self._tsZ.lane.number, symbol),
NextValue(self._tsZ.lane.valid, 1)
]
)
self.fsm.act("TSn-RATE",
If(lane.rx_symbol[8],
self.error.eq(1)
),
NextValue(self._tsZ.rate.raw_bits(), lane.rx_symbol),
NextState("TSn-CTRL")
self.parser.rule(
name="TSn-FTS",
cond=lambda symbol: ~symbol[8],
succ="TSn-RATE",
action=lambda symbol: [
NextValue(self._tsZ.n_fts, symbol)
]
)
self.fsm.act("TSn-CTRL",
If(lane.rx_symbol[8],
self.error.eq(1)
),
NextValue(self._tsZ.ctrl.raw_bits(), lane.rx_symbol),
NextState("TSn-ID0")
self.parser.rule(
name="TSn-RATE",
cond=lambda symbol: ~symbol[8],
succ="TSn-CTRL",
action=lambda symbol: [
NextValue(self._tsZ.rate.raw_bits(), symbol)
]
)
self.fsm.act("TSn-ID0",
If(lane.rx_symbol == D(10,2),
NextValue(ts_idx, 0),
NextValue(self._tsZ.ts_id, 0),
).Elif(lane.rx_symbol == D(5,2),
NextValue(ts_idx, 1),
NextValue(self._tsZ.ts_id, 1),
).Elif(lane.rx_symbol == D(21,5),
NextValue(ts_idx, 2),
NextValue(self._tsZ.valid, 0),
).Elif(lane.rx_symbol == D(26,5),
NextValue(ts_idx, 3),
NextValue(self._tsZ.valid, 0),
).Else(
self.error.eq(1)
),
NextValue(id_ctr, 1),
NextValue(ts_id, lane.rx_symbol),
NextState("TSn-IDn")
self.parser.rule(
name="TSn-CTRL",
cond=lambda symbol: ~symbol[8],
succ="TSn-ID0",
action=lambda symbol: [
NextValue(self._tsZ.ctrl.raw_bits(), symbol)
]
)
self.fsm.act("TSn-IDn",
If(lane.rx_symbol != ts_id,
self.error.eq(1)
),
NextValue(id_ctr, id_ctr + 1),
If(id_ctr == 9,
If(self._tsZ.raw_bits() == self._tsY.raw_bits(),
NextValue(self.ts.raw_bits(), self._tsY.raw_bits())
).Else(
NextValue(self.ts.valid, 0)
),
If(ts_idx[1],
self.parser.rule(
name="TSn-ID0",
cond=lambda symbol: (symbol == D(10,2)) |
(symbol == D( 5,2)) |
(symbol == D(21,5)) |
(symbol == D(26,5)),
succ="TSn-ID1",
action=lambda symbol: [
NextMemory(ts_id, symbol),
If(symbol == D(10,2),
NextValue(ts_inv, 0),
NextValue(self._tsZ.ts_id, 0),
).Elif(symbol == D(5,2),
NextValue(ts_inv, 0),
NextValue(self._tsZ.ts_id, 1),
).Elif(symbol == D(21,5),
NextValue(ts_inv, 1),
).Elif(symbol == D(26,5),
NextValue(ts_inv, 1),
)
]
)
for n in range(1, 9):
self.parser.rule(
name="TSn-ID%d" % n,
cond=lambda symbol: symbol == Memory(ts_id),
succ="TSn-ID%d" % (n + 1)
)
self.parser.rule(
name="TSn-ID9",
cond=lambda symbol: symbol == Memory(ts_id),
succ="COMMA",
action=lambda symbol: [
NextValue(self.ts.valid, 0),
If(ts_inv,
NextValue(lane.rx_invert, ~lane.rx_invert)
).Elif(self._tsZ.raw_bits() == self._tsY.raw_bits(),
NextValue(self.ts.raw_bits(), self._tsY.raw_bits())
),
NextState("COMMA")
)
]
)


@@ -159,8 +191,6 @@ def __init__(self, lane):

###

id_ctr = Signal(max=10)

self.submodules.fsm = ResetInserter()(FSM(reset_state="IDLE"))
self.fsm.act("IDLE",
lane.tx_set_disp.eq(1),
@@ -193,17 +223,14 @@ def __init__(self, lane):
)
self.fsm.act("TSn-CTRL",
lane.tx_symbol.eq(self.ts.ctrl.raw_bits()),
NextValue(id_ctr, 0),
NextState("TSn-IDn")
NextState("TSn-ID0")
)
self.fsm.act("TSn-IDn",
NextValue(id_ctr, id_ctr + 1),
If(self.ts.ts_id == 0,
lane.tx_symbol.eq(D(10,2))
).Else(
lane.tx_symbol.eq(D(5,2))
),
If(id_ctr == 9,
NextState("IDLE")
for n in range(0, 10):
self.fsm.act("TSn-ID%d" % n,
If(self.ts.ts_id == 0,
lane.tx_symbol.eq(D(10,2))
).Else(
lane.tx_symbol.eq(D(5,2))
),
NextState("IDLE" if n == 9 else "TSn-ID%d" % (n + 1))
)
)
17 changes: 9 additions & 8 deletions yumewatari/test/phy.py
Original file line number Diff line number Diff line change
@@ -12,10 +12,10 @@ def __init__(self):
self.submodules.phy = PCIeRXPHY(self.lane)

def do_finalize(self):
self.states = {v: k for k, v in self.phy.fsm.encoding.items()}
self.states = {v: k for k, v in self.phy.parser.fsm.encoding.items()}

def phy_state(self):
return self.states[(yield self.phy.fsm.state)]
return self.states[(yield self.phy.parser.fsm.state)]

def transmit(self, symbols):
for symbol in symbols:
@@ -73,11 +73,11 @@ def test_rx_tsn_cycle_by_cycle(self, tb):
yield tb.lane.rx_symbol.eq(D(5,2))
yield
yield from self.assertSignal(tb.phy._tsZ.ts_id, 1)
yield from self.assertState(tb, "TSn-IDn")
for _ in range(8):
yield from self.assertState(tb, "TSn-ID1")
for n in range(2, 10):
yield tb.lane.rx_symbol.eq(D(5,2))
yield
yield from self.assertState(tb, "TSn-IDn")
yield from self.assertState(tb, "TSn-ID%d" % n)
yield tb.lane.rx_symbol.eq(K(28,5))
yield
yield from self.assertSignal(tb.phy._tsZ.valid, 1)
@@ -127,7 +127,7 @@ def test_rx_ts1_inverted_valid(self, tb):
K(28,5),
])
yield from self.assertSignal(tb.lane.rx_invert, 1)
yield from self.assertTSnState(tb.phy._tsZ, valid=0)
yield from self.assertTSnState(tb.phy._tsZ)

@simulation_test
def test_rx_ts2_inverted_valid(self, tb):
@@ -136,7 +136,7 @@ def test_rx_ts2_inverted_valid(self, tb):
K(28,5),
])
yield from self.assertSignal(tb.lane.rx_invert, 1)
yield from self.assertTSnState(tb.phy._tsZ, valid=0)
yield from self.assertTSnState(tb.phy._tsZ)

@simulation_test
def test_rx_ts1_link_valid(self, tb):
@@ -218,7 +218,6 @@ def test_rx_ts1_ctrl_valid(self, tb):
):
yield from self.tb.transmit([
K(28,5), 0xaa, 0x1a, 0xff, 0b0010, bit, *[D(10,2) for _ in range(10)],
K(28,0),
])
yield from self.assertTSnState(tb.phy._tsZ,
link_valid=1, link_number=0xaa,
@@ -237,6 +236,8 @@ def test_rx_ts1_ctrl_invalid(self, tb):
@simulation_test
def test_rx_ts1_idN_invalid(self, tb):
for n in range(10):
yield self.tb.lane.rx_symbol.eq(0)
yield
yield from self.tb.transmit([
K(28,5), 0xaa, 0x1a, 0xff, 0b0010, 0b0001, *[D(10,2) for _ in range(n)], 0x1ee
])