Package: cocotbext-spi
SPI Interface for CocotbGitHub repository:https://github.com/schang412/cocotbext-spiIntroductionSPI simulation framework forcocotb.InstallationInstallation from pip (release version, stable):pipinstallcocotbext-spiInstallation from git (latest development version, potentially unstable):pipinstallhttps://github.com/schang412/cocotbext-spi/archive/main.zipInstallation for active development:gitclonehttps://github.com/schang412/cocotbext-spi
pipinstall-ecocotbext-spiDocumentation and UsageSee thetestsdirectory for complete testbenches using these modules.SPI SignalsThe SPI bus signals are bundled together into aSpiBusclass.If the port instantiations look like:modulemy_module(inputwiresclk,inputwiremosi,outputwiremiso,inputwirecs,// active-low)TheSpiBusclass can be created as:fromcocotbext.spiimportSpiBusspi_bus=SpiBus.from_entity(dut)If there is some prefix, thefrom_prefixclass method may be used:modulemy_module(inputwirespi0_sclk,inputwirespi0_mosi,outputwirespi0_miso,inputwirespi0_cs,// active-low)spi_bus=SpiBus.from_prefix(dut,"spi0")If the chip select has been renamed for clarity:modulemy_module(inputwirespi0_sclk,inputwirespi0_mosi,outputwirespi0_miso,inputwirespi0_ncs,// active-low)spi_bus=SpiBus.from_prefix(dut,"spi0",cs_name="ncs")SPI ConfigSPI Configuration parameters are bundled together into aSpiConfigclass.To create the object simply call it like a class and pass in arguments:fromcocotbext.spiimportSpiConfigspi_config=SpiConfig(word_width=16,# number of bits in a SPI transactionsclk_freq=25e6,# clock rate in Hzcpol=False,# clock idle polaritycpha=True,# clock phase (CPHA=True means data sampled on second edge)msb_first=True,# the order that bits are clocked onto the wiredata_output_idle=1,# the idle value of the MOSI or MISO lineframe_spacing_ns=1,# the spacing between frames that the master waits for or the slave obeys#       the slave should raise SpiFrameError if this is not obeyed.ignore_rx_value=None,# MISO value that should be ignored when receivedcs_active_low=True# the chip select is active low)All parameters are optional, and the defaults are shown above.SPI MasterTheSpiMasterclass acts as an SPI Master endpoint.To use this class, import it, configure it, and connect to the dut.fromcocotbext.spiimportSpiMaster,SpiBus,SpiConfigspi_bus=SpiBus.from_entity(dut)spi_config=SpiConfig(word_width=16,# all parameters optionalsclk_freq=25e6,# these are the defaultscpol=False,cpha=True,msb_first=True,cs_active_low=True# optional (assumed True))spi_master=SpiMaster(spi_bus,spi_config)To send data into a design withSpiMaster, callwrite()orwrite_nowait(). Accepted data types are iterables of ints including lists, bytes, bytearrays, etc. Optionally, call wait() to wait for the transmit operation to complete. We can take a look at the data received back withread()orread_nowait()# TX/RX transaction examplespi_master.write_nowait([0xFFFF])awaitspi_master.wait()read_bytes=awaitspi_master.read()print(read_bytes)# we can alternatively call (which has equivalent functionality)awaitspi_master.write([0xFFFF])read_bytes=awaitspi_masetr.read()Constructor Parametersbus: SpiBusconfig: SpiConfigMethodswrite(data): send data (blocking)write_nowait(data): send data (non-blocking)read(count=-1): read count bytes from buffer, reading whole buffer by default (blocking)read_nowait(count=-1): read count bytes from buffer, reading whole buffer by default (non-blocking)count_tx(): returns the number of items in the transmit queuecount_rx(): returns the number of items in the receive queueempty_tx(): returns True if the transmit queue is emptyempty_rx(): returns True if the receive queue is emptyidle(): returns True if the transmit and receive buffers are emptyclear(): drop all data in the queueSPI SlaveTheSpiSlaveBaseacts as an abstract class for a SPI Slave Endpoint.To use this class, import it and inherit it. Then use the subclass as the slave and connect it to the dut.fromcocotbext.spiimportSpiMaster,SpiBus,SpiConfigclassSimpleSpiSlave(SpiSlaveBase):def__init__(self,bus):self._config=SpiConfig()self.content=0super().__init__(bus)asyncdefget_content(self):awaitself.idle.wait()returnself.contentasyncdef_transaction(self,frame_start,frame_end):awaitframe_startself.idle.clear()self.content=int(awaitself._shift(16,tx_word=(0xAAAA)))awaitframe_endspi_slave=SimpleSpiSlave(SpiBus.from_entity(dut))ImplementationAll SPI Slave Classes should:inherit the SpiSlaveBase classdefineself._configadjust the values for:word_widthcphacpolmsb_firstframe_spacing_nsimplement a_transactioncoroutinethe coroutine should take 3 arguments, self, frame_start and frame_endthe coroutine should await frame_start at the transaction start, and frame_end when done.frame_start and frame_end are Rising and Falling edges of the chip select based on the chip select polaritywhen the coroutine receives a frame_start signal, it should clear theself.idleEvent.self.idleis automatically set when_transactionreturnswhen implementing a method to read the class contents, make sure to await theself.idle, otherwise the data may not be up to date because the device is in the middle of a transaction.Simulated DevicesThis framework includes some SPI Slave devices built in. A list of supported devices can be found incocotbext/spi/devicesand are sorted by vendor.To use these devices, you can simply import them.fromcocotbext.spi.devices.TIimportDRV8306spi_slave=DRV8306(SpiBus.from_entity(dut,cs_name="ncs"))To submit a new device, make a pull request.