Exemple #1
0
def test_xula_vga(args=None):
    args = tb_default_args(args)

    resolution = (64, 48,)
    refresh_rate = 60
    line_rate = 31250
    color_depth = (3, 4, 3,)

    clock = Clock(0, frequency=12e6)
    reset = Reset(0, active=1, async=False)
    glbl = Global(clock, reset)
    vga = VGA(color_depth=color_depth)
    vga_hsync, vga_vsync = Signals(bool(0), 2)
    vga_red, vga_green, vga_blue = Signals(intbv(0)[6:], 3)
    vselect = Signal(bool(0))
    pxlen, active = Signals(bool(0), 2)

    @myhdl.block
    def bench():
        tbdut = xula_vga(
            clock, reset,
            vselect, vga_hsync, vga_vsync,
            vga_red, vga_green, vga_blue,
            pxlen, active,
            resolution=resolution, color_depth=color_depth,
            refresh_rate=refresh_rate, line_rate=line_rate
        )
        tbclk = clock.gen()

        mdl = VGADisplay(frequency=clock.frequency,
                         resolution=resolution,
                         refresh_rate=refresh_rate,
                         line_rate=line_rate,
                         color_depth=color_depth)
        tbmdl = mdl.process(glbl, vga)

        @instance
        def tbstim():
            yield delay(100000)
            raise StopSimulation

        return tbdut, tbclk, tbmdl, tbstim

    # run the above stimulus, the above is not self checking it simply
    # verifies the code will simulate.
    run_testbench(bench, args=args)
    portmap = dict(vselect=vselect, hsync=vga_hsync, vsync=vga_vsync,
                   red=vga_red, green=vga_green, blue=vga_blue,
                   clock=clock)

    # convert the module, check for any conversion errors
    tb_convert(xula_vga, **portmap)
Exemple #2
0
def test(args=None):
    if args is None:
        args = Namespace(trace=False)

    clock = Clock(0, frequency=50e6)
    reset = Reset(0, active=0, isasync=False)
    sdi, sdo = Signals(bool(0), 2)

    pin = Signals(intbv(0)[16:0], 1)
    pout = Signals(intbv(0)[16:0], 3)
    valid = Signal(bool(0))

    @myhdl.block
    def bench_serio():
        tbclk = clock.gen()
        tbdut = io_stub(clock, reset, sdi, sdo, pin, pout, valid)

        @instance
        def tbstim():
            yield reset.pulse(13)
            yield clock.posedge

            for pp in pout:
                pp.next = 0

            sdi.next = False
            yield delay(200)
            yield clock.posedge

            for ii in range(1000):
                yield clock.posedge
                assert not sdo
            assert pin[0] == 0

            for pp in pout:
                pp.next = 0xFFFF
            sdi.next = True
            yield valid.posedge
            yield delay(200)
            yield clock.posedge

            for ii in range(1000):
                yield clock.posedge
                assert sdo
            assert pin[0] == 0xFFFF

            raise StopSimulation

        return tbdut, tbclk, tbstim

    run_testbench(bench_serio, args=args)
Exemple #3
0
def led_peripheral(glbl, regbus, leds, base_address=0x8240):
    """ LED memory-map peripheral
    This (rather silly) core will select different LED
    displays based on the memory-mapped select register.
    """

    ndrv = 3  # the number of different drivers
    regfile.base_address = base_address 
    
    clock, reset = glbl.clock, glbl.reset
    rleds = Signal(intbv(0)[len(leds):])

    # assign the LED port to the local register
    assign(leds, rleds)

    # memory-mapped registers
    greg = regbus.add(regfile, 'led')

    # led bus from each driver
    dled = Signals(intbv(0)[len(leds):0], ndrv)

    # instantiate different LED drivers
    led_insts = [None for _ in range(ndrv)]
    led_insts[0] = led_stroby(clock, reset, dled[0])
    led_insts[1] = led_count(clock, reset, dled[1])
    led_insts[2] = led_dance(clock, reset, dled[2])

    @always_seq(clock.posedge, reset=reset)
    def beh():
        for ii in range(ndrv):
            idx = regfile.select
            rleds.next = dled[idx-1]

    return myhdl.instances()
