diff --git a/docs/library/spi_engine/axi_spi_engine.rst b/docs/library/spi_engine/axi_spi_engine.rst index aa219216312..7743fdb677b 100644 --- a/docs/library/spi_engine/axi_spi_engine.rst +++ b/docs/library/spi_engine/axi_spi_engine.rst @@ -116,8 +116,18 @@ If an application attempts to read data while the FIFO is empty undefined data is returned and the state of the FIFO remains unmodified. It is possible to read the first entry in the SDI FIFO without removing it by reading from the SDI_FIFO_PEEK register. +It is important to point out that each read represents one lane of the SPI. So, +for ``NUM_OF_SDI`` lanes it is necessary to read ``NUM_OF_SDI`` times. Reading +always starts from lane 0. The number of valid entries in the SDI FIFO register can be queried by reading -the SDI_FIFO_LEVEL register. +the SDI_FIFO_LEVEL register. This value must be a multiple of ``NUM_OF_SDI``. + +Data can be inserted into the SDO FIFO by writing to the SDO_FIFO register +**only the valid lanes**. For example, if there are 4 lanes and just 2 of them +are enabled, then the programmer must write 2 values to this register. The +remaining lanes will contain ``SDO_DEFAULT`` value defined in +:ref:`spi_engine execution`. The number of valid lanes are defined in +:ref:`spi_engine configuration-registers`. If the peripheral is disabled by setting the ENABLE register to 0 any data stored in the FIFOs is discarded and the state of the FIFO is reset. diff --git a/docs/library/spi_engine/instruction-format.rst b/docs/library/spi_engine/instruction-format.rst index 11aec5b2112..07b1e6e57ac 100644 --- a/docs/library/spi_engine/instruction-format.rst +++ b/docs/library/spi_engine/instruction-format.rst @@ -3,8 +3,8 @@ SPI Engine Instruction Set Specification ================================================================================ -The SPI Engine instruction set is a simple 16-bit instruction set of which -13-bits are currently allocated (bits 15,11,10 are always 0). +The SPI Engine instruction set is a 16-bit instruction set of which 13-bits are +currently allocated (bits 15,11,10 are always 0). Instructions -------------------------------------------------------------------------------- @@ -12,22 +12,51 @@ Instructions Transfer Instruction ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -== == == == == == = = = = = = = = = = -15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 -== == == == == == = = = = = = = = = = -0 0 0 0 0 0 r w n n n n n n n n -== == == == == == = = = = = = = = = = - -The transfer instructions perform a low-level SPI transfer. It will generate -SCLK transitions for the specified amount of cycles according to the SPI -configuration register. If the r bit is set the SDI pin will be sampled and -stored in the shift register at the end of each word the data is output on the -SDI_DATA stream. If the w bit is set the SDO pin will be updated with the data -received from the SDO_DATA stream. If the w bit is set the sdo_t signal will -also be set to 0 for the duration of the transfer. If the SDI_DATA stream is not -able to accept data or the SDO_DATA stream is not able to provide data the -execution is stalled at the end/start of the transfer until data is -accepted/becomes available. +.. list-table:: + :header-rows: 1 + + * - 15 + - 14 + - 13 + - 12 + - 11 + - 10 + - 9 + - 8 + - 7 + - 6 + - 5 + - 4 + - 3 + - 2 + - 1 + - 0 + * - rv + - 0 + - 0 + - 0 + - rv + - rv + - r + - w + - n + - n + - n + - n + - n + - n + - n + - n + +The transfer instruction perform a low-level SPI transfer. It generates SCLK +transitions for the specified amount of cycles according to the SPI +configuration register. If the r bit is set, the SDI pin is sampled and stored +in the shift register up to the end of the word, and then is output in the +SDI_DATA stream. If the w bit is set, the SDO pin is updated with the data +received from the SDO_DATA stream. By setting w bit, the sdo_t pin is set to 0 +during the transfer. If the SDI_DATA stream cannot accept any data or SDO_DATA +stream cannot provide any data, then the execution module is stalled until +there's no longer any backpressure condition. .. list-table:: :widths: 10 15 75 @@ -36,17 +65,20 @@ accepted/becomes available. * - Bits - Name - Description + * - rv + - Reserved + - Reserved bit. Must always be set to 0. * - r - Read - - If set to 1 data will be read from the SDI pin during and the read words - will be available on the SDI_DATA interface. + - If set to 1, data is read from the SDI pin and the read words are + available on the SDI_DATA interface. * - w - Write - - If set to 1 data will be taken from the SDO_DATA interface and output on + - If set to 1, data is consumed from the SDO_DATA interface and output on the SDO pin. * - n - Length - - n + 1 number of words that will be transferred. + - n + 1 number of words to be transferred. .. _spi_engine cs-instruction: @@ -54,29 +86,59 @@ accepted/becomes available. Chip-Select Instruction ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -== == == == == == = = = = = = = = = = -15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 -== == == == == == = = = = = = = = = = -0 0 0 1 0 0 t t s s s s s s s s -== == == == == == = = = = = = = = = = - -The chip-select instruction updates the value chip-select output signal of the -SPI Engine execution module. - -The physical outputs on each pin may be inverted relative to the command -according to the mask set by :ref:`spi_engine cs-invert-mask-instruction`. The -Invert Mask acts only on the output registers of the Chip-Select pins. Thus, if -the last 8 bits of the Chip-Select instruction are 0xFE, only CS[0] will be -active regardless of polarity. The polarity inversion process (if needed) is -transparent to the programmer. - -Before and after the update is performed the execution module is paused for the -specified delay. The length of the delay depends on the module clock frequency, -the setting of the prescaler register and the parameter :math:`t` of the -instruction. This delay is inserted before and after the update of the -chip-select signal, so the total execution time of the chip-select instruction -is twice the delay, with an added fixed 2 clock cycles (fast clock, not -prescaled) before for the internal logic. +.. list-table:: + :header-rows: 1 + + * - 15 + - 14 + - 13 + - 12 + - 11 + - 10 + - 9 + - 8 + - 7 + - 6 + - 5 + - 4 + - 3 + - 2 + - 1 + - 0 + * - rv + - 0 + - 0 + - 1 + - rv + - rv + - t + - t + - s + - s + - s + - s + - s + - s + - s + - s + +The chip-select instruction updates the value of the chip-select output signal +of the SPI Engine execution module. + +The physical output value for every pin may differ from what is defined in this +instruction depending on the mask set in the +:ref:`spi_engine cs-invert-mask-instruction`, because this mask acts only on +the output registers of the Chip-Select pins. Thus, if the last 8 bits of the +Chip-Select instruction are 0xFE, only CS[0] is active regardless of polarity. +The polarity inversion process (if needed) is transparent to the programmer. + +Before and after any update, the execution module is paused for the specified +delay. The length of the delay depends on the module clock frequency, the +value of the prescaler register, and the parameter :math:`t` of the +instruction. This delay is inserted before and after any update of the +chip-select signal which results in twice the delay value defined. Furthermore, +it is necessary to add 2 clock cycles for the internal logic (fast clock, not +prescaled). .. math:: @@ -93,6 +155,9 @@ prescaled) before for the internal logic. * - Bits - Name - Description + * - rv + - Reserved + - Reserved bit. Must always be set to 0. * - t - Delay - Delay before and after setting the new configuration. @@ -103,15 +168,45 @@ prescaled) before for the internal logic. Configuration Write Instruction ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -== == == == == == = = = = = = = = = = -15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 -== == == == == == = = = = = = = = = = -0 0 1 0 0 0 r r v v v v v v v v -== == == == == == = = = = = = = = = = +.. list-table:: + :header-rows: 1 -The configuration writes instruction updates a -:ref:`spi_engine configuration-registers` -of the SPI Engine execution module with a new value. + * - 15 + - 14 + - 13 + - 12 + - 11 + - 10 + - 9 + - 8 + - 7 + - 6 + - 5 + - 4 + - 3 + - 2 + - 1 + - 0 + * - rv + - 0 + - 1 + - 0 + - rv + - rv + - rg + - rg + - v + - v + - v + - v + - v + - v + - v + - v + +The configuration write instruction updates the +:ref:`spi_engine configuration-registers` of the SPI Engine execution module +with a new value. .. list-table:: :widths: 10 15 75 @@ -120,12 +215,17 @@ of the SPI Engine execution module with a new value. * - Bits - Name - Description - * - r + * - rv + - Reserved + - Reserved bit. Must always be set to 0. + * - rg - Register - - Configuration register address. - 2'b00 = :ref:`spi_engine prescaler-configuration-register` - 2'b01 = :ref:`spi_engine spi-configuration-register` - 2'b10 = :ref:`spi_engine dynamic-transfer-length-register`. + - Configuration register address: + + - 2'b00 = :ref:`spi_engine prescaler-configuration-register`. + - 2'b01 = :ref:`spi_engine spi-configuration-register`. + - 2'b10 = :ref:`spi_engine dynamic-transfer-length-register`. + - 2'b11 = :ref:`spi_engine spi-lane-mask-register`. * - v - Value - New value for the configuration register. @@ -133,17 +233,44 @@ of the SPI Engine execution module with a new value. Synchronize Instruction ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -== == == == == == = = = = = = = = = = -15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 -== == == == == == = = = = = = = = = = -0 0 1 1 0 0 0 0 n n n n n n n n -== == == == == == = = = = = = = = = = +.. list-table:: + :header-rows: 1 -The synchronize instruction generates a synchronization event on the SYNC output -stream. This can be used to monitor the progress of the command stream. The -synchronize instruction is also used by the :ref:`spi_engine interconnect` -module to identify the end of a transaction and re-start the arbitration -process. + * - 15 + - 14 + - 13 + - 12 + - 11 + - 10 + - 9 + - 8 + - 7 + - 6 + - 5 + - 4 + - 3 + - 2 + - 1 + - 0 + * - rv + - 0 + - 1 + - 1 + - rv + - rv + - 0 + - 0 + - v + - v + - v + - v + - v + - v + - v + - v + +The synchronize instruction generates a synchronization event on the SYNC +output stream. This can be used to monitor the progress of the command stream. .. list-table:: :widths: 10 15 75 @@ -152,24 +279,57 @@ process. * - Bits - Name - Description - * - n + * - rv + - Reserved + - Reserved bit. Must always be set to 0. + * - v - id - Value of the generated synchronization event. Sleep Instruction ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -== == == == == == = = = = = = = = = = -15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 -== == == == == == = = = = = = = = = = -0 0 1 1 0 0 0 1 t t t t t t t t -== == == == == == = = = = = = = = = = +.. list-table:: + :header-rows: 1 + + * - 15 + - 14 + - 13 + - 12 + - 11 + - 10 + - 9 + - 8 + - 7 + - 6 + - 5 + - 4 + - 3 + - 2 + - 1 + - 0 + * - rv + - 0 + - 1 + - 1 + - rv + - rv + - 0 + - 1 + - t + - t + - t + - t + - t + - t + - t + - t The sleep instruction stops the execution of the command stream for the -specified amount of time. The time is based on the external clock frequency the -configuration value of the prescaler register and the time parameter of the -instruction. A fixed delay of two clock cycles (fast, not affected by the prescaler) -is the minimum, needed by the internal logic. +specified amount of time. The sleep time relies on the external clock +frequency, the configuration value of the prescaler register, and the time +parameter of the instruction. Also, a 2 clock-cycle delay is required for +internal logic (fast clock, not prescaled). .. math:: @@ -182,38 +342,71 @@ is the minimum, needed by the internal logic. * - Bits - Name - Description + * - rv + - Reserved + - Reserved bit. Must always be set to 0. * - t - Time - - The amount of prescaler cycles to wait, minus one. + - The amount of prescaler cycles to wait minus one. .. _spi_engine cs-invert-mask-instruction: CS Invert Mask Instruction ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -== == == == == == = = = = = = = = = = -15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 -== == == == == == = = = = = = = = = = -0 1 0 0 r r r r m m m m m m m m -== == == == == == = = = = = = = = = = +.. list-table:: + :header-rows: 1 -The CS Invert Mask Instructions allows the user to select on a per-pin basis + * - 15 + - 14 + - 13 + - 12 + - 11 + - 10 + - 9 + - 8 + - 7 + - 6 + - 5 + - 4 + - 3 + - 2 + - 1 + - 0 + * - rv + - 1 + - 0 + - 0 + - rv + - rv + - rv + - rv + - m + - m + - m + - m + - m + - m + - m + - m + +The CS Invert Mask Instruction allows the user to select on a per-pin basis whether the Chip Select will be active-low (default) or active-high (inverted). -Note that the Chip-Select instructions should remain the same because the value -of CS is inverted at the output register, and additional logic (e.g. reset -counters) occurs when the CS active state is asserted. -Since the physical values on the pins are inverted at the output, the current -Invert Mask does not affect the use of the :ref:`spi_engine cs-instruction`. As -an example, a Chip-Select Instruction with the 's' field equal to 0xFE will +.. note:: + Chip-Select instruction must remain the same since the value of CS is inverted at + the output register. So, current Invert Mask does not affect the use of the + :ref:`spi_engine cs-instruction`. Additional logic (e.g. reset counters) + occurs when the CS active state is asserted. + +For example, a Chip-Select Instruction with the 's' field equal to 0xFE will always result in only CS[0] being active. For an Invert Mask of 0xFF, this would result on only CS[0] being high. For an Invert Mask of 0x00, this would result on only CS[0] being low. For an Invert Mask of 0x01, this would result on all CS pins being high, but only CS[0] is active in this case (since it's the only one currently treated as active-high). -This was introduced in -version 1.02.00 of the core. +**This was introduced in version 1.02.00 of the core.** .. list-table:: :widths: 10 15 75 @@ -222,7 +415,7 @@ version 1.02.00 of the core. * - Bits - Name - Description - * - r + * - rv - reserved - Reserved for future use. Must always be set to 0. * - m @@ -235,15 +428,15 @@ version 1.02.00 of the core. Configuration Registers -------------------------------------------------------------------------------- -The SPI Engine execution module has a set of 8-bit configuration registers which -can be used to dynamically modify the behavior of the module at runtime. +The SPI Engine execution module has a set of 8-bit configuration registers +which can be used to dynamically modify the behavior of the module at runtime. .. _spi_engine spi-configuration-register: SPI Configuration Register ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The SPI configuration register configures various aspects of the low-level SPI +The SPI configuration register configures several aspects of the low-level SPI bus behavior. .. list-table:: @@ -265,29 +458,60 @@ bus behavior. - Configures the output of the three_wire pin. * - [1] - CPOL - - Configures the polarity of the SCLK signal. When 0, the idle state of - the SCLK signal is low. When 1, the idle state of the SCLK signal is - high. + - Configures the polarity of the SCLK signal: + + - When 0, the idle state of the SCLK signal is low. + - When 1, the idle state of the SCLK signal is high. * - [0] - CPHA - - Configures the phase of the SCLK signal. When 0, data is sampled on the - leading edge and updated on the trailing edge. When 1, data is - sampled on the trailing edge and updated on the leading edge. + - Configures the phase of the SCLK signal: + + - When 0, data is sampled on the leading edge and updated on the + trailing edge. + - When 1, data is sampled on the trailing edge and updated on the + leading edge. + + +.. _spi_engine spi-lane-mask-register: + +SPI Lane Mask Register +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This register configures the mask that defines which lanes are active +(active-high). The user must define a mask that contains up to ``NUM_OF_SDI`` +lanes (the number of activated lanes cannot be bigger than the numer of lanes). +For now, it is possible to have up to 8 lanes due to the instruction size. + +.. list-table:: + :widths: 10 15 50 + :header-rows: 1 + + * - Bits + - Name + - Description + * - [7:0] + - Lane mask + - Only bits set to 1 have their respective lane active. .. _spi_engine prescaler-configuration-register: Prescaler Configuration Register ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The prescaler configuration register configures the divider that is applied to +The prescaler configuration register defines the divider that is applied to the module clock when generating the SCLK signal and other internal control -signals used by the sleep and chip-select commands. +signals used by the sleep and chip-select instructions. -===== ==== ======================= -Bits Name Description -===== ==== ======================= -[7:0] div Prescaler clock divider -===== ==== ======================= +.. list-table:: + :widths: 10 15 30 + :header-rows: 1 + + * - Bits + - Name + - Description + * - [7:0] + - Div + - Prescaler clock divider. The default value of div is 0. The frequency of the SCLK signal is derived from the module clock frequency using the following formula: @@ -296,9 +520,6 @@ using the following formula: f\_{sclk} = \frac{f_{clk}}{((div + 1) * 2)} - -If no prescaler block is present div is 0. - .. _spi_engine dynamic-transfer-length-register: Dynamic Transfer Length Register @@ -306,12 +527,17 @@ Dynamic Transfer Length Register The dynamic transfer length register sets the length (in bits) of a transfer. By default, the transfer length is equal to the DATA_WIDTH of the execution module. -If required the user can reduce this length by setting this register. A general -rule of thumb is to set the DATA_WIDTH parameter to the largest transfer length -supported by the target device. - -===== ==== ======================= -Bits Name Description -===== ==== ======================= -[7:0] div Dynamic transfer length -===== ==== ======================= +If required, the user can reduce this length by setting this register. A +general rule of thumb is to set the DATA_WIDTH parameter to be the largest +transfer length supported by the target device. + +.. list-table:: + :widths: 10 15 30 + :header-rows: 1 + + * - Bits + - Name + - Description + * - [7:0] + - Div + - Dynamic transfer length. diff --git a/docs/library/spi_engine/spi_engine.svg b/docs/library/spi_engine/spi_engine.svg index 0dc20a28b8e..31ee134b6a1 100644 --- a/docs/library/spi_engine/spi_engine.svg +++ b/docs/library/spi_engine/spi_engine.svg @@ -1,125 +1,894 @@ - + - - - - - - - - - - - - - image/svg+xml - - - - - - - - sdi - sdo - - - Data Shift Register - - - SDI - - - - - - - - SDO - - - - - + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + sdi + sdo + + + Data Shift Register - - - - - MultifunctionCounter and CompareUnit - - - - Prescaler - - - - clk - - trigger - - sclk - - Instruction Decoder - - - - - - - CMD - - - - chip-select - - - cs - - CE - - D - Q - - - SYNC - - - - - - - - - - - - - - - Clock Gen - - - - CE - three_wire - - Config - - - - - - - - - - - + + SDI + + + + + + + + SDO + + + + + + + + + + offload active + + + + + + + + + + + MultifunctionCounter and CompareUnit + + + + Prescaler + + + + clk + + trigger + + sclk + + Instruction Decoder + + + + + + + CMD + + + + chip-select + + + cs + + CE + + D + Q + + + SYNC + + + + + + + + + + + + + + + Clock Gen + + + + CE + three_wire + + Config + + + + + + + + + + + + + cphacpol + sleep_compare + + active - cphacpol - sleep_compare - - active - diff --git a/docs/library/spi_engine/spi_engine_execution.rst b/docs/library/spi_engine/spi_engine_execution.rst index b8dcc524bf9..d0a8ab2198a 100644 --- a/docs/library/spi_engine/spi_engine_execution.rst +++ b/docs/library/spi_engine/spi_engine_execution.rst @@ -59,6 +59,8 @@ Signal and Interface Pins - :ref:`spi_engine control-interface` subordinate. SPI Engine Control stream that contains commands and data for the execution module. + * - s_offload_active_ctrl + - | Defines whether offload mode is active or not (active high). * - spi - :ref:`spi_engine spi-bus-interface` controller. Low-level SPI bus interface that is controlled by peripheral. @@ -80,6 +82,11 @@ that translates the incoming commands into an internal control signal, a multi-function counter and compares unit that is responsible for handling the timing and a shift register which holds the received and transmitted SPI data. +The shift register has a different behavior for offload and FIFO mode. Offload +mode needs to have all of its lanes active for allowing prefetch of data, +otherwise it is going to wait for the write instruction. That is controlled +through the ``s_offload_active_ctrl`` interface. + The module has an optional programmable pre-scaler register that can be used to divide the external clock to the counter and compare unit. diff --git a/docs/library/spi_engine/spi_engine_interconnect.rst b/docs/library/spi_engine/spi_engine_interconnect.rst index e9e1e14a87c..94b223ad5f2 100644 --- a/docs/library/spi_engine/spi_engine_interconnect.rst +++ b/docs/library/spi_engine/spi_engine_interconnect.rst @@ -8,14 +8,14 @@ SPI Engine Interconnect Module The :git-hdl:`SPI Engine Interconnect ` allows connecting multiple :ref:`spi_engine control-interface` managers to a single :ref:`spi_engine control-interface` subordinate. -This enables multiple command stream generators to connect to a single -:ref:`spi_engine execution` and consequential give them access to the same SPI bus. -The interconnect modules take care of properly arbitrating between the different -command streams. +This enables two command stream generators to connect to a single +:ref:`spi_engine execution` and consequentially give them access to the same SPI bus. +The interconnect module is responsible for proper arbitration between the command +streams. -Combining multiple command stream generators in a design and connecting them to -a single execution module allows for the creation of flexible and efficient -designs using standard components. +Combining two command stream generators in a design and connecting them to a single +execution module allows the creation of an efficient and flexible design by using +standard components. Files -------------------------------------------------------------------------------- @@ -50,33 +50,33 @@ Signal and Interface Pins .. hdl-interfaces:: * - clk - - A signals of the module are synchronous to this clock. + - | * - resetn - - Synchronous active-low reset. - Resets the internal state of the module. + - | Synchronous active-low reset. + | Resets the internal state of the module. * - s0_ctrl - | :ref:`spi_engine control-interface` subordinate. - | Connects to the first control interface manager. + | Connects to the offload control interface manager. * - s1_ctrl - | :ref:`spi_engine control-interface` subordinate. - | Connects to the second control interface manager. + | Connects to the fifo control interface manager. * - m_ctrl - | :ref:`spi_engine control-interface` manager. | Connects to the control interface subordinate. + * - s_interconnect_ctrl + - | m_interconnect_ctrl (:ref:`spi_engine offload`) subordinate. + | Defines whether offload mode is active or not (active high). + * - m_offload_active_ctrl + - | Forwards the signals of the s_interconnect_ctrl interface. + | Defines whether offload mode is active or not (active high). Theory of Operation -------------------------------------------------------------------------------- -The SPI Engine Interconnect module has multiple -:ref:`spi_engine control-interface` subordinate ports and a single -:ref:`spi_engine control-interface` manager port. -It can be used to connect multiple command stream generators to a single command -execution engine. Arbitration between the streams is done on a priority -basis, streams with a lower index have higher priority. This means if commands -are present on two streams arbitration will be granted to the one with the lower -index. Once arbitration has been granted the port it was granted to stays in -control until it sends a SYNC command. When the interconnect module sees a SYNC -command arbitration will be re-evaluated after the SYNC command has been -completed. This makes sure that once a SPI transaction consisting of multiple -commands has been started it is able to complete without being interrupted by a -higher priority stream. +The SPI Engine Interconnect module has two :ref:`spi_engine control-interface` +subordinate ports and a single :ref:`spi_engine control-interface` manager +port. It can be used to connect two command stream generators to a single +command execution engine. Arbitration between streams is done based on the +``s_interconnect_ctrl`` interface, there is a copy of this interface to +``m_offload_active_ctrl`` to indicate whether the stream belongs to offload (s0) or +fifo mode (s1). \ No newline at end of file diff --git a/docs/library/spi_engine/spi_engine_offload.rst b/docs/library/spi_engine/spi_engine_offload.rst index 37b1058a909..61256932979 100644 --- a/docs/library/spi_engine/spi_engine_offload.rst +++ b/docs/library/spi_engine/spi_engine_offload.rst @@ -80,3 +80,5 @@ Signal and Interface Pins * - s_axis_sdo - Streaming AXI peripheral Input stream for SPI data to be sent. Only present when ``SDO_STREAMING`` parameter is set to 1. + * - m_interconnect_ctrl + - | Defines whether offload mode is active or not (active high). diff --git a/docs/regmap/adi_regmap_spi_engine.txt b/docs/regmap/adi_regmap_spi_engine.txt index 5cd86d6f9ef..bccf7e641df 100644 --- a/docs/regmap/adi_regmap_spi_engine.txt +++ b/docs/regmap/adi_regmap_spi_engine.txt @@ -9,7 +9,7 @@ ENDTITLE REG 0x00 VERSION -Version of the peripheral. Follows semantic versioning. Current version 1.04.02. +Version of the peripheral. Follows semantic versioning. Current version 1.06.00. ENDREG FIELD @@ -19,13 +19,13 @@ RO ENDFIELD FIELD -[15:8] 0x00000005 +[15:8] 0x00000006 VERSION_MINOR RO ENDFIELD FIELD -[7:0] 0x00000001 +[7:0] 0x00000000 VERSION_PATCH RO ENDFIELD @@ -369,21 +369,6 @@ ENDFIELD ############################################################################################ ############################################################################################ -REG -0x3b -SDI_FIFO_MSB -ENDREG - -FIELD -[31:0] -SDI_FIFO_MSB -RO -Store SDI's 32 bits MSB, if exists. -ENDFIELD - -############################################################################################ -############################################################################################ - REG 0x3c SDI_FIFO_PEEK diff --git a/library/spi_engine/axi_spi_engine/Makefile b/library/spi_engine/axi_spi_engine/Makefile index 19d442d8ab7..9067ac74b04 100644 --- a/library/spi_engine/axi_spi_engine/Makefile +++ b/library/spi_engine/axi_spi_engine/Makefile @@ -20,6 +20,7 @@ XILINX_DEPS += ../../spi_engine/interfaces/spi_engine_offload_ctrl.xml XILINX_DEPS += ../../spi_engine/interfaces/spi_engine_offload_ctrl_rtl.xml XILINX_LIB_DEPS += util_axis_fifo +XILINX_LIB_DEPS += util_axis_fifo_asym XILINX_LIB_DEPS += util_cdc XILINX_INTERFACE_DEPS += spi_engine/interfaces diff --git a/library/spi_engine/axi_spi_engine/axi_spi_engine.v b/library/spi_engine/axi_spi_engine/axi_spi_engine.v index 378b982007a..ad58e39212d 100644 --- a/library/spi_engine/axi_spi_engine/axi_spi_engine.v +++ b/library/spi_engine/axi_spi_engine/axi_spi_engine.v @@ -106,11 +106,11 @@ module axi_spi_engine #( input sdo_data_ready, output sdo_data_valid, - output [(DATA_WIDTH-1):0] sdo_data, + output [(DATA_WIDTH)-1:0] sdo_data, output sdi_data_ready, input sdi_data_valid, - input [(NUM_OF_SDI * DATA_WIDTH-1):0] sdi_data, + input [(NUM_OF_SDI * DATA_WIDTH)-1:0] sdi_data, output sync_ready, input sync_valid, @@ -122,7 +122,7 @@ module axi_spi_engine #( output [15:0] offload0_cmd_wr_data, output offload0_sdo_wr_en, - output [(DATA_WIDTH-1):0] offload0_sdo_wr_data, + output [(DATA_WIDTH)-1:0] offload0_sdo_wr_data, output offload0_mem_reset, output offload0_enable, @@ -133,7 +133,7 @@ module axi_spi_engine #( input [7:0] offload_sync_data ); - localparam PCORE_VERSION = 'h010501; + localparam PCORE_VERSION = 'h010600; localparam S_AXI = 0; localparam UP_FIFO = 1; @@ -152,16 +152,15 @@ module axi_spi_engine #( wire sdo_fifo_almost_empty; wire up_sdo_fifo_almost_empty; - wire [(DATA_WIDTH-1):0] sdo_fifo_in_data; + wire [DATA_WIDTH-1:0] sdo_fifo_in_data; wire sdo_fifo_in_ready; wire sdo_fifo_in_valid; - wire sdi_fifo_out_data_msb_s; - wire [SDI_FIFO_ADDRESS_WIDTH-1:0] sdi_fifo_level; + wire [31:0] sdi_fifo_level; wire sdi_fifo_almost_full; wire up_sdi_fifo_almost_full; - wire [(NUM_OF_SDI * DATA_WIDTH-1):0] sdi_fifo_out_data; + wire [DATA_WIDTH-1:0] sdi_fifo_out_data; wire sdi_fifo_out_ready; wire sdi_fifo_out_valid; @@ -325,15 +324,6 @@ module axi_spi_engine #( end end - generate - if (NUM_OF_SDI > 1) begin - // Only the first two SDI data can be recovered through AXI regmap - assign sdi_fifo_out_data_msb_s = sdi_fifo_out_data[DATA_WIDTH+:DATA_WIDTH]; - end else begin - assign sdi_fifo_out_data_msb_s = sdi_fifo_out_data; - end - endgenerate - reg [7:0] offload_sdo_mem_address_width = OFFLOAD0_SDO_MEM_ADDRESS_WIDTH; reg [7:0] offload_cmd_mem_address_width = OFFLOAD0_CMD_MEM_ADDRESS_WIDTH; reg [7:0] sdi_fifo_address_width = SDI_FIFO_ADDRESS_WIDTH; @@ -356,10 +346,9 @@ module axi_spi_engine #( 8'h31: up_rdata_ff <= offload_sync_id; 8'h34: up_rdata_ff <= cmd_fifo_room; 8'h35: up_rdata_ff <= sdo_fifo_room; - 8'h36: up_rdata_ff <= (sdi_fifo_out_valid == 1) ? sdi_fifo_level + 1 : sdi_fifo_level; /* beacuse of first-word-fall-through */ + 8'h36: up_rdata_ff <= (sdi_fifo_out_valid == 1) ? sdi_fifo_level + 1 : sdi_fifo_level; /* because of first-word-fall-through */ 8'h3a: up_rdata_ff <= sdi_fifo_out_data[DATA_WIDTH-1:0]; - 8'h3b: up_rdata_ff <= sdi_fifo_out_data_msb_s; /* store SDI's 32 bits MSB, if exists */ - 8'h3c: up_rdata_ff <= sdi_fifo_out_data; /* PEEK register */ + 8'h3c: up_rdata_ff <= sdi_fifo_out_data[DATA_WIDTH-1:0]; /* PEEK register */ 8'h40: up_rdata_ff <= {offload0_enable_reg}; 8'h41: up_rdata_ff <= {offload0_enabled_s}; 8'h80: up_rdata_ff <= CFG_INFO_0; @@ -421,7 +410,9 @@ module axi_spi_engine #( .ASYNC_CLK(ASYNC_SPI_CLK), .M_AXIS_REGISTERED(0), .ALMOST_EMPTY_THRESHOLD(1), - .ALMOST_FULL_THRESHOLD(1) + .ALMOST_FULL_THRESHOLD(1), + .TLAST_EN(0), + .TKEEP_EN(0) ) i_cmd_fifo ( .s_axis_aclk(clk), .s_axis_aresetn(up_sw_resetn), @@ -430,22 +421,25 @@ module axi_spi_engine #( .s_axis_data(cmd_fifo_in_data), .s_axis_room(cmd_fifo_room), .s_axis_tlast(1'b0), + .s_axis_tkeep(), .s_axis_full(), .s_axis_almost_full(), + .m_axis_aclk(spi_clk), .m_axis_aresetn(spi_resetn), .m_axis_ready(cmd_ready), .m_axis_valid(cmd_valid), .m_axis_data(cmd_data), .m_axis_tlast(), + .m_axis_tkeep(), + .m_axis_level(), .m_axis_empty(), - .m_axis_almost_empty(cmd_fifo_almost_empty), - .m_axis_level()); + .m_axis_almost_empty(cmd_fifo_almost_empty)); assign sdo_fifo_in_valid = up_wreq_s == 1'b1 && up_waddr_s == 8'h39; - assign sdo_fifo_in_data = up_wdata_s[(DATA_WIDTH-1):0]; + assign sdo_fifo_in_data = up_wdata_s[DATA_WIDTH-1:0]; - util_axis_fifo #( + util_axis_fifo #( .DATA_WIDTH(DATA_WIDTH), .ASYNC_CLK(ASYNC_SPI_CLK), .ADDRESS_WIDTH(SDO_FIFO_ADDRESS_WIDTH), @@ -460,6 +454,7 @@ module axi_spi_engine #( .s_axis_data(sdo_fifo_in_data), .s_axis_room(sdo_fifo_room), .s_axis_tlast(1'b0), + .s_axis_tkeep(), .s_axis_full(), .s_axis_almost_full(), .m_axis_aclk(spi_clk), @@ -468,20 +463,25 @@ module axi_spi_engine #( .m_axis_valid(sdo_data_valid), .m_axis_data(sdo_data), .m_axis_tlast(), + .m_axis_tkeep(), .m_axis_level(), .m_axis_empty(), .m_axis_almost_empty(sdo_fifo_almost_empty)); assign sdi_fifo_out_ready = up_rreq_s == 1'b1 && up_raddr_s == 8'h3a; - util_axis_fifo #( - .DATA_WIDTH(NUM_OF_SDI * DATA_WIDTH), + util_axis_fifo_asym #( .ASYNC_CLK(ASYNC_SPI_CLK), - .ADDRESS_WIDTH(SDI_FIFO_ADDRESS_WIDTH), + .S_DATA_WIDTH(NUM_OF_SDI * DATA_WIDTH), + .M_DATA_WIDTH(DATA_WIDTH), + .ADDRESS_WIDTH(SDO_FIFO_ADDRESS_WIDTH), .M_AXIS_REGISTERED(0), .ALMOST_EMPTY_THRESHOLD(1), - .ALMOST_FULL_THRESHOLD(31) - ) i_sdi_fifo ( + .ALMOST_FULL_THRESHOLD(1), + .TLAST_EN(0), + .TKEEP_EN(0), + .REDUCED_FIFO(0) + ) i_sdi_fifo( .s_axis_aclk(spi_clk), .s_axis_aresetn(spi_resetn), .s_axis_ready(sdi_data_ready), @@ -489,14 +489,17 @@ module axi_spi_engine #( .s_axis_data(sdi_data), .s_axis_room(), .s_axis_tlast(), + .s_axis_tkeep(), .s_axis_full(), .s_axis_almost_full(sdi_fifo_almost_full), + .m_axis_aclk(clk), .m_axis_aresetn(up_sw_resetn), .m_axis_ready(sdi_fifo_out_ready), .m_axis_valid(sdi_fifo_out_valid), .m_axis_data(sdi_fifo_out_data), .m_axis_tlast(), + .m_axis_tkeep(), .m_axis_level(sdi_fifo_level), .m_axis_empty(), .m_axis_almost_empty()); @@ -508,7 +511,9 @@ module axi_spi_engine #( .DATA_WIDTH(8), .ASYNC_CLK(ASYNC_SPI_CLK), .ADDRESS_WIDTH(SYNC_FIFO_ADDRESS_WIDTH), - .M_AXIS_REGISTERED(0) + .M_AXIS_REGISTERED(0), + .TLAST_EN(0), + .TKEEP_EN(0) ) i_sync_fifo ( .s_axis_aclk(spi_clk), .s_axis_aresetn(spi_resetn), @@ -516,14 +521,21 @@ module axi_spi_engine #( .s_axis_valid(sync_valid), .s_axis_data(sync_data), .s_axis_room(), + .s_axis_tlast(), + .s_axis_tkeep(), .s_axis_full(), + .s_axis_almost_full(), + .m_axis_aclk(clk), .m_axis_aresetn(up_sw_resetn), .m_axis_ready(1'b1), .m_axis_valid(sync_fifo_valid), .m_axis_data(sync_fifo_data), + .m_axis_tlast(), + .m_axis_tkeep(), .m_axis_level(), - .m_axis_empty()); + .m_axis_empty(), + .m_axis_almost_empty()); // synchronization FIFO for the offload command interface wire up_offload0_cmd_wr_en_s; @@ -533,7 +545,9 @@ module axi_spi_engine #( .DATA_WIDTH(16), .ASYNC_CLK(ASYNC_SPI_CLK), .ADDRESS_WIDTH(SYNC_FIFO_ADDRESS_WIDTH), - .M_AXIS_REGISTERED(0) + .M_AXIS_REGISTERED(0), + .TLAST_EN(0), + .TKEEP_EN(0) ) i_offload_cmd_fifo ( .s_axis_aclk(clk), .s_axis_aresetn(up_sw_resetn), @@ -541,27 +555,36 @@ module axi_spi_engine #( .s_axis_valid(up_offload0_cmd_wr_en_s), .s_axis_data(up_offload0_cmd_wr_data_s), .s_axis_room(), + .s_axis_tlast(), + .s_axis_tkeep(), .s_axis_full(), + .s_axis_almost_full(), + .m_axis_aclk(spi_clk), .m_axis_aresetn(spi_resetn), .m_axis_ready(1'b1), .m_axis_valid(offload0_cmd_wr_en), .m_axis_data(offload0_cmd_wr_data), + .m_axis_tlast(), + .m_axis_tkeep(), .m_axis_level(), - .m_axis_empty()); + .m_axis_empty(), + .m_axis_almost_empty()); assign up_offload0_cmd_wr_en_s = up_wreq_s == 1'b1 && up_waddr_s == 8'h44; assign up_offload0_cmd_wr_data_s = up_wdata_s[15:0]; // synchronization FIFO for the offload SDO interface wire up_offload0_sdo_wr_en_s; - wire [DATA_WIDTH-1:0] up_offload0_sdo_wr_data_s; + wire [(DATA_WIDTH-1):0] up_offload0_sdo_wr_data_s; util_axis_fifo #( .DATA_WIDTH(DATA_WIDTH), .ASYNC_CLK(ASYNC_SPI_CLK), .ADDRESS_WIDTH(SYNC_FIFO_ADDRESS_WIDTH), - .M_AXIS_REGISTERED(0) + .M_AXIS_REGISTERED(0), + .TLAST_EN(0), + .TKEEP_EN(0) ) i_offload_sdo_fifo ( .s_axis_aclk(clk), .s_axis_aresetn(up_sw_resetn), @@ -569,14 +592,21 @@ module axi_spi_engine #( .s_axis_valid(up_offload0_sdo_wr_en_s), .s_axis_data(up_offload0_sdo_wr_data_s), .s_axis_room(), + .s_axis_tlast(), + .s_axis_tkeep(), .s_axis_full(), + .s_axis_almost_full(), + .m_axis_aclk(spi_clk), .m_axis_aresetn(spi_resetn), .m_axis_ready(1'b1), .m_axis_valid(offload0_sdo_wr_en), .m_axis_data(offload0_sdo_wr_data), + .m_axis_tlast(), + .m_axis_tkeep(), .m_axis_level(), - .m_axis_empty()); + .m_axis_empty(), + .m_axis_almost_empty()); assign up_offload0_sdo_wr_en_s = up_wreq_s == 1'b1 && up_waddr_s == 8'h45; assign up_offload0_sdo_wr_data_s = up_wdata_s[DATA_WIDTH-1:0]; @@ -586,7 +616,9 @@ module axi_spi_engine #( .DATA_WIDTH(8), .ASYNC_CLK(ASYNC_SPI_CLK), .ADDRESS_WIDTH(SYNC_FIFO_ADDRESS_WIDTH), - .M_AXIS_REGISTERED(0) + .M_AXIS_REGISTERED(0), + .TLAST_EN(0), + .TKEEP_EN(0) ) i_offload_sync_fifo ( .s_axis_aclk(spi_clk), .s_axis_aresetn(spi_resetn), @@ -594,14 +626,21 @@ module axi_spi_engine #( .s_axis_valid(offload_sync_valid), .s_axis_data(offload_sync_data), .s_axis_room(), + .s_axis_tlast(), + .s_axis_tkeep(), .s_axis_full(), + .s_axis_almost_full(), + .m_axis_aclk(clk), .m_axis_aresetn(up_sw_resetn), .m_axis_ready(1'b1), .m_axis_valid(offload_sync_fifo_valid), .m_axis_data(offload_sync_fifo_data), + .m_axis_tlast(), + .m_axis_tkeep(), .m_axis_level(), - .m_axis_empty()); + .m_axis_empty(), + .m_axis_almost_empty()); end else begin /* ASYNC_SPI_CLK == 0 */ diff --git a/library/spi_engine/axi_spi_engine/axi_spi_engine_ip.tcl b/library/spi_engine/axi_spi_engine/axi_spi_engine_ip.tcl index b775c377c78..bdf4497fc94 100644 --- a/library/spi_engine/axi_spi_engine/axi_spi_engine_ip.tcl +++ b/library/spi_engine/axi_spi_engine/axi_spi_engine_ip.tcl @@ -24,6 +24,7 @@ adi_ip_ttcl axi_spi_engine "axi_spi_engine_constr.ttcl" adi_ip_add_core_dependencies [list \ analog.com:$VIVADO_IP_LIBRARY:util_axis_fifo:1.0 \ analog.com:$VIVADO_IP_LIBRARY:util_cdc:1.0 \ + analog.com:$VIVADO_IP_LIBRARY:util_axis_fifo_asym:1.0 \ ] set_property company_url {https://wiki.analog.com/resources/fpga/peripherals/spi_engine/axi} [ipx::current_core] diff --git a/library/spi_engine/scripts/spi_engine.tcl b/library/spi_engine/scripts/spi_engine.tcl index 0b22305e30a..0413a5674fe 100644 --- a/library/spi_engine/scripts/spi_engine.tcl +++ b/library/spi_engine/scripts/spi_engine.tcl @@ -71,6 +71,7 @@ proc spi_engine_create {{name "spi_engine"} {data_width 32} {async_spi_clk 1} {n ad_connect $offload/trigger trigger ad_connect $execution/spi m_spi + ad_connect $interconnect/m_offload_active_ctrl $execution/s_offload_active_ctrl if {$sdo_streaming == 1} { ad_connect $offload/s_axis_sdo s_axis_sample diff --git a/library/spi_engine/spi_engine_execution/Makefile b/library/spi_engine/spi_engine_execution/Makefile index 34b7f1e7039..a1239d2354e 100644 --- a/library/spi_engine/spi_engine_execution/Makefile +++ b/library/spi_engine/spi_engine_execution/Makefile @@ -8,6 +8,7 @@ LIBRARY_NAME := spi_engine_execution GENERIC_DEPS += spi_engine_execution.v GENERIC_DEPS += spi_engine_execution_shiftreg.v +GENERIC_DEPS += spi_engine_execution_shiftreg_data_assemble.v XILINX_DEPS += spi_engine_execution_constr.ttcl XILINX_DEPS += spi_engine_execution_ip.tcl @@ -16,6 +17,8 @@ XILINX_DEPS += ../../spi_engine/interfaces/spi_engine.xml XILINX_DEPS += ../../spi_engine/interfaces/spi_engine_ctrl.xml XILINX_DEPS += ../../spi_engine/interfaces/spi_engine_ctrl_rtl.xml XILINX_DEPS += ../../spi_engine/interfaces/spi_engine_rtl.xml +XILINX_DEPS += ../../spi_engine/interfaces/spi_engine_interconnect_ctrl.xml +XILINX_DEPS += ../../spi_engine/interfaces/spi_engine_interconnect_ctrl_rtl.xml XILINX_INTERFACE_DEPS += spi_engine/interfaces diff --git a/library/spi_engine/spi_engine_execution/spi_engine_execution.v b/library/spi_engine/spi_engine_execution/spi_engine_execution.v index f64c1155f55..7958a860b7f 100644 --- a/library/spi_engine/spi_engine_execution/spi_engine_execution.v +++ b/library/spi_engine/spi_engine_execution/spi_engine_execution.v @@ -48,6 +48,7 @@ module spi_engine_execution #( ) ( input clk, input resetn, + input s_offload_active, output reg active, @@ -57,7 +58,7 @@ module spi_engine_execution #( input sdo_data_valid, output sdo_data_ready, - input [(DATA_WIDTH-1):0] sdo_data, + input [(DATA_WIDTH)-1:0] sdo_data, input sdi_data_ready, output sdi_data_valid, @@ -69,7 +70,7 @@ module spi_engine_execution #( input echo_sclk, output reg sclk, - output reg sdo, + output reg [NUM_OF_SDI-1:0] sdo, output reg sdo_t, input [NUM_OF_SDI-1:0] sdi, output reg [NUM_OF_CS-1:0] cs, @@ -88,6 +89,8 @@ module spi_engine_execution #( localparam REG_CLK_DIV = 2'b00; localparam REG_CONFIG = 2'b01; localparam REG_WORD_LENGTH = 2'b10; + localparam REG_SPI_LANE_CONFIG = 2'b11; + localparam ALL_ACTIVE_LANE_MASK = (2 ** NUM_OF_SDI) - 1; //by default all lanes are enabled localparam BIT_COUNTER_WIDTH = DATA_WIDTH > 16 ? 5 : DATA_WIDTH > 8 ? 4 : 3; @@ -122,8 +125,7 @@ module spi_engine_execution #( reg [7:0] word_length = DATA_WIDTH; reg [7:0] last_bit_count = DATA_WIDTH-1; reg [7:0] left_aligned = 8'b0; - - assign first_bit = ((bit_counter == 'h0) || (bit_counter == word_length)); + reg [7:0] spi_lane_mask = ALL_ACTIVE_LANE_MASK; reg [15:0] cmd_d1; @@ -139,7 +141,7 @@ module spi_engine_execution #( wire sdo_enabled_io; wire sdi_enabled_io; - wire sdo_int_s; + wire [NUM_OF_SDI-1:0] sdo_int_s; wire last_bit; wire echo_last_bit; @@ -149,12 +151,13 @@ module spi_engine_execution #( wire [2:0] inst = cmd[14:12]; wire [2:0] inst_d1 = cmd_d1[14:12]; + //command decoder wire exec_cmd = cmd_ready && cmd_valid; wire exec_transfer_cmd = exec_cmd && inst == CMD_TRANSFER; - wire exec_cs_inv_cmd = exec_cmd && inst == CMD_CS_INV; - wire exec_write_cmd = exec_cmd && inst == CMD_WRITE; wire exec_chipselect_cmd = exec_cmd && inst == CMD_CHIPSELECT; + wire exec_write_cmd = exec_cmd && inst == CMD_WRITE; wire exec_misc_cmd = exec_cmd && inst == CMD_MISC; + wire exec_cs_inv_cmd = exec_cmd && inst == CMD_CS_INV; wire exec_sync_cmd = exec_misc_cmd && cmd[8] == MISC_SYNC; wire trigger_tx; @@ -173,17 +176,23 @@ module spi_engine_execution #( wire sdo_io_ready; (* direct_enable = "yes" *) wire cs_gen; + + assign first_bit = ((bit_counter == 'h0) || (bit_counter == word_length)); spi_engine_execution_shiftreg #( .DEFAULT_SPI_CFG(DEFAULT_SPI_CFG), + .ALL_ACTIVE_LANE_MASK(ALL_ACTIVE_LANE_MASK), .DATA_WIDTH(DATA_WIDTH), .NUM_OF_SDI(NUM_OF_SDI), .SDI_DELAY(SDI_DELAY), .ECHO_SCLK(ECHO_SCLK), - .CMD_TRANSFER(CMD_TRANSFER) + .CMD_TRANSFER(CMD_TRANSFER), + .CMD_WRITE(CMD_WRITE), + .REG_SPI_LANE_CONFIG(REG_SPI_LANE_CONFIG) ) shiftreg ( .clk(clk), .resetn(resetn), + .s_offload_active(s_offload_active), .sdi(sdi), .sdo_int(sdo_int_s), .echo_sclk(echo_sclk), @@ -199,6 +208,7 @@ module spi_engine_execution #( .sdo_idle_state(sdo_idle_state), .left_aligned(left_aligned), .word_length(word_length), + .spi_lane_mask(spi_lane_mask), .sdo_io_ready(sdo_io_ready), .echo_last_bit(echo_last_bit), .transfer_active(transfer_active), @@ -223,8 +233,9 @@ module spi_engine_execution #( assign sdi_enabled_io = (exec_transfer_cmd) ? cmd[9] : sdi_enabled; always @(posedge clk) begin - if (cmd_ready & cmd_valid) - cmd_d1 <= cmd; + if (cmd_ready & cmd_valid) begin + cmd_d1 <= cmd; + end end always @(posedge clk) begin @@ -249,6 +260,7 @@ module spi_engine_execution #( clk_div <= DEFAULT_CLK_DIV; word_length <= DATA_WIDTH; left_aligned <= 8'b0; + spi_lane_mask <= ALL_ACTIVE_LANE_MASK; end else if (exec_write_cmd == 1'b1) begin if (cmd[9:8] == REG_CONFIG) begin cpha <= cmd[0]; @@ -261,6 +273,8 @@ module spi_engine_execution #( // the max value of this reg must be DATA_WIDTH word_length <= cmd[7:0]; left_aligned <= DATA_WIDTH - cmd[7:0]; // needed 1 cycle before transfer_active goes high + end else if (cmd[9:8] == REG_SPI_LANE_CONFIG) begin + spi_lane_mask <= cmd[7:0]; //max number of spi lanes == 8 end end end @@ -403,9 +417,9 @@ module spi_engine_execution #( assign sync = cmd_d1[7:0]; assign io_ready1 = (sdi_data_valid == 1'b0 || sdi_data_ready == 1'b1) && - (sdo_enabled_io == 1'b0 || sdo_io_ready == 1'b1); + (sdo_enabled_io == 1'b0 || sdo_io_ready == 1'b1); assign io_ready2 = (sdi_enabled == 1'b0 || sdi_data_ready == 1'b1) && - (sdo_enabled_io == 1'b0 || last_transfer == 1'b1 || sdo_io_ready == 1'b1); + (sdo_enabled_io == 1'b0 || last_transfer == 1'b1 || sdo_io_ready == 1'b1); always @(posedge clk) begin if (idle == 1'b1) begin @@ -468,11 +482,11 @@ module spi_engine_execution #( if (resetn == 1'b0) begin transfer_done <= 1'b0; end else begin - if (ECHO_SCLK) begin + if (ECHO_SCLK) begin transfer_done <= echo_last_bit && echo_last_transfer; - end else begin + end else begin transfer_done <= (wait_for_io && io_ready1 && last_transfer) || (!wait_for_io && transfer_active && end_of_word && last_transfer ); - end + end end end diff --git a/library/spi_engine/spi_engine_execution/spi_engine_execution_hw.tcl b/library/spi_engine/spi_engine_execution/spi_engine_execution_hw.tcl index 85385a42a0c..a7b709f532e 100644 --- a/library/spi_engine/spi_engine_execution/spi_engine_execution_hw.tcl +++ b/library/spi_engine/spi_engine_execution/spi_engine_execution_hw.tcl @@ -11,7 +11,8 @@ ad_ip_create spi_engine_execution {SPI Engine Execution} set_module_property ELABORATION_CALLBACK p_elaboration ad_ip_files spi_engine_execution [list\ spi_engine_execution.v \ - spi_engine_execution_shiftreg.v] + spi_engine_execution_shiftreg.v \ + spi_engine_execution_shiftreg_data_assemble.v] # parameters @@ -36,6 +37,13 @@ proc p_elaboration {} { ad_interface signal active output 1 + # interconnect direction interface + + add_interface s_interconnect_ctrl conduit end + add_interface_port s_interconnect_ctrl s_interconnect_dir interconnect_dir input 1 + set_interface_property s_interconnect_ctrl associatedClock if_clk + set_interface_property s_interconnect_ctrl associatedReset if_resetn + # command interface add_interface cmd axi4stream end diff --git a/library/spi_engine/spi_engine_execution/spi_engine_execution_ip.tcl b/library/spi_engine/spi_engine_execution/spi_engine_execution_ip.tcl index 5b625ae4a89..05257e8751e 100644 --- a/library/spi_engine/spi_engine_execution/spi_engine_execution_ip.tcl +++ b/library/spi_engine/spi_engine_execution/spi_engine_execution_ip.tcl @@ -11,6 +11,7 @@ adi_ip_files spi_engine_execution [list \ "spi_engine_execution_constr.ttcl" \ "spi_engine_execution.v" \ "spi_engine_execution_shiftreg.v" \ + "spi_engine_execution_shiftreg_data_assemble.v" \ ] adi_ip_properties_lite spi_engine_execution @@ -42,6 +43,14 @@ adi_add_bus "ctrl" "slave" \ } adi_add_bus_clock "clk" "ctrl" "resetn" +adi_add_bus "s_offload_active_ctrl" "slave" \ + "analog.com:interface:spi_engine_interconnect_ctrl_rtl:1.0" \ + "analog.com:interface:spi_engine_interconnect_ctrl:1.0" \ + { \ + {"s_offload_active" "interconnect_dir"} \ + } +adi_add_bus_clock "clk" "s_offload_active_ctrl" "resetn" + adi_add_bus "spi" "master" \ "analog.com:interface:spi_engine_rtl:1.0" \ "analog.com:interface:spi_engine:1.0" \ diff --git a/library/spi_engine/spi_engine_execution/spi_engine_execution_shiftreg.v b/library/spi_engine/spi_engine_execution/spi_engine_execution_shiftreg.v index fd264a6423f..293d0600797 100644 --- a/library/spi_engine/spi_engine_execution/spi_engine_execution_shiftreg.v +++ b/library/spi_engine/spi_engine_execution/spi_engine_execution_shiftreg.v @@ -38,26 +38,32 @@ module spi_engine_execution_shiftreg #( parameter DEFAULT_SPI_CFG = 0, + parameter ALL_ACTIVE_LANE_MASK = 8'hFF, parameter DATA_WIDTH = 8, parameter NUM_OF_SDI = 1, parameter [1:0] SDI_DELAY = 2'b00, parameter ECHO_SCLK = 0, - parameter [2:0]CMD_TRANSFER = 3'b000 + parameter [2:0] CMD_TRANSFER = 3'b000, + parameter [2:0] CMD_WRITE = 3'b010, + parameter [1:0] REG_SPI_LANE_CONFIG = 2'b11 ) ( input clk, input resetn, + //interconnect interface + input s_offload_active, + // spi io input [NUM_OF_SDI-1:0] sdi, - output sdo_int, + output [NUM_OF_SDI-1:0] sdo_int, input echo_sclk, // spi data - input [(DATA_WIDTH-1):0] sdo_data, - input sdo_data_valid, - output sdo_data_ready, + input [(DATA_WIDTH)-1:0] sdo_data, + input sdo_data_valid, + output sdo_data_ready, - output [(NUM_OF_SDI * DATA_WIDTH-1):0] sdi_data, + output [(NUM_OF_SDI * DATA_WIDTH)-1:0] sdi_data, output reg sdi_data_valid, input sdi_data_ready, @@ -68,6 +74,7 @@ module spi_engine_execution_shiftreg #( input sdo_idle_state, input [ 7:0] left_aligned, input [ 7:0] word_length, + input [ 7:0] spi_lane_mask, // timing from main fsm output sdo_io_ready, @@ -80,54 +87,69 @@ module spi_engine_execution_shiftreg #( ); reg [ 7:0] sdi_counter = 8'b0; - reg [(DATA_WIDTH-1):0] data_sdo_shift = 'h0; reg [ SDI_DELAY+1:0] trigger_rx_d = {(SDI_DELAY+2){1'b0}}; - reg [(DATA_WIDTH-1):0] aligned_sdo_data, sdo_data_reg; - reg data_sdo_v; - wire sdo_toshiftreg; + wire data_sdo_v; //data_sdo_v == existence of valid data + wire sdo_toshiftreg; //it is using the valid data for shifting in this cycle wire last_sdi_bit; wire trigger_rx_s; wire [2:0] current_instr = current_cmd[14:12]; - - // sdo data handshake - assign sdo_data_ready = (!data_sdo_v) || sdo_toshiftreg; + wire sdo_data_ready_int; + + // sdo_data_ready_int is active when two conditions are true: + // ((!data_sdo_v) || (sdo_toshiftreg)) + // there's room for storing sdo data + // AND + // (s_offload_active || current_instr == CMD_TRANSFER) + // when s_offload_active, it is possible to prefetch + // when !s_offload_active, it is waiting for write instruction + assign sdo_data_ready_int = ((!data_sdo_v) || (sdo_toshiftreg)) && (s_offload_active || current_instr == CMD_TRANSFER); + assign sdo_data_ready = sdo_data_ready_int; assign sdo_io_ready = data_sdo_v; - always @(posedge clk ) begin - if (resetn == 1'b0) begin - data_sdo_v <= 1'b0; - end else begin - if (sdo_data_ready && sdo_data_valid) begin - data_sdo_v <= 1'b1; - sdo_data_reg <= sdo_data; - end else if (sdo_toshiftreg) begin - data_sdo_v <= 1'b0; - end - end - end - // pipelined shifter for sdo_data - always @(posedge clk ) begin - if (resetn == 1'b0) begin - aligned_sdo_data <= 0; - end else begin - aligned_sdo_data <= sdo_data_reg << left_aligned; - end - end + wire [(NUM_OF_SDI * DATA_WIDTH)-1:0] aligned_sdo_data; + spi_engine_execution_shiftreg_data_assemble #( + .ALL_ACTIVE_LANE_MASK(ALL_ACTIVE_LANE_MASK), + .DATA_WIDTH(DATA_WIDTH), + .NUM_OF_SDI(NUM_OF_SDI), + .CMD_WRITE(CMD_WRITE), + .REG_SPI_LANE_CONFIG(REG_SPI_LANE_CONFIG) + ) sdo_data_assemble ( + .clk (clk), + .resetn (resetn), + .data (sdo_data), + .data_ready (sdo_data_ready_int), + .data_valid (sdo_data_valid), + .current_cmd (current_cmd), + .lane_mask (spi_lane_mask), + .idle_state (sdo_idle_state), + .left_aligned (left_aligned), + .transfer_active (transfer_active), + .trigger_tx (trigger_tx), + .first_bit (first_bit), + .sdo_enabled(sdo_enabled), + .data_assembled (aligned_sdo_data), + .last_handshake (data_sdo_v) + ); - // Load the SDO parallel data into the SDO shift register. In case of a custom - // data width, additional bit shifting must done at load. - always @(posedge clk) begin - if (!sdo_enabled || (current_instr != CMD_TRANSFER)) begin - data_sdo_shift <= {DATA_WIDTH{sdo_idle_state}}; - end else if (transfer_active == 1'b1 && trigger_tx == 1'b1) begin - if (first_bit == 1'b1) begin - data_sdo_shift <= aligned_sdo_data; - end else begin - data_sdo_shift <= {data_sdo_shift[(DATA_WIDTH-2):0], 1'b0}; + genvar i; + for (i = 0; i < NUM_OF_SDI; i = i + 1) begin: g_sdo_shift_reg + // Load the SDO parallel data into the SDO shift register. + reg [(DATA_WIDTH-1):0] data_sdo_shift = 'h0; + always @(posedge clk) begin + if (!sdo_enabled || (current_instr != CMD_TRANSFER)) begin + data_sdo_shift <= {DATA_WIDTH{sdo_idle_state}}; + end else if (transfer_active == 1'b1 && trigger_tx == 1'b1) begin + if (first_bit == 1'b1) begin + data_sdo_shift <= spi_lane_mask[i] ? aligned_sdo_data[i * DATA_WIDTH+:DATA_WIDTH] : {DATA_WIDTH{sdo_idle_state}}; + end else begin + data_sdo_shift <= {data_sdo_shift[(DATA_WIDTH-2):0], 1'b0}; + end end end + + assign sdo_int[i] = data_sdo_shift[DATA_WIDTH-1]; end - assign sdo_int = data_sdo_shift[DATA_WIDTH-1]; + assign sdo_toshiftreg = (transfer_active && trigger_tx && first_bit && sdo_enabled); // In case of an interface with high clock rate (SCLK > 50MHz), the latch of @@ -149,7 +171,6 @@ module spi_engine_execution_shiftreg #( // used to latch the MISO lines, improving the overall timing margin of the // interface. - genvar i; // NOTE: SPI configuration (CPOL/PHA) is only hardware configurable at this point, unless ECHO_SCLK=0 generate if (ECHO_SCLK == 1) begin : g_echo_sclk_miso_latch @@ -226,10 +247,6 @@ module spi_engine_execution_shiftreg #( end - assign sdi_data = sdi_data_latch; - assign last_sdi_bit = last_sdi_bit_r; - assign echo_last_bit = !last_sdi_bit_m[3] && last_sdi_bit_m[2]; - // sdi_data_valid is synchronous to SPI clock, so synchronize the // last_sdi_bit to SPI clock @@ -242,6 +259,10 @@ module spi_engine_execution_shiftreg #( end end + assign sdi_data = sdi_data_latch; + assign last_sdi_bit = last_sdi_bit_r; + assign echo_last_bit = !last_sdi_bit_m[3] && last_sdi_bit_m[2]; + always @(posedge clk) begin if (cs_activate) begin sdi_data_valid <= 1'b0; diff --git a/library/spi_engine/spi_engine_execution/spi_engine_execution_shiftreg_data_assemble.v b/library/spi_engine/spi_engine_execution/spi_engine_execution_shiftreg_data_assemble.v new file mode 100644 index 00000000000..978408ff9c6 --- /dev/null +++ b/library/spi_engine/spi_engine_execution/spi_engine_execution_shiftreg_data_assemble.v @@ -0,0 +1,127 @@ +module spi_engine_execution_shiftreg_data_assemble #( + parameter ALL_ACTIVE_LANE_MASK = 8'hFF, + parameter DATA_WIDTH = 8, + parameter NUM_OF_SDI = 1, + parameter [2:0] CMD_WRITE = 3'b010, + parameter [1:0] REG_SPI_LANE_CONFIG = 2'b11 +) ( + input clk, + input resetn, + input [(DATA_WIDTH)-1:0] data, + input data_ready, + input data_valid, + input [15:0] current_cmd, + input [7:0] lane_mask, + input idle_state, + input [7:0] left_aligned, + input transfer_active, + input trigger_tx, + input first_bit, + input sdo_enabled, + output [(NUM_OF_SDI * DATA_WIDTH)-1:0] data_assembled, + output last_handshake +); + +// This module is responsible to align data for different lane masks +// if lane_mask has all of its SDOs activated, then it allows prefetch data +// if not, non activated serial lines have their data fulfilled with idle_state and buffer the remaining activated lines +// also, in this mode it is not possible to prefetch data + +reg last_handshake_int; +reg [(NUM_OF_SDI * DATA_WIDTH)-1:0] aligned_data; +reg [ (DATA_WIDTH)-1:0] data_reg; +reg [ 3:0] count_active_lanes = 0; + +wire sdo_toshiftreg = (transfer_active && trigger_tx && first_bit && sdo_enabled); +wire [2:0] current_instr = current_cmd[14:12]; +wire [1:0] configuration_register = current_cmd[9:8]; +wire exec_lane_config_cmd = ((current_instr == CMD_WRITE) && (configuration_register == REG_SPI_LANE_CONFIG)); + +integer num_active_lanes = NUM_OF_SDI; +integer lane_index = 0; +integer lane_index_d = 0; +integer valid_index = 0; +integer valid_indices [0:7]; + +assign data_assembled = aligned_data; +assign last_handshake = last_handshake_int; + +// register data +always @(posedge clk) begin + if (resetn == 1'b0) begin + data_reg <= {DATA_WIDTH{idle_state}}; + end else begin + if (data_ready && data_valid) begin + data_reg <= data; + end + end +end + +// Align data to have its bits on the MSB bits +// data is left shifted left_aligned times, where left_aligned equals to DATA_WIDTH - word_length +// word_length comes from the dynamic transfer length register +always @(posedge clk) begin + if (resetn == 1'b0) begin + aligned_data <= {(NUM_OF_SDI * DATA_WIDTH){idle_state}}; + end else begin + aligned_data[valid_index * DATA_WIDTH+:DATA_WIDTH] <= data_reg << left_aligned; + end +end + +// data line counter and stores activated lines +// it returns valid_indices array necessary for correct buffering of data +integer i; +integer j; +integer mask_index; +always @(posedge clk) begin + if (resetn == 1'b0) begin + mask_index <= 0; + j <= 0; + end else begin + if (exec_lane_config_cmd) begin + count_active_lanes = 0; + i = 0; + j <= 0; + for (i = 0; i < NUM_OF_SDI; i = i + 1) begin + count_active_lanes = count_active_lanes + current_cmd[i]; + end + num_active_lanes <= count_active_lanes; + end else begin + if (j < NUM_OF_SDI) begin + if (lane_mask[j]) begin + valid_indices[mask_index] <= j; + mask_index <= mask_index + 1; + end + j <= j + 1; + end + end + end +end + +// handshake counter +// it will increment up to num_active_lanes +// The last handshake is used by external logic to enable sdo_io_ready +// retrieves the correct lane_index used to align data +always @(posedge clk) begin + if (resetn == 1'b0) begin + lane_index <= 0; + lane_index_d <= 0; + valid_index <= 0; + last_handshake_int <= 1'b0; + end else begin + if (data_ready && data_valid) begin + last_handshake_int <= (lane_index == (num_active_lanes-1)) ? 1'b1 : 1'b0; + if (lane_index < (num_active_lanes-1)) begin + lane_index <= lane_index + 1; + end else begin + lane_index <= 0; + end + lane_index_d <= lane_index; + valid_index <= (lane_mask == ALL_ACTIVE_LANE_MASK) ? lane_index : valid_indices[lane_index_d]; + end else if (sdo_toshiftreg) begin + last_handshake_int <= 1'b0; + end + end +end + +endmodule diff --git a/library/spi_engine/spi_engine_interconnect/spi_engine_interconnect.v b/library/spi_engine/spi_engine_interconnect/spi_engine_interconnect.v index 9fe18eac282..67cf74b994d 100644 --- a/library/spi_engine/spi_engine_interconnect/spi_engine_interconnect.v +++ b/library/spi_engine/spi_engine_interconnect/spi_engine_interconnect.v @@ -43,7 +43,8 @@ module spi_engine_interconnect #( input clk, input resetn, - input interconnect_dir, + input s_interconnect_dir, + output m_offload_active, output m_cmd_valid, input m_cmd_ready, @@ -94,8 +95,9 @@ module spi_engine_interconnect #( output [7:0] s1_sync ); - `define spi_engine_interconnect_mux(s0, s1) (interconnect_dir == 1'b1 ? s0 : s1) + `define spi_engine_interconnect_mux(s0, s1) (s_interconnect_dir == 1'b1 ? s0 : s1) + assign m_offload_active = s_interconnect_dir; assign m_cmd_data = `spi_engine_interconnect_mux(s0_cmd_data, s1_cmd_data); assign m_cmd_valid = `spi_engine_interconnect_mux(s0_cmd_valid, s1_cmd_valid); assign s0_cmd_ready = `spi_engine_interconnect_mux(m_cmd_ready, 1'b0); diff --git a/library/spi_engine/spi_engine_interconnect/spi_engine_interconnect_hw.tcl b/library/spi_engine/spi_engine_interconnect/spi_engine_interconnect_hw.tcl index a64057fee69..16ace0161c4 100644 --- a/library/spi_engine/spi_engine_interconnect/spi_engine_interconnect_hw.tcl +++ b/library/spi_engine/spi_engine_interconnect/spi_engine_interconnect_hw.tcl @@ -29,7 +29,7 @@ proc p_elaboration {} { # interconnect direction interface add_interface s_interconnect_ctrl conduit end - add_interface_port s_interconnect_ctrl interconnect_dir interconnect_dir input 1 + add_interface_port s_interconnect_ctrl s_interconnect_dir interconnect_dir input 1 set_interface_property s_interconnect_ctrl associatedClock if_clk set_interface_property s_interconnect_ctrl associatedReset if_resetn diff --git a/library/spi_engine/spi_engine_interconnect/spi_engine_interconnect_ip.tcl b/library/spi_engine/spi_engine_interconnect/spi_engine_interconnect_ip.tcl index 79710328e08..8e277720eba 100644 --- a/library/spi_engine/spi_engine_interconnect/spi_engine_interconnect_ip.tcl +++ b/library/spi_engine/spi_engine_interconnect/spi_engine_interconnect_ip.tcl @@ -43,10 +43,18 @@ adi_add_bus "s_interconnect_ctrl" "slave" \ "analog.com:interface:spi_engine_interconnect_ctrl_rtl:1.0" \ "analog.com:interface:spi_engine_interconnect_ctrl:1.0" \ { \ - {"interconnect_dir" "interconnect_dir"} \ + {"s_interconnect_dir" "interconnect_dir"} \ } adi_add_bus_clock "clk" "s_interconnect_ctrl" "resetn" +adi_add_bus "m_offload_active_ctrl" "master" \ + "analog.com:interface:spi_engine_interconnect_ctrl_rtl:1.0" \ + "analog.com:interface:spi_engine_interconnect_ctrl:1.0" \ + { \ + {"m_offload_active" "interconnect_dir"} \ + } +adi_add_bus_clock "clk" "m_offload_active_ctrl" "resetn" + foreach prefix [list "s0" "s1"] { adi_add_bus [format "%s_ctrl" $prefix] "slave" \ "analog.com:interface:spi_engine_ctrl_rtl:1.0" \ diff --git a/library/spi_engine/spi_engine_offload/spi_engine_offload.v b/library/spi_engine/spi_engine_offload/spi_engine_offload.v index 329984b41f2..f60bde2128b 100644 --- a/library/spi_engine/spi_engine_offload/spi_engine_offload.v +++ b/library/spi_engine/spi_engine_offload/spi_engine_offload.v @@ -80,7 +80,7 @@ module spi_engine_offload #( input sdi_data_valid, output sdi_data_ready, - input [(NUM_OF_SDI * DATA_WIDTH-1):0] sdi_data, + input [(NUM_OF_SDI * DATA_WIDTH)-1:0] sdi_data, input sync_valid, output sync_ready, @@ -88,7 +88,7 @@ module spi_engine_offload #( output offload_sdi_valid, input offload_sdi_ready, - output [(NUM_OF_SDI * DATA_WIDTH-1):0] offload_sdi_data, + output [(NUM_OF_SDI * DATA_WIDTH)-1:0] offload_sdi_data, output interconnect_dir );