├── src_files.yml ├── README.md ├── CHANGELOG.md ├── spi_master_clkgen.sv ├── spi_master_tx.sv ├── spi_master_rx.sv ├── spi_master_fifo.sv ├── LICENSE ├── axi_spi_master.sv ├── spi_master_controller.sv └── spi_master_axi_if.sv /src_files.yml: -------------------------------------------------------------------------------- 1 | axi_spi_master: 2 | files: [ 3 | axi_spi_master.sv, 4 | spi_master_axi_if.sv, 5 | spi_master_clkgen.sv, 6 | spi_master_controller.sv, 7 | spi_master_fifo.sv, 8 | spi_master_rx.sv, 9 | spi_master_tx.sv, 10 | ] 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AXI SPI Master 2 | 3 | This is an implementation of an SPI master that is controlled via an AXI bus. 4 | It has FIFOs for transmitting and receiving data. It supports both the normal 5 | SPI mode and QPI mode with 4 data lines. 6 | This IP was written for the use in the [PULP platform](http://pulp.ethz.ch/). 7 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](http://keepachangelog.com/), and this project adheres to 6 | [Semantic Versioning](http://semver.org). 7 | 8 | ## [v0.1.1] - 2018-06-21 9 | 10 | ### Fixed 11 | 12 | - In normal SPI mode, data is now correctly read from the `sdi1` (MISO) pin, instead of the `sdi0` 13 | (MOSI) pin. 14 | 15 | ## v0.1.0 - 2018-06-21 16 | 17 | Initial versioned release. 18 | 19 | [v0.1.1]: https://github.com/pulp-platform/axi_spi_master/compare/v0.1.0...v0.1.1 20 | -------------------------------------------------------------------------------- /spi_master_clkgen.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_master_clkgen 12 | ( 13 | input logic clk, 14 | input logic rstn, 15 | input logic en, 16 | input logic [7:0] clk_div, 17 | input logic clk_div_valid, 18 | output logic spi_clk, 19 | output logic spi_fall, 20 | output logic spi_rise 21 | ); 22 | 23 | logic [7:0] counter_trgt; 24 | logic [7:0] counter_trgt_next; 25 | logic [7:0] counter; 26 | logic [7:0] counter_next; 27 | 28 | logic spi_clk_next; 29 | logic running; 30 | 31 | always_comb 32 | begin 33 | spi_rise = 1'b0; 34 | spi_fall = 1'b0; 35 | if (clk_div_valid) 36 | counter_trgt_next = clk_div; 37 | else 38 | counter_trgt_next = counter_trgt; 39 | 40 | if (counter == counter_trgt) 41 | begin 42 | counter_next = 0; 43 | spi_clk_next = ~spi_clk; 44 | if(spi_clk == 1'b0) 45 | spi_rise = running; 46 | else 47 | spi_fall = running; 48 | end 49 | else 50 | begin 51 | counter_next = counter + 1; 52 | spi_clk_next = spi_clk; 53 | end 54 | end 55 | 56 | always_ff @(posedge clk, negedge rstn) 57 | begin 58 | if (rstn == 1'b0) 59 | begin 60 | counter_trgt <= 'h0; 61 | counter <= 'h0; 62 | spi_clk <= 1'b0; 63 | running <= 1'b0; 64 | end 65 | else 66 | begin 67 | counter_trgt <= counter_trgt_next; 68 | if ( !((spi_clk==1'b0)&&(~en)) ) 69 | begin 70 | running <= 1'b1; 71 | spi_clk <= spi_clk_next; 72 | counter <= counter_next; 73 | end 74 | else 75 | running <= 1'b0; 76 | end 77 | end 78 | 79 | 80 | 81 | endmodule 82 | -------------------------------------------------------------------------------- /spi_master_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_master_tx 12 | ( 13 | input logic clk, 14 | input logic rstn, 15 | input logic en, 16 | input logic tx_edge, 17 | output logic tx_done, 18 | output logic sdo0, 19 | output logic sdo1, 20 | output logic sdo2, 21 | output logic sdo3, 22 | input logic en_quad_in, 23 | input logic [15:0] counter_in, 24 | input logic counter_in_upd, 25 | input logic [31:0] data, 26 | input logic data_valid, 27 | output logic data_ready, 28 | output logic clk_en_o 29 | ); 30 | 31 | logic [31:0] data_int; 32 | logic [31:0] data_int_next; 33 | logic [15:0] counter; 34 | logic [15:0] counter_trgt; 35 | logic [15:0] counter_next; 36 | logic [15:0] counter_trgt_next; 37 | logic done; 38 | logic reg_done; 39 | 40 | enum logic [0:0] { IDLE, TRANSMIT } tx_CS, tx_NS; 41 | 42 | assign sdo0 = (en_quad_in) ? data_int[28] : data_int[31]; 43 | assign sdo1 = data_int[29]; 44 | assign sdo2 = data_int[30]; 45 | assign sdo3 = data_int[31]; 46 | 47 | assign tx_done = done; 48 | 49 | assign reg_done = (!en_quad_in && (counter[4:0] == 5'b11111)) || (en_quad_in && (counter[2:0] == 3'b111)); 50 | 51 | always_comb 52 | begin 53 | if (counter_in_upd) 54 | counter_trgt_next = (en_quad_in) ? {2'b00,counter_in[15:2]} : counter_in; 55 | else 56 | counter_trgt_next = counter_trgt; 57 | end 58 | 59 | assign done = (counter == counter_trgt-1) && tx_edge; 60 | 61 | always_comb 62 | begin 63 | tx_NS = tx_CS; 64 | clk_en_o = 1'b0; 65 | data_int_next = data_int; 66 | data_ready = 1'b0; 67 | counter_next = counter; 68 | 69 | case (tx_CS) 70 | IDLE: begin 71 | clk_en_o = 1'b0; 72 | 73 | if (en && data_valid) begin 74 | data_int_next = data; 75 | data_ready = 1'b1; 76 | tx_NS = TRANSMIT; 77 | end 78 | end 79 | 80 | TRANSMIT: begin 81 | clk_en_o = 1'b1; 82 | 83 | if (tx_edge) begin 84 | counter_next = counter + 1; 85 | data_int_next = (en_quad_in) ? {data_int[27:0],4'b0000} : {data_int[30:0],1'b0}; 86 | 87 | if (tx_done) begin 88 | counter_next = 0; 89 | 90 | if (en && data_valid) begin 91 | data_int_next = data; 92 | data_ready = 1'b1; 93 | tx_NS = TRANSMIT; 94 | end else begin 95 | clk_en_o = 1'b0; 96 | tx_NS = IDLE; 97 | end 98 | end else if (reg_done) begin 99 | if (data_valid) begin 100 | data_int_next = data; 101 | data_ready = 1'b1; 102 | end else begin 103 | clk_en_o = 1'b0; 104 | tx_NS = IDLE; 105 | end 106 | end 107 | end 108 | end 109 | endcase 110 | end 111 | 112 | always_ff @(posedge clk, negedge rstn) 113 | begin 114 | if (~rstn) 115 | begin 116 | counter <= 0; 117 | counter_trgt <= 'h8; 118 | data_int <= 'h0; 119 | tx_CS <= IDLE; 120 | end 121 | else 122 | begin 123 | counter <= counter_next; 124 | counter_trgt <= counter_trgt_next; 125 | data_int <= data_int_next; 126 | tx_CS <= tx_NS; 127 | end 128 | end 129 | endmodule 130 | -------------------------------------------------------------------------------- /spi_master_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_master_rx 12 | ( 13 | input logic clk, 14 | input logic rstn, 15 | input logic en, 16 | input logic rx_edge, 17 | output logic rx_done, 18 | input logic sdi0, 19 | input logic sdi1, 20 | input logic sdi2, 21 | input logic sdi3, 22 | input logic en_quad_in, 23 | input logic [15:0] counter_in, 24 | input logic counter_in_upd, 25 | output logic [31:0] data, 26 | input logic data_ready, 27 | output logic data_valid, 28 | output logic clk_en_o 29 | ); 30 | 31 | logic [31:0] data_int; 32 | logic [31:0] data_int_next; 33 | logic [15:0] counter; 34 | logic [15:0] counter_trgt; 35 | logic [15:0] counter_next; 36 | logic [15:0] counter_trgt_next; 37 | logic done; 38 | logic reg_done; 39 | enum logic [1:0] { IDLE, RECEIVE, WAIT_FIFO, WAIT_FIFO_DONE } rx_CS, rx_NS; 40 | 41 | 42 | assign reg_done = (!en_quad_in && (counter[4:0] == 5'b11111)) || (en_quad_in && (counter[2:0] == 3'b111)); 43 | 44 | assign data = data_int_next; 45 | assign rx_done = done; 46 | 47 | always_comb 48 | begin 49 | if (counter_in_upd) 50 | counter_trgt_next = (en_quad_in) ? {2'b00,counter_in[15:2]} : counter_in; 51 | else 52 | counter_trgt_next = counter_trgt; 53 | end 54 | 55 | assign done = (counter == counter_trgt-1) && rx_edge; 56 | 57 | always_comb 58 | begin 59 | rx_NS = rx_CS; 60 | clk_en_o = 1'b0; 61 | data_int_next = data_int; 62 | data_valid = 1'b0; 63 | counter_next = counter; 64 | 65 | case (rx_CS) 66 | IDLE: begin 67 | clk_en_o = 1'b0; 68 | 69 | // check first if there is available space instead of later 70 | if (en) begin 71 | rx_NS = RECEIVE; 72 | end 73 | end 74 | 75 | RECEIVE: begin 76 | clk_en_o = 1'b1; 77 | 78 | if (rx_edge) begin 79 | counter_next = counter + 1; 80 | 81 | if (en_quad_in) 82 | data_int_next = {data_int[27:0],sdi3,sdi2,sdi1,sdi0}; 83 | else 84 | data_int_next = {data_int[30:0],sdi1}; 85 | 86 | if (rx_done) begin 87 | counter_next = 0; 88 | data_valid = 1'b1; 89 | 90 | if (data_ready) 91 | rx_NS = IDLE; 92 | else 93 | rx_NS = WAIT_FIFO_DONE; 94 | end else if (reg_done) begin 95 | data_valid = 1'b1; 96 | 97 | if (~data_ready) begin 98 | // no space in the FIFO, wait for free space 99 | clk_en_o = 1'b0; 100 | rx_NS = WAIT_FIFO; 101 | end 102 | end 103 | end 104 | end 105 | 106 | WAIT_FIFO_DONE: begin 107 | data_valid = 1'b1; 108 | if (data_ready) 109 | rx_NS = IDLE; 110 | end 111 | 112 | WAIT_FIFO: begin 113 | data_valid = 1'b1; 114 | if (data_ready) 115 | rx_NS = RECEIVE; 116 | end 117 | endcase 118 | end 119 | 120 | 121 | always_ff @(posedge clk, negedge rstn) 122 | begin 123 | if (rstn == 0) 124 | begin 125 | counter <= 0; 126 | counter_trgt <= 'h8; 127 | data_int <= '0; 128 | rx_CS <= IDLE; 129 | end 130 | else 131 | begin 132 | counter <= counter_next; 133 | counter_trgt <= counter_trgt_next; 134 | data_int <= data_int_next; 135 | rx_CS <= rx_NS; 136 | end 137 | end 138 | 139 | endmodule 140 | -------------------------------------------------------------------------------- /spi_master_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 | `define log2(VALUE) ((VALUE) < ( 1 ) ? 0 : (VALUE) < ( 2 ) ? 1 : (VALUE) < ( 4 ) ? 2 : (VALUE) < ( 8 ) ? 3 : (VALUE) < ( 16 ) ? 4 : (VALUE) < ( 32 ) ? 5 : (VALUE) < ( 64 ) ? 6 : (VALUE) < ( 128 ) ? 7 : (VALUE) < ( 256 ) ? 8 : (VALUE) < ( 512 ) ? 9 : (VALUE) < ( 1024 ) ? 10 : (VALUE) < ( 2048 ) ? 11 : (VALUE) < ( 4096 ) ? 12 : (VALUE) < ( 8192 ) ? 13 : (VALUE) < ( 16384 ) ? 14 : (VALUE) < ( 32768 ) ? 15 : (VALUE) < ( 65536 ) ? 16 : (VALUE) < ( 131072 ) ? 17 : (VALUE) < ( 262144 ) ? 18 : (VALUE) < ( 524288 ) ? 19 : (VALUE) < ( 1048576 ) ? 20 : (VALUE) < ( 1048576 * 2 ) ? 21 : (VALUE) < ( 1048576 * 4 ) ? 22 : (VALUE) < ( 1048576 * 8 ) ? 23 : (VALUE) < ( 1048576 * 16 ) ? 24 : 25) 12 | 13 | module spi_master_fifo 14 | #( 15 | parameter DATA_WIDTH = 32, 16 | parameter BUFFER_DEPTH = 2, 17 | parameter LOG_BUFFER_DEPTH = `log2(BUFFER_DEPTH) 18 | ) 19 | ( 20 | input logic clk_i, 21 | input logic rst_ni, 22 | 23 | input logic clr_i, 24 | 25 | output logic [LOG_BUFFER_DEPTH:0] elements_o, 26 | 27 | output logic [DATA_WIDTH-1 : 0] data_o, 28 | output logic valid_o, 29 | input logic ready_i, 30 | 31 | input logic valid_i, 32 | input logic [DATA_WIDTH-1 : 0] data_i, 33 | output logic ready_o 34 | ); 35 | 36 | // Internal data structures 37 | logic [LOG_BUFFER_DEPTH-1:0] pointer_in; // location to which we last wrote 38 | logic [LOG_BUFFER_DEPTH-1:0] pointer_out; // location from which we last sent 39 | logic [LOG_BUFFER_DEPTH:0] elements; // number of elements in the buffer 40 | logic [DATA_WIDTH-1:0] buffer [BUFFER_DEPTH - 1 : 0]; 41 | 42 | wire full; 43 | 44 | integer loop1; 45 | 46 | assign full = (elements == BUFFER_DEPTH); 47 | assign elements_o = elements; 48 | 49 | always_ff @(posedge clk_i, negedge rst_ni) 50 | begin: elements_sequential 51 | if (rst_ni == 1'b0) 52 | elements <= 0; 53 | else 54 | begin 55 | if (clr_i) 56 | elements <= 0; 57 | else 58 | begin 59 | // ------------------ 60 | // Are we filling up? 61 | // ------------------ 62 | // One out, none in 63 | if (ready_i && valid_o && (!valid_i || full)) 64 | elements <= elements - 1; 65 | // None out, one in 66 | else if ((!valid_o || !ready_i) && valid_i && !full) 67 | elements <= elements + 1; 68 | // Else, either one out and one in, or none out and none in - stays unchanged 69 | end 70 | end 71 | end 72 | 73 | always_ff @(posedge clk_i, negedge rst_ni) 74 | begin: buffers_sequential 75 | if (rst_ni == 1'b0) 76 | begin 77 | for (loop1 = 0 ; loop1 < BUFFER_DEPTH ; loop1 = loop1 + 1) 78 | buffer[loop1] <= 0; 79 | end 80 | else 81 | begin 82 | // Update the memory 83 | if (valid_i && !full) 84 | buffer[pointer_in] <= data_i; 85 | end 86 | end 87 | 88 | always_ff @(posedge clk_i, negedge rst_ni) 89 | begin: sequential 90 | if (rst_ni == 1'b0) 91 | begin 92 | pointer_out <= 0; 93 | pointer_in <= 0; 94 | end 95 | else 96 | begin 97 | if(clr_i) 98 | begin 99 | pointer_out <= 0; 100 | pointer_in <= 0; 101 | end 102 | else 103 | begin 104 | // ------------------------------------ 105 | // Check what to do with the input side 106 | // ------------------------------------ 107 | // We have some input, increase by 1 the input pointer 108 | if (valid_i && !full) 109 | begin 110 | if (pointer_in == $unsigned(BUFFER_DEPTH - 1)) 111 | pointer_in <= 0; 112 | else 113 | pointer_in <= pointer_in + 1; 114 | end 115 | // Else we don't have any input, the input pointer stays the same 116 | 117 | // ------------------------------------- 118 | // Check what to do with the output side 119 | // ------------------------------------- 120 | // We had pushed one flit out, we can try to go for the next one 121 | if (ready_i && valid_o) 122 | begin 123 | if (pointer_out == $unsigned(BUFFER_DEPTH - 1)) 124 | pointer_out <= 0; 125 | else 126 | pointer_out <= pointer_out + 1; 127 | end 128 | // Else stay on the same output location 129 | end 130 | end 131 | end 132 | 133 | // Update output ports 134 | assign data_o = buffer[pointer_out]; 135 | assign valid_o = (elements != 0); 136 | 137 | assign ready_o = ~full; 138 | 139 | endmodule 140 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /axi_spi_master.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 log2(VALUE) ((VALUE) < ( 1 ) ? 0 : (VALUE) < ( 2 ) ? 1 : (VALUE) < ( 4 ) ? 2 : (VALUE) < ( 8 ) ? 3 : (VALUE) < ( 16 ) ? 4 : (VALUE) < ( 32 ) ? 5 : (VALUE) < ( 64 ) ? 6 : (VALUE) < ( 128 ) ? 7 : (VALUE) < ( 256 ) ? 8 : (VALUE) < ( 512 ) ? 9 : (VALUE) < ( 1024 ) ? 10 : (VALUE) < ( 2048 ) ? 11 : (VALUE) < ( 4096 ) ? 12 : (VALUE) < ( 8192 ) ? 13 : (VALUE) < ( 16384 ) ? 14 : (VALUE) < ( 32768 ) ? 15 : (VALUE) < ( 65536 ) ? 16 : (VALUE) < ( 131072 ) ? 17 : (VALUE) < ( 262144 ) ? 18 : (VALUE) < ( 524288 ) ? 19 : (VALUE) < ( 1048576 ) ? 20 : (VALUE) < ( 1048576 * 2 ) ? 21 : (VALUE) < ( 1048576 * 4 ) ? 22 : (VALUE) < ( 1048576 * 8 ) ? 23 : (VALUE) < ( 1048576 * 16 ) ? 24 : 25) 12 | 13 | module axi_spi_master 14 | #( 15 | parameter AXI4_ADDRESS_WIDTH = 32, 16 | parameter AXI4_RDATA_WIDTH = 32, 17 | parameter AXI4_WDATA_WIDTH = 32, 18 | parameter AXI4_USER_WIDTH = 4, 19 | parameter AXI4_ID_WIDTH = 16, 20 | parameter BUFFER_DEPTH = 8 21 | ) 22 | ( 23 | input logic s_axi_aclk, 24 | input logic s_axi_aresetn, 25 | 26 | input logic s_axi_awvalid, 27 | input logic [AXI4_ID_WIDTH-1:0] s_axi_awid, 28 | input logic [7:0] s_axi_awlen, 29 | input logic [AXI4_ADDRESS_WIDTH-1:0] s_axi_awaddr, 30 | input logic [AXI4_USER_WIDTH-1:0] s_axi_awuser, 31 | output logic s_axi_awready, 32 | 33 | input logic s_axi_wvalid, 34 | input logic [AXI4_WDATA_WIDTH-1:0] s_axi_wdata, 35 | input logic [AXI4_WDATA_WIDTH/8-1:0] s_axi_wstrb, 36 | input logic s_axi_wlast, 37 | input logic [AXI4_USER_WIDTH-1:0] s_axi_wuser, 38 | output logic s_axi_wready, 39 | 40 | output logic s_axi_bvalid, 41 | output logic [AXI4_ID_WIDTH-1:0] s_axi_bid, 42 | output logic [1:0] s_axi_bresp, 43 | output logic [AXI4_USER_WIDTH-1:0] s_axi_buser, 44 | input logic s_axi_bready, 45 | 46 | input logic s_axi_arvalid, 47 | input logic [AXI4_ID_WIDTH-1:0] s_axi_arid, 48 | input logic [7:0] s_axi_arlen, 49 | input logic [AXI4_ADDRESS_WIDTH-1:0] s_axi_araddr, 50 | input logic [AXI4_USER_WIDTH-1:0] s_axi_aruser, 51 | output logic s_axi_arready, 52 | 53 | output logic s_axi_rvalid, 54 | output logic [AXI4_ID_WIDTH-1:0] s_axi_rid, 55 | output logic [AXI4_RDATA_WIDTH-1:0] s_axi_rdata, 56 | output logic [1:0] s_axi_rresp, 57 | output logic s_axi_rlast, 58 | output logic [AXI4_USER_WIDTH-1:0] s_axi_ruser, 59 | input logic s_axi_rready, 60 | 61 | output logic [1:0] events_o, 62 | 63 | output logic spi_clk, 64 | output logic spi_csn0, 65 | output logic spi_csn1, 66 | output logic spi_csn2, 67 | output logic spi_csn3, 68 | output logic [1:0] spi_mode, 69 | output logic spi_sdo0, 70 | output logic spi_sdo1, 71 | output logic spi_sdo2, 72 | output logic spi_sdo3, 73 | input logic spi_sdi0, 74 | input logic spi_sdi1, 75 | input logic spi_sdi2, 76 | input logic spi_sdi3 77 | ); 78 | 79 | 80 | localparam LOG_BUFFER_DEPTH = `log2(BUFFER_DEPTH); 81 | 82 | logic [7:0] spi_clk_div; 83 | logic spi_clk_div_valid; 84 | logic [31:0] spi_status; 85 | logic [31:0] spi_addr; 86 | logic [5:0] spi_addr_len; 87 | logic [31:0] spi_cmd; 88 | logic [5:0] spi_cmd_len; 89 | logic [15:0] spi_data_len; 90 | logic [15:0] spi_dummy_rd; 91 | logic [15:0] spi_dummy_wr; 92 | logic spi_swrst; 93 | logic spi_rd; 94 | logic spi_wr; 95 | logic spi_qrd; 96 | logic spi_qwr; 97 | logic [3:0] spi_csreg; 98 | logic [31:0] spi_data_tx; 99 | logic spi_data_tx_valid; 100 | logic spi_data_tx_ready; 101 | logic [31:0] spi_data_rx; 102 | logic spi_data_rx_valid; 103 | logic spi_data_rx_ready; 104 | logic [6:0] spi_ctrl_status; 105 | logic [31:0] spi_ctrl_data_tx; 106 | logic spi_ctrl_data_tx_valid; 107 | logic spi_ctrl_data_tx_ready; 108 | logic [31:0] spi_ctrl_data_rx; 109 | logic spi_ctrl_data_rx_valid; 110 | logic spi_ctrl_data_rx_ready; 111 | 112 | logic s_eot; 113 | 114 | logic [LOG_BUFFER_DEPTH:0] elements_tx; 115 | logic [LOG_BUFFER_DEPTH:0] elements_rx; 116 | logic [LOG_BUFFER_DEPTH:0] elements_tx_old; 117 | logic [LOG_BUFFER_DEPTH:0] elements_rx_old; 118 | 119 | localparam FILL_BITS = 7-LOG_BUFFER_DEPTH; 120 | 121 | assign spi_status = {{FILL_BITS{1'b0}},elements_tx,{FILL_BITS{1'b0}},elements_rx,9'h0,spi_ctrl_status}; 122 | assign events_o[0] = (((elements_rx==4'b0100) && (elements_rx_old==4'b0101)) || ((elements_tx==4'b0101) && (elements_tx_old==4'b0100))); 123 | assign events_o[1] = s_eot; 124 | 125 | always_ff @(posedge s_axi_aclk or negedge s_axi_aresetn) 126 | begin 127 | if (s_axi_aresetn == 1'b0) 128 | begin 129 | elements_rx_old <= 'h0; 130 | elements_tx_old <= 'h0; 131 | end 132 | else 133 | begin 134 | elements_rx_old <= elements_rx; 135 | elements_tx_old <= elements_tx; 136 | end 137 | end 138 | 139 | spi_master_axi_if 140 | #( 141 | .AXI4_ADDRESS_WIDTH(AXI4_ADDRESS_WIDTH), 142 | .AXI4_RDATA_WIDTH(AXI4_RDATA_WIDTH), 143 | .AXI4_WDATA_WIDTH(AXI4_WDATA_WIDTH), 144 | .AXI4_USER_WIDTH(AXI4_USER_WIDTH), 145 | .AXI4_ID_WIDTH(AXI4_ID_WIDTH) 146 | ) 147 | u_axiregs 148 | ( 149 | .s_axi_aclk(s_axi_aclk), 150 | .s_axi_aresetn(s_axi_aresetn), 151 | 152 | .s_axi_awvalid(s_axi_awvalid), 153 | .s_axi_awid(s_axi_awid), 154 | .s_axi_awlen(s_axi_awlen), 155 | .s_axi_awaddr(s_axi_awaddr), 156 | .s_axi_awuser(s_axi_awuser), 157 | .s_axi_awready(s_axi_awready), 158 | 159 | .s_axi_wvalid(s_axi_wvalid), 160 | .s_axi_wdata(s_axi_wdata), 161 | .s_axi_wstrb(s_axi_wstrb), 162 | .s_axi_wlast(s_axi_wlast), 163 | .s_axi_wuser(s_axi_wuser), 164 | .s_axi_wready(s_axi_wready), 165 | 166 | .s_axi_bvalid(s_axi_bvalid), 167 | .s_axi_bid(s_axi_bid), 168 | .s_axi_bresp(s_axi_bresp), 169 | .s_axi_buser(s_axi_buser), 170 | .s_axi_bready(s_axi_bready), 171 | 172 | .s_axi_arvalid(s_axi_arvalid), 173 | .s_axi_arid(s_axi_arid), 174 | .s_axi_arlen(s_axi_arlen), 175 | .s_axi_araddr(s_axi_araddr), 176 | .s_axi_aruser(s_axi_aruser), 177 | .s_axi_arready(s_axi_arready), 178 | 179 | .s_axi_rvalid(s_axi_rvalid), 180 | .s_axi_rid(s_axi_rid), 181 | .s_axi_rdata(s_axi_rdata), 182 | .s_axi_rresp(s_axi_rresp), 183 | .s_axi_rlast(s_axi_rlast), 184 | .s_axi_ruser(s_axi_ruser), 185 | .s_axi_rready(s_axi_rready), 186 | 187 | .spi_clk_div(spi_clk_div), 188 | .spi_clk_div_valid(spi_clk_div_valid), 189 | .spi_status(spi_status), 190 | .spi_addr(spi_addr), 191 | .spi_addr_len(spi_addr_len), 192 | .spi_cmd(spi_cmd), 193 | .spi_cmd_len(spi_cmd_len), 194 | .spi_data_len(spi_data_len), 195 | .spi_dummy_rd(spi_dummy_rd), 196 | .spi_dummy_wr(spi_dummy_wr), 197 | .spi_swrst(spi_swrst), 198 | .spi_rd(spi_rd), 199 | .spi_wr(spi_wr), 200 | .spi_qrd(spi_qrd), 201 | .spi_qwr(spi_qwr), 202 | .spi_csreg(spi_csreg), 203 | .spi_data_tx(spi_data_tx), 204 | .spi_data_tx_valid(spi_data_tx_valid), 205 | .spi_data_tx_ready(spi_data_tx_ready), 206 | .spi_data_rx(spi_data_rx), 207 | .spi_data_rx_valid(spi_data_rx_valid), 208 | .spi_data_rx_ready(spi_data_rx_ready) 209 | ); 210 | 211 | spi_master_fifo 212 | #( 213 | .DATA_WIDTH(32), 214 | .BUFFER_DEPTH(BUFFER_DEPTH) 215 | ) 216 | u_txfifo 217 | ( 218 | .clk_i(s_axi_aclk), 219 | .rst_ni(s_axi_aresetn), 220 | .clr_i(spi_swrst), 221 | 222 | .elements_o(elements_tx), 223 | 224 | .data_o(spi_ctrl_data_tx), 225 | .valid_o(spi_ctrl_data_tx_valid), 226 | .ready_i(spi_ctrl_data_tx_ready), 227 | 228 | .valid_i(spi_data_tx_valid), 229 | .data_i(spi_data_tx), 230 | .ready_o(spi_data_tx_ready) 231 | ); 232 | 233 | spi_master_fifo 234 | #( 235 | .DATA_WIDTH(32), 236 | .BUFFER_DEPTH(BUFFER_DEPTH) 237 | ) 238 | u_rxfifo 239 | ( 240 | .clk_i(s_axi_aclk), 241 | .rst_ni(s_axi_aresetn), 242 | .clr_i(spi_swrst), 243 | 244 | .elements_o(elements_rx), 245 | 246 | .data_o(spi_data_rx), 247 | .valid_o(spi_data_rx_valid), 248 | .ready_i(spi_data_rx_ready), 249 | 250 | .valid_i(spi_ctrl_data_rx_valid), 251 | .data_i(spi_ctrl_data_rx), 252 | .ready_o(spi_ctrl_data_rx_ready) 253 | ); 254 | 255 | spi_master_controller u_spictrl 256 | ( 257 | .clk(s_axi_aclk), 258 | .rstn(s_axi_aresetn), 259 | .eot(s_eot), 260 | .spi_clk_div(spi_clk_div), 261 | .spi_clk_div_valid(spi_clk_div_valid), 262 | .spi_status(spi_ctrl_status), 263 | .spi_addr(spi_addr), 264 | .spi_addr_len(spi_addr_len), 265 | .spi_cmd(spi_cmd), 266 | .spi_cmd_len(spi_cmd_len), 267 | .spi_data_len(spi_data_len), 268 | .spi_dummy_rd(spi_dummy_rd), 269 | .spi_dummy_wr(spi_dummy_wr), 270 | .spi_swrst(spi_swrst), 271 | .spi_rd(spi_rd), 272 | .spi_wr(spi_wr), 273 | .spi_qrd(spi_qrd), 274 | .spi_qwr(spi_qwr), 275 | .spi_csreg(spi_csreg), 276 | .spi_ctrl_data_tx(spi_ctrl_data_tx), 277 | .spi_ctrl_data_tx_valid(spi_ctrl_data_tx_valid), 278 | .spi_ctrl_data_tx_ready(spi_ctrl_data_tx_ready), 279 | .spi_ctrl_data_rx(spi_ctrl_data_rx), 280 | .spi_ctrl_data_rx_valid(spi_ctrl_data_rx_valid), 281 | .spi_ctrl_data_rx_ready(spi_ctrl_data_rx_ready), 282 | .spi_clk(spi_clk), 283 | .spi_csn0(spi_csn0), 284 | .spi_csn1(spi_csn1), 285 | .spi_csn2(spi_csn2), 286 | .spi_csn3(spi_csn3), 287 | .spi_mode(spi_mode), 288 | .spi_sdo0(spi_sdo0), 289 | .spi_sdo1(spi_sdo1), 290 | .spi_sdo2(spi_sdo2), 291 | .spi_sdo3(spi_sdo3), 292 | .spi_sdi0(spi_sdi0), 293 | .spi_sdi1(spi_sdi1), 294 | .spi_sdi2(spi_sdi2), 295 | .spi_sdi3(spi_sdi3) 296 | ); 297 | 298 | endmodule 299 | -------------------------------------------------------------------------------- /spi_master_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_master_controller 16 | ( 17 | input logic clk, 18 | input logic rstn, 19 | output logic eot, 20 | input logic [7:0] spi_clk_div, 21 | input logic spi_clk_div_valid, 22 | output logic [6:0] spi_status, 23 | input logic [31:0] spi_addr, 24 | input logic [5:0] spi_addr_len, 25 | input logic [31:0] spi_cmd, 26 | input logic [5:0] spi_cmd_len, 27 | input logic [15:0] spi_data_len, 28 | input logic [15:0] spi_dummy_rd, 29 | input logic [15:0] spi_dummy_wr, 30 | input logic [3:0] spi_csreg, 31 | input logic spi_swrst, //FIXME Not used at all 32 | input logic spi_rd, 33 | input logic spi_wr, 34 | input logic spi_qrd, 35 | input logic spi_qwr, 36 | input logic [31:0] spi_ctrl_data_tx, 37 | input logic spi_ctrl_data_tx_valid, 38 | output logic spi_ctrl_data_tx_ready, 39 | output logic [31:0] spi_ctrl_data_rx, 40 | output logic spi_ctrl_data_rx_valid, 41 | input logic spi_ctrl_data_rx_ready, 42 | output logic spi_clk, 43 | output logic spi_csn0, 44 | output logic spi_csn1, 45 | output logic spi_csn2, 46 | output logic spi_csn3, 47 | output logic [1:0] spi_mode, 48 | output logic spi_sdo0, 49 | output logic spi_sdo1, 50 | output logic spi_sdo2, 51 | output logic spi_sdo3, 52 | input logic spi_sdi0, 53 | input logic spi_sdi1, 54 | input logic spi_sdi2, 55 | input logic spi_sdi3 56 | ); 57 | 58 | logic spi_rise; 59 | logic spi_fall; 60 | 61 | logic spi_clock_en; 62 | 63 | logic spi_en_tx; 64 | logic spi_en_rx; 65 | 66 | logic [15:0] counter_tx; 67 | logic counter_tx_valid; 68 | logic [15:0] counter_rx; 69 | logic counter_rx_valid; 70 | 71 | logic [31:0] data_to_tx; 72 | logic data_to_tx_valid; 73 | logic data_to_tx_ready; 74 | 75 | logic en_quad; 76 | logic en_quad_int; 77 | logic do_tx; //FIXME NOT USED at all!! 78 | logic do_rx; 79 | 80 | logic tx_done; 81 | logic rx_done; 82 | 83 | logic [1:0] s_spi_mode; 84 | 85 | logic ctrl_data_valid; 86 | 87 | logic spi_cs; 88 | 89 | logic tx_clk_en; 90 | logic rx_clk_en; 91 | 92 | enum logic [2:0] {DATA_NULL,DATA_EMPTY,DATA_CMD,DATA_ADDR,DATA_FIFO} ctrl_data_mux; 93 | 94 | enum logic [4:0] {IDLE,CMD,ADDR,MODE,DUMMY,DATA_TX,DATA_RX,WAIT_EDGE} state,state_next; 95 | 96 | assign en_quad = spi_qrd | spi_qwr | en_quad_int; 97 | 98 | spi_master_clkgen u_clkgen 99 | ( 100 | .clk ( clk ), 101 | .rstn ( rstn ), 102 | .en ( spi_clock_en ), 103 | .clk_div ( spi_clk_div ), 104 | .clk_div_valid ( spi_clk_div_valid ), 105 | .spi_clk ( spi_clk ), 106 | .spi_fall ( spi_fall ), 107 | .spi_rise ( spi_rise ) 108 | ); 109 | 110 | spi_master_tx u_txreg 111 | ( 112 | .clk ( clk ), 113 | .rstn ( rstn ), 114 | .en ( spi_en_tx ), 115 | .tx_edge ( spi_fall ), 116 | .tx_done ( tx_done ), 117 | .sdo0 ( spi_sdo0 ), 118 | .sdo1 ( spi_sdo1 ), 119 | .sdo2 ( spi_sdo2 ), 120 | .sdo3 ( spi_sdo3 ), 121 | .en_quad_in ( en_quad ), 122 | .counter_in ( counter_tx ), 123 | .counter_in_upd ( counter_tx_valid ), 124 | .data ( data_to_tx ), 125 | .data_valid ( data_to_tx_valid ), 126 | .data_ready ( data_to_tx_ready ), 127 | .clk_en_o ( tx_clk_en ) 128 | ); 129 | 130 | spi_master_rx u_rxreg 131 | ( 132 | .clk ( clk ), 133 | .rstn ( rstn ), 134 | .en ( spi_en_rx ), 135 | .rx_edge ( spi_rise ), 136 | .rx_done ( rx_done ), 137 | .sdi0 ( spi_sdi0 ), 138 | .sdi1 ( spi_sdi1 ), 139 | .sdi2 ( spi_sdi2 ), 140 | .sdi3 ( spi_sdi3 ), 141 | .en_quad_in ( en_quad ), 142 | .counter_in ( counter_rx ), 143 | .counter_in_upd ( counter_rx_valid ), 144 | .data ( spi_ctrl_data_rx ), 145 | .data_valid ( spi_ctrl_data_rx_valid ), 146 | .data_ready ( spi_ctrl_data_rx_ready ), 147 | .clk_en_o ( rx_clk_en ) 148 | ); 149 | 150 | always_comb 151 | begin 152 | data_to_tx = 'h0; 153 | data_to_tx_valid = 1'b0; 154 | spi_ctrl_data_tx_ready = 1'b0; 155 | 156 | case(ctrl_data_mux) 157 | DATA_NULL: 158 | begin 159 | data_to_tx = '0; 160 | data_to_tx_valid = 1'b0; 161 | spi_ctrl_data_tx_ready = 1'b0; 162 | end 163 | 164 | DATA_EMPTY: 165 | begin 166 | data_to_tx = '0; 167 | data_to_tx_valid = 1'b1; 168 | end 169 | 170 | DATA_CMD: 171 | begin 172 | data_to_tx = spi_cmd; 173 | data_to_tx_valid = ctrl_data_valid; 174 | spi_ctrl_data_tx_ready = 1'b0; 175 | end 176 | 177 | DATA_ADDR: 178 | begin 179 | data_to_tx = spi_addr; 180 | data_to_tx_valid = ctrl_data_valid; 181 | spi_ctrl_data_tx_ready = 1'b0; 182 | end 183 | 184 | DATA_FIFO: 185 | begin 186 | data_to_tx = spi_ctrl_data_tx; 187 | data_to_tx_valid = spi_ctrl_data_tx_valid; 188 | spi_ctrl_data_tx_ready = data_to_tx_ready; 189 | end 190 | endcase 191 | end 192 | 193 | always_comb 194 | begin 195 | spi_cs = 1'b1; 196 | spi_clock_en = 1'b0; 197 | counter_tx = '0; 198 | counter_tx_valid = 1'b0; 199 | counter_rx = '0; 200 | counter_rx_valid = 1'b0; 201 | state_next = state; 202 | ctrl_data_mux = DATA_NULL; 203 | ctrl_data_valid = 1'b0; 204 | spi_en_rx = 1'b0; 205 | spi_en_tx = 1'b0; 206 | spi_status = '0; 207 | s_spi_mode = `SPI_QUAD_RX; 208 | eot = 1'b0; 209 | case(state) 210 | IDLE: 211 | begin 212 | spi_status[0] = 1'b1; 213 | s_spi_mode = `SPI_QUAD_RX; 214 | if (spi_rd || spi_wr || spi_qrd || spi_qwr) 215 | begin 216 | spi_cs = 1'b0; 217 | spi_clock_en = 1'b1; 218 | 219 | if (spi_cmd_len != 0) 220 | begin 221 | s_spi_mode = (spi_qrd | spi_qwr) ? `SPI_QUAD_TX : `SPI_STD; 222 | counter_tx = {8'h0,spi_cmd_len}; 223 | counter_tx_valid = 1'b1; 224 | ctrl_data_mux = DATA_CMD; 225 | ctrl_data_valid = 1'b1; 226 | spi_en_tx = 1'b1; 227 | state_next = CMD; 228 | end 229 | else if (spi_addr_len != 0) 230 | begin 231 | s_spi_mode = (spi_qrd | spi_qwr) ? `SPI_QUAD_TX : `SPI_STD; 232 | counter_tx = {8'h0,spi_addr_len}; 233 | counter_tx_valid = 1'b1; 234 | ctrl_data_mux = DATA_ADDR; 235 | ctrl_data_valid = 1'b1; 236 | spi_en_tx = 1'b1; 237 | state_next = ADDR; 238 | end 239 | else if (spi_data_len != 0) 240 | begin 241 | if (spi_rd || spi_qrd) 242 | begin 243 | s_spi_mode = (spi_qrd) ? `SPI_QUAD_RX : `SPI_STD; 244 | if(spi_dummy_rd != 0) 245 | begin 246 | counter_tx = en_quad ? {spi_dummy_rd[13:0],2'b00} : spi_dummy_rd; 247 | counter_tx_valid = 1'b1; 248 | spi_en_tx = 1'b1; 249 | ctrl_data_mux = DATA_EMPTY; 250 | state_next = DUMMY; 251 | end 252 | else 253 | begin 254 | counter_rx = spi_data_len; 255 | counter_rx_valid = 1'b1; 256 | spi_en_rx = 1'b1; 257 | state_next = DATA_RX; 258 | end 259 | end 260 | else 261 | begin 262 | s_spi_mode = (spi_qwr) ? `SPI_QUAD_TX : `SPI_STD; 263 | if(spi_dummy_wr != 0) 264 | begin 265 | counter_tx = en_quad ? {spi_dummy_wr[13:0],2'b00} : spi_dummy_wr; 266 | counter_tx_valid = 1'b1; 267 | ctrl_data_mux = DATA_EMPTY; 268 | spi_en_tx = 1'b1; 269 | state_next = DUMMY; 270 | end 271 | else 272 | begin 273 | counter_tx = spi_data_len; 274 | counter_tx_valid = 1'b1; 275 | ctrl_data_mux = DATA_FIFO; 276 | ctrl_data_valid = 1'b0; 277 | spi_en_tx = 1'b1; 278 | state_next = DATA_TX; 279 | end 280 | end 281 | end 282 | end 283 | else 284 | begin 285 | spi_cs = 1'b1; 286 | state_next = IDLE; 287 | end 288 | end 289 | 290 | CMD: 291 | begin 292 | spi_status[1] = 1'b1; 293 | spi_cs = 1'b0; 294 | spi_clock_en = 1'b1; 295 | s_spi_mode = (en_quad) ? `SPI_QUAD_TX : `SPI_STD; 296 | if (tx_done) 297 | begin 298 | if (spi_addr_len != 0) 299 | begin 300 | s_spi_mode = (en_quad) ? `SPI_QUAD_TX : `SPI_STD; 301 | counter_tx = {8'h0,spi_addr_len}; 302 | counter_tx_valid = 1'b1; 303 | ctrl_data_mux = DATA_ADDR; 304 | ctrl_data_valid = 1'b1; 305 | spi_en_tx = 1'b1; 306 | state_next = ADDR; 307 | end 308 | else if (spi_data_len != 0) 309 | begin 310 | if (do_rx) 311 | begin 312 | s_spi_mode = (en_quad) ? `SPI_QUAD_RX : `SPI_STD; 313 | if(spi_dummy_rd != 0) 314 | begin 315 | counter_tx = en_quad ? {spi_dummy_rd[13:0],2'b00} : spi_dummy_rd; 316 | counter_tx_valid = 1'b1; 317 | spi_en_tx = 1'b1; 318 | ctrl_data_mux = DATA_EMPTY; 319 | state_next = DUMMY; 320 | end 321 | else 322 | begin 323 | counter_rx = spi_data_len; 324 | counter_rx_valid = 1'b1; 325 | spi_en_rx = 1'b1; 326 | state_next = DATA_RX; 327 | end 328 | end 329 | else 330 | begin 331 | s_spi_mode = (en_quad) ? `SPI_QUAD_TX : `SPI_STD; 332 | if(spi_dummy_wr != 0) 333 | begin 334 | counter_tx = en_quad ? {spi_dummy_wr[13:0],2'b00} : spi_dummy_wr; 335 | counter_tx_valid = 1'b1; 336 | ctrl_data_mux = DATA_EMPTY; 337 | spi_en_tx = 1'b1; 338 | state_next = DUMMY; 339 | end 340 | else 341 | begin 342 | counter_tx = spi_data_len; 343 | counter_tx_valid = 1'b1; 344 | ctrl_data_mux = DATA_FIFO; 345 | ctrl_data_valid = 1'b1; 346 | spi_en_tx = 1'b1; 347 | state_next = DATA_TX; 348 | end 349 | end 350 | end 351 | else 352 | begin 353 | state_next = IDLE; 354 | end 355 | end 356 | else 357 | begin 358 | spi_en_tx = 1'b1; 359 | state_next = CMD; 360 | end 361 | end 362 | 363 | ADDR: 364 | begin 365 | spi_en_tx = 1'b1; 366 | spi_status[2] = 1'b1; 367 | spi_cs = 1'b0; 368 | spi_clock_en = 1'b1; 369 | s_spi_mode = (en_quad) ? `SPI_QUAD_TX : `SPI_STD; 370 | 371 | if (tx_done) 372 | begin 373 | if (spi_data_len != 0) 374 | begin 375 | if (do_rx) 376 | begin 377 | s_spi_mode = (en_quad) ? `SPI_QUAD_RX : `SPI_STD; 378 | if(spi_dummy_rd != 0) 379 | begin 380 | counter_tx = en_quad ? {spi_dummy_rd[13:0],2'b00} : spi_dummy_rd; 381 | counter_tx_valid = 1'b1; 382 | spi_en_tx = 1'b1; 383 | ctrl_data_mux = DATA_EMPTY; 384 | state_next = DUMMY; 385 | end 386 | else 387 | begin 388 | counter_rx = spi_data_len; 389 | counter_rx_valid = 1'b1; 390 | spi_en_rx = 1'b1; 391 | state_next = DATA_RX; 392 | end 393 | end 394 | else 395 | begin 396 | s_spi_mode = (en_quad) ? `SPI_QUAD_TX : `SPI_STD; 397 | spi_en_tx = 1'b1; 398 | 399 | if(spi_dummy_wr != 0) begin 400 | counter_tx = en_quad ? {spi_dummy_wr[13:0],2'b00} : spi_dummy_wr; 401 | counter_tx_valid = 1'b1; 402 | ctrl_data_mux = DATA_EMPTY; 403 | state_next = DUMMY; 404 | end else begin 405 | counter_tx = spi_data_len; 406 | counter_tx_valid = 1'b1; 407 | ctrl_data_mux = DATA_FIFO; 408 | ctrl_data_valid = 1'b1; 409 | state_next = DATA_TX; 410 | end 411 | end 412 | end 413 | else 414 | begin 415 | state_next = IDLE; 416 | end 417 | end 418 | end 419 | 420 | MODE: 421 | begin 422 | spi_status[3] = 1'b1; 423 | spi_cs = 1'b0; 424 | spi_clock_en = 1'b1; 425 | spi_en_tx = 1'b1; 426 | end 427 | 428 | DUMMY: 429 | begin 430 | spi_en_tx = 1'b1; 431 | spi_status[4] = 1'b1; 432 | spi_cs = 1'b0; 433 | spi_clock_en = 1'b1; 434 | s_spi_mode = (en_quad) ? `SPI_QUAD_RX : `SPI_STD; 435 | 436 | if (tx_done) begin 437 | if (spi_data_len != 0) begin 438 | if (do_rx) begin 439 | counter_rx = spi_data_len; 440 | counter_rx_valid = 1'b1; 441 | spi_en_rx = 1'b1; 442 | state_next = DATA_RX; 443 | end else begin 444 | counter_tx = spi_data_len; 445 | counter_tx_valid = 1'b1; 446 | s_spi_mode = (en_quad) ? `SPI_QUAD_TX : `SPI_STD; 447 | 448 | spi_clock_en = tx_clk_en; 449 | spi_en_tx = 1'b1; 450 | state_next = DATA_TX; 451 | end 452 | end 453 | else 454 | begin 455 | eot = 1'b1; 456 | state_next = IDLE; 457 | end 458 | end 459 | else 460 | begin 461 | ctrl_data_mux = DATA_EMPTY; 462 | spi_en_tx = 1'b1; 463 | state_next = DUMMY; 464 | end 465 | end 466 | 467 | DATA_TX: 468 | begin 469 | spi_status[5] = 1'b1; 470 | spi_cs = 1'b0; 471 | spi_clock_en = tx_clk_en; 472 | ctrl_data_mux = DATA_FIFO; 473 | ctrl_data_valid = 1'b1; 474 | spi_en_tx = 1'b1; 475 | s_spi_mode = (en_quad) ? `SPI_QUAD_TX : `SPI_STD; 476 | 477 | if (tx_done) begin 478 | eot = 1'b1; 479 | state_next = IDLE; 480 | spi_clock_en = 1'b0; 481 | end else begin 482 | state_next = DATA_TX; 483 | end 484 | end 485 | 486 | DATA_RX: 487 | begin 488 | spi_status[6] = 1'b1; 489 | spi_cs = 1'b0; 490 | spi_clock_en = rx_clk_en; 491 | s_spi_mode = (en_quad) ? `SPI_QUAD_RX : `SPI_STD; 492 | 493 | if (rx_done) begin 494 | state_next = WAIT_EDGE; 495 | end else begin 496 | spi_en_rx = 1'b1; 497 | state_next = DATA_RX; 498 | end 499 | end 500 | WAIT_EDGE: 501 | begin 502 | spi_status[6] = 1'b1; 503 | spi_cs = 1'b0; 504 | spi_clock_en = 1'b0; 505 | s_spi_mode = (en_quad) ? `SPI_QUAD_RX : `SPI_STD; 506 | 507 | if (spi_fall) begin 508 | eot = 1'b1; 509 | state_next = IDLE; 510 | end else begin 511 | state_next = WAIT_EDGE; 512 | end 513 | end 514 | endcase 515 | end 516 | 517 | 518 | always_ff @(posedge clk, negedge rstn) 519 | begin 520 | if (rstn == 1'b0) 521 | begin 522 | state <= IDLE; 523 | en_quad_int <= 1'b0; 524 | do_rx <= 1'b0; 525 | do_tx <= 1'b0; 526 | spi_mode <= `SPI_QUAD_RX; 527 | end 528 | else 529 | begin 530 | state <= state_next; 531 | spi_mode <= s_spi_mode; 532 | if (spi_qrd || spi_qwr) 533 | en_quad_int <= 1'b1; 534 | else if (state_next == IDLE) 535 | en_quad_int <= 1'b0; 536 | 537 | if (spi_rd || spi_qrd) 538 | begin 539 | do_rx <= 1'b1; 540 | do_tx <= 1'b0; 541 | end 542 | else if (spi_wr || spi_qwr) 543 | begin 544 | do_rx <= 1'b0; 545 | do_tx <= 1'b1; 546 | end 547 | else if (state_next == IDLE) 548 | begin 549 | do_rx <= 1'b0; 550 | do_tx <= 1'b0; 551 | end 552 | end 553 | end 554 | 555 | assign spi_csn0 = ~spi_csreg[0] | spi_cs; 556 | assign spi_csn1 = ~spi_csreg[1] | spi_cs; 557 | assign spi_csn2 = ~spi_csreg[2] | spi_cs; 558 | assign spi_csn3 = ~spi_csreg[3] | spi_cs; 559 | 560 | endmodule 561 | -------------------------------------------------------------------------------- /spi_master_axi_if.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 log2(VALUE) ((VALUE) < ( 1 ) ? 0 : (VALUE) < ( 2 ) ? 1 : (VALUE) < ( 4 ) ? 2 : (VALUE) < ( 8 ) ? 3 : (VALUE) < ( 16 ) ? 4 : (VALUE) < ( 32 ) ? 5 : (VALUE) < ( 64 ) ? 6 : (VALUE) < ( 128 ) ? 7 : (VALUE) < ( 256 ) ? 8 : (VALUE) < ( 512 ) ? 9 : (VALUE) < ( 1024 ) ? 10 : (VALUE) < ( 2048 ) ? 11 : (VALUE) < ( 4096 ) ? 12 : (VALUE) < ( 8192 ) ? 13 : (VALUE) < ( 16384 ) ? 14 : (VALUE) < ( 32768 ) ? 15 : (VALUE) < ( 65536 ) ? 16 : (VALUE) < ( 131072 ) ? 17 : (VALUE) < ( 262144 ) ? 18 : (VALUE) < ( 524288 ) ? 19 : (VALUE) < ( 1048576 ) ? 20 : (VALUE) < ( 1048576 * 2 ) ? 21 : (VALUE) < ( 1048576 * 4 ) ? 22 : (VALUE) < ( 1048576 * 8 ) ? 23 : (VALUE) < ( 1048576 * 16 ) ? 24 : 25) 12 | `define OKAY 2'b00 13 | `define EXOKAY 2'b01 14 | `define SLVERR 2'b10 15 | `define DECERR 2'b11 16 | 17 | `define REG_STATUS 3'b000 18 | `define REG_CLKDIV 3'b001 19 | `define REG_SPICMD 3'b010 20 | `define REG_SPIADR 3'b011 21 | `define REG_SPILEN 3'b100 22 | `define REG_SPIDUM 3'b101 23 | 24 | module spi_master_axi_if #( 25 | parameter AXI4_ADDRESS_WIDTH = 32, 26 | parameter AXI4_RDATA_WIDTH = 32, 27 | parameter AXI4_WDATA_WIDTH = 32, 28 | parameter AXI4_USER_WIDTH = 4, 29 | parameter AXI4_ID_WIDTH = 16 30 | ) ( 31 | input logic s_axi_aclk, 32 | input logic s_axi_aresetn, 33 | 34 | input logic s_axi_awvalid, 35 | input logic [AXI4_ID_WIDTH-1:0] s_axi_awid, 36 | input logic [7:0] s_axi_awlen, 37 | input logic [AXI4_ADDRESS_WIDTH-1:0] s_axi_awaddr, 38 | input logic [AXI4_USER_WIDTH-1:0] s_axi_awuser, 39 | output logic s_axi_awready, 40 | 41 | input logic s_axi_wvalid, 42 | input logic [AXI4_WDATA_WIDTH-1:0] s_axi_wdata, 43 | input logic [AXI4_WDATA_WIDTH/8-1:0] s_axi_wstrb, 44 | input logic s_axi_wlast, 45 | input logic [AXI4_USER_WIDTH-1:0] s_axi_wuser, 46 | output logic s_axi_wready, 47 | 48 | output logic s_axi_bvalid, 49 | output logic [AXI4_ID_WIDTH-1:0] s_axi_bid, 50 | output logic [1:0] s_axi_bresp, 51 | output logic [AXI4_USER_WIDTH-1:0] s_axi_buser, 52 | input logic s_axi_bready, 53 | 54 | input logic s_axi_arvalid, 55 | input logic [AXI4_ID_WIDTH-1:0] s_axi_arid, 56 | input logic [7:0] s_axi_arlen, 57 | input logic [AXI4_ADDRESS_WIDTH-1:0] s_axi_araddr, 58 | input logic [AXI4_USER_WIDTH-1:0] s_axi_aruser, 59 | output logic s_axi_arready, 60 | 61 | output logic s_axi_rvalid, 62 | output logic [AXI4_ID_WIDTH-1:0] s_axi_rid, 63 | output logic [AXI4_RDATA_WIDTH-1:0] s_axi_rdata, 64 | output logic [1:0] s_axi_rresp, 65 | output logic s_axi_rlast, 66 | output logic [AXI4_USER_WIDTH-1:0] s_axi_ruser, 67 | input logic s_axi_rready, 68 | 69 | output logic [7:0] spi_clk_div, 70 | output logic spi_clk_div_valid, 71 | input logic [31:0] spi_status, 72 | output logic [31:0] spi_addr, 73 | output logic [5:0] spi_addr_len, 74 | output logic [31:0] spi_cmd, 75 | output logic [5:0] spi_cmd_len, 76 | output logic [3:0] spi_csreg, 77 | output logic [15:0] spi_data_len, 78 | output logic [15:0] spi_dummy_rd, 79 | output logic [15:0] spi_dummy_wr, 80 | output logic spi_swrst, 81 | output logic spi_rd, 82 | output logic spi_wr, 83 | output logic spi_qrd, 84 | output logic spi_qwr, 85 | output logic [31:0] spi_data_tx, 86 | output logic spi_data_tx_valid, 87 | input logic spi_data_tx_ready, 88 | input logic [31:0] spi_data_rx, 89 | input logic spi_data_rx_valid, 90 | output logic spi_data_rx_ready 91 | ); 92 | 93 | localparam WR_ADDR_CMP = `log2(AXI4_WDATA_WIDTH/8)-1; 94 | localparam RD_ADDR_CMP = `log2(AXI4_RDATA_WIDTH/8)-1; 95 | 96 | localparam OFFSET_BIT = ( `log2(AXI4_WDATA_WIDTH-1) - 3 ); // Address Offset: OFFSET IS 32bit--> 2bit; 64bit--> 3bit; 128bit--> 4bit and so on 97 | 98 | logic [4:0] wr_addr; 99 | logic [4:0] rd_addr; 100 | 101 | logic is_tx_fifo_sel; 102 | logic is_rx_fifo_sel; 103 | logic is_tx_fifo_sel_q; 104 | logic is_rx_fifo_sel_q; 105 | 106 | logic read_req; 107 | logic [4:0] read_address; 108 | logic sample_AR; 109 | logic [4:0] ARADDR_Q; 110 | logic [7:0] ARLEN_Q; 111 | logic decr_ARLEN; 112 | logic [7:0] CountBurstCS; 113 | logic [7:0] CountBurstNS; 114 | logic [AXI4_ID_WIDTH-1:0] ARID_Q; 115 | logic [AXI4_USER_WIDTH-1:0] ARUSER_Q; 116 | 117 | 118 | logic write_req; 119 | logic [4:0] write_address; 120 | logic sample_AW; 121 | logic [4:0] AWADDR_Q; 122 | logic [7:0] AWLEN_Q; 123 | logic decr_AWLEN; 124 | logic [7:0] CountBurst_AW_CS; 125 | logic [7:0] CountBurst_AW_NS; 126 | logic [AXI4_ID_WIDTH-1:0] AWID_Q; 127 | logic [AXI4_USER_WIDTH-1:0] AWUSER_Q; 128 | 129 | enum logic [2:0] { IDLE, SINGLE, BURST, WAIT_WDATA_BURST, WAIT_WDATA_SINGLE, BURST_RESP } AR_CS, AR_NS, AW_CS, AW_NS; 130 | 131 | assign wr_addr = s_axi_awaddr[WR_ADDR_CMP+4:WR_ADDR_CMP]; 132 | assign rd_addr = s_axi_araddr[RD_ADDR_CMP+4:RD_ADDR_CMP]; 133 | 134 | assign is_tx_fifo_sel = (wr_addr[3] == 1'b1); 135 | assign is_rx_fifo_sel = (rd_addr[4] == 1'b1); 136 | 137 | assign spi_data_tx = s_axi_wdata[31:0]; 138 | 139 | //read fsm 140 | always_ff @(posedge s_axi_aclk, negedge s_axi_aresetn) 141 | begin 142 | if(s_axi_aresetn == 1'b0) 143 | begin 144 | AR_CS <= IDLE; 145 | ARADDR_Q <= '0; 146 | CountBurstCS <= '0; 147 | ARID_Q <= '0; 148 | ARUSER_Q <= '0; 149 | is_tx_fifo_sel_q <= '0; 150 | is_rx_fifo_sel_q <= '0; 151 | end 152 | else 153 | begin 154 | AR_CS <= AR_NS; 155 | CountBurstCS <= CountBurstNS; 156 | 157 | is_tx_fifo_sel_q <= is_tx_fifo_sel; 158 | is_rx_fifo_sel_q <= is_rx_fifo_sel; 159 | 160 | if(sample_AR) 161 | ARLEN_Q <= s_axi_arlen; 162 | else 163 | if(decr_ARLEN) 164 | ARLEN_Q <= ARLEN_Q -1'b1; 165 | 166 | if(sample_AR) 167 | begin 168 | ARID_Q <= s_axi_arid; 169 | ARADDR_Q <= read_address; 170 | ARUSER_Q <= s_axi_aruser; 171 | end 172 | end 173 | end 174 | 175 | always_comb 176 | begin 177 | s_axi_arready = 1'b0; 178 | read_address = '0; 179 | read_req = 1'b0; 180 | sample_AR = 1'b0; 181 | decr_ARLEN = 1'b0; 182 | CountBurstNS = CountBurstCS; 183 | spi_data_rx_ready = 1'b0; 184 | s_axi_rvalid = 1'b0; 185 | s_axi_rresp = `OKAY; 186 | s_axi_ruser = '0; 187 | s_axi_rlast = 1'b0; 188 | s_axi_rid = '0; 189 | AR_NS = AR_CS; 190 | 191 | case(AR_CS) 192 | IDLE: 193 | begin 194 | s_axi_arready = 1'b1; 195 | if(s_axi_arvalid) 196 | begin 197 | sample_AR = 1'b1; 198 | read_req = 1'b1; 199 | read_address = rd_addr; 200 | if(s_axi_arlen == 0) 201 | begin 202 | AR_NS = SINGLE; 203 | CountBurstNS = '0; 204 | end 205 | else 206 | begin 207 | AR_NS = BURST; 208 | CountBurstNS = CountBurstCS + 1'b1; 209 | end 210 | end 211 | else 212 | begin 213 | AR_NS = IDLE; 214 | CountBurstNS = '0; 215 | end 216 | end //~ IDLE 217 | 218 | SINGLE: 219 | begin 220 | 221 | s_axi_rresp = `OKAY; 222 | s_axi_rid = ARID_Q; 223 | s_axi_ruser = ARUSER_Q; 224 | s_axi_rlast = 1'b1; 225 | read_address = ARADDR_Q; 226 | // if RX FIFO selected rvalid if valid data in FIFO 227 | if (is_rx_fifo_sel_q) 228 | s_axi_rvalid = spi_data_rx_valid; 229 | else 230 | s_axi_rvalid = 1'b1; 231 | // we have a valid response here, waiting to be delivered 232 | // valid response is either no RX FIFO selected or RX FIFO selected and data available in FIFO 233 | if( (s_axi_rready && !is_rx_fifo_sel_q) | (s_axi_rready && is_rx_fifo_sel_q && spi_data_rx_valid)) 234 | begin 235 | if (is_rx_fifo_sel_q) 236 | spi_data_rx_ready = 1'b1; 237 | 238 | s_axi_arready = 1'b1; 239 | if(s_axi_arvalid) 240 | begin 241 | sample_AR = 1'b1; 242 | read_req = 1'b1; 243 | read_address = rd_addr; 244 | 245 | if(s_axi_arlen == 0) 246 | begin 247 | AR_NS = SINGLE; 248 | CountBurstNS = '0; 249 | end 250 | else 251 | begin 252 | AR_NS = BURST; 253 | CountBurstNS = CountBurstCS + 1'b1; 254 | end 255 | end 256 | else 257 | begin 258 | AR_NS = IDLE; 259 | CountBurstNS = '0; 260 | end 261 | end 262 | else // NOt ready: stay here untile RR RADY is OK 263 | begin 264 | AR_NS = SINGLE; 265 | read_req = 1'b1; 266 | read_address = ARADDR_Q; 267 | CountBurstNS = '0; 268 | end 269 | 270 | end //~ SINGLE 271 | 272 | BURST: 273 | begin 274 | s_axi_rresp = `OKAY; 275 | s_axi_rid = ARID_Q; 276 | s_axi_ruser = ARUSER_Q; 277 | read_address = ARADDR_Q; 278 | 279 | if (is_rx_fifo_sel_q) 280 | s_axi_rvalid = spi_data_rx_valid; 281 | else 282 | s_axi_rvalid = 1'b1; 283 | 284 | if( (s_axi_rready && !is_rx_fifo_sel_q) | (s_axi_rready && is_rx_fifo_sel_q && spi_data_rx_valid)) 285 | begin 286 | if (is_rx_fifo_sel_q) 287 | spi_data_rx_ready = 1'b1; 288 | 289 | if(ARLEN_Q > 0) 290 | begin 291 | AR_NS = BURST; 292 | read_req = 1'b1; // read the previous address 293 | decr_ARLEN = 1'b1; 294 | read_address = ARADDR_Q + CountBurstCS ; 295 | s_axi_rlast = 1'b0; 296 | s_axi_arready = 1'b0; 297 | end 298 | else //BURST_LAST 299 | begin 300 | s_axi_rlast = 1'b1; 301 | s_axi_arready = 1'b1; 302 | // Check if there are any pending request 303 | if(s_axi_arvalid) 304 | begin 305 | sample_AR = 1'b1; 306 | read_req = 1'b1; 307 | read_address = rd_addr; 308 | 309 | if(s_axi_arlen == 0) 310 | begin 311 | AR_NS = SINGLE; 312 | CountBurstNS = 0; 313 | end 314 | else 315 | begin 316 | AR_NS = BURST; 317 | CountBurstNS = 1; 318 | end 319 | end 320 | else 321 | begin 322 | AR_NS = IDLE; 323 | CountBurstNS = 0; 324 | end 325 | 326 | end 327 | end 328 | else 329 | begin 330 | AR_NS = BURST; 331 | read_req = 1'b1; // read the previous address 332 | decr_ARLEN = 1'b0; 333 | read_address = ARADDR_Q + CountBurstCS; 334 | s_axi_arready = 1'b0; 335 | end 336 | 337 | end //~ BURST 338 | default : begin 339 | 340 | end //~default 341 | endcase 342 | end 343 | 344 | //Write FSM 345 | always_ff @(posedge s_axi_aclk, negedge s_axi_aresetn) 346 | begin 347 | if(s_axi_aresetn == 1'b0) 348 | begin 349 | AW_CS <= IDLE; 350 | AWADDR_Q <= '0; 351 | CountBurst_AW_CS <= '0; 352 | AWID_Q <= '0; 353 | AWUSER_Q <= '0; 354 | end 355 | else 356 | begin 357 | AW_CS <= AW_NS; 358 | CountBurst_AW_CS <= CountBurst_AW_NS; 359 | if(sample_AW) 360 | begin 361 | AWLEN_Q <= s_axi_awlen; 362 | AWADDR_Q <= wr_addr; 363 | AWID_Q <= s_axi_awid; 364 | AWUSER_Q <= s_axi_awuser; 365 | end 366 | else 367 | if(decr_AWLEN) 368 | begin 369 | AWLEN_Q <= AWLEN_Q -1'b1; 370 | end 371 | end 372 | end 373 | 374 | always_comb 375 | begin 376 | s_axi_awready = 1'b0; 377 | s_axi_wready = 1'b0; 378 | write_address = '0; 379 | write_req = 1'b0; 380 | sample_AW = 1'b0; 381 | decr_AWLEN = 1'b0; 382 | CountBurst_AW_NS = CountBurst_AW_CS; 383 | s_axi_bid = '0; 384 | s_axi_bresp = `OKAY; 385 | s_axi_buser = '0; 386 | s_axi_bvalid = 1'b0; 387 | AW_NS = AW_CS; 388 | 389 | case(AW_CS) 390 | IDLE: 391 | begin 392 | s_axi_awready = 1'b1; 393 | 394 | if(s_axi_awvalid) 395 | begin 396 | sample_AW = 1'b1; 397 | 398 | if(s_axi_wvalid) 399 | begin 400 | s_axi_wready = 1'b1; 401 | write_req = 1'b1; 402 | write_address = wr_addr; 403 | if(s_axi_awlen == 0) 404 | begin 405 | AW_NS = SINGLE; 406 | CountBurst_AW_NS = 0; 407 | end 408 | else 409 | begin 410 | AW_NS = BURST; 411 | CountBurst_AW_NS = 1; 412 | end 413 | end 414 | else // GOT ADDRESS WRITE, not DATA 415 | begin 416 | s_axi_wready = 1'b1; 417 | write_req = 1'b0; 418 | write_address = '0; 419 | 420 | if(s_axi_awlen == 0) 421 | begin 422 | AW_NS = WAIT_WDATA_SINGLE; 423 | CountBurst_AW_NS = 0; 424 | end 425 | else 426 | begin 427 | AW_NS = WAIT_WDATA_BURST; 428 | CountBurst_AW_NS = 0; 429 | end 430 | end 431 | end 432 | else 433 | begin 434 | s_axi_wready = 1'b1; 435 | AW_NS = IDLE; 436 | CountBurst_AW_NS = '0; 437 | end 438 | 439 | end //~ IDLE 440 | 441 | 442 | WAIT_WDATA_BURST : 443 | begin 444 | s_axi_awready = 1'b0; 445 | 446 | if(s_axi_wvalid) 447 | begin 448 | s_axi_wready = 1'b1; 449 | write_req = 1'b1; 450 | write_address = AWADDR_Q; 451 | AW_NS = BURST; 452 | CountBurst_AW_NS = 1; 453 | decr_AWLEN = 1'b1; 454 | end 455 | else 456 | begin 457 | s_axi_wready = 1'b1; 458 | write_req = 1'b0; 459 | AW_NS = WAIT_WDATA_BURST; // wait for data 460 | CountBurst_AW_NS = '0; 461 | end 462 | 463 | end //~WAIT_WDATA_BURST 464 | 465 | WAIT_WDATA_SINGLE : 466 | begin 467 | s_axi_awready = 1'b0; 468 | CountBurst_AW_NS = '0; 469 | 470 | if(s_axi_wvalid) 471 | begin 472 | s_axi_wready = 1'b1; 473 | write_req = 1'b1; 474 | write_address = AWADDR_Q; 475 | AW_NS = SINGLE; 476 | end 477 | else 478 | begin 479 | s_axi_wready = 1'b1; 480 | write_req = 1'b0; 481 | AW_NS = WAIT_WDATA_SINGLE; // wait for data 482 | end 483 | end 484 | 485 | SINGLE: begin 486 | s_axi_bid = AWID_Q; 487 | s_axi_bresp = `OKAY; 488 | s_axi_buser = AWUSER_Q; 489 | s_axi_bvalid = 1'b1; 490 | 491 | // we have a valid response here, waiting to be delivered 492 | if(s_axi_bready) 493 | begin 494 | s_axi_awready = 1'b1; 495 | if(s_axi_awvalid) 496 | begin 497 | sample_AW = 1'b1; 498 | write_req = 1'b1; 499 | write_address = wr_addr; 500 | 501 | if(s_axi_awlen == 0) 502 | begin 503 | AW_NS = SINGLE; 504 | CountBurst_AW_NS = '0; 505 | end 506 | else 507 | begin 508 | AW_NS = BURST; 509 | CountBurst_AW_NS = CountBurst_AW_CS + 1'b1; 510 | end 511 | end 512 | else 513 | begin 514 | AW_NS = IDLE; 515 | CountBurst_AW_NS = '0; 516 | end 517 | end 518 | else // NOt ready: stay here untile RR RADY is OK 519 | begin 520 | AW_NS = SINGLE; 521 | CountBurst_AW_NS = '0; 522 | s_axi_awready = 1'b0; 523 | end 524 | 525 | end //~ SINGLE 526 | 527 | BURST: 528 | begin 529 | 530 | CountBurst_AW_NS = CountBurst_AW_CS; 531 | s_axi_awready = 1'b0; 532 | 533 | //write_address = AWADDR_Q + CountBurst_AW_CS ; 534 | write_address = AWADDR_Q; //TODO check burst type 535 | 536 | if(s_axi_wvalid) 537 | begin 538 | s_axi_wready = 1'b1; 539 | write_req = 1'b1; // read the previous address 540 | decr_AWLEN = 1'b1; 541 | CountBurst_AW_NS = CountBurst_AW_CS + 1'b1; 542 | end 543 | else 544 | begin 545 | s_axi_wready = 1'b1; 546 | write_req = 1'b0; // read the previous address 547 | decr_AWLEN = 1'b0; 548 | 549 | end 550 | if(AWLEN_Q > 0) 551 | begin 552 | AW_NS = BURST; 553 | // AWREADY = 1'b0; 554 | end 555 | else 556 | begin 557 | AW_NS = BURST_RESP; 558 | // AWREADY = 1'b1; 559 | end 560 | end //~ BURST 561 | 562 | 563 | BURST_RESP : 564 | begin 565 | s_axi_bvalid = 1'b1; 566 | s_axi_bid = AWID_Q; 567 | s_axi_bresp = `OKAY; 568 | s_axi_buser = AWUSER_Q; 569 | if(s_axi_bready) 570 | begin 571 | s_axi_awready = 1'b1; 572 | // Check if there are any pending request 573 | if(s_axi_awvalid) 574 | begin 575 | sample_AW = 1'b1; 576 | if(s_axi_wvalid) 577 | begin 578 | s_axi_wready = 1'b1; 579 | write_req = 1'b1; 580 | write_address = wr_addr; 581 | if(s_axi_awlen == 0) 582 | begin 583 | AW_NS = SINGLE; 584 | CountBurst_AW_NS = 0; 585 | end 586 | else 587 | begin 588 | AW_NS = BURST; 589 | CountBurst_AW_NS = 1; 590 | end 591 | end 592 | else // GOT ADDRESS WRITE, not DATA 593 | begin 594 | s_axi_wready = 1'b1; 595 | write_req = 1'b0; 596 | write_address = '0; 597 | 598 | if(s_axi_awlen == 0) 599 | begin 600 | AW_NS = WAIT_WDATA_SINGLE; 601 | CountBurst_AW_NS = 0; 602 | end 603 | else 604 | begin 605 | AW_NS = WAIT_WDATA_BURST; 606 | CountBurst_AW_NS = 0; 607 | end 608 | end 609 | end 610 | else 611 | begin 612 | s_axi_wready = 1'b1; 613 | AW_NS = IDLE; 614 | CountBurst_AW_NS = '0; 615 | end 616 | end 617 | else //~BREADY 618 | begin 619 | AW_NS = BURST_RESP; 620 | s_axi_awready = 1'b0; 621 | s_axi_wready = 1'b0; 622 | end 623 | end 624 | endcase 625 | end 626 | 627 | always @( posedge s_axi_aclk or negedge s_axi_aresetn ) 628 | begin 629 | if ( s_axi_aresetn == 1'b0 ) 630 | begin 631 | spi_swrst = 1'b0; 632 | spi_rd = 1'b0; 633 | spi_wr = 1'b0; 634 | spi_qrd = 1'b0; 635 | spi_qwr = 1'b0; 636 | spi_clk_div_valid = 1'b0; 637 | spi_clk_div = 'h0; 638 | spi_cmd = 'h0; 639 | spi_addr = 'h0; 640 | spi_cmd_len = 'h0; 641 | spi_addr_len = 'h0; 642 | spi_data_len = 'h0; 643 | spi_dummy_rd = 'h0; 644 | spi_dummy_wr = 'h0; 645 | spi_csreg = 'h0; 646 | end 647 | else if (write_req) 648 | begin 649 | spi_swrst = 1'b0; 650 | spi_rd = 1'b0; 651 | spi_wr = 1'b0; 652 | spi_qrd = 1'b0; 653 | spi_qwr = 1'b0; 654 | spi_clk_div_valid = 1'b0; 655 | case(write_address) 656 | `REG_STATUS: 657 | begin 658 | if ( s_axi_wstrb[0] == 1 ) 659 | begin 660 | spi_rd = s_axi_wdata[0]; 661 | spi_wr = s_axi_wdata[1]; 662 | spi_qrd = s_axi_wdata[2]; 663 | spi_qwr = s_axi_wdata[3]; 664 | spi_swrst = s_axi_wdata[4]; 665 | end 666 | if ( s_axi_wstrb[1] == 1 ) 667 | begin 668 | spi_csreg = s_axi_wdata[11:8]; 669 | end 670 | end 671 | `REG_CLKDIV: 672 | if ( s_axi_wstrb[0] == 1 ) 673 | begin 674 | spi_clk_div = s_axi_wdata[7:0]; 675 | spi_clk_div_valid = 1'b1; 676 | end 677 | `REG_SPICMD: 678 | for ( int byte_index = 0; byte_index < 4; byte_index = byte_index+1 ) 679 | if ( s_axi_wstrb[byte_index] == 1 ) 680 | spi_cmd[byte_index*8 +: 8] = s_axi_wdata[(byte_index*8) +: 8]; 681 | `REG_SPIADR: 682 | for ( int byte_index = 0; byte_index < 4; byte_index = byte_index+1 ) 683 | if ( s_axi_wstrb[byte_index] == 1 ) 684 | spi_addr[byte_index*8 +: 8] = s_axi_wdata[(byte_index*8) +: 8]; 685 | `REG_SPILEN: 686 | begin 687 | if ( s_axi_wstrb[0] == 1 ) 688 | spi_cmd_len = s_axi_wdata[7:0]; 689 | if ( s_axi_wstrb[1] == 1 ) 690 | spi_addr_len = s_axi_wdata[15:8]; 691 | if ( s_axi_wstrb[2] == 1 ) 692 | spi_data_len[7:0] = s_axi_wdata[23:16]; 693 | if ( s_axi_wstrb[3] == 1 ) 694 | spi_data_len[15:8] = s_axi_wdata[31:24]; 695 | end 696 | `REG_SPIDUM: 697 | begin 698 | if ( s_axi_wstrb[0] == 1 ) 699 | spi_dummy_rd[7:0] = s_axi_wdata[7:0]; 700 | if ( s_axi_wstrb[1] == 1 ) 701 | spi_dummy_rd[15:8] = s_axi_wdata[15:8]; 702 | if ( s_axi_wstrb[2] == 1 ) 703 | spi_dummy_wr[7:0] = s_axi_wdata[23:16]; 704 | if ( s_axi_wstrb[3] == 1 ) 705 | spi_dummy_wr[15:8] = s_axi_wdata[31:24]; 706 | end 707 | endcase 708 | end 709 | else 710 | begin 711 | spi_swrst = 1'b0; 712 | spi_rd = 1'b0; 713 | spi_wr = 1'b0; 714 | spi_qrd = 1'b0; 715 | spi_qwr = 1'b0; 716 | spi_clk_div_valid = 1'b0; 717 | end 718 | end // SLAVE_REG_WRITE_PROC 719 | 720 | 721 | // implement slave model register read mux 722 | always_comb 723 | begin 724 | s_axi_rdata = {32'h0,spi_data_rx}; 725 | case(read_address) 726 | `REG_STATUS: 727 | s_axi_rdata[31:0] = spi_status; 728 | `REG_CLKDIV: 729 | s_axi_rdata[31:0] = {24'h0,spi_clk_div}; 730 | `REG_SPICMD: 731 | s_axi_rdata[31:0] = spi_cmd; 732 | `REG_SPIADR: 733 | s_axi_rdata[31:0] = spi_addr; 734 | `REG_SPILEN: 735 | s_axi_rdata[31:0] = {spi_data_len,2'b00,spi_addr_len,2'b00,spi_cmd_len}; 736 | `REG_SPIDUM: 737 | s_axi_rdata[31:0] = {spi_dummy_wr,spi_dummy_rd}; 738 | endcase 739 | end // SLAVE_REG_READ_PROC 740 | 741 | assign spi_data_tx_valid = write_req & (write_address[3] == 1'b1); 742 | 743 | endmodule 744 | --------------------------------------------------------------------------------