Exemple #4
0
def convert():
    """convert the faux-top-level"""
    clock = Clock(0, frequency=50e6)
    reset = Reset(0, active=1, isasync=False)
    sck, mosi, miso, ss = Signals(bool(0), 4)
    inst = spi_controller_top(clock, reset, sck, mosi, miso, ss)
    tb_convert(inst)
Exemple #5
0
def test_spi_slave(args=None):
    args = tb_default_args(args)
    clock = Clock(0, frequency=50e6)
    reset = Reset(0, active=1, async=False)
    glbl = Global(clock, reset)
    spibus, fifobus = SPIBus(), FIFOBus()

    # monitor the FIFOBus signals
    data = Signal(intbv(0)[8:])
    rd, wr, full, empty = Signals(bool(0), 4)

    @myhdl.block
    def bench_spi_slave():
        tbdut = spi_slave_fifo(glbl, spibus, fifobus)
        tbclk = clock.gen()

        @instance
        def tbstim():
            yield reset.pulse(40)
            yield delay(1000)
            yield clock.posedge

            # @todo: make generic
            # @todo: random_sequence = [randint(0, fifobus.write_data.max) for _ in range(ntx)]
            yield spibus.writeread(0x55)
            yield spibus.writeread(0xAA)
            yield spibus.writeread(0xCE)
            assert spibus.get_read_data() == 0x55
            yield spibus.writeread(0x01)
            assert spibus.get_read_data() == 0xAA
            yield spibus.writeread(0x01)
            assert spibus.get_read_data() == 0xCE

            raise StopSimulation

        @always_comb
        def tb_fifo_loopback():
            if not fifobus.full:
                fifobus.write.next = not fifobus.empty
                fifobus.read.next = not fifobus.empty
            fifobus.write_data.next = fifobus.read_data

        # monitors
        @always_comb
        def tbmon():
            data.next = fifobus.read_data
            rd.next = fifobus.read
            wr.next = fifobus.write
            full.next = fifobus.full
            empty.next = fifobus.empty

        return tbdut, tbclk, tbstim, tb_fifo_loopback, tbmon

    run_testbench(bench_spi_slave, args=args)
Exemple #6
0
def test_conversion():
    clock = Clock(0, frequency=50e6)
    reset = Reset(0, active=0, isasync=False)
    sdi, sdo = Signals(bool(0), 2)

    # a top-level conversion stub
    @myhdl.block
    def top_stub(clock, reset, sdi, sdo):
        pin = [Signal(intbv(0)[16:0]) for _ in range(1)]
        pout = [Signal(intbv(0)[16:0]) for _ in range(3)]
        valid = Signal(bool(0))
        stub_inst = io_stub(clock, reset, sdi, sdo, pin, pout, valid)
        return stub_inst

    # convert the design stub
    inst = top_stub(clock, reset, sdi, sdo)
    tb_convert(inst)
Exemple #7
0
    def bench_spi_cso():
        spi_controller.debug = True  # enable debug monitors
        tbdut = spi_controller(glbl, spibus, fifobus, cso=cso)
        tbeep = spiee.process(clock, reset, spibus)
        tbclk = clock.gen(hticks=5)

        @instance
        def tbstim():
            yield reset.pulse(33)
            yield delay(100)
            yield clock.posedge

            try:
                # enable the SPI core
                cso.enable.next = True
                cso.bypass_fifo.next = True
                cso.loopback.next = True

                # write to the transmit FIFO
                values = (0x02, 0x00, 0x00, 0x00, 0x55)
                for data in values:
                    cso.tx_byte.next = data
                    cso.tx_write.next = True
                    yield clock.posedge
                cso.tx_write.next = False

                while cso.tx_fifo_count > 0:
                    yield delay(100)

                while cso.rx_fifo_count < 5:
                    yield delay(100)

                ii, nticks = 0, 0
                while ii < len(values):
                    if cso.rx_empty:
                        cso.rx_read.next = False
                    else:
                        cso.rx_read.next = True
                    if cso.rx_byte_valid:
                        assert values[ii] == cso.rx_byte, \
                            "{:<4d}: data mismatch, {:02X} != {:02X}".format(
                                ii, int(values[ii]), int(cso.rx_byte))
                        ii += 1
                        nticks = 0
                    yield clock.posedge, cso.rx_empty.posedge
                    cso.rx_read.next = False

                    if nticks > 30:
                        raise TimeoutError
                    nticks += 1

                cso.rx_read.next = False
                yield clock.posedge

            except AssertionError as err:
                asserr.next = True
                print("@E: assertion {}".format(err))
                yield delay(100)
                traceback.print_exc()
                raise err

            raise StopSimulation

        # monitor signals for debugging
        tx_write, rx_read = Signals(bool(0), 2)

        @always_comb
        def tbmon():
            rx_read.next = cso.rx_read
            tx_write.next = cso.tx_write

        return tbdut, tbeep, tbclk, tbstim, tbmon
