init
This commit is contained in:
commit
7f23de0ce8
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
build
|
||||||
|
__pycache__
|
||||||
|
test
|
||||||
|
*.vcd
|
||||||
|
*.cfg
|
||||||
|
*.txt
|
BIN
bscan_spi_xc7a100t.bit
Normal file
BIN
bscan_spi_xc7a100t.bit
Normal file
Binary file not shown.
26
buffer.py
Normal file
26
buffer.py
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
from migen import *
|
||||||
|
from migen.genlib.fifo import SyncFIFO
|
||||||
|
|
||||||
|
|
||||||
|
class Buffer(Module):
|
||||||
|
def __init__(self, width=8, depth=128):
|
||||||
|
self.din = Signal(width)
|
||||||
|
self.i_stb = Signal()
|
||||||
|
|
||||||
|
self.dout = Signal(width)
|
||||||
|
self.o_stb = Signal()
|
||||||
|
self.o_ack = Signal()
|
||||||
|
|
||||||
|
# Underlying data structure
|
||||||
|
self.submodules.fifo = SyncFIFO(width, depth)
|
||||||
|
|
||||||
|
self.comb += [
|
||||||
|
# TX path
|
||||||
|
self.o_stb.eq(self.fifo.readable),
|
||||||
|
self.dout.eq(self.fifo.dout),
|
||||||
|
self.fifo.re.eq(self.o_ack),
|
||||||
|
|
||||||
|
# RX path
|
||||||
|
self.fifo.we.eq(self.i_stb),
|
||||||
|
self.fifo.din.eq(self.din),
|
||||||
|
]
|
22
comm.py
Normal file
22
comm.py
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import serial
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
comm = serial.Serial("/dev/ttyUSB3", 115200)
|
||||||
|
# comm.write(b"Hello World!")
|
||||||
|
|
||||||
|
# for _ in range(32):
|
||||||
|
while True:
|
||||||
|
byte = comm.read(2)
|
||||||
|
print(f'{byte[0]:0>8b}' + f'{byte[1]:0>8b}')
|
||||||
|
byte = comm.read(1)
|
||||||
|
print(f'{byte[0]:0>8b}')
|
||||||
|
# cached_byte = None
|
||||||
|
# while True:
|
||||||
|
# byte = comm.read(1)
|
||||||
|
# if byte != cached_byte:
|
||||||
|
# cached_byte = byte
|
||||||
|
# print(f'{byte[0]:0>8b}')
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
55
default.nix
Normal file
55
default.nix
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
{ pkgs ? import <nixpkgs> {} }:
|
||||||
|
|
||||||
|
let
|
||||||
|
migen = pkgs.python3Packages.buildPythonPackage rec {
|
||||||
|
name = "migen";
|
||||||
|
|
||||||
|
src = pkgs.fetchFromGitHub {
|
||||||
|
owner = "m-labs";
|
||||||
|
repo = "migen";
|
||||||
|
rev = "7bc4eb1387b39159a74c1dbd1b820728e0bfbbaa";
|
||||||
|
sha256 = "039jk8y7f0vhr32svg3nd23i88c0bhws8ngxwk9bdznfxvhiy1h6";
|
||||||
|
fetchSubmodules = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
propagatedBuildInputs = with pkgs.python3Packages; [ colorama ];
|
||||||
|
};
|
||||||
|
|
||||||
|
vivadoDeps = pkgs: with pkgs; [
|
||||||
|
libxcrypt
|
||||||
|
ncurses5
|
||||||
|
zlib
|
||||||
|
libuuid
|
||||||
|
xorg.libSM
|
||||||
|
xorg.libICE
|
||||||
|
xorg.libXrender
|
||||||
|
xorg.libX11
|
||||||
|
xorg.libXext
|
||||||
|
xorg.libXtst
|
||||||
|
xorg.libXi
|
||||||
|
freetype
|
||||||
|
fontconfig
|
||||||
|
];
|
||||||
|
|
||||||
|
vivadoEnv = pkgs.buildFHSUserEnv {
|
||||||
|
name = "vivado-env";
|
||||||
|
targetPkgs = vivadoDeps;
|
||||||
|
};
|
||||||
|
|
||||||
|
vivado = pkgs.buildFHSUserEnv {
|
||||||
|
name = "vivado";
|
||||||
|
targetPkgs = vivadoDeps;
|
||||||
|
profile = "set -e; source /opt/Xilinx/Vivado/2022.2/settings64.sh";
|
||||||
|
runScript = "vivado";
|
||||||
|
};
|
||||||
|
|
||||||
|
in pkgs.mkShell {
|
||||||
|
name = "UART-Testing";
|
||||||
|
buildInputs = [
|
||||||
|
migen
|
||||||
|
pkgs.python3Packages.pyserial
|
||||||
|
vivado
|
||||||
|
vivadoEnv
|
||||||
|
pkgs.python3Packages.numpy
|
||||||
|
];
|
||||||
|
}
|
29
eem_helpers.py
Normal file
29
eem_helpers.py
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
from migen import *
|
||||||
|
from migen.build.generic_platform import *
|
||||||
|
|
||||||
|
|
||||||
|
def _eem_signal(i):
|
||||||
|
n = "d{}".format(i)
|
||||||
|
if i == 0:
|
||||||
|
n += "_cc"
|
||||||
|
return n
|
||||||
|
|
||||||
|
|
||||||
|
def _eem_pin(eem, i, pol):
|
||||||
|
return "eem{}:{}_{}".format(eem, _eem_signal(i), pol)
|
||||||
|
|
||||||
|
|
||||||
|
def default_iostandard(eem):
|
||||||
|
return IOStandard("LVDS_25")
|
||||||
|
|
||||||
|
|
||||||
|
def diff_io(eem, iostandard=default_iostandard):
|
||||||
|
return [("dio{}".format(eem), i,
|
||||||
|
Subsignal("p", Pins(_eem_pin(eem, i, "p"))),
|
||||||
|
Subsignal("n", Pins(_eem_pin(eem, i, "n"))),
|
||||||
|
iostandard(eem))
|
||||||
|
for i in range(8)]
|
||||||
|
|
||||||
|
|
||||||
|
def generate_pads(platform, eem):
|
||||||
|
platform.add_extension(diff_io(eem))
|
41
io_loopback.py
Normal file
41
io_loopback.py
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
from migen import *
|
||||||
|
|
||||||
|
|
||||||
|
class IOLoopBack(Module):
|
||||||
|
def __init__(self, pads):
|
||||||
|
self.o = Signal(4)
|
||||||
|
self.i = Signal(4)
|
||||||
|
self.t = Signal(4)
|
||||||
|
|
||||||
|
for i in range(4):
|
||||||
|
self.specials += Instance("IOBUFDS",
|
||||||
|
o_O=self.o[i],
|
||||||
|
io_IO=pads[i].p,
|
||||||
|
io_IOB=pads[i].n,
|
||||||
|
i_I=self.i[i],
|
||||||
|
# Always enable input buffer, so it is actually a loop back
|
||||||
|
i_T=self.t[i],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class SingleIOLoopback(Module):
|
||||||
|
def __init__(self, pad):
|
||||||
|
self.o = Signal()
|
||||||
|
self.i = Signal()
|
||||||
|
self.t = Signal()
|
||||||
|
|
||||||
|
self.specials += Instance(
|
||||||
|
# "IOBUFDS_DCIEN",
|
||||||
|
"IOBUFDS",
|
||||||
|
# p_DIFF_TERM="TRUE",
|
||||||
|
# p_IBUF_LOW_PWR="TRUE",
|
||||||
|
# p_USE_IBUFDISABLE="TRUE",
|
||||||
|
o_O=self.o,
|
||||||
|
io_IO=pad.p,
|
||||||
|
io_IOB=pad.n,
|
||||||
|
i_I=self.i,
|
||||||
|
# Always enable input buffer, so it is actually a loop back
|
||||||
|
i_T=self.t,
|
||||||
|
# i_IBUFDISABLE=~self.t,
|
||||||
|
# i_DCITERMDISABLE=~self.t,
|
||||||
|
)
|
125
kasli_crg.py
Normal file
125
kasli_crg.py
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
from migen import *
|
||||||
|
from migen.build.platforms.sinara import kasli
|
||||||
|
from migen.genlib.resetsync import AsyncResetSynchronizer
|
||||||
|
|
||||||
|
|
||||||
|
class KasliCRG(Module):
|
||||||
|
def __init__(self, platform):
|
||||||
|
self.platform = platform
|
||||||
|
|
||||||
|
# Generated clock domains
|
||||||
|
self.clock_domains.cd_sys = ClockDomain()
|
||||||
|
self.clock_domains.cd_sys5x = ClockDomain()
|
||||||
|
self.clock_domains.cd_rx_sys = ClockDomain()
|
||||||
|
self.clock_domains.cd_rx_sys5x = ClockDomain()
|
||||||
|
self.clock_domains.cd_clk200 = ClockDomain()
|
||||||
|
|
||||||
|
# Configure system clock using GTP ports
|
||||||
|
self.sys_clk_freq = 125e6
|
||||||
|
clk125 = self.platform.request("clk125_gtp")
|
||||||
|
clk125_buf = Signal()
|
||||||
|
clk125_div2 = Signal()
|
||||||
|
|
||||||
|
self.specials += Instance("IBUFDS_GTE2",
|
||||||
|
i_CEB=0,
|
||||||
|
i_I=clk125.p, i_IB=clk125.n,
|
||||||
|
o_O=clk125_buf,
|
||||||
|
o_ODIV2=clk125_div2)
|
||||||
|
|
||||||
|
# MMCM to generate different frequencies
|
||||||
|
mmcm_fb = Signal()
|
||||||
|
mmcm_locked = Signal()
|
||||||
|
mmcm_sys = Signal()
|
||||||
|
mmcm_sys5x = Signal()
|
||||||
|
mmcm_rx_sys = Signal()
|
||||||
|
mmcm_rx_sys5x = Signal()
|
||||||
|
|
||||||
|
# PLL to IDELAYCTRL clock
|
||||||
|
pll_locked = Signal()
|
||||||
|
pll_fb = Signal()
|
||||||
|
pll_clk200 = Signal()
|
||||||
|
|
||||||
|
# Actual MMCM/PLL instances
|
||||||
|
self.specials += [
|
||||||
|
Instance("MMCME2_BASE",
|
||||||
|
p_CLKIN1_PERIOD=16.0,
|
||||||
|
i_CLKIN1=clk125_div2,
|
||||||
|
|
||||||
|
i_CLKFBIN=mmcm_fb,
|
||||||
|
o_CLKFBOUT=mmcm_fb,
|
||||||
|
o_LOCKED=mmcm_locked,
|
||||||
|
|
||||||
|
# VCO @ 1.25GHz with MULT=20
|
||||||
|
p_CLKFBOUT_MULT_F=20.0, p_DIVCLK_DIVIDE=1,
|
||||||
|
|
||||||
|
# ~125MHz
|
||||||
|
p_CLKOUT0_DIVIDE_F=10.0, p_CLKOUT0_PHASE=0.0, o_CLKOUT0=mmcm_sys,
|
||||||
|
|
||||||
|
# ~625MHz
|
||||||
|
p_CLKOUT1_DIVIDE=2, p_CLKOUT1_PHASE=0.0, o_CLKOUT1=mmcm_sys5x,
|
||||||
|
|
||||||
|
# ~125MHz separated from sysclk, for RX
|
||||||
|
p_CLKOUT2_DIVIDE=10, p_CLKOUT2_PHASE=0.0, o_CLKOUT2=mmcm_rx_sys,
|
||||||
|
|
||||||
|
# ~625MHz separated from sysclk, for RX
|
||||||
|
p_CLKOUT3_DIVIDE=2, p_CLKOUT3_PHASE=0.0, o_CLKOUT3=mmcm_rx_sys5x,
|
||||||
|
|
||||||
|
# Leftovers...
|
||||||
|
# p_CLKOUT2_DIVIDE=2, p_CLKOUT2_PHASE=90.0, o_CLKOUT2=mmcm_sys4x_dqs,
|
||||||
|
),
|
||||||
|
Instance("PLLE2_BASE",
|
||||||
|
p_CLKIN1_PERIOD=16.0,
|
||||||
|
i_CLKIN1=clk125_div2,
|
||||||
|
|
||||||
|
i_CLKFBIN=pll_fb,
|
||||||
|
o_CLKFBOUT=pll_fb,
|
||||||
|
o_LOCKED=pll_locked,
|
||||||
|
|
||||||
|
# VCO @ 1GHz
|
||||||
|
p_CLKFBOUT_MULT=16, p_DIVCLK_DIVIDE=1,
|
||||||
|
|
||||||
|
# 200MHz for IDELAYCTRL
|
||||||
|
p_CLKOUT0_DIVIDE=5, p_CLKOUT0_PHASE=0.0, o_CLKOUT0=pll_clk200,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
self.specials += [
|
||||||
|
Instance("BUFG", i_I=mmcm_sys, o_O=self.cd_sys.clk),
|
||||||
|
Instance("BUFG", i_I=mmcm_sys5x, o_O=self.cd_sys5x.clk),
|
||||||
|
Instance("BUFG", i_I=mmcm_rx_sys, o_O=self.cd_rx_sys.clk),
|
||||||
|
Instance("BUFG", i_I=mmcm_rx_sys5x, o_O=self.cd_rx_sys5x.clk),
|
||||||
|
Instance("BUFG", i_I=pll_clk200, o_O=self.cd_clk200.clk),
|
||||||
|
AsyncResetSynchronizer(self.cd_clk200, ~pll_locked),
|
||||||
|
]
|
||||||
|
|
||||||
|
reset_counter = Signal(4, reset=15)
|
||||||
|
ic_reset = Signal(reset=1)
|
||||||
|
self.sync.clk200 += \
|
||||||
|
If(reset_counter != 0,
|
||||||
|
reset_counter.eq(reset_counter - 1)
|
||||||
|
).Else(
|
||||||
|
ic_reset.eq(0)
|
||||||
|
)
|
||||||
|
self.specials += Instance("IDELAYCTRL", i_REFCLK=ClockSignal("clk200"), i_RST=ic_reset)
|
||||||
|
|
||||||
|
# Add clock costraints for all clock signals
|
||||||
|
platform.add_period_constraint(self.cd_sys.clk, 8.)
|
||||||
|
platform.add_period_constraint(self.cd_sys5x.clk, 1.6)
|
||||||
|
platform.add_period_constraint(self.cd_rx_sys.clk, 8.)
|
||||||
|
platform.add_period_constraint(self.cd_rx_sys5x.clk, 1.6)
|
||||||
|
platform.add_period_constraint(self.cd_clk200.clk, 5.)
|
||||||
|
|
||||||
|
platform.add_platform_command(
|
||||||
|
"set_false_path -quiet "
|
||||||
|
"-through [get_pins -filter {{REF_PIN_NAME == OQ || REF_PIN_NAME == TQ}} "
|
||||||
|
"-of [get_cells -filter {{REF_NAME == OSERDESE2}}]] "
|
||||||
|
"-to [get_pins -filter {{REF_PIN_NAME == D}} "
|
||||||
|
"-of [get_cells -filter {{REF_NAME == ISERDESE2}}]]"
|
||||||
|
)
|
||||||
|
platform.add_platform_command(
|
||||||
|
"set_false_path -quiet "
|
||||||
|
"-through [get_pins -filter {{REF_PIN_NAME == OQ || REF_PIN_NAME == TQ}} "
|
||||||
|
"-of [get_cells -filter {{REF_NAME == OSERDESE2}}]] "
|
||||||
|
"-to [get_pins -filter {{REF_PIN_NAME == DDLY}} "
|
||||||
|
"-of [get_cells -filter {{REF_NAME == ISERDESE2}}]]"
|
||||||
|
)
|
44
loopback.py
Normal file
44
loopback.py
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
from migen import *
|
||||||
|
from migen.build.platforms.sinara import kasli
|
||||||
|
from migen.genlib.fifo import SyncFIFO
|
||||||
|
from uart import UART
|
||||||
|
from kasli_crg import KasliCRG
|
||||||
|
|
||||||
|
|
||||||
|
class UARTLoopBack(Module):
|
||||||
|
def __init__(self, sys_clk_freq):
|
||||||
|
self.uart_rx = Signal()
|
||||||
|
self.uart_tx = Signal()
|
||||||
|
|
||||||
|
self.submodules.uart = UART(round((115200/sys_clk_freq)*2**32))
|
||||||
|
self.comb += [
|
||||||
|
self.uart.phy_rx.eq(self.uart_rx),
|
||||||
|
self.uart_tx.eq(self.uart.phy_tx),
|
||||||
|
]
|
||||||
|
|
||||||
|
# Attach buffer between UART RX --> TX
|
||||||
|
# This constitutes the loopback channel
|
||||||
|
self.submodules.buffer = SyncFIFO(8, 64)
|
||||||
|
self.comb += [
|
||||||
|
self.buffer.din.eq(self.uart.rx_data),
|
||||||
|
self.buffer.we.eq(self.uart.rx_stb),
|
||||||
|
self.uart.tx_data.eq(self.buffer.dout),
|
||||||
|
self.uart.tx_stb.eq(self.buffer.readable),
|
||||||
|
self.buffer.re.eq(self.uart.tx_ack),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
platform = kasli.Platform(hw_rev="v2.0")
|
||||||
|
crg = KasliCRG(platform)
|
||||||
|
top = UARTLoopBack(crg.sys_clk_freq)
|
||||||
|
|
||||||
|
# Wire up UART core to the pads
|
||||||
|
uart_pads = platform.request("serial")
|
||||||
|
top.comb += [
|
||||||
|
top.uart_rx.eq(uart_pads.rx),
|
||||||
|
uart_pads.tx.eq(top.uart_tx),
|
||||||
|
]
|
||||||
|
|
||||||
|
top.submodules += crg
|
||||||
|
platform.build(top)
|
122
serdes.py
Normal file
122
serdes.py
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
from migen import *
|
||||||
|
|
||||||
|
|
||||||
|
class SerTX(Module):
|
||||||
|
def __init__(self):
|
||||||
|
self.txdata = Signal(20)
|
||||||
|
self.ser_out = Signal(4)
|
||||||
|
self.t_out = Signal(4)
|
||||||
|
# TODO: Create T pins
|
||||||
|
|
||||||
|
# Transmitter PHY: 4-wire
|
||||||
|
for i in range(4):
|
||||||
|
# Serialize 5 bits into each channel
|
||||||
|
|
||||||
|
# TX SERDES
|
||||||
|
self.specials += Instance("OSERDESE2",
|
||||||
|
p_DATA_RATE_OQ="SDR", p_DATA_RATE_TQ="BUF",
|
||||||
|
p_DATA_WIDTH=5, p_TRISTATE_WIDTH=1,
|
||||||
|
p_INIT_OQ=0b00000,
|
||||||
|
o_OQ=self.ser_out[i], o_TQ=self.t_out[i],
|
||||||
|
i_RST=ResetSignal(),
|
||||||
|
i_CLK=ClockSignal("sys5x"),
|
||||||
|
i_CLKDIV=ClockSignal(),
|
||||||
|
i_D1=self.txdata[i*5 + 0],
|
||||||
|
i_D2=self.txdata[i*5 + 1],
|
||||||
|
i_D3=self.txdata[i*5 + 2],
|
||||||
|
i_D4=self.txdata[i*5 + 3],
|
||||||
|
i_D5=self.txdata[i*5 + 4],
|
||||||
|
i_TCE=1, i_OCE=1,
|
||||||
|
# TODO: Hardcode t_in? Output disable is always unnecessary?
|
||||||
|
i_T1=0)
|
||||||
|
|
||||||
|
|
||||||
|
class DesRX(Module):
|
||||||
|
def __init__(self):
|
||||||
|
self.rxdata = Signal(20)
|
||||||
|
self.ser_in = Signal(4)
|
||||||
|
|
||||||
|
for i in range(4):
|
||||||
|
# Deserialize 5 bits from each channel
|
||||||
|
# RX SERDES
|
||||||
|
self.specials += [
|
||||||
|
Instance("ISERDESE2",
|
||||||
|
p_DATA_RATE="SDR",
|
||||||
|
p_DATA_WIDTH=5,
|
||||||
|
p_INTERFACE_TYPE="NETWORKING", p_NUM_CE=1,
|
||||||
|
o_Q1=self.rxdata[i*5 + 4],
|
||||||
|
o_Q2=self.rxdata[i*5 + 3],
|
||||||
|
o_Q3=self.rxdata[i*5 + 2],
|
||||||
|
o_Q4=self.rxdata[i*5 + 1],
|
||||||
|
o_Q5=self.rxdata[i*5 + 0],
|
||||||
|
i_D=self.ser_in[i],
|
||||||
|
i_CLK=ClockSignal("rx_sys5x"),
|
||||||
|
i_CLKB=~ClockSignal("rx_sys5x"),
|
||||||
|
i_CE1=1,
|
||||||
|
i_RST=ResetSignal("rx_sys"),
|
||||||
|
i_CLKDIV=ClockSignal("rx_sys")),
|
||||||
|
|
||||||
|
# # Tunable delay
|
||||||
|
# Instance("IDELAYE2",
|
||||||
|
# p_DELAY_SRC="IDATAIN", p_SIGNAL_PATTERN="DATA",
|
||||||
|
# p_CINVCTRL_SEL="FALSE", p_HIGH_PERFORMANCE_MODE="TRUE", p_REFCLK_FREQUENCY=200.0,
|
||||||
|
# p_PIPE_SEL="FALSE", p_IDELAY_TYPE="VARIABLE", p_IDELAY_VALUE=0,
|
||||||
|
|
||||||
|
# i_C=ClockSignal(),
|
||||||
|
# i_LD=self._dly_sel.storage[i//8] & self._rdly_dq_rst.re,
|
||||||
|
# i_CE=self._dly_sel.storage[i//8] & self._rdly_dq_inc.re,
|
||||||
|
# i_LDPIPEEN=0, i_INC=1,
|
||||||
|
|
||||||
|
# i_IDATAIN=dq_i_nodelay, o_DATAOUT=dq_i_delayed
|
||||||
|
# )
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
# class DoubleDesRX(Module):
|
||||||
|
# def __init__(self):
|
||||||
|
# self.rxdata = Signal(20)
|
||||||
|
|
||||||
|
# self.rx_first_edge = Signal()
|
||||||
|
# rx_raw = Array(Signal(20) for _ in range(2))
|
||||||
|
# self.comb += [
|
||||||
|
# rxdata.eq(rx_raw[self.rx_first_edge])
|
||||||
|
# ]
|
||||||
|
|
||||||
|
# # Receiver PHY: 4-wire
|
||||||
|
# for i in range(4):
|
||||||
|
# # Deserialize 5 bits from each channel
|
||||||
|
# # With 2x oversampling
|
||||||
|
|
||||||
|
# # RX SERDES
|
||||||
|
# self.specials += Instance("ISERDESE2", p_DATA_RATE="DDR",
|
||||||
|
# p_DATA_WIDTH=10,
|
||||||
|
# p_INTERFACE_TYPE="NETWORKING", p_NUM_CE=1,
|
||||||
|
# p_SERDES_MODE="MASTER",
|
||||||
|
# o_Q1=rx_raw[1][i*5 + 4], o_Q2=rx_raw[0][i*5 + 4],
|
||||||
|
# o_Q3=rx_raw[1][i*5 + 3], o_Q4=rx_raw[0][i*5 + 3],
|
||||||
|
# o_Q5=rx_raw[1][i*5 + 2], o_Q6=rx_raw[0][i*5 + 2],
|
||||||
|
# o_Q7=rx_raw[1][i*5 + 1], o_Q8=rx_raw[0][i*5 + 1],
|
||||||
|
# i_D=self.ser_in[i],
|
||||||
|
# # We are using 5x for SDR
|
||||||
|
# i_CLK=ClockSignal("sys5x"),
|
||||||
|
# i_CLKB=~ClockSignal("sys5x"),
|
||||||
|
# i_CE1=1,
|
||||||
|
# i_RST=ResetSignal(),
|
||||||
|
# i_CLKDIV=ClockSignal(),
|
||||||
|
# o_SHIFTOUT1=serdes_link1,
|
||||||
|
# o_SHIFTOUT2=serdes_link2)
|
||||||
|
|
||||||
|
# self.specials += Instance("ISERDESE2", p_DATA_RATE="DDR",
|
||||||
|
# p_DATA_WIDTH=10,
|
||||||
|
# p_INTERFACE_TYPE="NETWORKING", p_NUM_CE=1,
|
||||||
|
# p_SERDES_MODE="SLAVE",
|
||||||
|
# o_Q1=rx_raw[1][i*5], o_Q2=rx_raw[0][i*5],
|
||||||
|
# i_D=self.ser_in[i],
|
||||||
|
# # We are using 5x for SDR
|
||||||
|
# i_CLK=ClockSignal("sys5x"),
|
||||||
|
# i_CLKB=~ClockSignal("sys5x"),
|
||||||
|
# i_CE1=1,
|
||||||
|
# i_RST=ResetSignal(),
|
||||||
|
# i_CLKDIV=ClockSignal(),
|
||||||
|
# i_SHIFTIN1=serdes_link1,
|
||||||
|
# i_SHIFTIN2=serdes_link2)
|
102
serdes_loopback.py
Normal file
102
serdes_loopback.py
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
from migen import *
|
||||||
|
from serdes import *
|
||||||
|
from migen.genlib.fifo import SyncFIFO
|
||||||
|
from migen.build.platforms.sinara import kasli
|
||||||
|
from migen.genlib.misc import WaitTimer
|
||||||
|
from kasli_crg import KasliCRG
|
||||||
|
from eem_helpers import generate_pads
|
||||||
|
from uart import UART
|
||||||
|
from io_loopback import IOLoopBack
|
||||||
|
|
||||||
|
|
||||||
|
SEPARATOR = Constant(0b0101)
|
||||||
|
|
||||||
|
class SerDesLoopBack(Module):
|
||||||
|
def __init__(self, io_pads, sys_clk_freq):
|
||||||
|
self.uart_rx = Signal()
|
||||||
|
self.uart_tx = Signal()
|
||||||
|
|
||||||
|
self.submodules.uart = UART(round((115200/sys_clk_freq)*2**32))
|
||||||
|
self.comb += [
|
||||||
|
self.uart.phy_rx.eq(self.uart_rx),
|
||||||
|
self.uart_tx.eq(self.uart.phy_tx),
|
||||||
|
]
|
||||||
|
|
||||||
|
self.submodules.tx = SerTX()
|
||||||
|
self.submodules.rx = DesRX()
|
||||||
|
|
||||||
|
# The actual channel
|
||||||
|
self.submodules.channel = IOLoopBack(io_pads)
|
||||||
|
|
||||||
|
# # Additional timer: Only permit UART transmission when timer is not up
|
||||||
|
# self.submodules.wait_timer = WaitTimer(10)
|
||||||
|
|
||||||
|
# Memoize the previous rxdata
|
||||||
|
self.rxdata_r = Signal(8)
|
||||||
|
|
||||||
|
# Attach FIFO to UART TX, send rate is too slow w.r.t sysclk
|
||||||
|
self.submodules.tx_fifo = SyncFIFO(8, 64)
|
||||||
|
|
||||||
|
self.comb += [
|
||||||
|
# RX path: From UART to channel
|
||||||
|
# self.rx_buffer.din.eq(self.uart.rx_data),
|
||||||
|
# self.rx_buffer.we.eq(self.uart.rx_stb),
|
||||||
|
self.tx.txdata[:8].eq(Mux(self.uart.rx_stb, self.uart.rx_data, 0)),
|
||||||
|
self.tx.txdata[8:12].eq(Mux(SEPARATOR, self.uart.rx_data, 0)),
|
||||||
|
self.tx.txdata[12:].eq(Mux(self.uart.rx_stb, self.uart.rx_data, 0)),
|
||||||
|
|
||||||
|
# Loopback channel
|
||||||
|
self.channel.i.eq(self.tx.ser_out),
|
||||||
|
self.rx.ser_in.eq(self.channel.o),
|
||||||
|
self.channel.t.eq(self.tx.t_out),
|
||||||
|
# self.rx.ser_in.eq(self.tx.ser_out),
|
||||||
|
# self.rx.ser_in[3].eq(self.tx.ser_out[3]),
|
||||||
|
|
||||||
|
# TX path
|
||||||
|
# self.uart.tx_data.eq(self.tx_buffer.dout),
|
||||||
|
# self.uart.tx_stb.eq(self.tx_buffer.readable),
|
||||||
|
# self.tx_buffer.re.eq(self.uart.tx_ack),
|
||||||
|
self.uart.tx_data.eq(self.tx_fifo.dout),
|
||||||
|
self.uart.tx_stb.eq(self.tx_fifo.readable),
|
||||||
|
self.tx_fifo.re.eq(self.uart.tx_ack),
|
||||||
|
]
|
||||||
|
|
||||||
|
# Timer control
|
||||||
|
self.sync += [
|
||||||
|
# Send data to FIFO if not repeated
|
||||||
|
If(self.rxdata_r != self.rx.rxdata[:8],
|
||||||
|
self.rxdata_r.eq(self.rx.rxdata),
|
||||||
|
self.tx_fifo.din.eq(self.rx.rxdata),
|
||||||
|
self.tx_fifo.we.eq(1)
|
||||||
|
).Else(
|
||||||
|
self.tx_fifo.we.eq(0)
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
# self.sync.sys5x += [
|
||||||
|
# self.rx.ser_in.eq(self.tx.ser_out),
|
||||||
|
# ]
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
platform = kasli.Platform(hw_rev="v2.0")
|
||||||
|
|
||||||
|
# Generate pads for the I/O blocks
|
||||||
|
eem = 0
|
||||||
|
generate_pads(platform, eem)
|
||||||
|
pads = [
|
||||||
|
platform.request("dio{}".format(eem), i) for i in range(4)
|
||||||
|
]
|
||||||
|
|
||||||
|
crg = KasliCRG(platform)
|
||||||
|
top = SerDesLoopBack(pads, crg.sys_clk_freq)
|
||||||
|
|
||||||
|
# Wire up UART core to the pads
|
||||||
|
uart_pads = platform.request("serial")
|
||||||
|
top.comb += [
|
||||||
|
top.uart_rx.eq(uart_pads.rx),
|
||||||
|
uart_pads.tx.eq(top.uart_tx),
|
||||||
|
]
|
||||||
|
|
||||||
|
top.submodules += crg
|
||||||
|
platform.build(top)
|
164
single_serdes_loopback.py
Normal file
164
single_serdes_loopback.py
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
from migen import *
|
||||||
|
from sync_serdes import *
|
||||||
|
from migen.genlib.fifo import SyncFIFO
|
||||||
|
from migen.build.platforms.sinara import kasli
|
||||||
|
from migen.genlib.misc import WaitTimer
|
||||||
|
from kasli_crg import KasliCRG
|
||||||
|
from eem_helpers import generate_pads
|
||||||
|
from uart import UART
|
||||||
|
from io_loopback import SingleIOLoopback
|
||||||
|
|
||||||
|
|
||||||
|
SEPARATOR = Constant(0b0101)
|
||||||
|
|
||||||
|
class SingleSerDesLoopBack(Module):
|
||||||
|
def __init__(self, io_pad, sys_clk_freq):
|
||||||
|
self.uart_rx = Signal()
|
||||||
|
self.uart_tx = Signal()
|
||||||
|
|
||||||
|
self.submodules.uart = UART(round((115200/sys_clk_freq)*2**32))
|
||||||
|
self.comb += [
|
||||||
|
self.uart.phy_rx.eq(self.uart_rx),
|
||||||
|
self.uart_tx.eq(self.uart.phy_tx),
|
||||||
|
]
|
||||||
|
|
||||||
|
self.submodules.tx = SingleLineTX()
|
||||||
|
self.submodules.rx = SingleLineRX()
|
||||||
|
self.submodules.phase_reader = PhaseReader()
|
||||||
|
# self.submodules.delay_optimizer = DelayOptimizer()
|
||||||
|
|
||||||
|
# The actual channel
|
||||||
|
self.submodules.channel = SingleIOLoopback(io_pad)
|
||||||
|
|
||||||
|
# Attach FIFO to UART TX, send rate is too slow w.r.t sysclk
|
||||||
|
self.submodules.tx_fifo = SyncFIFO(8, 64)
|
||||||
|
|
||||||
|
self.comb += [
|
||||||
|
# Repetitively send 0b00100
|
||||||
|
self.tx.txdata.eq(0b00100),
|
||||||
|
|
||||||
|
# Loopback channel
|
||||||
|
self.channel.i.eq(self.tx.ser_out),
|
||||||
|
self.rx.ser_in_no_dly.eq(self.channel.o),
|
||||||
|
self.channel.t.eq(self.tx.t_out),
|
||||||
|
|
||||||
|
# TX path
|
||||||
|
self.uart.tx_data.eq(self.tx_fifo.dout),
|
||||||
|
self.uart.tx_stb.eq(self.tx_fifo.readable),
|
||||||
|
self.tx_fifo.re.eq(self.uart.tx_ack),
|
||||||
|
]
|
||||||
|
|
||||||
|
# Route deserializer to phase_reader & the delay tap optimizer
|
||||||
|
self.comb += [
|
||||||
|
# Start the reader initially
|
||||||
|
self.phase_reader.start.eq(1),
|
||||||
|
# Delay tap optimizer will start after the reader is done
|
||||||
|
# self.delay_optimizer.start.eq(0),
|
||||||
|
|
||||||
|
# RXDATA for both reader and optimzer
|
||||||
|
self.phase_reader.loopback_rxdata.eq(self.rx.rxdata),
|
||||||
|
# TODO: Reconnet
|
||||||
|
# self.delay_optimizer.loopback_rxdata.eq(self.rx.rxdata),
|
||||||
|
|
||||||
|
# Delay tap value
|
||||||
|
self.phase_reader.delay_tap.eq(self.rx.cnt_out),
|
||||||
|
# TODO: Reconnet
|
||||||
|
# self.delay_optimizer.delay_tap.eq(self.rx.cnt_out),
|
||||||
|
|
||||||
|
# Increment control enable, such that phase_reader can
|
||||||
|
# increment tap value after delay measurement
|
||||||
|
# Re-assign the incremnet control to the optimizer after the optimizer has started
|
||||||
|
# If(self.delay_optimizer.start,
|
||||||
|
# self.rx.ce.eq(self.delay_optimizer.inc_en),
|
||||||
|
# ).Else(
|
||||||
|
# self.rx.ce.eq(self.phase_reader.inc_en),
|
||||||
|
# )
|
||||||
|
self.rx.ce.eq(self.phase_reader.inc_en),
|
||||||
|
]
|
||||||
|
|
||||||
|
# Show measured result on UART
|
||||||
|
delay_tap = Signal(6)
|
||||||
|
|
||||||
|
fsm = FSM(reset_state="WAIT_DONE")
|
||||||
|
self.submodules += fsm
|
||||||
|
|
||||||
|
fsm.act("WAIT_DONE",
|
||||||
|
If(self.phase_reader.done,
|
||||||
|
NextState("WRITE_UPPER"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
fsm.act("WRITE_UPPER",
|
||||||
|
# Exist state if all results are sent
|
||||||
|
If(delay_tap == 32,
|
||||||
|
NextState("TERMINATE"),
|
||||||
|
).Elif(self.tx_fifo.writable,
|
||||||
|
self.tx_fifo.we.eq(1),
|
||||||
|
self.tx_fifo.din.eq(self.phase_reader.data_result[delay_tap][8:]),
|
||||||
|
NextState("WRITE_LOWER"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
fsm.act("WRITE_LOWER",
|
||||||
|
self.tx_fifo.we.eq(1),
|
||||||
|
self.tx_fifo.din.eq(self.phase_reader.data_result[delay_tap][:8]),
|
||||||
|
NextValue(delay_tap, delay_tap + 1),
|
||||||
|
NextState("WRITE_UPPER"),
|
||||||
|
)
|
||||||
|
|
||||||
|
# fsm.act("FIND_OPT_DELAY",
|
||||||
|
# self.delay_optimizer.start.eq(1),
|
||||||
|
# self.rx.ce.eq(self.delay_optimizer.inc_en),
|
||||||
|
# If(self.delay_optimizer.done,
|
||||||
|
# NextState("WRITE_OPT"),
|
||||||
|
# ).Else(
|
||||||
|
# NextState("FIND_OPT_DELAY")
|
||||||
|
# )
|
||||||
|
# )
|
||||||
|
|
||||||
|
# fsm.act("WRITE_OPT",
|
||||||
|
# self.tx_fifo.we.eq(1),
|
||||||
|
# self.tx_fifo.din.eq(self.delay_optimizer.opt_delay_tap),
|
||||||
|
# NextState("TERMINATE")
|
||||||
|
# )
|
||||||
|
|
||||||
|
fsm.act("TERMINATE",
|
||||||
|
NextState("TERMINATE"),
|
||||||
|
)
|
||||||
|
|
||||||
|
# # Output control
|
||||||
|
# self.sync += [
|
||||||
|
# # Send data to FIFO if not repeated
|
||||||
|
# If(self.rxdata_r[:5] != self.rx.rxdata,
|
||||||
|
# self.rxdata_r.eq(self.rx.rxdata),
|
||||||
|
# self.tx_fifo.din.eq(self.rx.rxdata),
|
||||||
|
# self.tx_fifo.we.eq(1)
|
||||||
|
# ).Else(
|
||||||
|
# self.tx_fifo.we.eq(0)
|
||||||
|
# )
|
||||||
|
# ]
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
platform = kasli.Platform(hw_rev="v2.0")
|
||||||
|
|
||||||
|
# Generate pads for the I/O blocks
|
||||||
|
eem = 3
|
||||||
|
generate_pads(platform, eem)
|
||||||
|
# pads = [
|
||||||
|
# platform.request("dio{}".format(eem), i) for i in range(4)
|
||||||
|
# ]
|
||||||
|
pad = platform.request("dio{}".format(eem), 0)
|
||||||
|
|
||||||
|
crg = KasliCRG(platform)
|
||||||
|
top = SingleSerDesLoopBack(pad, crg.sys_clk_freq)
|
||||||
|
|
||||||
|
# Wire up UART core to the pads
|
||||||
|
uart_pads = platform.request("serial")
|
||||||
|
top.comb += [
|
||||||
|
top.uart_rx.eq(uart_pads.rx),
|
||||||
|
uart_pads.tx.eq(top.uart_tx),
|
||||||
|
]
|
||||||
|
|
||||||
|
top.submodules += crg
|
||||||
|
platform.build(top)
|
387
sync_serdes.py
Normal file
387
sync_serdes.py
Normal file
@ -0,0 +1,387 @@
|
|||||||
|
from migen import *
|
||||||
|
from migen.genlib.misc import WaitTimer
|
||||||
|
from util import PriorityEncoderMSB
|
||||||
|
|
||||||
|
|
||||||
|
class SingleLineTX(Module):
|
||||||
|
def __init__(self):
|
||||||
|
self.txdata = Signal(5)
|
||||||
|
self.ser_out = Signal()
|
||||||
|
self.t_out = Signal()
|
||||||
|
|
||||||
|
# TX SERDES
|
||||||
|
self.specials += Instance("OSERDESE2",
|
||||||
|
p_DATA_RATE_OQ="SDR", p_DATA_RATE_TQ="BUF",
|
||||||
|
p_DATA_WIDTH=5, p_TRISTATE_WIDTH=1,
|
||||||
|
p_INIT_OQ=0b00000,
|
||||||
|
o_OQ=self.ser_out, o_TQ=self.t_out,
|
||||||
|
i_RST=ResetSignal(),
|
||||||
|
i_CLK=ClockSignal("sys5x"),
|
||||||
|
i_CLKDIV=ClockSignal(),
|
||||||
|
i_D1=self.txdata[0],
|
||||||
|
i_D2=self.txdata[1],
|
||||||
|
i_D3=self.txdata[2],
|
||||||
|
i_D4=self.txdata[3],
|
||||||
|
i_D5=self.txdata[4],
|
||||||
|
i_TCE=1, i_OCE=1,
|
||||||
|
# TODO: Hardcode t_in? Output disable is always unnecessary?
|
||||||
|
i_T1=0)
|
||||||
|
|
||||||
|
|
||||||
|
class SingleLineRX(Module):
|
||||||
|
def __init__(self):
|
||||||
|
self.rxdata = Signal(10)
|
||||||
|
self.ser_in_no_dly = Signal()
|
||||||
|
self.ce = Signal()
|
||||||
|
self.cnt_out = Signal(5)
|
||||||
|
self.opt_delay = Signal(5)
|
||||||
|
|
||||||
|
ser_in = Signal()
|
||||||
|
shifts = Signal(2)
|
||||||
|
|
||||||
|
self.specials += [
|
||||||
|
# Master deserializer
|
||||||
|
Instance("ISERDESE2",
|
||||||
|
p_DATA_RATE="DDR",
|
||||||
|
p_DATA_WIDTH=10,
|
||||||
|
p_INTERFACE_TYPE="NETWORKING",
|
||||||
|
p_NUM_CE=1,
|
||||||
|
p_SERDES_MODE="MASTER",
|
||||||
|
p_IOBDELAY="IFD",
|
||||||
|
o_Q1=self.rxdata[9],
|
||||||
|
o_Q2=self.rxdata[8],
|
||||||
|
o_Q3=self.rxdata[7],
|
||||||
|
o_Q4=self.rxdata[6],
|
||||||
|
o_Q5=self.rxdata[5],
|
||||||
|
o_Q6=self.rxdata[4],
|
||||||
|
o_Q7=self.rxdata[3],
|
||||||
|
o_Q8=self.rxdata[2],
|
||||||
|
o_SHIFTOUT1=shifts[0],
|
||||||
|
o_SHIFTOUT2=shifts[1],
|
||||||
|
i_DDLY=ser_in,
|
||||||
|
i_BITSLIP=0,
|
||||||
|
i_CLK=ClockSignal("rx_sys5x"),
|
||||||
|
i_CLKB=~ClockSignal("rx_sys5x"),
|
||||||
|
i_CE1=1,
|
||||||
|
i_RST=ResetSignal("rx_sys"),
|
||||||
|
i_CLKDIV=ClockSignal("rx_sys")),
|
||||||
|
|
||||||
|
# Slave deserializer
|
||||||
|
Instance("ISERDESE2",
|
||||||
|
p_DATA_RATE="DDR",
|
||||||
|
p_DATA_WIDTH=10,
|
||||||
|
p_INTERFACE_TYPE="NETWORKING",
|
||||||
|
p_NUM_CE=1,
|
||||||
|
p_SERDES_MODE="SLAVE",
|
||||||
|
p_IOBDELAY="IFD",
|
||||||
|
o_Q3=self.rxdata[1],
|
||||||
|
o_Q4=self.rxdata[0],
|
||||||
|
# i_DDLY=ser_in,
|
||||||
|
i_BITSLIP=0,
|
||||||
|
i_CLK=ClockSignal("rx_sys5x"),
|
||||||
|
i_CLKB=~ClockSignal("rx_sys5x"),
|
||||||
|
i_CE1=1,
|
||||||
|
i_RST=ResetSignal("rx_sys"),
|
||||||
|
i_CLKDIV=ClockSignal("rx_sys"),
|
||||||
|
i_SHIFTIN1=shifts[0],
|
||||||
|
i_SHIFTIN2=shifts[1]),
|
||||||
|
|
||||||
|
# Tunable delay
|
||||||
|
Instance("IDELAYE2",
|
||||||
|
p_DELAY_SRC="IDATAIN",
|
||||||
|
p_SIGNAL_PATTERN="DATA",
|
||||||
|
p_CINVCTRL_SEL="FALSE",
|
||||||
|
p_HIGH_PERFORMANCE_MODE="TRUE",
|
||||||
|
# REFCLK refers to the clock source of IDELAYCTRL
|
||||||
|
p_REFCLK_FREQUENCY=200.0,
|
||||||
|
p_PIPE_SEL="FALSE",
|
||||||
|
p_IDELAY_TYPE="VARIABLE",
|
||||||
|
p_IDELAY_VALUE=0,
|
||||||
|
|
||||||
|
i_C=ClockSignal("rx_sys"),
|
||||||
|
# i_LD=self._dly_sel.storage[i//8] & self._rdly_dq_rst.re,
|
||||||
|
# i_CE=self._dly_sel.storage[i//8] & self._rdly_dq_inc.re,
|
||||||
|
i_LD=0,
|
||||||
|
i_CE=self.ce, # TODO: Port output
|
||||||
|
i_LDPIPEEN=0,
|
||||||
|
i_INC=1, # Always increment
|
||||||
|
|
||||||
|
# Allow aligner to check delay tap value
|
||||||
|
o_CNTVALUEOUT=self.cnt_out,
|
||||||
|
|
||||||
|
i_IDATAIN=self.ser_in_no_dly, o_DATAOUT=ser_in
|
||||||
|
),
|
||||||
|
|
||||||
|
# IDELAYCTRL is with the clocking
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class BitSlipReader(Module):
|
||||||
|
def __init__(self):
|
||||||
|
# IN
|
||||||
|
self.loopback_rxdata = Signal(10)
|
||||||
|
self.start = Signal()
|
||||||
|
|
||||||
|
# Wait for stabilization after bitslip
|
||||||
|
self.submodules.stab_timer = WaitTimer(511)
|
||||||
|
|
||||||
|
# OUT
|
||||||
|
self.done = Signal()
|
||||||
|
self.bitslip = Signal()
|
||||||
|
self.data_result = Array(Signal(10) for _ in range(5))
|
||||||
|
|
||||||
|
self.slip_count = Signal(3)
|
||||||
|
|
||||||
|
fsm = FSM(reset_state="WAIT_START")
|
||||||
|
self.submodules += fsm
|
||||||
|
|
||||||
|
fsm.act("WAIT_START",
|
||||||
|
If(self.start,
|
||||||
|
NextState("WAIT_TIMER"),
|
||||||
|
).Else(
|
||||||
|
NextState("WAIT_START"),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
fsm.act("WAIT_TIMER",
|
||||||
|
self.stab_timer.wait.eq(1),
|
||||||
|
If(self.stab_timer.done,
|
||||||
|
NextState("SAMPLE"),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
fsm.act("SAMPLE",
|
||||||
|
# Wait is reset now
|
||||||
|
# Explicit assignment is unnecessary, as combinatorial statement
|
||||||
|
# falls back to he default value when not driven
|
||||||
|
|
||||||
|
# Keep result alive until reset
|
||||||
|
NextValue(self.data_result[self.slip_count], self.loopback_rxdata),
|
||||||
|
NextValue(self.slip_count, self.slip_count + 1),
|
||||||
|
NextState("HIGH_BITSLIP_FIRST"),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Pulsing BITSLIP alternate between 1 right shift and 3 left shifts
|
||||||
|
# We are trying to figure out which 2-bits are the slave copying from
|
||||||
|
# Hence, we only want shifts by 2. Pulsing twice does exactly that.
|
||||||
|
fsm.act("HIGH_BITSLIP_FIRST",
|
||||||
|
self.bitslip.eq(1),
|
||||||
|
NextState("LOW_BITSLIP"),
|
||||||
|
)
|
||||||
|
|
||||||
|
fsm.act("LOW_BITSLIP",
|
||||||
|
# bitslip signal is auto-reset
|
||||||
|
NextState("HIGH_BITSLIP_SECOND"),
|
||||||
|
)
|
||||||
|
|
||||||
|
fsm.act("HIGH_BITSLIP_SECOND",
|
||||||
|
self.bitslip.eq(1),
|
||||||
|
If(self.slip_count == 5,
|
||||||
|
NextState("TERMINATE"),
|
||||||
|
).Else(
|
||||||
|
NextState("WAIT_TIMER"),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
fsm.act("TERMINATE",
|
||||||
|
self.done.eq(1),
|
||||||
|
NextState("TERMINATE"),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class PhaseReader(Module):
|
||||||
|
def __init__(self):
|
||||||
|
# Drive IDELAYE2 CE pin to increment delay
|
||||||
|
# The signal should only last for 1 cycle
|
||||||
|
self.inc_en = Signal()
|
||||||
|
self.loopback_rxdata = Signal(10)
|
||||||
|
self.delay_tap = Signal(5)
|
||||||
|
|
||||||
|
# Pull up to start the phase reader
|
||||||
|
self.start = Signal()
|
||||||
|
|
||||||
|
self.data_result = Array(Signal(10) for _ in range(32))
|
||||||
|
self.done = Signal()
|
||||||
|
|
||||||
|
# Wait for stabilization after increment
|
||||||
|
self.submodules.stab_timer = WaitTimer(511)
|
||||||
|
|
||||||
|
fsm = FSM(reset_state="WAIT_START")
|
||||||
|
self.submodules += fsm
|
||||||
|
|
||||||
|
fsm.act("WAIT_START",
|
||||||
|
If(self.start,
|
||||||
|
NextState("WAIT_TIMER"),
|
||||||
|
).Else(
|
||||||
|
NextState("WAIT_START"),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
fsm.act("WAIT_TIMER",
|
||||||
|
self.stab_timer.wait.eq(1),
|
||||||
|
If(self.stab_timer.done,
|
||||||
|
NextState("SAMPLE"),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
fsm.act("SAMPLE",
|
||||||
|
# Wait is reset now
|
||||||
|
# Explicit assignment is unnecessary, as combinatorial statement
|
||||||
|
# falls back to he default value when not driven
|
||||||
|
|
||||||
|
# Keep result alive until reset
|
||||||
|
NextValue(self.data_result[self.delay_tap], self.loopback_rxdata),
|
||||||
|
NextState("HIGH_CE"),
|
||||||
|
)
|
||||||
|
|
||||||
|
fsm.act("HIGH_CE",
|
||||||
|
self.inc_en.eq(1),
|
||||||
|
NextState("LOW_CE"),
|
||||||
|
)
|
||||||
|
|
||||||
|
fsm.act("LOW_CE",
|
||||||
|
# TAP OUT is available 1 cycle after the pulse
|
||||||
|
# Explicit signal reset is unnecessary, as signal assigned by
|
||||||
|
# combinatorial logic in FSM is after leaving the setting block
|
||||||
|
NextState("READ_TAP"),
|
||||||
|
)
|
||||||
|
|
||||||
|
fsm.act("READ_TAP",
|
||||||
|
If(self.delay_tap != 0,
|
||||||
|
NextState("WAIT_TIMER"),
|
||||||
|
).Else(
|
||||||
|
NextState("PROBE_FIN"),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
fsm.act("PROBE_FIN",
|
||||||
|
self.done.eq(1),
|
||||||
|
NextState("PROBE_FIN"),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class DelayOptimizer(Module):
|
||||||
|
def __init__(self):
|
||||||
|
# IN
|
||||||
|
# Signals from the channel
|
||||||
|
self.loopback_rxdata = Signal(10)
|
||||||
|
self.delay_tap = Signal(5)
|
||||||
|
|
||||||
|
# IN
|
||||||
|
# Signal to start the calculation
|
||||||
|
self.start = Signal()
|
||||||
|
|
||||||
|
# OUT
|
||||||
|
# Signal for controlling the channel delay tap
|
||||||
|
self.inc_en = Signal()
|
||||||
|
|
||||||
|
# OUT
|
||||||
|
# The optimal delay
|
||||||
|
self.opt_delay_tap = Signal(5)
|
||||||
|
|
||||||
|
# OUT
|
||||||
|
# Optimal delay is calculated
|
||||||
|
self.done = Signal()
|
||||||
|
|
||||||
|
# Priority encoder for finding the pulse location
|
||||||
|
self.submodules.pulse_encoder = PriorityEncoderMSB(10)
|
||||||
|
|
||||||
|
# Wait for stabilization after increment
|
||||||
|
self.submodules.stab_timer = WaitTimer(511)
|
||||||
|
|
||||||
|
# Intermediate signals
|
||||||
|
self.expected_pulse = Signal(max=9)
|
||||||
|
self.min_delay = Signal(5)
|
||||||
|
self.max_offset = Signal(5)
|
||||||
|
|
||||||
|
# Translate rxdata into array to allow indexing
|
||||||
|
self.rxdata_array = Array(Signal() for _ in range(10))
|
||||||
|
self.comb += [ self.rxdata_array[i].eq(self.loopback_rxdata[i]) for i in range(10) ]
|
||||||
|
|
||||||
|
fsm = FSM(reset_state="WAIT_START")
|
||||||
|
self.submodules += fsm
|
||||||
|
|
||||||
|
fsm.act("WAIT_START",
|
||||||
|
If(self.start,
|
||||||
|
NextState("WAIT_ZERO"),
|
||||||
|
).Else(
|
||||||
|
NextState("WAIT_START"),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
fsm.act("WAIT_ZERO",
|
||||||
|
self.stab_timer.wait.eq(1),
|
||||||
|
If(self.stab_timer.done,
|
||||||
|
NextState("SAMPLE_ZERO"),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
fsm.act("SAMPLE_ZERO",
|
||||||
|
# Oversampling should guarantee the detection
|
||||||
|
# However, priority encoder itself does not wraparound
|
||||||
|
# So, we need to avoid passing wrapped around pulse signal into
|
||||||
|
# the priority encoder.
|
||||||
|
If(self.loopback_rxdata[0] & self.loopback_rxdata[-1],
|
||||||
|
NextValue(self.expected_pulse, 1),
|
||||||
|
).Else(
|
||||||
|
self.pulse_encoder.i.eq(self.loopback_rxdata),
|
||||||
|
If(self.pulse_encoder.o == 9,
|
||||||
|
NextValue(self.expected_pulse, 0),
|
||||||
|
).Else(
|
||||||
|
NextValue(self.expected_pulse, self.pulse_encoder.o + 1),
|
||||||
|
)
|
||||||
|
),
|
||||||
|
# Goto the next delay tap and wait for the pulse.
|
||||||
|
NextState("INC_PULSE_DELAY_IN"),
|
||||||
|
)
|
||||||
|
|
||||||
|
fsm.act("WAIT_PULSE_IN",
|
||||||
|
self.stab_timer.wait.eq(1),
|
||||||
|
If(self.stab_timer.done,
|
||||||
|
NextState("SAMPLE_PULSE_IN"),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
fsm.act("SAMPLE_PULSE_IN",
|
||||||
|
If(self.rxdata_array[self.expected_pulse],
|
||||||
|
NextValue(self.min_delay, self.delay_tap),
|
||||||
|
NextState("INC_PULSE_DELAY_OUT"),
|
||||||
|
).Else(
|
||||||
|
NextState("INC_PULSE_DELAY_IN"),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
fsm.act("INC_PULSE_DELAY_IN",
|
||||||
|
# This signal is automatically deasserted after this state
|
||||||
|
self.inc_en.eq(1),
|
||||||
|
NextState("WAIT_PULSE_IN"),
|
||||||
|
)
|
||||||
|
|
||||||
|
fsm.act("WAIT_PULSE_OUT",
|
||||||
|
self.stab_timer.wait.eq(1),
|
||||||
|
If(self.stab_timer.done,
|
||||||
|
NextState("SAMPLE_PULSE_OUT"),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
fsm.act("SAMPLE_PULSE_OUT",
|
||||||
|
If(~self.rxdata_array[self.expected_pulse],
|
||||||
|
NextValue(self.opt_delay_tap, self.min_delay + (self.max_offset >> 1)),
|
||||||
|
NextState("TERMINATE"),
|
||||||
|
).Else(
|
||||||
|
NextValue(self.max_offset, self.max_offset + 1),
|
||||||
|
NextState("INC_PULSE_DELAY_OUT"),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
fsm.act("INC_PULSE_DELAY_OUT",
|
||||||
|
# This signal is automatically deasserted after this state
|
||||||
|
self.inc_en.eq(1),
|
||||||
|
NextState("WAIT_PULSE_OUT"),
|
||||||
|
)
|
||||||
|
|
||||||
|
fsm.act("TERMINATE",
|
||||||
|
self.done.eq(1),
|
||||||
|
NextState("TERMINATE"),
|
||||||
|
)
|
198
test_aligner.py
Normal file
198
test_aligner.py
Normal file
@ -0,0 +1,198 @@
|
|||||||
|
from migen import *
|
||||||
|
from sync_serdes import PhaseReader, DelayOptimizer, BitSlipReader
|
||||||
|
import random
|
||||||
|
|
||||||
|
|
||||||
|
def reader_testbench(dut, rxdata_list):
|
||||||
|
yield dut.delay_tap.eq(0)
|
||||||
|
yield dut.start.eq(1)
|
||||||
|
assert (yield dut.stab_timer.wait) == 0
|
||||||
|
|
||||||
|
for i in range(32):
|
||||||
|
yield dut.loopback_rxdata.eq(rxdata_list[i])
|
||||||
|
yield
|
||||||
|
yield
|
||||||
|
assert (yield dut.stab_timer.wait) == 1
|
||||||
|
|
||||||
|
# Keep yielding until the DUT gives CE signal
|
||||||
|
while (yield dut.inc_en) == 0:
|
||||||
|
yield
|
||||||
|
|
||||||
|
# Check that inc_en is deassrted after 1 clock cycle
|
||||||
|
yield
|
||||||
|
assert (yield dut.inc_en) == 0
|
||||||
|
|
||||||
|
# Load a new tap value
|
||||||
|
yield dut.delay_tap.eq(i + 1)
|
||||||
|
yield
|
||||||
|
# Nothing to check in the READ_TAP state
|
||||||
|
yield
|
||||||
|
|
||||||
|
assert(yield dut.done) == 1
|
||||||
|
|
||||||
|
for i in range(32):
|
||||||
|
signal = yield dut.data_result[i]
|
||||||
|
expected = rxdata_list[i]
|
||||||
|
assert signal == expected
|
||||||
|
|
||||||
|
for i in range(200):
|
||||||
|
assert (yield dut.inc_en) == 0
|
||||||
|
yield
|
||||||
|
|
||||||
|
# Untouched delay: Record should be invariant
|
||||||
|
for i in range(32):
|
||||||
|
signal = yield dut.data_result[i]
|
||||||
|
expected = rxdata_list[i]
|
||||||
|
assert signal == expected
|
||||||
|
|
||||||
|
|
||||||
|
def optimal_delay_testbench(dut, pulse_list, cycles, pulse_index, min_delay, max_offset, opt_delay_tap):
|
||||||
|
# Start the module
|
||||||
|
yield dut.delay_tap.eq(0)
|
||||||
|
yield dut.start.eq(1)
|
||||||
|
assert (yield dut.stab_timer.wait) == 0
|
||||||
|
|
||||||
|
for i in range(cycles):
|
||||||
|
# Pass in a new rxdata for sampling
|
||||||
|
# The stab_timer should start waiting after
|
||||||
|
yield dut.loopback_rxdata.eq(pulse_list[i])
|
||||||
|
yield
|
||||||
|
yield
|
||||||
|
assert (yield dut.stab_timer.wait) == 1
|
||||||
|
|
||||||
|
# Eventually, the wait will end
|
||||||
|
# Either it triggers an increment or a finished signal
|
||||||
|
# And we will get the expected pulse location
|
||||||
|
# inc_en is pulsed after this is found
|
||||||
|
if i == (cycles - 1):
|
||||||
|
while (yield dut.done) == 0:
|
||||||
|
yield
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
while (yield dut.inc_en) == 0:
|
||||||
|
yield
|
||||||
|
|
||||||
|
# Then we increment the rxdata index
|
||||||
|
yield dut.delay_tap.eq(i + 1)
|
||||||
|
yield
|
||||||
|
|
||||||
|
# Fast-forward to the result
|
||||||
|
# while (yield dut.done) == 0:
|
||||||
|
# yield
|
||||||
|
|
||||||
|
assert (yield dut.done) == 1
|
||||||
|
assert (yield dut.expected_pulse) == pulse_index
|
||||||
|
assert (yield dut.min_delay) == min_delay
|
||||||
|
assert (yield dut.max_offset) == max_offset
|
||||||
|
assert (yield dut.opt_delay_tap) == opt_delay_tap
|
||||||
|
|
||||||
|
for _ in range(100):
|
||||||
|
yield
|
||||||
|
|
||||||
|
# Invariant test: Everything is frozen after done
|
||||||
|
assert (yield dut.done) == 1
|
||||||
|
assert (yield dut.expected_pulse) == pulse_index
|
||||||
|
assert (yield dut.min_delay) == min_delay
|
||||||
|
assert (yield dut.max_offset) == max_offset
|
||||||
|
assert (yield dut.opt_delay_tap) == opt_delay_tap
|
||||||
|
|
||||||
|
|
||||||
|
def bitslip_reader_tb(dut, rxdata_list):
|
||||||
|
# Start the module
|
||||||
|
yield dut.start.eq(1)
|
||||||
|
assert (yield dut.stab_timer.wait) == 0
|
||||||
|
|
||||||
|
for i in range(5):
|
||||||
|
yield dut.loopback_rxdata.eq(rxdata_list[i])
|
||||||
|
yield
|
||||||
|
yield
|
||||||
|
assert (yield dut.stab_timer.wait) == 1
|
||||||
|
|
||||||
|
# Keep yielding until the DUT gives BITSLIP signal
|
||||||
|
while (yield dut.bitslip) == 0:
|
||||||
|
yield
|
||||||
|
|
||||||
|
# There will be 2 BITSLIP pulses
|
||||||
|
# Both BITSLIP pulses should last for 1 cycle
|
||||||
|
assert (yield dut.bitslip) == 1
|
||||||
|
yield
|
||||||
|
assert (yield dut.bitslip) == 0
|
||||||
|
yield
|
||||||
|
assert (yield dut.bitslip) == 1
|
||||||
|
yield
|
||||||
|
assert (yield dut.bitslip) == 0
|
||||||
|
|
||||||
|
assert (yield dut.done) == 1
|
||||||
|
# The result in the module should contain all rxdata
|
||||||
|
for i, rxdata in enumerate(rxdata_list):
|
||||||
|
assert (yield dut.data_result[i]) == rxdata
|
||||||
|
|
||||||
|
yield
|
||||||
|
yield
|
||||||
|
|
||||||
|
|
||||||
|
# # Random testing for delay reader
|
||||||
|
# for _ in range(32):
|
||||||
|
# rxdata_list = [ random.getrandbits(10) for _ in range(32) ]
|
||||||
|
# dut = PhaseReader()
|
||||||
|
# run_simulation(dut, reader_testbench(dut, rxdata_list), vcd_name="phase_reader.vcd")
|
||||||
|
|
||||||
|
# # Random testing for optimal delay calculation
|
||||||
|
# # Generate a delay list
|
||||||
|
# start = random.randint(0, 9)
|
||||||
|
# start_length = random.randint(1, 10)
|
||||||
|
# offset = random.randint(4, 5)
|
||||||
|
|
||||||
|
# current_index = start
|
||||||
|
# remaining_length = start_length
|
||||||
|
# single_pulse_list = []
|
||||||
|
|
||||||
|
# expected_index = (current_index + 1) % 10
|
||||||
|
# expected_length = 10
|
||||||
|
|
||||||
|
# for tap in range(32 + offset):
|
||||||
|
# single_pulse_list.append(1 << current_index)
|
||||||
|
# remaining_length -= 1
|
||||||
|
|
||||||
|
# if remaining_length == 0:
|
||||||
|
# current_index = (current_index + 1) % 10
|
||||||
|
# remaining_length = 10
|
||||||
|
|
||||||
|
# pulse_list = list(single_pulse_list)
|
||||||
|
# for i in range(offset, 32):
|
||||||
|
# pulse_list[i] |= single_pulse_list[i - offset]
|
||||||
|
|
||||||
|
# found_start_edge = False
|
||||||
|
# max_offset = 0
|
||||||
|
|
||||||
|
# # Calculate min_delay
|
||||||
|
# for i, pulse in enumerate(pulse_list):
|
||||||
|
# if (pulse & (1 << expected_index)) != 0:
|
||||||
|
# if not found_start_edge:
|
||||||
|
# min_delay = i
|
||||||
|
# found_start_edge = True
|
||||||
|
# else:
|
||||||
|
# max_offset += 1
|
||||||
|
# if (pulse & (1 << expected_index)) == 0 and found_start_edge:
|
||||||
|
# cycles = i + 1
|
||||||
|
# break
|
||||||
|
|
||||||
|
# print(min_delay)
|
||||||
|
# print(max_offset)
|
||||||
|
# print(cycles)
|
||||||
|
# opt_delay = int(min_delay + (max_offset / 2))
|
||||||
|
# print(opt_delay)
|
||||||
|
|
||||||
|
# # Simulate
|
||||||
|
# dut = DelayOptimizer()
|
||||||
|
# run_simulation(dut, optimal_delay_testbench(
|
||||||
|
# dut, pulse_list, cycles, expected_index,
|
||||||
|
# min_delay, max_offset, opt_delay),
|
||||||
|
# vcd_name="delay_opt.vcd"
|
||||||
|
# )
|
||||||
|
|
||||||
|
# Random test for bitslip reader
|
||||||
|
for _ in range(32):
|
||||||
|
rxdata_list = [ random.getrandbits(10) for _ in range(5) ]
|
||||||
|
dut = BitSlipReader()
|
||||||
|
run_simulation(dut, bitslip_reader_tb(dut, rxdata_list), vcd_name="bitslip_reader.vcd")
|
46
test_buffer.py
Normal file
46
test_buffer.py
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
from migen import *
|
||||||
|
from buffer import Buffer
|
||||||
|
|
||||||
|
|
||||||
|
BUFFER_DEPTH=8
|
||||||
|
full_test_bytes = [0xDE, 0xAD, 0xBE, 0xEF, 0xBA, 0xD0, 0xCA, 0xFE, 0x15, 0x77]
|
||||||
|
|
||||||
|
|
||||||
|
def testbench(dut, length=10):
|
||||||
|
# dut = Buffer(8, BUFFER_DEPTH)
|
||||||
|
# full_test_bytes = [0xDE, 0xAD, 0xBE, 0xEF, 0xBA, 0xD0, 0xCA, 0xFE, 0x15, 0x77]
|
||||||
|
|
||||||
|
test_data = full_test_bytes[:min(BUFFER_DEPTH, length)]
|
||||||
|
|
||||||
|
# Initial condition, stb low
|
||||||
|
yield dut.i_stb.eq(0)
|
||||||
|
yield
|
||||||
|
|
||||||
|
# Append bytes one-by-one
|
||||||
|
for number in test_data:
|
||||||
|
yield dut.din.eq(number)
|
||||||
|
yield dut.i_stb.eq(1)
|
||||||
|
yield
|
||||||
|
|
||||||
|
# Deassert input strobe to buffers
|
||||||
|
yield dut.i_stb.eq(0)
|
||||||
|
yield
|
||||||
|
|
||||||
|
# Receive bytes on-by-one
|
||||||
|
for number in test_data:
|
||||||
|
assert (yield dut.o_stb) == 1
|
||||||
|
assert (yield dut.dout) == number
|
||||||
|
yield dut.o_ack.eq(1)
|
||||||
|
yield
|
||||||
|
yield dut.o_ack.eq(0)
|
||||||
|
yield
|
||||||
|
yield
|
||||||
|
|
||||||
|
yield
|
||||||
|
yield
|
||||||
|
assert (yield dut.o_stb) == 0
|
||||||
|
|
||||||
|
|
||||||
|
for buffer_len in range(len(full_test_bytes)):
|
||||||
|
dut = Buffer(8, BUFFER_DEPTH)
|
||||||
|
run_simulation(dut, testbench(dut, length=buffer_len), vcd_name="buffer.vcd")
|
105
uart.py
Normal file
105
uart.py
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
from migen import *
|
||||||
|
from migen.genlib.cdc import MultiReg
|
||||||
|
|
||||||
|
|
||||||
|
class UART(Module):
|
||||||
|
def __init__(self, tuning_word):
|
||||||
|
self.phy_rx = Signal()
|
||||||
|
self.phy_tx = Signal()
|
||||||
|
|
||||||
|
self.rx_data = Signal(8)
|
||||||
|
self.rx_stb = Signal()
|
||||||
|
|
||||||
|
self.tx_data = Signal(8)
|
||||||
|
self.tx_stb = Signal()
|
||||||
|
self.tx_ack = Signal()
|
||||||
|
|
||||||
|
# # #
|
||||||
|
|
||||||
|
#
|
||||||
|
# RX
|
||||||
|
#
|
||||||
|
|
||||||
|
uart_clk_rxen = Signal()
|
||||||
|
phase_accumulator_rx = Signal(32)
|
||||||
|
|
||||||
|
rx = Signal()
|
||||||
|
self.specials += MultiReg(self.phy_rx, rx)
|
||||||
|
rx_r = Signal()
|
||||||
|
rx_reg = Signal(8)
|
||||||
|
rx_bitcount = Signal(4)
|
||||||
|
rx_busy = Signal()
|
||||||
|
rx_done = self.rx_stb
|
||||||
|
rx_data = self.rx_data
|
||||||
|
self.sync += [
|
||||||
|
rx_done.eq(0),
|
||||||
|
rx_r.eq(rx),
|
||||||
|
If(~rx_busy,
|
||||||
|
If(~rx & rx_r, # look for start bit
|
||||||
|
rx_busy.eq(1),
|
||||||
|
rx_bitcount.eq(0),
|
||||||
|
)
|
||||||
|
).Else(
|
||||||
|
If(uart_clk_rxen,
|
||||||
|
rx_bitcount.eq(rx_bitcount + 1),
|
||||||
|
If(rx_bitcount == 0,
|
||||||
|
If(rx, # verify start bit
|
||||||
|
rx_busy.eq(0)
|
||||||
|
)
|
||||||
|
).Elif(rx_bitcount == 9,
|
||||||
|
rx_busy.eq(0),
|
||||||
|
If(rx, # verify stop bit
|
||||||
|
rx_data.eq(rx_reg),
|
||||||
|
rx_done.eq(1)
|
||||||
|
)
|
||||||
|
).Else(
|
||||||
|
rx_reg.eq(Cat(rx_reg[1:], rx))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
]
|
||||||
|
self.sync += \
|
||||||
|
If(rx_busy,
|
||||||
|
Cat(phase_accumulator_rx, uart_clk_rxen).eq(phase_accumulator_rx + tuning_word)
|
||||||
|
).Else(
|
||||||
|
Cat(phase_accumulator_rx, uart_clk_rxen).eq(2**31)
|
||||||
|
)
|
||||||
|
|
||||||
|
#
|
||||||
|
# TX
|
||||||
|
#
|
||||||
|
uart_clk_txen = Signal()
|
||||||
|
phase_accumulator_tx = Signal(32)
|
||||||
|
|
||||||
|
self.phy_tx.reset = 1
|
||||||
|
|
||||||
|
tx_reg = Signal(8)
|
||||||
|
tx_bitcount = Signal(4)
|
||||||
|
tx_busy = Signal()
|
||||||
|
self.sync += [
|
||||||
|
self.tx_ack.eq(0),
|
||||||
|
If(self.tx_stb & ~tx_busy & ~self.tx_ack,
|
||||||
|
tx_reg.eq(self.tx_data),
|
||||||
|
tx_bitcount.eq(0),
|
||||||
|
tx_busy.eq(1),
|
||||||
|
self.phy_tx.eq(0)
|
||||||
|
).Elif(uart_clk_txen & tx_busy,
|
||||||
|
tx_bitcount.eq(tx_bitcount + 1),
|
||||||
|
If(tx_bitcount == 8,
|
||||||
|
self.phy_tx.eq(1)
|
||||||
|
).Elif(tx_bitcount == 9,
|
||||||
|
self.phy_tx.eq(1),
|
||||||
|
tx_busy.eq(0),
|
||||||
|
self.tx_ack.eq(1),
|
||||||
|
).Else(
|
||||||
|
self.phy_tx.eq(tx_reg[0]),
|
||||||
|
tx_reg.eq(Cat(tx_reg[1:], 0))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
]
|
||||||
|
self.sync += \
|
||||||
|
If(tx_busy,
|
||||||
|
Cat(phase_accumulator_tx, uart_clk_txen).eq(phase_accumulator_tx + tuning_word)
|
||||||
|
).Else(
|
||||||
|
Cat(phase_accumulator_tx, uart_clk_txen).eq(0)
|
||||||
|
)
|
11
util.py
Normal file
11
util.py
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
from migen import *
|
||||||
|
|
||||||
|
|
||||||
|
class PriorityEncoderMSB(Module):
|
||||||
|
def __init__(self, width):
|
||||||
|
self.i = Signal(width) # one-hot, msb has priority
|
||||||
|
self.o = Signal(max=max(2, width)) # binary
|
||||||
|
self.n = Signal() # none
|
||||||
|
for j in range(width): # first has priority
|
||||||
|
self.comb += If(self.i[j], self.o.eq(j))
|
||||||
|
self.comb += self.n.eq(self.i == 0)
|
Loading…
Reference in New Issue
Block a user