def __init__(self, size=(8, 8), min=0, max=8): """ Arguments: size (tuple): min (int): the min value of the data contained in the matrix max (int)L the max value of the data contained in the matrix """ assert isinstance(size, tuple) assert len(size) == 2 self.size = size nrows, ncols = size self.nitems = nrows * ncols dtype = intbv(0, min=min, max=max) self.nbits = len(dtype) self.dtype = dtype self.mat = [Signals(dtype, ncols) for _ in range(nrows)] self.data = self.get_bit_vector() self.valid = Signal(bool(0)) self.ready = Signal(bool(0))
def fifo_cdc(glbl, emesh_i, emesh_o): """ map the packet interfaces to the FIFO interface """ wr, rd = Signals(bool(0), 2) fifo_intf = FIFOBus(width=len(emesh_i.bits)) @always_comb def beh_assign(): wr.next = emesh_i.access and not fifo_intf.full rd.next = not fifo_intf.empty and not emesh_i.wait emesh_o.wait.next = fifo_intf.full @always(emesh_o.clock.posedge) def beh_access(): if not emesh_i.wait: emesh_o.access.next = fifo_intf.read # assign signals ot the FIFO interface fifo_intf.write_data = emesh_i.bits fifo_intf.read_data = emesh_o.bits fifo_inst = cores.fifo.fifo_async(clock_write=emesh_i.clock, clock_read=emesh_o.clock, fifobus=fifo_intf, reset=glbl.reset, size=16) return beh_assign, beh_access, fifo_inst
def __init__(self, size=8, min=0, max=8): """ Arguments: size: the number of items to have in the block min: the data min value max: the data max value """ self.size = size dtype = intbv(0, min=min, max=max) self.data = Signals(dtype, size) self.valid = Signal(bool(0)) self.ready = Signal(bool(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) Arguments: glbl (Global): global clock and reset spibus (SPIBus): the external SPI interface fifobus (FIFOBus): the fifo interface cso (ControlStatus): the control status signals """ fifosize = 8 # 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(width=fifobus.width) writepath = FIFOBus(width=fifobus.width) # the FIFO instances tx_fifo_inst = fifo_fast(glbl, writepath, size=fifosize) rx_fifo_inst = fifo_fast(glbl, readpath, size=fifosize) 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 syncro(clock, icap, icaps) 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 process(self, skip_init=True): """ @todo: documentation """ intf = self.intf # external SDRAM memory interface state = Signal(self.States.IDLE) cmdmn = Signal(self.Commands.NOP) Commands, States = self.Commands, self.States @instance def mproc(): cmd = self.Commands.NOP refresh_counter = 0 # emulate the initialization sequence / requirement if skip_init: pass # don't do anything else: pass # @todo: do init while True: # check the refresh counter if refresh_counter >= intf.cyc_ref: # @todo: create specific exception for refresh error raise ValueError refresh_counter += 1 intf.dqi.next = None # release the bi-dir bus (default) bs, addr = int(intf.bs), int(intf.addr) if intf.cke: cmd = intf.get_command() # @todo: need to add the device specific states if cmd == Commands.NOP: pass # print("[SDRAM] nop commands") elif cmd == Commands.ACT: pass # print("[SDRAM] ack commands") elif cmd == Commands.WR: # @todo look at the intf.dq bus and only get if valid data = 0 if intf.dq is not None: data = int(intf.dq) assert intf.dq == intf.wdq self.banks[bs][addr] = data elif cmd == Commands.RD: data = 0 if addr in self.banks[bs]: data = self.banks[bs][addr] intf.rdq.next = data intf.dqi.next = data # this command, will always be one clock delayed cmdmn.next = cmd # synchronous RAM :) # @todo: if 'ddr' in intf.ver yield intf.clk.posedge, intf.clk.negedge yield intf.clk.posedge # in the model the following signals are not used in a generator. # The traceSignals will skip over these signals because it doesn't # think it is used. Mirror the signals here so they are traced. cs, ras, cas, we = Signals(bool(0), 4) @always_comb def mon(): cs.next = intf.cs ras.next = intf.ras cas.next = intf.cas we.next = intf.we return mproc, mon
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(glbl, writepath) # user write path tbfrx = fifo_fast(glbl, 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 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, control status access cso=None, # control-status object # ---[ Module Parameters ]--- include_fifo=True, # include an 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() fifosize = 8 # -- 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(width=fifobus.width) # internal FIFO side (FIFO to internal bus) irx = FIFOBus(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(glbl, fifobus=itx, size=fifosize) fifo_rx_inst = fifo_fast(glbl, fifobus=irx, size=fifosize) @always_comb def beh_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 beh_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 beh_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 beh_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 beh_x_mosi(): # @todo lsb control signal x_mosi.next = treg[7] @always_comb def beh_gate_mosi(): if cso.loopback: spibus.mosi.next = False else: spibus.mosi.next = x_mosi @always_comb # (clock.posedge) def beh_spi_sigs(): spibus.sck.next = x_sck if cso.loopback: x_miso.next = x_mosi else: x_miso.next = spibus.miso @always_comb def beh_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 and instances return myhdl.instances()