Exemple #8
0
def bench_conversion_fifo_async():
    args = Namespace(width=8, size=32, fifosize=64, name='test')

    clock_write, clock_read = Signals(bool(0), 2)
    reset = ResetSignal(0, active=1, async=False)

    fbus = FIFOBus(width=args.width)
    fifosize = args.fifosize
    tbdut = fifo_async(clock_write, clock_read, fbus, reset, size=fifosize)

    @instance
    def tbclkr():
        clock_read.next = False
        while True:
            yield delay(5)
            clock_read.next = not clock_read

    @instance
    def tbclkw():
        clock_write.next = False
        while True:
            yield delay(5)
            clock_write.next = not clock_write

    @instance
    def tbstim():
        print("start simulation")
        fbus.write.next = False
        fbus.write_data.next = 0
        fbus.read.next = False
        fbus.clear.next = False

        print("reset")
        reset.next = reset.active
        yield delay(10)
        reset.next = not reset.active
        yield delay(10)

        print("some clock cycles")
        for ii in range(10):
            yield clock_write.posedge

        print("some writes")
        for ii in range(fifosize):
            fbus.write.next = True
            fbus.write_data.next = ii
            yield clock_write.posedge
        fbus.write.next = False
        yield clock_write.posedge

        for ii in range(fifosize):
            fbus.read.next = True
            yield clock_read.posedge
            print("%d %d %d %d" % (
                fbus.write,
                fbus.write_data,
                fbus.read,
                fbus.read_data,
            ))
        fbus.read.next = False

        print("end simulation")
        raise StopSimulation

    return myhdl.instances()
Exemple #9
0
    def bench_fifo_sync():

        tbdut = fifo_sync(glbl, fbus, size=args.size)
        tbclk = clock.gen()

        @instance
        def tbstim():
            fbus.write_data.next = 0xFE
            reset.next = reset.active
            yield delay(33)
            reset.next = not reset.active
            for ii in range(5):
                yield clock.posedge

            # test the normal cases
            for num_bytes in range(1, args.size + 1):

                # write some bytes
                for ii in range(num_bytes):
                    yield clock.posedge
                    fbus.write_data.next = ii + 0xCE
                    fbus.write.next = True

                yield clock.posedge
                fbus.write.next = False
                fbus.write_data.next = 0xFE

                # if 16 bytes written make sure FIFO is full
                yield clock.posedge
                if num_bytes == args.size:
                    assert fbus.full, "FIFO should be full!"
                    assert not fbus.empty, "FIFO should not be empty"

                # fbus.read.next = True
                # yield clock.posedge
                for ii in range(5):
                    yield clock.posedge
                    if not fbus.empty:
                        break

                for ii in range(num_bytes):
                    fbus.read.next = True
                    yield clock.posedge
                    assert fbus.read_valid
                    assert fbus.read_data == ii + 0xCE, \
                        "rdata %x ii %x " % (fbus.read_data, ii + 0xCE)

                fbus.read.next = False
                yield clock.posedge
                assert fbus.empty

            raise StopSimulation

        w = args.width
        write_data, read_data = Signals(intbv(0)[w:], 2)

        @always_comb
        def tbmon():
            write_data.next = fbus.write_data
            read_data.next = fbus.read_data

        return tbdut, tbclk, tbstim, tbmon
    def bench_command_bridge():
        tbclk = clock.gen()
        tbdut = command_bridge(glbl, fifobus, memmap)

        readpath, writepath = FIFOBus(), FIFOBus()
        readpath.clock = writepath.clock = clock
        tbmap = fifobus.assign_read_write_paths(readpath, writepath)
        tbftx = fifo_fast(reset, clock, writepath)  # user write path
        tbfrx = fifo_fast(reset, clock, readpath)  # user read path

        # @todo: add other bus types
        tbmem = memmap_peripheral_bb(clock, reset, memmap)

        # save the data read ...
        read_value = []

        @instance
        def tbstim():
            yield reset.pulse(32)
            fifobus.read.next = False
            fifobus.write.next = False
            assert not fifobus.full
            assert fifobus.empty
            assert fifobus.read_data == 0
            fifobus.write_data.next = 0

            try:
                # test a single address
                pkt = CommandPacket(True, 0x0000)
                yield pkt.put(readpath)
                yield pkt.get(writepath, read_value, [0])

                pkt = CommandPacket(False, 0x0000, [0x5555AAAA])
                yield pkt.put(readpath)
                yield pkt.get(writepath, read_value, [0x5555AAAA])

                # test a bunch of random addresses
                for ii in range(nloops):
                    randaddr = randint(0, (2**20) - 1)
                    randdata = randint(0, (2**32) - 1)
                    pkt = CommandPacket(False, randaddr, [randdata])
                    yield pkt.put(readpath)
                    yield pkt.get(writepath, read_value, [randdata])

            except Exception as err:
                print("Error: {}".format(str(err)))
                traceback.print_exc()

            yield delay(2000)
            raise StopSimulation

        wp_read, wp_valid = Signals(bool(0), 2)
        wp_read_data = Signal(intbv(0)[8:])
        wp_empty, wp_full = Signals(bool(0), 2)

        @always_comb
        def tbmon():
            wp_read.next = writepath.read
            wp_read_data.next = writepath.read_data
            wp_valid.next = writepath.read_valid
            wp_full.next = writepath.full
            wp_empty.next = writepath.empty

        return tbclk, tbdut, tbmap, tbftx, tbfrx, tbmem, tbstim, tbmon
