├── LICENSE ├── README.md ├── axi_spi_slave.sv ├── spi_slave_axi_plug.sv ├── spi_slave_cmd_parser.sv ├── spi_slave_controller.sv ├── spi_slave_dc_fifo.sv ├── spi_slave_regs.sv ├── spi_slave_rx.sv ├── spi_slave_syncro.sv ├── spi_slave_tx.sv └── src_files.yml /LICENSE: -------------------------------------------------------------------------------- 1 | SOLDERPAD HARDWARE LICENSE version 0.51 2 | 3 | This license is based closely on the Apache License Version 2.0, but is not 4 | approved or endorsed by the Apache Foundation. A copy of the non-modified 5 | Apache License 2.0 can be found at http://www.apache.org/licenses/LICENSE-2.0. 6 | 7 | As this license is not currently OSI or FSF approved, the Licensor permits any 8 | Work licensed under this License, at the option of the Licensee, to be treated 9 | as licensed under the Apache License Version 2.0 (which is so approved). 10 | 11 | This License is licensed under the terms of this License and in particular 12 | clause 7 below (Disclaimer of Warranties) applies in relation to its use. 13 | 14 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 15 | 16 | 1. Definitions. 17 | 18 | “License” shall mean the terms and conditions for use, reproduction, and 19 | distribution as defined by Sections 1 through 9 of this document. 20 | 21 | “Licensor” shall mean the Rights owner or entity authorized by the Rights owner 22 | that is granting the License. 23 | 24 | “Legal Entity” shall mean the union of the acting entity and all other entities 25 | that control, are controlled by, or are under common control with that entity. 26 | For the purposes of this definition, “control” means (i) the power, direct or 27 | indirect, to cause the direction or management of such entity, whether by 28 | contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the 29 | outstanding shares, or (iii) beneficial ownership of such entity. 30 | 31 | “You” (or “Your”) shall mean an individual or Legal Entity exercising 32 | permissions granted by this License. 33 | 34 | “Rights” means copyright and any similar right including design right (whether 35 | registered or unregistered), semiconductor topography (mask) rights and 36 | database rights (but excluding Patents and Trademarks). 37 | 38 | “Source” form shall mean the preferred form for making modifications, including 39 | but not limited to source code, net lists, board layouts, CAD files, 40 | documentation source, and configuration files. 41 | 42 | “Object” form shall mean any form resulting from mechanical transformation or 43 | translation of a Source form, including but not limited to compiled object 44 | code, generated documentation, the instantiation of a hardware design and 45 | conversions to other media types, including intermediate forms such as 46 | bytecodes, FPGA bitstreams, artwork and semiconductor topographies (mask 47 | works). 48 | 49 | “Work” shall mean the work of authorship, whether in Source form or other 50 | Object form, made available under the License, as indicated by a Rights notice 51 | that is included in or attached to the work (an example is provided in the 52 | Appendix below). 53 | 54 | “Derivative Works” shall mean any work, whether in Source or Object form, that 55 | is based on (or derived from) the Work and for which the editorial revisions, 56 | annotations, elaborations, or other modifications represent, as a whole, an 57 | original work of authorship. For the purposes of this License, Derivative Works 58 | shall not include works that remain separable from, or merely link (or bind by 59 | name) or physically connect to or interoperate with the interfaces of, the Work 60 | and Derivative Works thereof. 61 | 62 | “Contribution” shall mean any design or work of authorship, including the 63 | original version of the Work and any modifications or additions to that Work or 64 | Derivative Works thereof, that is intentionally submitted to Licensor for 65 | inclusion in the Work by the Rights owner or by an individual or Legal Entity 66 | authorized to submit on behalf of the Rights owner. For the purposes of this 67 | definition, “submitted” means any form of electronic, verbal, or written 68 | communication sent to the Licensor or its representatives, including but not 69 | limited to communication on electronic mailing lists, source code control 70 | systems, and issue tracking systems that are managed by, or on behalf of, the 71 | Licensor for the purpose of discussing and improving the Work, but excluding 72 | communication that is conspicuously marked or otherwise designated in writing 73 | by the Rights owner as “Not a Contribution.” 74 | 75 | “Contributor” shall mean Licensor and any individual or Legal Entity on behalf 76 | of whom a Contribution has been received by Licensor and subsequently 77 | incorporated within the Work. 78 | 79 | 2. Grant of License. Subject to the terms and conditions of this License, each 80 | Contributor hereby grants to You a perpetual, worldwide, non-exclusive, 81 | no-charge, royalty-free, irrevocable license under the Rights to reproduce, 82 | prepare Derivative Works of, publicly display, publicly perform, sublicense, 83 | and distribute the Work and such Derivative Works in Source or Object form and 84 | do anything in relation to the Work as if the Rights did not exist. 85 | 86 | 3. Grant of Patent License. Subject to the terms and conditions of this 87 | License, each Contributor hereby grants to You a perpetual, worldwide, 88 | non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this 89 | section) patent license to make, have made, use, offer to sell, sell, import, 90 | and otherwise transfer the Work, where such license applies only to those 91 | patent claims licensable by such Contributor that are necessarily infringed by 92 | their Contribution(s) alone or by combination of their Contribution(s) with the 93 | Work to which such Contribution(s) was submitted. If You institute patent 94 | litigation against any entity (including a cross-claim or counterclaim in a 95 | lawsuit) alleging that the Work or a Contribution incorporated within the Work 96 | constitutes direct or contributory patent infringement, then any patent 97 | licenses granted to You under this License for that Work shall terminate as of 98 | the date such litigation is filed. 99 | 100 | 4. Redistribution. You may reproduce and distribute copies of the Work or 101 | Derivative Works thereof in any medium, with or without modifications, and in 102 | Source or Object form, provided that You meet the following conditions: 103 | 104 | You must give any other recipients of the Work or Derivative Works a copy 105 | of this License; and 106 | 107 | You must cause any modified files to carry prominent notices stating that 108 | You changed the files; and 109 | 110 | You must retain, in the Source form of any Derivative Works that You 111 | distribute, all copyright, patent, trademark, and attribution notices from 112 | the Source form of the Work, excluding those notices that do not pertain to 113 | any part of the Derivative Works; and 114 | 115 | If the Work includes a “NOTICE” text file as part of its distribution, then 116 | any Derivative Works that You distribute must include a readable copy of 117 | the attribution notices contained within such NOTICE file, excluding those 118 | notices that do not pertain to any part of the Derivative Works, in at 119 | least one of the following places: within a NOTICE text file distributed as 120 | part of the Derivative Works; within the Source form or documentation, if 121 | provided along with the Derivative Works; or, within a display generated by 122 | the Derivative Works, if and wherever such third-party notices normally 123 | appear. The contents of the NOTICE file are for informational purposes only 124 | and do not modify the License. You may add Your own attribution notices 125 | within Derivative Works that You distribute, alongside or as an addendum to 126 | the NOTICE text from the Work, provided that such additional attribution 127 | notices cannot be construed as modifying the License. You may add Your own 128 | copyright statement to Your modifications and may provide additional or 129 | different license terms and conditions for use, reproduction, or 130 | distribution of Your modifications, or for any such Derivative Works as a 131 | whole, provided Your use, reproduction, and distribution of the Work 132 | otherwise complies with the conditions stated in this License. 133 | 134 | 5. Submission of Contributions. Unless You explicitly state otherwise, any 135 | Contribution intentionally submitted for inclusion in the Work by You to the 136 | Licensor shall be under the terms and conditions of this License, without any 137 | additional terms or conditions. Notwithstanding the above, nothing herein shall 138 | supersede or modify the terms of any separate license agreement you may have 139 | executed with Licensor regarding such Contributions. 140 | 141 | 6. Trademarks. This License does not grant permission to use the trade names, 142 | trademarks, service marks, or product names of the Licensor, except as required 143 | for reasonable and customary use in describing the origin of the Work and 144 | reproducing the content of the NOTICE file. 145 | 146 | 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in 147 | writing, Licensor provides the Work (and each Contributor provides its 148 | Contributions) on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 149 | KIND, either express or implied, including, without limitation, any warranties 150 | or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 151 | PARTICULAR PURPOSE. You are solely responsible for determining the 152 | appropriateness of using or redistributing the Work and assume any risks 153 | associated with Your exercise of permissions under this License. 154 | 155 | 8. Limitation of Liability. In no event and under no legal theory, whether in 156 | tort (including negligence), contract, or otherwise, unless required by 157 | applicable law (such as deliberate and grossly negligent acts) or agreed to in 158 | writing, shall any Contributor be liable to You for damages, including any 159 | direct, indirect, special, incidental, or consequential damages of any 160 | character arising as a result of this License or out of the use or inability to 161 | use the Work (including but not limited to damages for loss of goodwill, work 162 | stoppage, computer failure or malfunction, or any and all other commercial 163 | damages or losses), even if such Contributor has been advised of the 164 | possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing the Work or 167 | Derivative Works thereof, You may choose to offer, and charge a fee for, 168 | acceptance of support, warranty, indemnity, or other liability obligations 169 | and/or rights consistent with this License. However, in accepting such 170 | obligations, You may act only on Your own behalf and on Your sole 171 | responsibility, not on behalf of any other Contributor, and only if You agree 172 | to indemnify, defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason of your 174 | accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AXI SPI Slave 2 | 3 | This is an implementation of a simple SPI slave. The SPI slave can be used by 4 | an external microcontroller to access the memory of the SoC where this IP is 5 | instantiated. The SPI slave uses the AXI bus to access the memory of the target 6 | SoC. 7 | 8 | It contains dual-clock FIFOs to perform the clock domain crossing from SPI to 9 | the SoC (AXI) domain. 10 | 11 | This IP depends on some PULP common cells like clock muxes, clock gates and 12 | clock inverters. Those can be found in the PULP common cells repository or in 13 | the PULPino RTL sources. The clock domain crossing functionality is reused from 14 | the AXI slice DC component, so make sure you compile the AXI slice DC when 15 | using this IP. 16 | -------------------------------------------------------------------------------- /axi_spi_slave.sv: -------------------------------------------------------------------------------- 1 | // Copyright 2015 ETH Zurich and University of Bologna. 2 | // Copyright and related rights are licensed under the Solderpad Hardware 3 | // License, Version 0.51 (the “License”); you may not use this file except in 4 | // compliance with the License. You may obtain a copy of the License at 5 | // http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law 6 | // or agreed to in writing, software, hardware and materials distributed under 7 | // this License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR 8 | // CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | // specific language governing permissions and limitations under the License. 10 | 11 | module axi_spi_slave 12 | #( 13 | parameter AXI_ADDR_WIDTH = 32, 14 | parameter AXI_DATA_WIDTH = 64, 15 | parameter AXI_USER_WIDTH = 6, 16 | parameter AXI_ID_WIDTH = 3, 17 | parameter DUMMY_CYCLES = 32 18 | ) 19 | ( 20 | input logic test_mode, 21 | input logic spi_sclk, 22 | input logic spi_cs, 23 | output logic [1:0] spi_mode, 24 | input logic spi_sdi0, 25 | input logic spi_sdi1, 26 | input logic spi_sdi2, 27 | input logic spi_sdi3, 28 | output logic spi_sdo0, 29 | output logic spi_sdo1, 30 | output logic spi_sdo2, 31 | output logic spi_sdo3, 32 | 33 | // AXI4 MASTER 34 | //*************************************** 35 | input logic axi_aclk, 36 | input logic axi_aresetn, 37 | // WRITE ADDRESS CHANNEL 38 | output logic axi_master_aw_valid, 39 | output logic [AXI_ADDR_WIDTH-1:0] axi_master_aw_addr, 40 | output logic [2:0] axi_master_aw_prot, 41 | output logic [3:0] axi_master_aw_region, 42 | output logic [7:0] axi_master_aw_len, 43 | output logic [2:0] axi_master_aw_size, 44 | output logic [1:0] axi_master_aw_burst, 45 | output logic axi_master_aw_lock, 46 | output logic [3:0] axi_master_aw_cache, 47 | output logic [3:0] axi_master_aw_qos, 48 | output logic [AXI_ID_WIDTH-1:0] axi_master_aw_id, 49 | output logic [AXI_USER_WIDTH-1:0] axi_master_aw_user, 50 | input logic axi_master_aw_ready, 51 | 52 | // READ ADDRESS CHANNEL 53 | output logic axi_master_ar_valid, 54 | output logic [AXI_ADDR_WIDTH-1:0] axi_master_ar_addr, 55 | output logic [2:0] axi_master_ar_prot, 56 | output logic [3:0] axi_master_ar_region, 57 | output logic [7:0] axi_master_ar_len, 58 | output logic [2:0] axi_master_ar_size, 59 | output logic [1:0] axi_master_ar_burst, 60 | output logic axi_master_ar_lock, 61 | output logic [3:0] axi_master_ar_cache, 62 | output logic [3:0] axi_master_ar_qos, 63 | output logic [AXI_ID_WIDTH-1:0] axi_master_ar_id, 64 | output logic [AXI_USER_WIDTH-1:0] axi_master_ar_user, 65 | input logic axi_master_ar_ready, 66 | 67 | // WRITE DATA CHANNEL 68 | output logic axi_master_w_valid, 69 | output logic [AXI_DATA_WIDTH-1:0] axi_master_w_data, 70 | output logic [AXI_DATA_WIDTH/8-1:0] axi_master_w_strb, 71 | output logic [AXI_USER_WIDTH-1:0] axi_master_w_user, 72 | output logic axi_master_w_last, 73 | input logic axi_master_w_ready, 74 | 75 | // READ DATA CHANNEL 76 | input logic axi_master_r_valid, 77 | input logic [AXI_DATA_WIDTH-1:0] axi_master_r_data, 78 | input logic [1:0] axi_master_r_resp, 79 | input logic axi_master_r_last, 80 | input logic [AXI_ID_WIDTH-1:0] axi_master_r_id, 81 | input logic [AXI_USER_WIDTH-1:0] axi_master_r_user, 82 | output logic axi_master_r_ready, 83 | 84 | // WRITE RESPONSE CHANNEL 85 | input logic axi_master_b_valid, 86 | input logic [1:0] axi_master_b_resp, 87 | input logic [AXI_ID_WIDTH-1:0] axi_master_b_id, 88 | input logic [AXI_USER_WIDTH-1:0] axi_master_b_user, 89 | output logic axi_master_b_ready 90 | ); 91 | 92 | logic en_quad; 93 | logic [7:0] rx_counter; 94 | logic rx_counter_upd; 95 | logic [31:0] rx_data; 96 | logic rx_data_valid; 97 | 98 | logic [7:0] tx_counter; 99 | logic tx_counter_upd; 100 | logic [31:0] tx_data; 101 | logic tx_data_valid; 102 | 103 | logic ctrl_rd_wr; 104 | 105 | logic [31:0] ctrl_addr; 106 | logic ctrl_addr_valid; 107 | 108 | logic [31:0] ctrl_data_rx; 109 | logic ctrl_data_rx_valid; 110 | logic ctrl_data_rx_ready; 111 | logic [31:0] ctrl_data_tx; 112 | logic ctrl_data_tx_valid; 113 | logic ctrl_data_tx_ready; 114 | 115 | logic [31:0] fifo_data_rx; 116 | logic fifo_data_rx_valid; 117 | logic fifo_data_rx_ready; 118 | logic [31:0] fifo_data_tx; 119 | logic fifo_data_tx_valid; 120 | logic fifo_data_tx_ready; 121 | 122 | logic [AXI_ADDR_WIDTH-1:0] addr_sync; 123 | logic addr_valid_sync; 124 | logic cs_sync; 125 | 126 | logic tx_done; 127 | logic rd_wr_sync; 128 | 129 | logic [15:0] wrap_length; 130 | 131 | spi_slave_rx u_rxreg 132 | ( 133 | .sclk ( spi_sclk ), 134 | .cs ( spi_cs ), 135 | .sdi0 ( spi_sdi0 ), 136 | .sdi1 ( spi_sdi1 ), 137 | .sdi2 ( spi_sdi2 ), 138 | .sdi3 ( spi_sdi3 ), 139 | .en_quad_in ( en_quad ), 140 | .counter_in ( rx_counter ), 141 | .counter_in_upd ( rx_counter_upd ), 142 | .data ( rx_data ), 143 | .data_ready ( rx_data_valid ) 144 | ); 145 | 146 | spi_slave_tx u_txreg 147 | ( 148 | .test_mode ( test_mode ), 149 | .sclk ( spi_sclk ), 150 | .cs ( spi_cs ), 151 | .sdo0 ( spi_sdo0 ), 152 | .sdo1 ( spi_sdo1 ), 153 | .sdo2 ( spi_sdo2 ), 154 | .sdo3 ( spi_sdo3 ), 155 | .en_quad_in ( en_quad ), 156 | .counter_in ( tx_counter ), 157 | .counter_in_upd ( tx_counter_upd ), 158 | .data ( tx_data ), 159 | .data_valid ( tx_data_valid ), 160 | .done ( tx_done ) 161 | ); 162 | 163 | spi_slave_controller 164 | #( 165 | .DUMMY_CYCLES ( DUMMY_CYCLES ) 166 | ) 167 | u_slave_sm 168 | ( 169 | .sclk ( spi_sclk ), 170 | .sys_rstn ( axi_aresetn ), 171 | .cs ( spi_cs ), 172 | .en_quad ( en_quad ), 173 | .pad_mode ( spi_mode ), 174 | .rx_counter ( rx_counter ), 175 | .rx_counter_upd ( rx_counter_upd ), 176 | .rx_data ( rx_data ), 177 | .rx_data_valid ( rx_data_valid ), 178 | .tx_counter ( tx_counter ), 179 | .tx_counter_upd ( tx_counter_upd ), 180 | .tx_data ( tx_data ), 181 | .tx_data_valid ( tx_data_valid ), 182 | .tx_done ( tx_done ), 183 | .ctrl_rd_wr ( ctrl_rd_wr ), 184 | .ctrl_addr ( ctrl_addr ), 185 | .ctrl_addr_valid ( ctrl_addr_valid ), 186 | .ctrl_data_rx ( ctrl_data_rx ), 187 | .ctrl_data_rx_valid ( ctrl_data_rx_valid ), 188 | .ctrl_data_rx_ready ( ctrl_data_rx_ready ), 189 | .ctrl_data_tx ( ctrl_data_tx ), 190 | .ctrl_data_tx_valid ( ctrl_data_tx_valid ), 191 | .ctrl_data_tx_ready ( ctrl_data_tx_ready ), 192 | .wrap_length ( wrap_length ) 193 | ); 194 | 195 | spi_slave_dc_fifo 196 | #( 197 | .DATA_WIDTH ( 32 ), 198 | .BUFFER_DEPTH ( 8 ) 199 | ) 200 | u_dcfifo_rx 201 | ( 202 | .clk_a ( spi_sclk ), 203 | .rstn_a ( axi_aresetn ), 204 | .data_a ( ctrl_data_rx ), 205 | .valid_a ( ctrl_data_rx_valid ), 206 | .ready_a ( ctrl_data_rx_ready ), 207 | .clk_b ( axi_aclk ), 208 | .rstn_b ( axi_aresetn ), 209 | .data_b ( fifo_data_rx ), 210 | .valid_b ( fifo_data_rx_valid ), 211 | .ready_b ( fifo_data_rx_ready ) 212 | ); 213 | 214 | spi_slave_dc_fifo 215 | #( 216 | .DATA_WIDTH ( 32 ), 217 | .BUFFER_DEPTH ( 8 ) 218 | ) 219 | u_dcfifo_tx 220 | ( 221 | .clk_a ( axi_aclk ), 222 | .rstn_a ( axi_aresetn ), 223 | .data_a ( fifo_data_tx ), 224 | .valid_a ( fifo_data_tx_valid ), 225 | .ready_a ( fifo_data_tx_ready ), 226 | .clk_b ( spi_sclk ), 227 | .rstn_b ( axi_aresetn ), 228 | .data_b ( ctrl_data_tx ), 229 | .valid_b ( ctrl_data_tx_valid ), 230 | .ready_b ( ctrl_data_tx_ready ) 231 | ); 232 | 233 | spi_slave_axi_plug 234 | #( 235 | .AXI_ADDR_WIDTH ( AXI_ADDR_WIDTH ), 236 | .AXI_DATA_WIDTH ( AXI_DATA_WIDTH ), 237 | .AXI_USER_WIDTH ( AXI_USER_WIDTH ), 238 | .AXI_ID_WIDTH ( AXI_ID_WIDTH ) 239 | ) 240 | u_axiplug 241 | ( 242 | .axi_aclk ( axi_aclk ), 243 | .axi_aresetn ( axi_aresetn ), 244 | .axi_master_aw_valid ( axi_master_aw_valid ), 245 | .axi_master_aw_addr ( axi_master_aw_addr ), 246 | .axi_master_aw_prot ( axi_master_aw_prot ), 247 | .axi_master_aw_region ( axi_master_aw_region ), 248 | .axi_master_aw_len ( axi_master_aw_len ), 249 | .axi_master_aw_size ( axi_master_aw_size ), 250 | .axi_master_aw_burst ( axi_master_aw_burst ), 251 | .axi_master_aw_lock ( axi_master_aw_lock ), 252 | .axi_master_aw_cache ( axi_master_aw_cache ), 253 | .axi_master_aw_qos ( axi_master_aw_qos ), 254 | .axi_master_aw_id ( axi_master_aw_id ), 255 | .axi_master_aw_user ( axi_master_aw_user ), 256 | .axi_master_aw_ready ( axi_master_aw_ready ), 257 | .axi_master_ar_valid ( axi_master_ar_valid ), 258 | .axi_master_ar_addr ( axi_master_ar_addr ), 259 | .axi_master_ar_prot ( axi_master_ar_prot ), 260 | .axi_master_ar_region ( axi_master_ar_region ), 261 | .axi_master_ar_len ( axi_master_ar_len ), 262 | .axi_master_ar_size ( axi_master_ar_size ), 263 | .axi_master_ar_burst ( axi_master_ar_burst ), 264 | .axi_master_ar_lock ( axi_master_ar_lock ), 265 | .axi_master_ar_cache ( axi_master_ar_cache ), 266 | .axi_master_ar_qos ( axi_master_ar_qos ), 267 | .axi_master_ar_id ( axi_master_ar_id ), 268 | .axi_master_ar_user ( axi_master_ar_user ), 269 | .axi_master_ar_ready ( axi_master_ar_ready ), 270 | .axi_master_w_valid ( axi_master_w_valid ), 271 | .axi_master_w_data ( axi_master_w_data ), 272 | .axi_master_w_strb ( axi_master_w_strb ), 273 | .axi_master_w_user ( axi_master_w_user ), 274 | .axi_master_w_last ( axi_master_w_last ), 275 | .axi_master_w_ready ( axi_master_w_ready ), 276 | .axi_master_r_valid ( axi_master_r_valid ), 277 | .axi_master_r_data ( axi_master_r_data ), 278 | .axi_master_r_resp ( axi_master_r_resp ), 279 | .axi_master_r_last ( axi_master_r_last ), 280 | .axi_master_r_id ( axi_master_r_id ), 281 | .axi_master_r_user ( axi_master_r_user ), 282 | .axi_master_r_ready ( axi_master_r_ready ), 283 | .axi_master_b_valid ( axi_master_b_valid ), 284 | .axi_master_b_resp ( axi_master_b_resp ), 285 | .axi_master_b_id ( axi_master_b_id ), 286 | .axi_master_b_user ( axi_master_b_user ), 287 | .axi_master_b_ready ( axi_master_b_ready ), 288 | .rxtx_addr ( addr_sync ), 289 | .rxtx_addr_valid ( addr_valid_sync ), 290 | .start_tx ( rd_wr_sync & addr_valid_sync ), 291 | .cs ( cs_sync ), 292 | .tx_data ( fifo_data_tx ), 293 | .tx_valid ( fifo_data_tx_valid ), 294 | .tx_ready ( fifo_data_tx_ready ), 295 | .rx_data ( fifo_data_rx ), 296 | .rx_valid ( fifo_data_rx_valid ), 297 | .rx_ready ( fifo_data_rx_ready ), 298 | .wrap_length ( wrap_length ) 299 | ); 300 | 301 | spi_slave_syncro 302 | #( 303 | .AXI_ADDR_WIDTH ( AXI_ADDR_WIDTH ) 304 | ) 305 | u_syncro 306 | ( 307 | .sys_clk ( axi_aclk ), 308 | .rstn ( axi_aresetn ), 309 | .cs ( spi_cs ), 310 | .address ( ctrl_addr ), 311 | .address_valid ( ctrl_addr_valid ), 312 | .rd_wr ( ctrl_rd_wr ), 313 | .cs_sync ( cs_sync ), 314 | .address_sync ( addr_sync ), 315 | .address_valid_sync ( addr_valid_sync ), 316 | .rd_wr_sync ( rd_wr_sync ) 317 | ); 318 | 319 | endmodule 320 | -------------------------------------------------------------------------------- /spi_slave_axi_plug.sv: -------------------------------------------------------------------------------- 1 | // Copyright 2015 ETH Zurich and University of Bologna. 2 | // Copyright and related rights are licensed under the Solderpad Hardware 3 | // License, Version 0.51 (the “License”); you may not use this file except in 4 | // compliance with the License. You may obtain a copy of the License at 5 | // http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law 6 | // or agreed to in writing, software, hardware and materials distributed under 7 | // this License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR 8 | // CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | // specific language governing permissions and limitations under the License. 10 | 11 | module spi_slave_axi_plug 12 | #( 13 | parameter AXI_ADDR_WIDTH = 32, 14 | parameter AXI_DATA_WIDTH = 64, 15 | parameter AXI_USER_WIDTH = 6, 16 | parameter AXI_ID_WIDTH = 3 17 | ) 18 | ( 19 | // AXI4 MASTER 20 | //*************************************** 21 | input logic axi_aclk, 22 | input logic axi_aresetn, 23 | // WRITE ADDRESS CHANNEL 24 | output logic axi_master_aw_valid, 25 | output logic [AXI_ADDR_WIDTH-1:0] axi_master_aw_addr, 26 | output logic [2:0] axi_master_aw_prot, 27 | output logic [3:0] axi_master_aw_region, 28 | output logic [7:0] axi_master_aw_len, 29 | output logic [2:0] axi_master_aw_size, 30 | output logic [1:0] axi_master_aw_burst, 31 | output logic axi_master_aw_lock, 32 | output logic [3:0] axi_master_aw_cache, 33 | output logic [3:0] axi_master_aw_qos, 34 | output logic [AXI_ID_WIDTH-1:0] axi_master_aw_id, 35 | output logic [AXI_USER_WIDTH-1:0] axi_master_aw_user, 36 | input logic axi_master_aw_ready, 37 | 38 | // READ ADDRESS CHANNEL 39 | output logic axi_master_ar_valid, 40 | output logic [AXI_ADDR_WIDTH-1:0] axi_master_ar_addr, 41 | output logic [2:0] axi_master_ar_prot, 42 | output logic [3:0] axi_master_ar_region, 43 | output logic [7:0] axi_master_ar_len, 44 | output logic [2:0] axi_master_ar_size, 45 | output logic [1:0] axi_master_ar_burst, 46 | output logic axi_master_ar_lock, 47 | output logic [3:0] axi_master_ar_cache, 48 | output logic [3:0] axi_master_ar_qos, 49 | output logic [AXI_ID_WIDTH-1:0] axi_master_ar_id, 50 | output logic [AXI_USER_WIDTH-1:0] axi_master_ar_user, 51 | input logic axi_master_ar_ready, 52 | 53 | // WRITE DATA CHANNEL 54 | output logic axi_master_w_valid, 55 | output logic [AXI_DATA_WIDTH-1:0] axi_master_w_data, 56 | output logic [AXI_DATA_WIDTH/8-1:0] axi_master_w_strb, 57 | output logic [AXI_USER_WIDTH-1:0] axi_master_w_user, 58 | output logic axi_master_w_last, 59 | input logic axi_master_w_ready, 60 | 61 | // READ DATA CHANNEL 62 | input logic axi_master_r_valid, 63 | input logic [AXI_DATA_WIDTH-1:0] axi_master_r_data, 64 | input logic [1:0] axi_master_r_resp, 65 | input logic axi_master_r_last, 66 | input logic [AXI_ID_WIDTH-1:0] axi_master_r_id, 67 | input logic [AXI_USER_WIDTH-1:0] axi_master_r_user, 68 | output logic axi_master_r_ready, 69 | 70 | // WRITE RESPONSE CHANNEL 71 | input logic axi_master_b_valid, 72 | input logic [1:0] axi_master_b_resp, 73 | input logic [AXI_ID_WIDTH-1:0] axi_master_b_id, 74 | input logic [AXI_USER_WIDTH-1:0] axi_master_b_user, 75 | output logic axi_master_b_ready, 76 | 77 | input logic [AXI_ADDR_WIDTH-1:0] rxtx_addr, 78 | input logic rxtx_addr_valid, 79 | input logic start_tx, 80 | input logic cs, 81 | output logic [31:0] tx_data, 82 | output logic tx_valid, 83 | input logic tx_ready, 84 | input logic [31:0] rx_data, 85 | input logic rx_valid, 86 | output logic rx_ready, 87 | 88 | input logic [15:0] wrap_length 89 | ); 90 | 91 | logic [AXI_ADDR_WIDTH-1:0] curr_addr; 92 | logic [AXI_ADDR_WIDTH-1:0] next_addr; 93 | logic [31:0] curr_data_rx; 94 | logic [AXI_DATA_WIDTH-1:0] curr_data_tx; 95 | logic incr_addr_w; 96 | logic incr_addr_r; 97 | logic sample_fifo; 98 | logic sample_axidata; 99 | 100 | // up to 64 kwords (256kB) 101 | logic [15:0] tx_counter; 102 | 103 | enum logic [2:0] {IDLE,DATA,AXIADDR,AXIDATA,AXIRESP} AR_CS,AR_NS,AW_CS,AW_NS; 104 | 105 | always_ff @(posedge axi_aclk or negedge axi_aresetn) 106 | begin 107 | if (axi_aresetn == 0) 108 | begin 109 | AW_CS <= IDLE; 110 | AR_CS <= IDLE; 111 | curr_data_rx <= 'h0; 112 | curr_data_tx <= 'h0; 113 | curr_addr <= 'h0; 114 | end 115 | else 116 | begin 117 | AW_CS <= AW_NS; 118 | AR_CS <= AR_NS; 119 | if (sample_fifo) 120 | begin 121 | curr_data_rx <= rx_data; 122 | end 123 | if (sample_axidata) 124 | curr_data_tx <= axi_master_r_data; 125 | if (rxtx_addr_valid) 126 | curr_addr <= rxtx_addr; 127 | else if (incr_addr_w | incr_addr_r) 128 | curr_addr <= next_addr; 129 | end 130 | end 131 | 132 | always_ff @(posedge axi_aclk or negedge axi_aresetn) 133 | begin 134 | if (axi_aresetn == 1'b0) 135 | tx_counter <= 16'h0; 136 | else if(start_tx) 137 | tx_counter <= 16'h0; 138 | else if(incr_addr_w | incr_addr_r) begin 139 | if(tx_counter == wrap_length-1) 140 | tx_counter <= 16'h0; 141 | else 142 | tx_counter <= tx_counter + 16'h1; 143 | end 144 | end 145 | 146 | always_comb 147 | begin 148 | next_addr = 32'b0; 149 | if(rxtx_addr_valid) 150 | next_addr = rxtx_addr; 151 | else if(tx_counter == wrap_length-1) 152 | next_addr = rxtx_addr; 153 | else 154 | next_addr = curr_addr + 32'h4; 155 | end 156 | 157 | always_comb 158 | begin 159 | AW_NS = IDLE; 160 | sample_fifo = 1'b0; 161 | rx_ready = 1'b0; 162 | axi_master_aw_valid = 1'b0; 163 | axi_master_w_valid = 1'b0; 164 | axi_master_b_ready = 1'b0; 165 | incr_addr_w = 1'b0; 166 | case(AW_CS) 167 | IDLE: 168 | begin 169 | if(rx_valid) 170 | begin 171 | sample_fifo = 1'b1; 172 | rx_ready = 1'b1; 173 | AW_NS = AXIADDR; 174 | end 175 | else 176 | begin 177 | AW_NS = IDLE; 178 | end 179 | end 180 | AXIADDR: 181 | begin 182 | axi_master_aw_valid = 1'b1; 183 | if (axi_master_aw_ready) 184 | AW_NS = AXIDATA; 185 | else 186 | AW_NS = AXIADDR; 187 | end 188 | AXIDATA: 189 | begin 190 | axi_master_w_valid = 1'b1; 191 | if (axi_master_w_ready) 192 | begin 193 | incr_addr_w = 1'b1; 194 | AW_NS = AXIRESP; 195 | end 196 | else 197 | AW_NS = AXIDATA; 198 | end 199 | AXIRESP: 200 | begin 201 | axi_master_b_ready = 1'b1; 202 | if (axi_master_b_valid) 203 | AW_NS = IDLE; 204 | else 205 | AW_NS = AXIRESP; 206 | end 207 | 208 | endcase 209 | end 210 | 211 | always_comb 212 | begin 213 | AR_NS = IDLE; 214 | tx_valid = 1'b0; 215 | axi_master_ar_valid = 1'b0; 216 | axi_master_r_ready = 1'b0; 217 | incr_addr_r = 1'b0; 218 | sample_axidata = 1'b0; 219 | case(AR_CS) 220 | IDLE: 221 | begin 222 | if(start_tx && !cs) 223 | begin 224 | AR_NS = AXIADDR; 225 | end 226 | else 227 | begin 228 | AR_NS = IDLE; 229 | end 230 | end 231 | DATA: 232 | begin 233 | tx_valid = 1'b1; 234 | if (cs) 235 | begin 236 | AR_NS = IDLE; 237 | end 238 | else 239 | begin 240 | if(tx_ready) 241 | begin 242 | incr_addr_r = 1'b1; 243 | AR_NS = AXIADDR; 244 | end 245 | else 246 | begin 247 | AR_NS = DATA; 248 | end 249 | end 250 | end 251 | AXIADDR: 252 | begin 253 | axi_master_ar_valid = 1'b1; 254 | if (axi_master_ar_ready) 255 | AR_NS = AXIRESP; 256 | else 257 | AR_NS = AXIADDR; 258 | end 259 | AXIRESP: 260 | begin 261 | axi_master_r_ready = 1'b1; 262 | if (axi_master_r_valid) 263 | begin 264 | sample_axidata = 1'b1; 265 | AR_NS = DATA; 266 | end 267 | else 268 | AR_NS = AXIRESP; 269 | end 270 | 271 | endcase 272 | end 273 | 274 | // for now, let us support only 32-bit reads! 275 | generate if (AXI_DATA_WIDTH == 32) 276 | assign tx_data = curr_data_tx[31:0]; 277 | else 278 | assign tx_data = curr_addr[2] ? curr_data_tx[63:32] : curr_data_tx[31:0]; 279 | endgenerate 280 | 281 | assign axi_master_aw_addr = curr_addr; 282 | assign axi_master_aw_prot = 'h0; 283 | assign axi_master_aw_region = 'h0; 284 | assign axi_master_aw_len = 'h0; 285 | assign axi_master_aw_size = 3'b010; 286 | assign axi_master_aw_burst = 'h0; 287 | assign axi_master_aw_lock = 'h0; 288 | assign axi_master_aw_cache = 'h0; 289 | assign axi_master_aw_qos = 'h0; 290 | assign axi_master_aw_id = 'h1; 291 | assign axi_master_aw_user = 'h0; 292 | 293 | assign axi_master_w_data = {AXI_DATA_WIDTH/32{curr_data_rx}}; // replicate curr_data_rx as often as needed 294 | generate if (AXI_DATA_WIDTH == 32) 295 | assign axi_master_w_strb = 4'hF; 296 | else 297 | assign axi_master_w_strb = curr_addr[2] ? 8'hF0 : 8'h0F; 298 | endgenerate 299 | assign axi_master_w_user = 'h0; 300 | assign axi_master_w_last = 1'b1; 301 | 302 | assign axi_master_ar_addr = curr_addr; 303 | assign axi_master_ar_prot = 'h0; 304 | assign axi_master_ar_region = 'h0; 305 | assign axi_master_ar_len = 'h0; 306 | assign axi_master_ar_size = 3'b010; 307 | assign axi_master_ar_burst = 'h0; 308 | assign axi_master_ar_lock = 'h0; 309 | assign axi_master_ar_cache = 'h0; 310 | assign axi_master_ar_qos = 'h0; 311 | assign axi_master_ar_id = 'h1; 312 | assign axi_master_ar_user = 'h0; 313 | 314 | endmodule 315 | -------------------------------------------------------------------------------- /spi_slave_cmd_parser.sv: -------------------------------------------------------------------------------- 1 | // Copyright 2015 ETH Zurich and University of Bologna. 2 | // Copyright and related rights are licensed under the Solderpad Hardware 3 | // License, Version 0.51 (the “License”); you may not use this file except in 4 | // compliance with the License. You may obtain a copy of the License at 5 | // http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law 6 | // or agreed to in writing, software, hardware and materials distributed under 7 | // this License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR 8 | // CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | // specific language governing permissions and limitations under the License. 10 | 11 | module spi_slave_cmd_parser( 12 | input logic [7:0] cmd, 13 | output logic get_addr, 14 | output logic get_mode, 15 | output logic get_data, 16 | output logic send_data, 17 | output logic enable_cont, 18 | output logic enable_regs, 19 | output logic wait_dummy, 20 | output logic error, 21 | output logic [1:0] reg_sel 22 | ); 23 | 24 | 25 | always_comb 26 | begin 27 | get_addr = 0; 28 | get_mode = 0; 29 | get_data = 0; 30 | send_data = 0; 31 | enable_cont = 0; 32 | enable_regs = 1'b0; 33 | wait_dummy = 0; 34 | reg_sel = 2'b00; 35 | error = 1'b1; 36 | case(cmd) 37 | 8'h1: //write reg0 38 | begin 39 | get_addr = 0; 40 | get_mode = 0; 41 | get_data = 1; 42 | send_data = 0; 43 | enable_cont = 0; 44 | enable_regs = 1'b1; 45 | error = 1'b0; 46 | wait_dummy = 0; 47 | reg_sel = 2'b00; 48 | end 49 | 8'h2: //write mem 50 | begin 51 | get_addr = 1; 52 | get_mode = 0; 53 | get_data = 1; 54 | send_data = 0; 55 | enable_cont = 1'b1; 56 | enable_regs = 1'b0; 57 | error = 1'b0; 58 | wait_dummy = 0; 59 | reg_sel = 2'b00; 60 | end 61 | 8'h5: //read reg0 62 | begin 63 | get_addr = 0; 64 | get_mode = 0; 65 | get_data = 0; 66 | send_data = 1; 67 | enable_cont = 0; 68 | enable_regs = 1'b1; 69 | error = 1'b0; 70 | wait_dummy = 0; 71 | reg_sel = 2'b00; 72 | end 73 | 8'h7: //read reg1 74 | begin 75 | get_addr = 0; 76 | get_mode = 0; 77 | get_data = 0; 78 | send_data = 1; 79 | enable_cont = 0; 80 | enable_regs = 1'b1; 81 | error = 1'b0; 82 | wait_dummy = 0; 83 | reg_sel = 2'b01; 84 | end 85 | 8'hB: //read mem 86 | begin 87 | get_addr = 1; 88 | get_mode = 0; 89 | get_data = 0; 90 | send_data = 1; 91 | enable_cont = 1'b1; 92 | enable_regs = 1'b0; 93 | error = 1'b0; 94 | wait_dummy = 1; 95 | reg_sel = 2'b00; 96 | end 97 | 8'h11: //write reg1 98 | begin 99 | get_addr = 1'b0; 100 | get_mode = 1'b0; 101 | get_data = 1'b1; 102 | send_data = 1'b0; 103 | enable_cont = 1'b0; 104 | enable_regs = 1'b1; 105 | error = 1'b0; 106 | wait_dummy = 1'b0; 107 | reg_sel = 2'b01; 108 | end 109 | 8'h20: // write reg2 110 | begin 111 | get_addr = 1'b0; 112 | get_mode = 1'b0; 113 | get_data = 1'b1; 114 | send_data = 1'b0; 115 | enable_cont = 1'b0; 116 | enable_regs = 1'b1; 117 | error = 1'b0; 118 | wait_dummy = 1'b0; 119 | reg_sel = 2'b10; 120 | end 121 | 8'h21: // read reg2 122 | begin 123 | get_addr = 1'b0; 124 | get_mode = 1'b0; 125 | get_data = 1'b0; 126 | send_data = 1'b1; 127 | enable_cont = 1'b0; 128 | enable_regs = 1'b1; 129 | error = 1'b0; 130 | wait_dummy = 1'b0; 131 | reg_sel = 2'b10; 132 | end 133 | 8'h30: // write reg3 134 | begin 135 | get_addr = 1'b0; 136 | get_mode = 1'b0; 137 | get_data = 1'b1; 138 | send_data = 1'b0; 139 | enable_cont = 1'b0; 140 | enable_regs = 1'b1; 141 | error = 1'b0; 142 | wait_dummy = 1'b0; 143 | reg_sel = 2'b11; 144 | end 145 | 8'h31: // read reg3 146 | begin 147 | get_addr = 1'b0; 148 | get_mode = 1'b0; 149 | get_data = 1'b0; 150 | send_data = 1'b1; 151 | enable_cont = 1'b0; 152 | enable_regs = 1'b1; 153 | error = 1'b0; 154 | wait_dummy = 1'b0; 155 | reg_sel = 2'b11; 156 | end 157 | endcase 158 | end 159 | 160 | endmodule 161 | -------------------------------------------------------------------------------- /spi_slave_controller.sv: -------------------------------------------------------------------------------- 1 | // Copyright 2015 ETH Zurich and University of Bologna. 2 | // Copyright and related rights are licensed under the Solderpad Hardware 3 | // License, Version 0.51 (the “License”); you may not use this file except in 4 | // compliance with the License. You may obtain a copy of the License at 5 | // http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law 6 | // or agreed to in writing, software, hardware and materials distributed under 7 | // this License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR 8 | // CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | // specific language governing permissions and limitations under the License. 10 | 11 | `define SPI_STD 2'b00 12 | `define SPI_QUAD_TX 2'b01 13 | `define SPI_QUAD_RX 2'b10 14 | 15 | module spi_slave_controller 16 | #( 17 | parameter DUMMY_CYCLES = 32 18 | ) 19 | (input logic sclk, 20 | input logic sys_rstn, 21 | input logic cs, 22 | output logic en_quad, 23 | output logic [1:0] pad_mode, 24 | output logic [7:0] rx_counter, 25 | output logic rx_counter_upd, 26 | input logic [31:0] rx_data, 27 | input logic rx_data_valid, 28 | output logic [7:0] tx_counter, 29 | output logic tx_counter_upd, 30 | output logic [31:0] tx_data, 31 | output logic tx_data_valid, 32 | input logic tx_done, 33 | output logic ctrl_rd_wr, 34 | output logic [31:0] ctrl_addr, 35 | output logic ctrl_addr_valid, 36 | output logic [31:0] ctrl_data_rx, 37 | output logic ctrl_data_rx_valid, 38 | input logic ctrl_data_rx_ready, 39 | input logic [31:0] ctrl_data_tx, 40 | input logic ctrl_data_tx_valid, 41 | output logic ctrl_data_tx_ready, 42 | output logic [15:0] wrap_length 43 | ); 44 | 45 | localparam REG_SIZE = 8; 46 | 47 | 48 | enum logic [2:0] {CMD,ADDR,MODE,DATA_TX,DATA_RX,DUMMY,ERROR} state,state_next; 49 | 50 | logic [7:0] command; 51 | 52 | logic decode_cmd_comb; 53 | 54 | logic [31:0] addr_reg; 55 | logic [7:0] cmd_reg; 56 | logic [7:0] mode_reg; 57 | logic [31:0] data_reg; 58 | 59 | logic sample_ADDR; 60 | logic sample_MODE; 61 | logic sample_CMD; 62 | logic sample_DATA; 63 | 64 | logic get_addr; 65 | logic wait_dummy; 66 | logic get_mode; 67 | logic get_data; 68 | logic send_data; 69 | logic enable_cont; 70 | logic enable_regs; 71 | logic cmd_error; 72 | logic [1:0] reg_sel; 73 | logic [REG_SIZE-1:0] reg_data; 74 | logic reg_valid; 75 | 76 | logic ctrl_data_tx_ready_next; 77 | logic [7:0] tx_counter_next; 78 | logic tx_counter_upd_next; 79 | logic tx_data_valid_next; 80 | logic tx_done_reg; 81 | logic [1:0] pad_mode_next; 82 | 83 | logic [7:0] s_dummy_cycles; 84 | 85 | assign command = decode_cmd_comb ? rx_data : cmd_reg; 86 | 87 | spi_slave_cmd_parser u_cmd_parser( 88 | .cmd(command), 89 | .get_addr(get_addr), 90 | .get_mode(get_mode), 91 | .get_data(get_data), 92 | .send_data(send_data), 93 | .wait_dummy(wait_dummy), 94 | .enable_cont(enable_cont), 95 | .enable_regs(enable_regs), 96 | .error(cmd_error), 97 | .reg_sel(reg_sel) 98 | ); 99 | 100 | spi_slave_regs #( 101 | .REG_SIZE(REG_SIZE) 102 | ) u_spiregs( 103 | .sclk(sclk), 104 | .rstn(sys_rstn), 105 | .wr_data(rx_data[REG_SIZE-1:0]), 106 | .wr_addr(reg_sel), 107 | .wr_data_valid(reg_valid), 108 | .rd_data(reg_data), 109 | .rd_addr(reg_sel), 110 | .dummy_cycles(s_dummy_cycles), 111 | .en_qpi(en_quad), 112 | .wrap_length(wrap_length) 113 | ); 114 | always_comb 115 | begin 116 | pad_mode = en_quad ? `SPI_QUAD_RX : `SPI_STD; 117 | rx_counter = 8'h1F; 118 | rx_counter_upd = 0; 119 | tx_counter_next = 8'h1F; 120 | tx_counter_upd_next = 0; 121 | decode_cmd_comb = 1'b0; 122 | sample_ADDR = 1'b0; 123 | sample_MODE = 1'b0; 124 | sample_CMD = 1'b0; 125 | sample_DATA = 1'b0; 126 | ctrl_data_rx_valid = 1'b0; 127 | ctrl_data_tx_ready_next = 1'b0; 128 | reg_valid = 1'b0; 129 | tx_data_valid_next = 1'b0; 130 | state_next = state; 131 | case(state) 132 | CMD: 133 | begin 134 | pad_mode = en_quad ? `SPI_QUAD_RX : `SPI_STD; 135 | decode_cmd_comb = 1'b1; 136 | ctrl_data_tx_ready_next = 1'b1; //empty TX fifo if not allready empty 137 | if(rx_data_valid) 138 | begin 139 | sample_CMD = 1'b1; 140 | if (get_addr) 141 | begin 142 | state_next = ADDR; 143 | rx_counter_upd = 1; 144 | rx_counter = en_quad ? 8'h7 : 8'h1F; 145 | end 146 | else if (get_data) 147 | begin 148 | state_next = DATA_RX; 149 | rx_counter_upd = 1; 150 | if (enable_regs) 151 | rx_counter = en_quad ? 8'h1 : 8'h7; 152 | end 153 | else 154 | begin 155 | state_next = DATA_TX; 156 | tx_counter_upd_next = 1; 157 | tx_data_valid_next = 1'b1; 158 | tx_counter_next = en_quad ? 8'h7 : 8'h1F; 159 | if (~enable_regs) 160 | ctrl_data_tx_ready_next = 1'b1; 161 | end 162 | end 163 | else 164 | begin 165 | state_next = CMD; 166 | end 167 | end 168 | ADDR: 169 | begin 170 | pad_mode = en_quad ? `SPI_QUAD_RX : `SPI_STD; 171 | ctrl_data_tx_ready_next = 1'b1; 172 | if(rx_data_valid) 173 | begin 174 | sample_ADDR = 1'b1; 175 | if (wait_dummy) 176 | begin 177 | state_next = DUMMY; 178 | rx_counter = s_dummy_cycles; 179 | rx_counter_upd = 1; 180 | end 181 | else if (send_data) 182 | begin 183 | state_next = DATA_TX; 184 | tx_counter_upd_next = 1; 185 | tx_counter_next = en_quad ? 8'h7 : 8'h1F; 186 | end 187 | else if (get_data) 188 | begin 189 | state_next = DATA_RX; 190 | rx_counter_upd = 1; 191 | rx_counter = en_quad ? 8'h7 : 8'h1F; 192 | end 193 | end 194 | else 195 | begin 196 | state_next = ADDR; 197 | end 198 | end 199 | MODE: 200 | begin 201 | pad_mode = en_quad ? `SPI_QUAD_RX : `SPI_STD; 202 | if(rx_data_valid) 203 | begin 204 | if (wait_dummy) 205 | begin 206 | state_next = DUMMY; 207 | rx_counter = DUMMY_CYCLES; 208 | rx_counter_upd = 1; 209 | end 210 | else if (get_data) 211 | begin 212 | state_next = DATA_RX; 213 | rx_counter = en_quad ? 8'h7 : 8'h1F; 214 | rx_counter_upd = 1; 215 | end 216 | else if (send_data) 217 | begin 218 | state_next = DATA_TX; 219 | tx_counter_next = en_quad ? 8'h7 : 8'h1F; 220 | tx_counter_upd_next = 1; 221 | tx_data_valid_next = 1'b1; 222 | if (~enable_regs) 223 | ctrl_data_tx_ready_next = 1'b1; 224 | end 225 | end 226 | else 227 | begin 228 | state_next = MODE; 229 | end 230 | end 231 | DUMMY: 232 | begin 233 | pad_mode = en_quad ? `SPI_QUAD_RX : `SPI_STD; 234 | if(rx_data_valid) 235 | begin 236 | if (get_data) 237 | begin 238 | state_next = DATA_RX; 239 | rx_counter = en_quad ? 8'h7 : 8'h1F; 240 | rx_counter_upd = 1; 241 | end 242 | else 243 | begin 244 | if (en_quad) 245 | pad_mode_next = `SPI_QUAD_TX; 246 | state_next = DATA_TX; 247 | tx_counter_next = en_quad ? 8'h7 : 8'h1F; 248 | tx_counter_upd_next = 1; 249 | tx_data_valid_next = 1'b1; 250 | if (~enable_regs) 251 | ctrl_data_tx_ready_next = 1'b1; 252 | end 253 | end 254 | else 255 | begin 256 | state_next = DUMMY; 257 | end 258 | end 259 | DATA_RX: 260 | begin 261 | pad_mode = en_quad ? `SPI_QUAD_RX : `SPI_STD; 262 | if(rx_data_valid) 263 | begin 264 | if (enable_regs) 265 | reg_valid = 1'b1; 266 | else 267 | ctrl_data_rx_valid = 1'b1; 268 | if (enable_cont) 269 | begin 270 | state_next = DATA_RX; 271 | rx_counter = en_quad ? 8'h7 : 8'h1F; 272 | rx_counter_upd = 1; 273 | end 274 | else 275 | begin 276 | state_next = CMD; 277 | rx_counter = en_quad ? 8'h1 : 8'h7; 278 | rx_counter_upd = 1; 279 | end 280 | end 281 | else 282 | begin 283 | state_next = DATA_RX; 284 | end 285 | end 286 | DATA_TX: 287 | begin 288 | pad_mode = en_quad ? `SPI_QUAD_TX : `SPI_STD; 289 | if(tx_done_reg) 290 | begin 291 | if (enable_cont) 292 | begin 293 | state_next = DATA_TX; 294 | tx_counter_next = en_quad ? 8'h7 : 8'h1F; 295 | tx_counter_upd_next = 1; 296 | tx_data_valid_next = 1'b1; 297 | if (~enable_regs) 298 | ctrl_data_tx_ready_next = 1'b1; 299 | end 300 | else 301 | begin 302 | state_next = CMD; 303 | rx_counter = en_quad ? 8'h1 : 8'h7; 304 | rx_counter_upd = 1; 305 | end 306 | end 307 | else 308 | begin 309 | state_next = DATA_TX; 310 | end 311 | end 312 | ERROR: 313 | begin 314 | state_next = ERROR; 315 | end 316 | endcase 317 | end 318 | 319 | 320 | always @(posedge sclk or posedge cs) 321 | begin 322 | if (cs == 1'b1) 323 | begin 324 | state <= CMD; 325 | end 326 | else 327 | begin 328 | state <= state_next; 329 | end 330 | end 331 | 332 | always @(posedge sclk or posedge cs) 333 | begin 334 | if (cs == 1'b1) 335 | begin 336 | addr_reg = 'h0; 337 | mode_reg = 'h0; 338 | data_reg = 'h0; 339 | cmd_reg = 'h0; 340 | tx_done_reg = 1'b0; 341 | ctrl_addr_valid = 1'b0; 342 | tx_counter_upd = 1'b0; 343 | tx_data_valid = 1'b0; 344 | ctrl_data_tx_ready = 1'b0; 345 | tx_counter = 'h0; 346 | tx_data = 'h0; 347 | end 348 | else 349 | begin 350 | if (sample_ADDR) addr_reg = rx_data; 351 | if (sample_MODE) mode_reg = rx_data[7:0]; 352 | if (sample_CMD) cmd_reg = rx_data[7:0]; 353 | if (sample_DATA) data_reg = rx_data; 354 | ctrl_addr_valid = sample_ADDR; 355 | tx_counter_upd = tx_counter_upd_next; 356 | tx_counter = tx_counter_next; 357 | tx_data_valid = tx_data_valid_next; 358 | tx_done_reg = tx_done; 359 | ctrl_data_tx_ready = ctrl_data_tx_ready_next; 360 | tx_data = (enable_regs) ? reg_data : ctrl_data_tx; 361 | end 362 | end 363 | 364 | assign ctrl_data_rx = rx_data; 365 | assign ctrl_addr = addr_reg; 366 | assign ctrl_rd_wr = send_data; 367 | 368 | endmodule 369 | -------------------------------------------------------------------------------- /spi_slave_dc_fifo.sv: -------------------------------------------------------------------------------- 1 | // Copyright 2015 ETH Zurich and University of Bologna. 2 | // Copyright and related rights are licensed under the Solderpad Hardware 3 | // License, Version 0.51 (the “License”); you may not use this file except in 4 | // compliance with the License. You may obtain a copy of the License at 5 | // http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law 6 | // or agreed to in writing, software, hardware and materials distributed under 7 | // this License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR 8 | // CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | // specific language governing permissions and limitations under the License. 10 | 11 | module spi_slave_dc_fifo #( 12 | parameter DATA_WIDTH = 32, 13 | parameter BUFFER_DEPTH = 8 14 | ) 15 | (input logic clk_a, 16 | input logic rstn_a, 17 | input logic [DATA_WIDTH-1:0] data_a, 18 | input logic valid_a, 19 | output logic ready_a, 20 | input logic clk_b, 21 | input logic rstn_b, 22 | output logic [DATA_WIDTH-1:0] data_b, 23 | output logic valid_b, 24 | input logic ready_b 25 | ); 26 | 27 | logic [DATA_WIDTH-1:0] data_async; 28 | logic [BUFFER_DEPTH-1:0] write_token; 29 | logic [BUFFER_DEPTH-1:0] read_pointer; 30 | 31 | dc_token_ring_fifo_din #( 32 | .DATA_WIDTH(DATA_WIDTH), 33 | .BUFFER_DEPTH(BUFFER_DEPTH) 34 | ) u_din ( 35 | .clk(clk_a), 36 | .rstn(rstn_a), 37 | .data(data_a), 38 | .valid(valid_a), 39 | .ready(ready_a), 40 | .write_token(write_token), 41 | .read_pointer(read_pointer), 42 | .data_async(data_async)); 43 | 44 | dc_token_ring_fifo_dout #( 45 | .DATA_WIDTH(DATA_WIDTH), 46 | .BUFFER_DEPTH(BUFFER_DEPTH) 47 | ) u_dout (.clk(clk_b), 48 | .rstn(rstn_b), 49 | .data(data_b), 50 | .valid(valid_b), 51 | .ready(ready_b), 52 | .write_token(write_token), 53 | .read_pointer(read_pointer), 54 | .data_async(data_async)); 55 | 56 | endmodule 57 | -------------------------------------------------------------------------------- /spi_slave_regs.sv: -------------------------------------------------------------------------------- 1 | // Copyright 2015 ETH Zurich and University of Bologna. 2 | // Copyright and related rights are licensed under the Solderpad Hardware 3 | // License, Version 0.51 (the “License”); you may not use this file except in 4 | // compliance with the License. You may obtain a copy of the License at 5 | // http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law 6 | // or agreed to in writing, software, hardware and materials distributed under 7 | // this License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR 8 | // CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | // specific language governing permissions and limitations under the License. 10 | 11 | module spi_slave_regs #( 12 | parameter REG_SIZE = 8 13 | ) ( 14 | input logic sclk, 15 | input logic rstn, 16 | input logic [REG_SIZE-1:0] wr_data, 17 | input logic [1:0] wr_addr, 18 | input logic wr_data_valid, 19 | output logic [REG_SIZE-1:0] rd_data, 20 | input logic [1:0] rd_addr, 21 | output logic [7:0] dummy_cycles, 22 | output logic en_qpi, 23 | output logic [15:0] wrap_length 24 | ); 25 | 26 | logic [REG_SIZE-1:0] reg0; // bit 0 enables qpi 27 | logic [REG_SIZE-1:0] reg1; // number of dummy cycles 28 | logic [REG_SIZE-1:0] reg2; // wrap length, low 29 | logic [REG_SIZE-1:0] reg3; // wrap length, high 30 | 31 | assign en_qpi = reg0[0]; 32 | assign dummy_cycles = reg1; 33 | assign wrap_length = {reg3,reg2}; 34 | 35 | always_comb 36 | begin 37 | case(rd_addr) 38 | 2'b00: 39 | rd_data = reg0; 40 | 2'b01: 41 | rd_data = reg1; 42 | 2'b10: 43 | rd_data = reg2; 44 | 2'b11: 45 | rd_data = reg3; 46 | endcase 47 | end 48 | 49 | always @(posedge sclk or negedge rstn) 50 | begin 51 | if (rstn == 0) 52 | begin 53 | reg0= 'h0; 54 | reg1= 'd32; 55 | reg2= 'h0; 56 | reg3= 'h0; 57 | end 58 | else 59 | begin 60 | if (wr_data_valid) 61 | begin 62 | case(wr_addr) 63 | 2'b00: 64 | reg0=wr_data; 65 | 2'b01: 66 | reg1=wr_data; 67 | 2'b10: 68 | reg2=wr_data; 69 | 2'b11: 70 | reg3=wr_data; 71 | endcase 72 | end 73 | end 74 | end 75 | endmodule 76 | -------------------------------------------------------------------------------- /spi_slave_rx.sv: -------------------------------------------------------------------------------- 1 | // Copyright 2015 ETH Zurich and University of Bologna. 2 | // Copyright and related rights are licensed under the Solderpad Hardware 3 | // License, Version 0.51 (the “License”); you may not use this file except in 4 | // compliance with the License. You may obtain a copy of the License at 5 | // http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law 6 | // or agreed to in writing, software, hardware and materials distributed under 7 | // this License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR 8 | // CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | // specific language governing permissions and limitations under the License. 10 | 11 | module spi_slave_rx 12 | ( 13 | input logic sclk, 14 | input logic cs, 15 | input logic sdi0, 16 | input logic sdi1, 17 | input logic sdi2, 18 | input logic sdi3, 19 | input logic en_quad_in, 20 | input logic [7:0] counter_in, 21 | input logic counter_in_upd, 22 | output logic [31:0] data, 23 | output logic data_ready 24 | ); 25 | 26 | reg [31:0] data_int; 27 | reg [31:0] data_int_next; 28 | reg [7:0] counter; 29 | reg [7:0] counter_trgt; 30 | reg [7:0] counter_next; 31 | reg [7:0] counter_trgt_next; 32 | 33 | logic running; 34 | logic running_next; 35 | 36 | assign data = data_int_next; 37 | 38 | always_comb 39 | begin 40 | if (counter_in_upd) 41 | counter_trgt_next = counter_in; 42 | else if ((counter_trgt == 8'h1) && !en_quad_in) 43 | counter_trgt_next = 8'h7; 44 | else 45 | counter_trgt_next = counter_trgt; 46 | 47 | if (counter_in_upd) 48 | running_next = 1'b1; 49 | else if (counter == counter_trgt) 50 | running_next = 1'b0; 51 | else 52 | running_next = running; 53 | 54 | if (running) 55 | begin 56 | if (counter == counter_trgt) 57 | begin 58 | counter_next = 'h0; 59 | data_ready = 1'b1; 60 | end 61 | else 62 | begin 63 | counter_next = counter + 1; 64 | data_ready = 1'b0; 65 | end 66 | if (en_quad_in) 67 | data_int_next = {data_int[27:0],sdi3,sdi2,sdi1,sdi0}; 68 | else 69 | data_int_next = {data_int[30:0],sdi0}; 70 | end 71 | else 72 | begin 73 | counter_next = counter; 74 | data_ready = 1'b0; 75 | data_int_next = data_int; 76 | end 77 | end 78 | 79 | 80 | always @(posedge sclk or posedge cs) 81 | begin 82 | if (cs == 1'b1) 83 | begin 84 | counter = 0; 85 | counter_trgt = 'h1; 86 | data_int = 'h0; 87 | running = 'h1; 88 | end 89 | else 90 | begin 91 | counter = counter_next; 92 | counter_trgt = counter_trgt_next; 93 | data_int = data_int_next; 94 | running = running_next; 95 | end 96 | end 97 | endmodule 98 | -------------------------------------------------------------------------------- /spi_slave_syncro.sv: -------------------------------------------------------------------------------- 1 | // Copyright 2015 ETH Zurich and University of Bologna. 2 | // Copyright and related rights are licensed under the Solderpad Hardware 3 | // License, Version 0.51 (the “License”); you may not use this file except in 4 | // compliance with the License. You may obtain a copy of the License at 5 | // http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law 6 | // or agreed to in writing, software, hardware and materials distributed under 7 | // this License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR 8 | // CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | // specific language governing permissions and limitations under the License. 10 | 11 | module spi_slave_syncro 12 | #( 13 | parameter AXI_ADDR_WIDTH = 32 14 | ) 15 | ( 16 | input logic sys_clk, 17 | input logic rstn, 18 | input logic cs, 19 | input logic [AXI_ADDR_WIDTH-1:0] address, 20 | input logic address_valid, 21 | input logic rd_wr, 22 | output logic cs_sync, 23 | output logic [AXI_ADDR_WIDTH-1:0] address_sync, 24 | output logic address_valid_sync, 25 | output logic rd_wr_sync 26 | ); 27 | 28 | logic [1:0] cs_reg; 29 | logic [2:0] valid_reg; 30 | logic [1:0] rdwr_reg; 31 | 32 | assign cs_sync = cs_reg[1]; 33 | assign address_valid_sync = ~valid_reg[2] & valid_reg[1]; //detect rising edge of addr valid 34 | assign address_sync = address; 35 | assign rd_wr_sync = rdwr_reg[1]; 36 | 37 | always @(posedge sys_clk or negedge rstn) 38 | begin 39 | if(rstn == 1'b0) 40 | begin 41 | cs_reg <= 2'b11; 42 | valid_reg <= 3'b000; 43 | rdwr_reg <= 2'b00; 44 | end 45 | else 46 | begin 47 | cs_reg <= {cs_reg[0],cs}; 48 | valid_reg <= {valid_reg[1:0],address_valid}; 49 | rdwr_reg <= {rdwr_reg[0],rd_wr}; 50 | end 51 | end 52 | 53 | endmodule 54 | -------------------------------------------------------------------------------- /spi_slave_tx.sv: -------------------------------------------------------------------------------- 1 | // Copyright 2015 ETH Zurich and University of Bologna. 2 | // Copyright and related rights are licensed under the Solderpad Hardware 3 | // License, Version 0.51 (the “License”); you may not use this file except in 4 | // compliance with the License. You may obtain a copy of the License at 5 | // http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law 6 | // or agreed to in writing, software, hardware and materials distributed under 7 | // this License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR 8 | // CONDITIONS OF ANY KIND, either express or implied. See the License for the 9 | // specific language governing permissions and limitations under the License. 10 | 11 | module spi_slave_tx 12 | ( 13 | input logic test_mode, 14 | input logic sclk, 15 | input logic cs, 16 | output logic sdo0, 17 | output logic sdo1, 18 | output logic sdo2, 19 | output logic sdo3, 20 | input logic en_quad_in, 21 | input logic [7:0] counter_in, 22 | input logic counter_in_upd, 23 | input logic [31:0] data, 24 | input logic data_valid, 25 | output logic done 26 | ); 27 | 28 | reg [31:0] data_int; 29 | reg [31:0] data_int_next; 30 | reg [7:0] counter; 31 | reg [7:0] counter_trgt; 32 | reg [7:0] counter_next; 33 | reg [7:0] counter_trgt_next; 34 | logic running; 35 | logic running_next; 36 | 37 | logic sclk_inv; 38 | logic sclk_test; 39 | 40 | assign sdo0 = (en_quad_in) ? data_int[28] : data_int[31]; 41 | assign sdo1 = (en_quad_in) ? data_int[29] : 1'b0; 42 | assign sdo2 = (en_quad_in) ? data_int[30] : 1'b0; 43 | assign sdo3 = (en_quad_in) ? data_int[31] : 1'b0; 44 | 45 | always_comb 46 | begin 47 | done = 1'b0; 48 | if (counter_in_upd) 49 | counter_trgt_next = counter_in; 50 | else 51 | counter_trgt_next = counter_trgt; 52 | 53 | if (counter_in_upd) 54 | running_next = 1'b1; 55 | else if (counter == counter_trgt) 56 | running_next = 1'b0; 57 | else 58 | running_next = running; 59 | 60 | if (running || counter_in_upd) 61 | begin 62 | if (counter == counter_trgt) 63 | begin 64 | done = 1'b1; 65 | counter_next = 0; 66 | end 67 | else 68 | counter_next = counter + 1; 69 | 70 | if (data_valid) 71 | data_int_next = data; 72 | else 73 | begin 74 | if (en_quad_in) 75 | data_int_next = {data_int[27:0],4'b0000}; 76 | else 77 | data_int_next = {data_int[30:0],1'b0}; 78 | end 79 | end 80 | else 81 | begin 82 | counter_next = counter; 83 | data_int_next = data_int; 84 | end 85 | end 86 | 87 | pulp_clock_inverter clk_inv_i 88 | ( 89 | .clk_i(sclk), 90 | .clk_o(sclk_inv) 91 | ); 92 | 93 | pulp_clock_mux2 clk_mux_i 94 | ( 95 | .clk0_i(sclk_inv), 96 | .clk1_i(sclk), 97 | .clk_sel_i(test_mode), 98 | .clk_o(sclk_test) 99 | ); 100 | 101 | always @(posedge sclk_test or posedge cs) 102 | begin 103 | if (cs == 1'b1) 104 | begin 105 | counter = 'h0; 106 | counter_trgt = 'h7; 107 | data_int = 'h0; 108 | running = 1'b0; 109 | end 110 | else 111 | begin 112 | counter = counter_next; 113 | counter_trgt = counter_trgt_next; 114 | data_int = data_int_next; 115 | running = running_next; 116 | end 117 | end 118 | endmodule 119 | -------------------------------------------------------------------------------- /src_files.yml: -------------------------------------------------------------------------------- 1 | axi_spi_slave: 2 | files: [ 3 | axi_spi_slave.sv, 4 | spi_slave_axi_plug.sv, 5 | spi_slave_cmd_parser.sv, 6 | spi_slave_controller.sv, 7 | spi_slave_dc_fifo.sv, 8 | spi_slave_regs.sv, 9 | spi_slave_rx.sv, 10 | spi_slave_syncro.sv, 11 | spi_slave_tx.sv, 12 | ] 13 | --------------------------------------------------------------------------------