From 26c7b2f78616585f74a62fa2fb2c712c20a142a0 Mon Sep 17 00:00:00 2001 From: Jean THOMAS Date: Fri, 26 Jun 2020 15:20:34 +0200 Subject: [PATCH] Add DDRSoC simulation --- gram/simulation/icarusecpix5platform.py | 180 ++++++++++++++ gram/simulation/runsimsoc.sh | 11 + gram/simulation/simsoc.py | 292 +++++++++++++++++++++++ gram/simulation/uartbridge.py | 304 ++++++++++++++++++++++++ 4 files changed, 787 insertions(+) create mode 100644 gram/simulation/icarusecpix5platform.py create mode 100755 gram/simulation/runsimsoc.sh create mode 100644 gram/simulation/simsoc.py create mode 100644 gram/simulation/uartbridge.py diff --git a/gram/simulation/icarusecpix5platform.py b/gram/simulation/icarusecpix5platform.py new file mode 100644 index 0000000..5c9e866 --- /dev/null +++ b/gram/simulation/icarusecpix5platform.py @@ -0,0 +1,180 @@ +import os +import subprocess + +from nmigen.build import * +from nmigen.vendor.lattice_ecp5 import * +from nmigen_boards.resources import * + + +__all__ = ["IcarusECPIX5Platform"] + + +class IcarusECPIX5Platform(LatticeECP5Platform): + device = "LFE5UM5G-85F" + package = "BG554" + speed = "8" + default_clk = "clk100" + default_rst = "rst" + + resources = [ + Resource("rst", 0, PinsN("AB1", dir="i"), Attrs(IO_TYPE="LVCMOS33")), + Resource("clk100", 0, Pins("K23", dir="i"), + Clock(100e6), Attrs(IO_TYPE="LVCMOS33")), + + RGBLEDResource(0, r="U21", g="W21", b="T24", + attrs=Attrs(IO_TYPE="LVCMOS33")), + RGBLEDResource(1, r="T23", g="R21", b="T22", + attrs=Attrs(IO_TYPE="LVCMOS33")), + RGBLEDResource(2, r="P21", g="R23", b="P22", + attrs=Attrs(IO_TYPE="LVCMOS33")), + RGBLEDResource(3, r="K21", g="K24", b="M21", + attrs=Attrs(IO_TYPE="LVCMOS33")), + + UARTResource(0, + rx="R26", tx="R24", + attrs=Attrs(IO_TYPE="LVCMOS33", PULLMODE="UP") + ), + + *SPIFlashResources(0, + cs="AA2", clk="AE3", miso="AE2", mosi="AD2", wp="AF2", hold="AE1", + attrs=Attrs(IO_TYPE="LVCMOS33") + ), + + Resource("eth_rgmii", 0, + Subsignal("rst", PinsN("C13", dir="o")), + Subsignal("mdio", Pins("A13", dir="io")), + Subsignal("mdc", Pins("C11", dir="o")), + Subsignal("tx_clk", Pins("A12", dir="o")), + Subsignal("tx_ctrl", Pins("C9", dir="o")), + Subsignal("tx_data", Pins("D8 C8 B8 A8", dir="o")), + Subsignal("rx_clk", Pins("E11", dir="i")), + Subsignal("rx_ctrl", Pins("A11", dir="i")), + Subsignal("rx_data", Pins("B11 A10 B10 A9", dir="i")), + Attrs(IO_TYPE="LVCMOS33") + ), + Resource("eth_int", 0, PinsN("B13", dir="i"), + Attrs(IO_TYPE="LVCMOS33")), + + *SDCardResources(0, + clk="P24", cmd="M24", dat0="N26", dat1="N25", dat2="N23", dat3="N21", cd="L22", + # TODO + # clk_fb="P25", cmd_dir="M23", dat0_dir="N24", dat123_dir="P26", + attrs=Attrs(IO_TYPE="LVCMOS33"), + ), + + # ERROR: cannot place differential IO at location PIOB + # if we choose to use DiffPairs + Resource("ddr3", 0, + Subsignal("clk", Pins("H3", dir="o")), + #Subsignal("clk", DiffPairs("H3", "J3", dir="o"), Attrs(IO_TYPE="SSTL135D_I")), + Subsignal("cke", Pins("P1", dir="o")), + Subsignal("we_n", Pins("R3", dir="o")), + Subsignal("ras_n", Pins("T3", dir="o")), + Subsignal("cas_n", Pins("P2", dir="o")), + Subsignal("a", Pins("T5 M3 L3 V6 K2 W6 K3 L1 H2 L2 N1 J1 M1 K1", dir="o")), + Subsignal("ba", Pins("U6 N3 N4", dir="o")), + #Subsignal("dqs", DiffPairs("V4 V1", "U5 U2", dir="io"), Attrs(IO_TYPE="SSTL135D_I")), + Subsignal("dqs", Pins("V4 V1", dir="io"), Attrs(IO_TYPE="SSTL135D_I", TERMINATION="OFF", DIFFRESISTOR="100")), + Subsignal("dq", Pins("T4 W4 R4 W5 R6 P6 P5 P4 R1 W3 T2 V3 U3 W1 T1 W2", dir="io")), + Subsignal("dm", Pins("U4 U1", dir="o")), + Subsignal("odt", Pins("P3", dir="o")), + Attrs(IO_TYPE="SSTL135_I") + ), + + Resource("hdmi", 0, + Subsignal("rst", PinsN("N6", dir="o")), + Subsignal("scl", Pins("C17", dir="io")), + Subsignal("sda", Pins("E17", dir="io")), + Subsignal("pclk", Pins("C1", dir="o")), + Subsignal("vsync", Pins("A4", dir="o")), + Subsignal("hsync", Pins("B4", dir="o")), + Subsignal("de", Pins("A3", dir="o")), + Subsignal("d", + Subsignal( + "b", Pins("AD25 AC26 AB24 AB25 B3 C3 D3 B1 C2 D2 D1 E3", dir="o")), + Subsignal( + "g", Pins("AA23 AA22 AA24 AA25 E1 F2 F1 D17 D16 E16 J6 H6", dir="o")), + Subsignal( + "r", Pins("AD26 AE25 AF25 AE26 E10 D11 D10 C10 D9 E8 H5 J4", dir="o")), + ), + Subsignal("mclk", Pins("E19", dir="o")), + Subsignal("sck", Pins("D6", dir="o")), + Subsignal("ws", Pins("C6", dir="o")), + Subsignal("i2s", Pins("A6 B6 A5 C5", dir="o")), + Subsignal("int", PinsN("C4", dir="i")), + Attrs(IO_TYPE="LVTTL33") + ), + + Resource("sata", 0, + Subsignal("tx", DiffPairs("AD16", "AD17", dir="o")), + Subsignal("rx", DiffPairs("AF15", "AF16", dir="i")), + Attrs(IO_TYPE="LVDS") + ), + + Resource("ulpi", 0, + Subsignal("rst", Pins("E23", dir="o")), + Subsignal("clk", Pins("H24", dir="i")), + Subsignal("dir", Pins("F22", dir="i")), + Subsignal("nxt", Pins("F23", dir="i")), + Subsignal("stp", Pins("H23", dir="o")), + Subsignal("data", Pins( + "M26 L25 L26 K25 K26 J23 J26 H25", dir="io")), + Attrs(IO_TYPE="LVCMOS33") + ), + + Resource("usbc_cfg", 0, + Subsignal("scl", Pins("D24", dir="io")), + Subsignal("sda", Pins("C24", dir="io")), + Subsignal("dir", Pins("B23", dir="i")), + Subsignal("id", Pins("D23", dir="i")), + Subsignal("int", PinsN("B24", dir="i")), + Attrs(IO_TYPE="LVCMOS33") + ), + Resource("usbc_mux", 0, + Subsignal("en", Pins("C23", dir="oe")), + Subsignal("amsel", Pins("B26", dir="oe")), + Subsignal("pol", Pins("D26", dir="o")), + Subsignal("lna", DiffPairs("AF9", "AF10", dir="i"), + Attrs(IO_TYPE="LVCMOS18D")), + Subsignal("lnb", DiffPairs("AD10", "AD11", + dir="o"), Attrs(IO_TYPE="LVCMOS18D")), + Subsignal("lnc", DiffPairs("AD7", "AD8", dir="o"), + Attrs(IO_TYPE="LVCMOS18D")), + Subsignal("lnd", DiffPairs("AF6", "AF7", dir="i"), + Attrs(IO_TYPE="LVCMOS18D")), + Attrs(IO_TYPE="LVCMOS33") + ), + ] + + connectors = [ + Connector("pmod", 0, "T25 U25 U24 V24 - - T26 U26 V26 W26 - -"), + Connector("pmod", 1, "U23 V23 U22 V21 - - W25 W24 W23 W22 - -"), + Connector("pmod", 2, "J24 H22 E21 D18 - - K22 J21 H21 D22 - -"), + Connector("pmod", 3, " E4 F4 E6 H4 - - F3 D4 D5 F5 - -"), + Connector("pmod", 4, "E26 D25 F26 F25 - - A25 A24 C26 C25 - -"), + Connector("pmod", 5, "D19 C21 B21 C22 - - D21 A21 A22 A23 - -"), + Connector("pmod", 6, "C16 B17 C18 B19 - - A17 A18 A19 C19 - -"), + Connector("pmod", 7, "D14 B14 E14 B16 - - C14 A14 A15 A16 - -"), + ] + + @property + def required_tools(self): + return ["yosys"] + + @property + def file_templates(self): + return { + **TemplatedPlatform.build_script_templates, + "{{name}}.v": r""" + /* {{autogenerated}} */ + {{emit_verilog()}} + """, + "{{name}}.debug.v": r""" + /* {{autogenerated}} */ + {{emit_debug_verilog()}} + """, + } + + @property + def command_templates(self): + return [] diff --git a/gram/simulation/runsimsoc.sh b/gram/simulation/runsimsoc.sh new file mode 100755 index 0000000..cb4fb0b --- /dev/null +++ b/gram/simulation/runsimsoc.sh @@ -0,0 +1,11 @@ +#!/bin/bash +set -e + +LIB_DIR=/usr/local/diamond/3.11_x64/ispfpga/verilog/data/ecp5u + +python simsoc.py +iverilog -g2012 -s simsoctb -o simsoc simsoctb.v build/top.debug.v dram_model/ddr3.v ${LIB_DIR}/ECLKSYNCB.v ${LIB_DIR}/EHXPLLL.v ${LIB_DIR}/PUR.v ${LIB_DIR}/GSR.v \ + ${LIB_DIR}/FD1S3AX.v ${LIB_DIR}/SGSR.v ${LIB_DIR}/ODDRX2F.v ${LIB_DIR}/ODDRX2DQA.v ${LIB_DIR}/DELAYF.v ${LIB_DIR}/BB.v ${LIB_DIR}/OB.v ${LIB_DIR}/IB.v \ + ${LIB_DIR}/DQSBUFM.v ${LIB_DIR}/UDFDL5_UDP_X.v ${LIB_DIR}/TSHX2DQSA.v ${LIB_DIR}/TSHX2DQA.v ${LIB_DIR}/ODDRX2DQSB.v ${LIB_DIR}/IDDRX2DQA.v DDRDLLA.patched.v \ + ${LIB_DIR}/CLKDIVF.v +vvp simsoc -vcd diff --git a/gram/simulation/simsoc.py b/gram/simulation/simsoc.py new file mode 100644 index 0000000..cb886b3 --- /dev/null +++ b/gram/simulation/simsoc.py @@ -0,0 +1,292 @@ +# This file is Copyright (c) 2020 LambdaConcept + +from nmigen import * +from nmigen.lib.cdc import ResetSynchronizer +from nmigen_soc import wishbone, memory + +from lambdasoc.cpu.minerva import MinervaCPU +from lambdasoc.periph.intc import GenericInterruptController +from lambdasoc.periph.serial import AsyncSerialPeripheral +from lambdasoc.periph.sram import SRAMPeripheral +from lambdasoc.periph.timer import TimerPeripheral +from lambdasoc.periph import Peripheral +from lambdasoc.soc.base import SoC + +from gram.core import gramCore +from gram.phy.ecp5ddrphy import ECP5DDRPHY +from gram.modules import MT41K256M16 +from gram.frontend.wishbone import gramWishbone + +from icarusecpix5platform import IcarusECPIX5Platform +from uartbridge import UARTBridge + +class PLL(Elaboratable): + def __init__(self, clkin, clksel=Signal(shape=2, reset=2), clkout1=Signal(), clkout2=Signal(), clkout3=Signal(), clkout4=Signal(), lock=Signal(), CLKI_DIV=1, CLKFB_DIV=1, CLK1_DIV=3, CLK2_DIV=4, CLK3_DIV=5, CLK4_DIV=6): + self.clkin = clkin + self.clkout1 = clkout1 + self.clkout2 = clkout2 + self.clkout3 = clkout3 + self.clkout4 = clkout4 + self.clksel = clksel + self.lock = lock + self.CLKI_DIV = CLKI_DIV + self.CLKFB_DIV = CLKFB_DIV + self.CLKOP_DIV = CLK1_DIV + self.CLKOS_DIV = CLK2_DIV + self.CLKOS2_DIV = CLK3_DIV + self.CLKOS3_DIV = CLK4_DIV + self.ports = [ + self.clkin, + self.clkout1, + self.clkout2, + self.clkout3, + self.clkout4, + self.clksel, + self.lock, + ] + + def elaborate(self, platform): + clkfb = Signal() + pll = Instance("EHXPLLL", + p_PLLRST_ENA='DISABLED', + p_INTFB_WAKE='DISABLED', + p_STDBY_ENABLE='DISABLED', + p_CLKOP_FPHASE=0, + p_CLKOP_CPHASE=1, + p_OUTDIVIDER_MUXA='DIVA', + p_CLKOP_ENABLE='ENABLED', + p_CLKOP_DIV=self.CLKOP_DIV, + p_CLKOS_DIV=self.CLKOS_DIV, + p_CLKOS2_DIV=self.CLKOS2_DIV, + p_CLKOS3_DIV=self.CLKOS3_DIV, + p_CLKFB_DIV=self.CLKFB_DIV, + p_CLKI_DIV=self.CLKI_DIV, + p_FEEDBK_PATH='CLKOP', + #p_FREQUENCY_PIN_CLKOP='200', + i_CLKI=self.clkin, + i_CLKFB=clkfb, + i_RST=0, + i_STDBY=0, + i_PHASESEL0=0, + i_PHASESEL1=0, + i_PHASEDIR=0, + i_PHASESTEP=0, + i_PLLWAKESYNC=0, + i_ENCLKOP=0, + i_ENCLKOS=0, + i_ENCLKOS2=0, + i_ENCLKOS3=0, + o_CLKOP=self.clkout1, + o_CLKOS=self.clkout2, + o_CLKOS2=self.clkout3, + o_CLKOS3=self.clkout4, + o_LOCK=self.lock, + ) + m = Module() + m.submodules += pll + with m.If(self.clksel == 0): + m.d.comb += clkfb.eq(self.clkout1) + with m.Elif(self.clksel == 1): + m.d.comb += clkfb.eq(self.clkout2) + with m.Elif(self.clksel == 2): + m.d.comb += clkfb.eq(self.clkout3) + with m.Else(): + m.d.comb += clkfb.eq(self.clkout4) + return m + + +class ECPIX5CRG(Elaboratable): + def __init__(self): + ... + + def elaborate(self, platform): + m = Module() + + # Get 100Mhz from oscillator + clk100 = platform.request("clk100") + cd_rawclk = ClockDomain("rawclk", local=True, reset_less=True) + m.d.comb += cd_rawclk.clk.eq(clk100) + m.domains += cd_rawclk + + # Reset + reset = platform.request(platform.default_rst).i + gsr0 = Signal() + gsr1 = Signal() + + m.submodules += [ + Instance("FD1S3AX", p_GSR="DISABLED", i_CK=ClockSignal("rawclk"), i_D=~reset, o_Q=gsr0), + Instance("FD1S3AX", p_GSR="DISABLED", i_CK=ClockSignal("rawclk"), i_D=gsr0, o_Q=gsr1), + Instance("SGSR", i_CLK=ClockSignal("rawclk"), i_GSR=gsr1), + ] + + # Power-on delay (655us) + podcnt = Signal(16, reset=2**16-1) + pod_done = Signal() + with m.If(podcnt != 0): + m.d.rawclk += podcnt.eq(podcnt-1) + m.d.comb += pod_done.eq(podcnt == 0) + + # Generating sync2x (200Mhz) and init (25Mhz) from clk100 + cd_sync2x = ClockDomain("sync2x", local=False) + cd_sync2x_unbuf = ClockDomain("sync2x_unbuf", local=True, reset_less=True) + cd_init = ClockDomain("init", local=False) + cd_sync = ClockDomain("sync", local=False, reset_less=True) + cd_dramsync = ClockDomain("dramsync", local=False) + m.submodules.pll = pll = PLL(ClockSignal("rawclk"), CLKI_DIV=1, CLKFB_DIV=2, CLK1_DIV=2, CLK2_DIV=16, CLK3_DIV=4, + clkout1=ClockSignal("sync2x_unbuf"), clkout2=ClockSignal("init")) + m.submodules += Instance("ECLKSYNCB", + i_ECLKI = ClockSignal("sync2x_unbuf"), + i_STOP = 0, + o_ECLKO = ClockSignal("sync2x")) + m.domains += cd_sync2x_unbuf + m.domains += cd_sync2x + m.domains += cd_init + m.domains += cd_sync + m.domains += cd_dramsync + m.d.comb += ResetSignal("init").eq(~pll.lock|~pod_done) + m.d.comb += ResetSignal("dramsync").eq(~pll.lock|~pod_done) + + rgb_led = platform.request("rgb_led", 2) + cnt = Signal(25) + m.d.sync += cnt.eq(cnt+1) + m.d.comb += rgb_led.r.eq(cnt[24]) + m.d.comb += rgb_led.g.eq(~pod_done) + m.d.comb += rgb_led.b.eq(~pll.lock) + + # Generating sync (100Mhz) from sync2x + + m.submodules += Instance("CLKDIVF", + p_DIV="2.0", + i_ALIGNWD=0, + i_CLKI=ClockSignal("sync2x"), + i_RST=0, + o_CDIVX=ClockSignal("sync")) + m.d.comb += ClockSignal("dramsync").eq(ClockSignal("sync")) + + return m + +class OldCRG(Elaboratable): + def elaborate(self, platform): + m = Module() + + m.submodules.pll = pll = PLL(ClockSignal( + "sync"), CLKI_DIV=1, CLKFB_DIV=2, CLK1_DIV=2, CLK2_DIV=16) + cd_sync2x = ClockDomain("sync2x", local=False) + m.d.comb += cd_sync2x.clk.eq(pll.clkout1) + m.domains += cd_sync2x + + cd_init = ClockDomain("init", local=False) + m.d.comb += cd_init.clk.eq(pll.clkout2) + m.domains += cd_init + + return m + +class ThinCRG(Elaboratable): + """ + Sync (clk100, resetless) => PLL => sync2x_unbuf (200Mhz) => ECLKSYNC => sync2x => CLKDIVF => dramsync + """ + + def __init__(self): + ... + + def elaborate(self, platform): + m = Module() + + # Power-on delay (655us) + podcnt = Signal(16, reset=2**16-1) + pod_done = Signal() + with m.If(podcnt != 0): + m.d.sync += podcnt.eq(podcnt-1) + m.d.comb += pod_done.eq(podcnt == 0) + + # Generating sync2x (200Mhz) and init (25Mhz) from clk100 + cd_sync2x = ClockDomain("sync2x", local=False) + cd_sync2x_unbuf = ClockDomain("sync2x_unbuf", local=True, reset_less=True) + cd_init = ClockDomain("init", local=False) + cd_dramsync = ClockDomain("dramsync", local=False) + m.submodules.pll = pll = PLL(ClockSignal("sync"), CLKI_DIV=1, CLKFB_DIV=2, CLK1_DIV=2, CLK2_DIV=16, CLK3_DIV=4, + clkout1=ClockSignal("sync2x_unbuf"), clkout2=ClockSignal("init")) + m.submodules += Instance("ECLKSYNCB", + i_ECLKI = ClockSignal("sync2x_unbuf"), + i_STOP = 0, + o_ECLKO = ClockSignal("sync2x")) + m.domains += cd_sync2x_unbuf + m.domains += cd_sync2x + m.domains += cd_init + m.domains += cd_dramsync + m.d.comb += ResetSignal("init").eq(~pll.lock|~pod_done) + m.d.comb += ResetSignal("dramsync").eq(~pll.lock|~pod_done) + + # Generating sync (100Mhz) from sync2x + m.submodules += Instance("CLKDIVF", + p_DIV="2.0", + i_ALIGNWD=0, + i_CLKI=ClockSignal("sync2x"), + i_RST=0, + o_CDIVX=ClockSignal("dramsync")) + + return m + + +class DDR3SoC(SoC, Elaboratable): + def __init__(self, *, clk_freq, + ddrphy_addr, dramcore_addr, + ddr_addr): + self._arbiter = wishbone.Arbiter(addr_width=30, data_width=32, granularity=8, + features={"cti", "bte"}) + self._decoder = wishbone.Decoder(addr_width=30, data_width=32, granularity=8, + features={"cti", "bte"}) + + self.crg = ECPIX5CRG() + + self.ub = UARTBridge(divisor=868, pins=platform.request("uart", 0)) + self._arbiter.add(self.ub.bus) + + self.ddrphy = ECP5DDRPHY(platform.request("ddr3", 0, dir={"dq":"-", "dqs":"-"})) + self._decoder.add(self.ddrphy.bus, addr=ddrphy_addr) + + ddrmodule = MT41K256M16(clk_freq, "1:4") + + self.dramcore = gramCore( + phy=self.ddrphy, + geom_settings=ddrmodule.geom_settings, + timing_settings=ddrmodule.timing_settings, + clk_freq=clk_freq) + self._decoder.add(self.dramcore.bus, addr=dramcore_addr) + + self.drambone = gramWishbone(self.dramcore) + self._decoder.add(self.drambone.bus, addr=ddr_addr) + + self.memory_map = self._decoder.bus.memory_map + + self.clk_freq = clk_freq + + def elaborate(self, platform): + m = Module() + + m.submodules.sysclk = self.crg + + m.submodules.arbiter = self._arbiter + m.submodules.ub = self.ub + + m.submodules.decoder = self._decoder + m.submodules.ddrphy = self.ddrphy + m.submodules.dramcore = self.dramcore + m.submodules.drambone = self.drambone + + m.d.comb += [ + self._arbiter.bus.connect(self._decoder.bus), + ] + + return m + + +if __name__ == "__main__": + platform = IcarusECPIX5Platform() + + soc = DDR3SoC(clk_freq=int(platform.default_clk_frequency), + ddrphy_addr=0x00008000, dramcore_addr=0x00009000, + ddr_addr=0x10000000) + + soc.build(do_build=True) + platform.build(soc) diff --git a/gram/simulation/uartbridge.py b/gram/simulation/uartbridge.py new file mode 100644 index 0000000..747f63f --- /dev/null +++ b/gram/simulation/uartbridge.py @@ -0,0 +1,304 @@ +from nmigen import * +from nmigen.lib.io import pin_layout +from nmigen_soc import wishbone +from nmigen_stdio.serial import AsyncSerial, AsyncSerialTX +from nmigen.back.pysim import * + +import unittest + +__ALL__ = ["UARTBridge"] + +class UARTBridge(Elaboratable): + def __init__(self, divisor, pins): + self.bus = wishbone.Interface(addr_width=30, + data_width=32, granularity=32) + self._pins = pins + self._divisor = divisor + + def elaborate(self, platform): + m = Module() + + m.submodules.serial = serial = AsyncSerial(divisor=self._divisor, pins=self._pins) + + address_width = 32 + data_width = 32 + + cmd = Signal(8) + length = Signal(8) + address = Signal(address_width) + data = Signal(data_width) + bytes_count = Signal(range(data_width//8)) + words_count = Signal(8) + + m.d.comb += [ + self.bus.dat_w.eq(data), + self.bus.adr.eq(address), + ] + + with m.FSM(): + with m.State("Receive-Cmd"): + m.d.comb += serial.rx.ack.eq(1) + + # Reset registers + m.d.sync += [ + bytes_count.eq(data_width//8-1), + words_count.eq(0), + ] + + with m.If(serial.rx.rdy): + m.d.sync += cmd.eq(serial.rx.data) + m.next = "Receive-Length" + + with m.State("Receive-Length"): + m.d.comb += serial.rx.ack.eq(1) + + with m.If(serial.rx.rdy): + m.d.sync += length.eq(serial.rx.data) + m.next = "Receive-Address" + + with m.State("Receive-Address"): + m.d.comb += serial.rx.ack.eq(1) + + with m.If(serial.rx.rdy): + m.d.sync += [ + address.eq(Cat(serial.rx.data, address)), + bytes_count.eq(bytes_count-1), + ] + + with m.If(bytes_count == 0): + with m.Switch(cmd): + with m.Case(0x01): + m.next = "Handle-Write" + with m.Case(0x02): + m.next = "Handle-Read" + with m.Case(): + m.next = "Receive-Cmd" + + with m.State("Handle-Write"): + m.d.comb += serial.rx.ack.eq(1) + + with m.If(serial.rx.rdy): + m.d.sync += [ + data.eq(Cat(serial.rx.data, data)), + bytes_count.eq(bytes_count-1), + ] + with m.If(bytes_count == 0): + m.next = "Write-Data" + + with m.State("Write-Data"): + m.d.comb += [ + self.bus.stb.eq(1), + self.bus.we.eq(1), + self.bus.cyc.eq(1), + self.bus.sel.eq(0xF), + ] + + with m.If(self.bus.ack): + m.next = "Receive-Cmd" + + + with m.State("Handle-Read"): + m.d.comb += [ + self.bus.stb.eq(1), + self.bus.we.eq(0), + self.bus.cyc.eq(1), + ] + + with m.If(self.bus.ack): + m.d.sync += [ + bytes_count.eq(data_width//8-1), + data.eq(self.bus.dat_r), + ] + m.next = "Send-Data" + + with m.State("Send-Data"): + m.d.comb += serial.tx.ack.eq(1) + + with m.Switch(bytes_count): + for i in range(data_width//8): + with m.Case(i): + m.d.comb += serial.tx.data.eq(data[i*8:(i+1)*8]) + + with m.If(serial.tx.rdy): + m.next = "Send-Data-Wait" + + with m.State("Send-Data-Wait"): + with m.If(serial.tx.rdy): + m.d.sync += [ + bytes_count.eq(bytes_count-1), + ] + + with m.If(bytes_count == 0): + m.next = "Receive-Cmd" + with m.Else(): + m.next = "Send-Data" + + return m + +def serial_write(serial, val): + while not (yield serial.tx.rdy): + yield + + yield serial.tx.data.eq(val) + yield serial.tx.ack.eq(1) + yield + + while (yield serial.tx.rdy): + yield + + yield serial.tx.ack.eq(0) + + while not (yield serial.tx.rdy): + yield + + yield + +def serial_read(serial): + yield serial.rx.ack.eq(1) + + while not (yield serial.rx.rdy): + yield + + data = (yield serial.rx.data) + yield serial.rx.ack.eq(0) + + while (yield serial.rx.rdy): + yield + + return data + +class UARTBridgeTestCase(unittest.TestCase): + # Minimum 5, lowest makes the simulation faster + divisor = 5 + timeout = 10000 + + def test_read(self): + pins = Record([("rx", pin_layout(1, dir="i")), + ("tx", pin_layout(1, dir="o"))]) + dut = UARTBridge(divisor=self.divisor, pins=pins) + serial = AsyncSerial(divisor=self.divisor) + m = Module() + m.submodules.bridge = dut + m.submodules.serial = serial + m.d.comb += [ + pins.rx.i.eq(serial.tx.o), + serial.rx.i.eq(pins.tx.o), + ] + + def process(): + # Send read command + yield from serial_write(serial, 0x02) + yield + + # Length = 1 + yield from serial_write(serial, 0x01) + yield + + # Send 0x4000 as address + yield from serial_write(serial, 0x00) + yield + yield from serial_write(serial, 0x00) + yield + yield from serial_write(serial, 0x40) + yield + yield from serial_write(serial, 0x00) + yield + + # Handle wishbone request + timeout = 0 + while not (yield dut.bus.cyc): + yield + timeout += 1 + if timeout > self.timeout: + raise RuntimeError("Simulation timed out") + + # Ensure Wishbone address is the one we asked for + self.assertEqual((yield dut.bus.adr), 0x00004000) + self.assertFalse((yield dut.bus.we)) + + # Answer + yield dut.bus.dat_r.eq(0x0DEFACED) + yield dut.bus.ack.eq(1) + yield + + # Check response on UART + rx = yield from serial_read(serial) + self.assertEqual(rx, 0x0D) + rx = yield from serial_read(serial) + self.assertEqual(rx, 0xEF) + rx = yield from serial_read(serial) + self.assertEqual(rx, 0xAC) + rx = yield from serial_read(serial) + self.assertEqual(rx, 0xED) + + yield + + sim = Simulator(m) + with sim.write_vcd("test_uartbridge.vcd"): + sim.add_clock(1e-6) + sim.add_sync_process(process) + sim.run() + + def test_write(self): + pins = Record([("rx", pin_layout(1, dir="i")), + ("tx", pin_layout(1, dir="o"))]) + dut = UARTBridge(divisor=self.divisor, pins=pins) + serial = AsyncSerial(divisor=self.divisor) + m = Module() + m.submodules.bridge = dut + m.submodules.serial = serial + m.d.comb += [ + pins.rx.i.eq(serial.tx.o), + serial.rx.i.eq(pins.tx.o), + ] + + def process(): + # Send write command + yield from serial_write(serial, 0x01) + yield + + # Length = 1 + yield from serial_write(serial, 0x01) + yield + + # Send 0x4000 as address + yield from serial_write(serial, 0x00) + yield + yield from serial_write(serial, 0x00) + yield + yield from serial_write(serial, 0x40) + yield + yield from serial_write(serial, 0x00) + yield + + # Send 0xFEEDFACE as value + yield from serial_write(serial, 0xFE) + yield + yield from serial_write(serial, 0xED) + yield + yield from serial_write(serial, 0xFA) + yield + yield from serial_write(serial, 0xCE) + + # Handle wishbone request + timeout = 0 + while not (yield dut.bus.cyc): + yield + timeout += 1 + if timeout > self.timeout: + raise RuntimeError("Simulation timed out") + + # Ensure Wishbone address is the one we asked for + self.assertEqual((yield dut.bus.adr), 0x00004000) + self.assertEqual((yield dut.bus.dat_w), 0xFEEDFACE) + self.assertTrue((yield dut.bus.we)) + + # Answer + yield dut.bus.ack.eq(1) + yield + + sim = Simulator(m) + with sim.write_vcd("test_uartbridge.vcd"): + sim.add_clock(1e-6) + sim.add_sync_process(process) + sim.run() -- 2.30.2