Exemple #11
0
def bench_fifo_mem_rand():
    nloops = 100
    w = width = 8
    wmax = 2**width

    # random data and addresses for test
    rand_data = tuple([randint(0, wmax - 1) for _ in range(nloops)])
    rand_addr = tuple([randint(0, wmax - 1) for _ in range(nloops)])

    clock, write, read = Signals(bool(0), 3)
    write_data, write_addr, read_data, read_addr = Signals(intbv(0)[w:0], 4)
    wad = Signal(write_addr.val)

    tbdut = fifo_mem(clock, write, write_data, write_addr, clock, read,
                     read_data, read_addr, wad)

    @instance
    def tbclkw():
        clock.next = False
        while True:
            yield delay(5)
            clock.next = not clock

    @instance
    def tbstim():
        print("start sim")
        write.next = False
        write_data.next = 0
        write_addr.next = 0
        read.next = False
        read_addr.next = 0

        print("delay some")
        yield delay(10)
        for ii in range(5):
            yield clock.posedge

        for ii in range(wmax):
            write.next = True
            write_data.next = 0
            yield clock.posedge
        write.next = False

        print("write loop")
        for ii in range(nloops):
            write.next = True
            write_data.next = rand_data[ii]
            write_addr.next = rand_addr[ii]
            read_addr.next = wad
            yield clock.posedge
            write.next = False
            for jj in range(3):
                yield clock.posedge
            print("%d %d %d %d" %
                  (write_addr, write_data, read_addr, read_data))
        write.next = False

        print("read loop")
        for ii in range(nloops):
            write_data.next = rand_data[ii]
            write_addr.next = rand_addr[ii]
            read_addr.next = rand_addr[ii]
            yield clock.posedge
            print("%d %d %d %d" %
                  (write_addr, write_data, read_addr, read_data))
        write.next = False
        yield clock.posedge

        print("end sim")
        raise StopSimulation

    return myhdl.instances()
