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)
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)
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()
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)
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)
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)
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
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()
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
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()
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()
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()
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)
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