Exemple #12
0
def spi_slave_fifo(glbl, spibus, fifobus):
    """
    This is an SPI slave peripheral, when the master starts clocking
    any data in the TX FIFO (fifobus.write) will be sent (the next
    byte) and the received byte will be copied to RX FIFO
    (fifobus.read).  The `cso` interface can be used to configure
    how the SPI slave peripheral behaves.

    Arguments (Ports):
        glbl (Global): global clock and reset
        spibus  (SPIBus): the external SPI interface
        fifobus (FIFOBus): the fifo interface
        cso (ControlStatus): the control status signals
    """

    # Use an async FIFO to transfer from the SPI SCK clock domain and
    # the internal clock domain.  This allows for high-speed SCK.
    clock, reset = glbl.clock, glbl.reset
    assert isinstance(spibus, SPIBus)
    assert isinstance(fifobus, FIFOBus)

    sck, csn = spibus.sck, spibus.csn
    # the FIFOs for the receive and transmit (external perspective)
    readpath = FIFOBus(size=fifobus.size, width=fifobus.width)
    writepath = FIFOBus(size=fifobus.size, width=fifobus.width)

    # the FIFO instances
    # @todo: replace with fifo_fast
    tx_fifo_inst = fifo_fast(reset, clock, writepath)
    rx_fifo_inst = fifo_fast(reset, clock, readpath)
    mp_fifo_inst = fifobus.assign_read_write_paths(readpath, writepath)

    spi_start = Signal(bool(0))
    ireg, icap, icaps = Signals(intbv(0)[8:], 3)
    oreg, ocap = Signals(intbv(0)[8:], 2)
    bitcnt, b2, b3 = Signals(intbv(0, min=0, max=10), 3)

    @always(sck.posedge, csn.negedge)
    def csn_falls():
        if sck:
            spi_start.next = False
        elif not csn:
            spi_start.next = True

    # SCK clock domain, this allows high SCK rates
    @always(sck.posedge, csn.posedge)
    def sck_capture_send():
        if csn:
            b2.next = 0
            bitcnt.next = 0
        else:
            if bitcnt == 0 or spi_start:
                spibus.miso.next = ocap[7]
                oreg.next = (ocap << 1) & 0xFF
            else:
                spibus.miso.next = oreg[7]
                oreg.next = (oreg << 1) & 0xFF

            ireg.next = concat(ireg[7:0], spibus.mosi)
            bitcnt.next = bitcnt + 1
            if bitcnt == (8 - 1):
                bitcnt.next = 0
                b2.next = 8
                icap.next = concat(ireg[7:0], spibus.mosi)
            else:
                b2.next = 0

    # synchronize the SCK domain to the clock domain
    isync1_inst = syncro(clock, icap, icaps)
    isync2_inst = syncro(clock, b2, b3)

    gotit = Signal(bool(0))

    @always(clock.posedge)
    def beh_io_capture():
        # default no writes
        readpath.write.next = False
        writepath.read.next = False

        if b3 == 0:
            gotit.next = False
        elif b3 == 8 and not gotit:
            readpath.write.next = True
            readpath.write_data.next = icaps
            gotit.next = True
            ocap.next = writepath.read_data
            if not writepath.empty:
                writepath.read.next = True

    return myhdl.instances()
Exemple #13
0
def fifo_mem(clock_w, write, write_data, write_addr, clock_r, read, read_data,
             read_addr, write_addr_delayed):
    """ Memory module used by FIFOs

    The write data takes two `clock_w` clock cycles to be latched
    into the memory array and one `clock_r` clock cycle to be latched
    into `read_data`.

    Arguments:
       clock_w: write clock
       write: write enable
       write_data: write data bus
       write_addr: write address bus
       clock_r: read clock
       read: read strobe, loads the next address into the output reg
       read_data: read data bus
       read_addr: read address bus
       write_addr_delayed: the write data takes multiple clock cycles
           before it is available in the memory (pipelines before and
           and after the memory array).  This is a delayed version of
           the write_addr that matches the write_data delay.

    The memory size is determine from the address width.
    """

    assert len(write_addr) == len(read_addr)
    addrsize = len(write_addr)
    memsize = 2**len(write_addr)
    assert len(write_data) == len(read_data)
    datasize = len(write_data)
    # print("FIFO memory size {}, data width {}, address width {}".format(
    #     memsize, datasize, addrsize
    # ))

    # create the list-of-signals to represent the memory array
    memarray = Signals(intbv(0)[datasize:0], memsize)

    addr_w, addr_wd, addr_r = Signals(modbv(0)[addrsize:], 3)
    din, dout = Signals(intbv(0)[datasize:], 2)
    wr = Signal(bool(0))

    @always(clock_w.posedge)
    def beh_write_capture():
        # inputs are registered
        wr.next = write
        addr_w.next = write_addr
        din.next = write_data

    @always(clock_w.posedge)
    def beh_mem_write():
        if wr:
            memarray[addr_w].next = din

    @always(clock_r.posedge)
    def beh_write_address_delayed():
        addr_wd.next = write_addr
        write_addr_delayed.next = addr_wd

    @always_comb
    def beh_read_next():
        # output is registered, this block (fifo_mem) is used as
        # memory for various FIFOs, on read assume incrementing
        # to the next address, get the next address.
        if read:
            addr_r.next = read_addr + 1
        else:
            addr_r.next = read_addr

    @always(clock_r.posedge)
    def beh_mem_read():
        dout.next = memarray[addr_r]

    @always_comb
    def beh_dataout():
        read_data.next = dout

    return myhdl.instances()
Exemple #14
0
def test_zybo_vga(args=None):
    args = tb_default_args(args)

    resolution = (
        80,
        60,
    )
    refresh_rate = 60
    line_rate = 31250
    color_depth = (
        6,
        6,
        6,
    )

    clock = Clock(0, frequency=125e6)
    glbl = Global(clock)
    vga = VGA(color_depth=color_depth)
    vga_hsync, vga_vsync = Signals(bool(0), 2)
    vga_red, vga_green, vga_blue = Signals(intbv(0)[6:], 3)
    led, btn = Signals(intbv(0)[4:], 2)

    @myhdl.block
    def bench():
        tbdut = zybo_vga(led,
                         btn,
                         vga_red,
                         vga_green,
                         vga_blue,
                         vga_hsync,
                         vga_vsync,
                         clock,
                         resolution=resolution,
                         color_depth=color_depth,
                         refresh_rate=refresh_rate,
                         line_rate=line_rate)
        tbclk = clock.gen()
        mdl = VGADisplay(frequency=clock.frequency,
                         resolution=resolution,
                         refresh_rate=refresh_rate,
                         line_rate=line_rate,
                         color_depth=color_depth)
        tbmdl = mdl.process(glbl, vga)

        @instance
        def tbstim():
            yield delay(100000)
            raise StopSimulation

        return tbdut, tbclk, tbmdl, tbstim

    # run the above testbench, the above testbench doesn't functionally
    # verify anything only verifies basics.
    run_testbench(bench, args=args)

    # test conversion
    portmap = dict(led=led,
                   btn=btn,
                   vga_red=vga_red,
                   vga_grn=vga_green,
                   vga_blu=vga_blue,
                   vga_hsync=vga_hsync,
                   vga_vsync=vga_vsync,
                   clock=clock)
    inst = zybo_vga(**portmap)
    tb_convert(inst)
Exemple #15
0
def spi_controller(
        # ---[ Module Ports]---
        glbl,  # global interface, clock, reset, etc.
        spibus,  # external SPI bus
        # optional ports
    fifobus=None,  # streaming interface, FIFO bus
        mmbus=None,  # memory-mapped bus, contro status access
        cso=None,  # control-status object

        # ---[ Module Parameters ]---
    include_fifo=True,  # include aan 8 byte deep FIFO
):
    """ SPI (Serial Peripheral Interface) module
    This module is an SPI controller (master) and can be used to interface
    with various external SPI devices.

    Arguments:
        glbl (Global): clock and reset interface
        spibus (SPIBus): external (off-chip) SPI bus
        fifobus (FIFOBus): interface to the FIFOs, write side is to
          the TX the read side from the RX.
        mmbus (MemoryMapped): a memory-mapped bus used to access
          the control-status signals.
        cso (ControlStatus): the control-status object used to control
          this peripheral

        include_fifo (bool): include the FIFO ... this is not fully
          implemented

    Note:
        At last check the register-file automation was not complete, only
        the `cso` external control or `cso` configuration can be utilized.
    """
    clock, reset = glbl.clock, glbl.reset
    if cso is None:
        cso = spi_controller.cso()

    # -- local signals --
    ena = Signal(False)
    clkcnt = Signal(modbv(0, min=0, max=2**12))
    bcnt = Signal(intbv(0, min=0, max=8))

    # separate tx and rx shift-registers (could be one in the same)
    treg = Signal(intbv(0)[8:])  # tx shift register
    rreg = Signal(intbv(0)[8:])  # rx shift register

    x_sck, x_ss, x_mosi, x_miso = Signals(bool(0), 4)

    # internal FIFO bus interfaces
    #   external FIFO side (FIFO to external SPI bus)
    itx = FIFOBus(size=fifobus.size, width=fifobus.width)
    #   internal FIFO side (FIFO to internal bus)
    irx = FIFOBus(size=fifobus.size, width=fifobus.width)

    states = enum('idle', 'wait_hclk', 'data_in', 'data_change', 'write_fifo',
                  'end')
    state = Signal(states.idle)

    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    # memory- mapped registers
    # add the peripheral's regfile to the bus (informational only)
    # @todo: the automatic building of the register files is incomplete
    if mmbus is not None:
        # the register-file (rf) will drive all the cso signals
        rf = cso.get_register_file()
        mmbus.add(rf, 'spi')

    # FIFO for the wishbone data transfer
    if include_fifo:
        fifo_fast.debug = spi_controller.debug
        fifo_tx_inst = fifo_fast(reset, clock, itx)
        fifo_rx_inst = fifo_fast(reset, clock, irx)

    @always_comb
    def rtl_assign():
        cso.tx_fifo_count.next = itx.count
        cso.rx_fifo_count.next = irx.count

        if clkcnt > 0:
            ena.next = False
        else:
            ena.next = True

    clock_counts = tuple([(2**ii) - 1 for ii in range(13)])

    @always(clock.posedge)
    def rtl_clk_div():
        if cso.enable and clkcnt != 0 and state != states.idle:
            clkcnt.next = (clkcnt - 1)
        else:
            clkcnt.next = clock_counts[cso.clock_divisor]

    @always_seq(clock.posedge, reset=reset)
    def rtl_state_and_more():
        """
        Designed to the following timing diagram

        SCK   CPOL=0 ______/---\___/---\___/---\___/---\___/---\___/---\___/---\___/---\___/---\ 
              CPOL=1 ------\___/---\___/---\___/---\___/---\___/---\___/---\___/---\___/---\___/ 
        SS           ---\_______________________________________________________________________ 
        CPHA=0 MOSI  ...|.0....|.1.....|.2.....|.3.....|.4.....|.5.....|.6.....|.7.....|.0.....| 
               MISO  ...|.0....|.1.....|.2.....|.3.....|.4.....|.5.....|.6.....|.7.....|.0.....| 
        CPHA=1 MOSI  ...|....0.....|.1.....|.2.....|.3.....|.4.....|.5.....|.6.....|.7.....|.0...
               MISO  ......|.0.....|.1.....|.2.....|.3.....|.4.....|.5.....|.6.....|.7.....|.0...
        """
        if not cso.enable:
            state.next = states.idle
            bcnt.next = 0
            treg.next = 0

            itx.read.next = False
            irx.write.next = False

            x_sck.next = False
            x_ss.next = False
        else:
            if not cso.freeze:
                # ~~~~ Idle state ~~~~
                if state == states.idle:
                    bcnt.next = 7
                    treg.next = itx.read_data
                    x_sck.next = cso.clock_polarity
                    irx.write.next = False

                    if not itx.empty and not irx.full:
                        itx.read.next = True
                        x_ss.next = False
                        if cso.clock_phase:  # Clock in on second phase
                            state.next = states.wait_hclk
                        else:  # Clock in on first phase
                            state.next = states.data_in
                    else:
                        itx.read.next = False
                        x_ss.next = True

                # ~~~~ Wait half clock period for cpha=1 ~~~~
                elif state == states.wait_hclk:
                    itx.read.next = False
                    irx.write.next = False
                    if ena:
                        x_sck.next = not x_sck
                        state.next = states.data_in

                # ~~~~ Clock data in (and out) ~~~~
                elif state == states.data_in:
                    itx.read.next = False
                    irx.write.next = False
                    if ena:  # clk div
                        x_sck.next = not x_sck
                        rreg.next = concat(rreg[7:0], x_miso)

                        if cso.clock_phase and bcnt == 0:
                            irx.write.next = True
                            if itx.empty or irx.full:
                                state.next = states.end
                            else:
                                state.next = states.data_change
                        else:
                            state.next = states.data_change

                # ~~~~ Get ready for next byte out/in ~~~~
                elif state == states.data_change:
                    itx.read.next = False
                    irx.write.next = False
                    if ena:
                        x_sck.next = not x_sck
                        if bcnt == 0:
                            if not cso.clock_phase:
                                irx.write.next = True

                            if itx.empty or irx.full:
                                state.next = states.end
                            else:  # more data to transfer
                                bcnt.next = 7
                                state.next = states.data_in
                                itx.read.next = True
                                treg.next = itx.read_data
                        else:
                            treg.next = concat(treg[7:0], intbv(0)[1:])
                            bcnt.next = bcnt - 1
                            state.next = states.data_in

                # ~~~~ End state ~~~~
                elif state == states.end:
                    itx.read.next = False
                    irx.write.next = False
                    if ena:  # Wait half clock cycle go idle
                        state.next = states.idle

                # Shouldn't happen, error in logic
                else:
                    state.next = states.idle
                    assert False, "SPI Invalid State"

    @always_comb
    def rtl_fifo_sel():
        """
        The `itx` and `irx` FIFO interfaces are driven by different
        logic depending on the configuration.  This modules accesses
        the `itx` read side and drives the `irx` write side.  The
        `itx` write side is driven by the `cso` or the `fifobus` port.
        The `irx` read side is accessed by the `cso` or the `fifobus`
        port.
        """
        if cso.bypass_fifo:
            # data comes from the register file
            cso.tx_empty.next = itx.empty
            cso.tx_full.next = itx.full
            itx.write_data.next = cso.tx_byte

            cso.rx_empty.next = irx.empty
            cso.rx_full.next = irx.full
            cso.rx_byte.next = irx.read_data
            cso.rx_byte_valid.next = irx.read_valid

            # @todo: if cso.tx_byte write signal (written by bus) drive the
            # @todo: FIFO write signals, same if the cso.rx_byte is accessed
            itx.write.next = cso.tx_write
            irx.read.next = cso.rx_read

        else:
            # data comes from external FIFO bus interface
            fifobus.full.next = itx.full
            itx.write_data.next = fifobus.write_data
            itx.write.next = fifobus.write

            fifobus.empty.next = irx.empty
            fifobus.read_data.next = irx.read_data
            fifobus.read_valid.next = irx.read_valid
            irx.read.next = fifobus.read

        # same for all modes
        irx.write_data.next = rreg

    @always_comb
    def rtl_x_mosi():
        # @todo lsb control signal
        x_mosi.next = treg[7]

    @always_comb
    def rtl_gate_mosi():
        if cso.loopback:
            spibus.mosi.next = False
        else:
            spibus.mosi.next = x_mosi

    @always_comb  #(clock.posedge)
    def rtl_spi_sigs():
        spibus.sck.next = x_sck
        if cso.loopback:
            x_miso.next = x_mosi
        else:
            x_miso.next = spibus.miso

    @always_comb
    def rtl_slave_select():
        if cso.manual_slave_select:
            spibus.ss.next = ~cso.slave_select
        elif x_ss:
            spibus.ss.next = 0xFF
        else:
            spibus.ss.next = ~cso.slave_select

    # myhdl generators in the __debug__ conditionals are not converted.
    if spi_controller.debug:

        @instance
        def mon_state():
            print("  :{:<8d}: initial state {}".format(now(), str(state)))

            while True:
                yield state
                print("  :{:<8d}: state transition --> {}".format(
                    now(), str(state)))

        fbidle = intbv('0000')[4:]

        @instance
        def mon_trace():
            while True:
                yield clock.posedge
                ccfb = concat(itx.write, itx.read, irx.write, irx.read)
                if ccfb != fbidle:
                    fstr = "  :{:<8d}: tx: w{} r{}, f{} e{}, rx: w{} r{} f{} e{}"
                    print(
                        fstr.format(
                            now(),
                            int(itx.write),
                            int(itx.read),
                            int(itx.full),
                            int(itx.empty),
                            int(irx.write),
                            int(irx.read),
                            int(irx.full),
                            int(irx.empty),
                        ))

        @always(clock.posedge)
        def mon_tx_fifo_write():
            if itx.write:
                print("   WRITE tx fifo {:02X}".format(int(itx.write_data)))
            if itx.read:
                print("   READ tx fifo {:02X}".format(int(itx.read_data)))

        @always(clock.posedge)
        def mon_rx_fifo_write():
            if irx.write:
                print("   WRITE rx fifo {:02X}".format(int(irx.write_data)))

            if irx.read:
                print("   READ rx fifo {:02X}".format(int(irx.read_data)))

    # return the myhdl generators
    gens = myhdl.instances()
    return gens