Tim Edwards | 04ba17f | 2020-10-02 22:27:50 -0400 | [diff] [blame^] | 1 | //------------------------------------------------------ |
| 2 | // caravel_spi_slave.v |
| 3 | //------------------------------------------------------ |
| 4 | // General purpose SPI slave module for the Caravel chip |
| 5 | //------------------------------------------------------ |
| 6 | // Written by Tim Edwards |
| 7 | // efabless, inc., September 28, 2020 |
| 8 | //------------------------------------------------ |
| 9 | // This file is distributed free and open source |
| 10 | //------------------------------------------------ |
| 11 | |
| 12 | // SCK --- Clock input |
| 13 | // SDI --- Data input |
| 14 | // SDO --- Data output |
| 15 | // CSB --- Chip select (sense negative) |
| 16 | // idata --- Data from chip to transmit out, in 8 bits |
| 17 | // odata --- Input data to chip, in 8 bits |
| 18 | // addr --- Decoded address to upstream circuits |
| 19 | // rdstb --- Read strobe, tells upstream circuit to supply next byte to idata |
| 20 | // wrstb --- Write strobe, tells upstream circuit to latch odata. |
| 21 | |
| 22 | // Data format (general purpose): |
| 23 | // 8 bit format |
| 24 | // 1st byte: Command word (see below) |
| 25 | // 2nd byte: Address word (register 0 to 255) |
| 26 | // 3rd byte: Data word (value 0 to 255) |
| 27 | |
| 28 | // Command format: |
| 29 | // 00000000 No operation |
| 30 | // 10000000 Write until CSB raised |
| 31 | // 01000000 Read until CSB raised |
| 32 | // 11000000 Simultaneous read/write until CSB raised |
| 33 | // 11000100 Pass-through read/write to management area flash SPI until CSB raised |
| 34 | // 11000101 Pass-through read/write to user area flash SPI until CSB raised |
| 35 | // wrnnn000 Read/write as above, for nnn = 1 to 7 bytes, then terminate |
| 36 | |
| 37 | // Lower three bits are reserved for future use. |
| 38 | // All serial bytes are read and written msb first. |
| 39 | |
| 40 | // Fixed control and status registers |
| 41 | |
| 42 | // Address 0 is reserved and contains flags for SPI mode. This is |
| 43 | // currently undefined and is always value 0. |
| 44 | // Address 1 is reserved and contains manufacturer ID low 8 bits. |
| 45 | // Address 2 is reserved and contains manufacturer ID high 4 bits. |
| 46 | // Address 3 is reserved and contains product ID (8 bits). |
| 47 | // Addresses 4 to 7 are reserved and contain the mask ID (32 bits). |
| 48 | // Addresses 8 to 255 are available for general purpose use. |
| 49 | |
| 50 | `define COMMAND 3'b000 |
| 51 | `define ADDRESS 3'b001 |
| 52 | `define DATA 3'b010 |
| 53 | `define USERPASS 3'b100 |
| 54 | `define MGMTPASS 3'b101 |
| 55 | |
| 56 | module caravel_spi_slave(reset, SCK, SDI, CSB, SDO, |
| 57 | sdoenb, idata, odata, oaddr, rdstb, wrstb, |
| 58 | pass_thru_mgmt, pass_thru_mgmt_delay, |
| 59 | pass_thru_user, pass_thru_user_delay, pass_thru_reset); |
| 60 | |
| 61 | input reset; |
| 62 | input SCK; |
| 63 | input SDI; |
| 64 | input CSB; |
| 65 | output SDO; |
| 66 | output sdoenb; |
| 67 | input [7:0] idata; |
| 68 | output [7:0] odata; |
| 69 | output [7:0] oaddr; |
| 70 | output rdstb; |
| 71 | output wrstb; |
| 72 | output pass_thru_mgmt; |
| 73 | output pass_thru_mgmt_delay; |
| 74 | output pass_thru_user; |
| 75 | output pass_thru_user_delay; |
| 76 | output pass_thru_reset; |
| 77 | |
| 78 | reg [7:0] addr; |
| 79 | reg wrstb; |
| 80 | reg rdstb; |
| 81 | reg sdoenb; |
| 82 | reg [2:0] state; |
| 83 | reg [2:0] count; |
| 84 | reg writemode; |
| 85 | reg readmode; |
| 86 | reg [2:0] fixed; |
| 87 | wire [7:0] odata; |
| 88 | reg [6:0] predata; |
| 89 | wire [7:0] oaddr; |
| 90 | reg [7:0] ldata; |
| 91 | reg pass_thru_mgmt; |
| 92 | reg pass_thru_mgmt_delay; |
| 93 | reg pre_pass_thru_mgmt; |
| 94 | reg pass_thru_user; |
| 95 | reg pass_thru_user_delay; |
| 96 | reg pre_pass_thru_user; |
| 97 | wire csb_reset; |
| 98 | |
| 99 | assign odata = {predata, SDI}; |
| 100 | assign oaddr = (state == `ADDRESS) ? {addr[6:0], SDI} : addr; |
| 101 | assign SDO = ldata[7]; |
| 102 | assign csb_reset = CSB | reset; |
| 103 | assign pass_thru_reset = pass_thru_mgmt_delay | pre_pass_thru_mgmt; |
| 104 | |
| 105 | // Readback data is captured on the falling edge of SCK so that |
| 106 | // it is guaranteed valid at the next rising edge. |
| 107 | always @(negedge SCK or posedge CSB) begin |
| 108 | if (CSB == 1'b1) begin |
| 109 | wrstb <= 1'b0; |
| 110 | ldata <= 8'b00000000; |
| 111 | sdoenb <= 1'b1; |
| 112 | end else begin |
| 113 | |
| 114 | // After CSB low, 1st SCK starts command |
| 115 | |
| 116 | if (state == `DATA) begin |
| 117 | if (readmode == 1'b1) begin |
| 118 | sdoenb <= 1'b0; |
| 119 | if (count == 3'b000) begin |
| 120 | ldata <= idata; |
| 121 | end else begin |
| 122 | ldata <= {ldata[6:0], 1'b0}; // Shift out |
| 123 | end |
| 124 | end else begin |
| 125 | sdoenb <= 1'b1; |
| 126 | end |
| 127 | |
| 128 | // Apply write strobe on SCK negative edge on the next-to-last |
| 129 | // data bit so that it updates data on the rising edge of SCK |
| 130 | // on the last data bit. |
| 131 | |
| 132 | if (count == 3'b111) begin |
| 133 | if (writemode == 1'b1) begin |
| 134 | wrstb <= 1'b1; |
| 135 | end |
| 136 | end else begin |
| 137 | wrstb <= 1'b0; |
| 138 | end |
| 139 | end else if (state == `MGMTPASS || state == `USERPASS) begin |
| 140 | wrstb <= 1'b0; |
| 141 | sdoenb <= 1'b0; |
| 142 | end else begin |
| 143 | wrstb <= 1'b0; |
| 144 | sdoenb <= 1'b1; |
| 145 | end // ! state `DATA |
| 146 | end // ! CSB |
| 147 | end // always @ ~SCK |
| 148 | |
| 149 | always @(posedge SCK or posedge CSB) begin |
| 150 | if (csb_reset == 1'b1) begin |
| 151 | // Default state on reset |
| 152 | addr <= 8'h00; |
| 153 | rdstb <= 1'b0; |
| 154 | predata <= 7'b0000000; |
| 155 | state <= `COMMAND; |
| 156 | count <= 3'b000; |
| 157 | readmode <= 1'b0; |
| 158 | writemode <= 1'b0; |
| 159 | fixed <= 3'b000; |
| 160 | pass_thru_mgmt <= 1'b0; |
| 161 | pass_thru_mgmt_delay <= 1'b0; |
| 162 | pre_pass_thru_mgmt <= 1'b0; |
| 163 | pass_thru_user = 1'b0; |
| 164 | pass_thru_user_delay <= 1'b0; |
| 165 | pre_pass_thru_user <= 1'b0; |
| 166 | end else begin |
| 167 | // After CSB low, 1st SCK starts command |
| 168 | if (state == `COMMAND) begin |
| 169 | rdstb <= 1'b0; |
| 170 | count <= count + 1; |
| 171 | if (count == 3'b000) begin |
| 172 | writemode <= SDI; |
| 173 | end else if (count == 3'b001) begin |
| 174 | readmode <= SDI; |
| 175 | end else if (count < 3'b101) begin |
| 176 | fixed <= {fixed[1:0], SDI}; |
| 177 | end else if (count < 3'b110) begin |
| 178 | pass_thru_mgmt_delay <= pre_pass_thru_mgmt; |
| 179 | pass_thru_user_delay <= pre_pass_thru_user; |
| 180 | end else if (count == 3'b111) begin |
| 181 | if (pre_pass_thru_mgmt == 1'b1) begin |
| 182 | state <= `MGMTPASS; |
| 183 | pre_pass_thru_mgmt <= 1'b0; |
| 184 | end else if (pre_pass_thru_user == 1'b1) begin |
| 185 | state <= `USERPASS; |
| 186 | pre_pass_thru_user <= 1'b0; |
| 187 | end else begin |
| 188 | state <= `ADDRESS; |
| 189 | end |
| 190 | end |
| 191 | end else if (state == `ADDRESS) begin |
| 192 | count <= count + 1; |
| 193 | addr <= {addr[6:0], SDI}; |
| 194 | if (count == 3'b111) begin |
| 195 | if (readmode == 1'b1) begin |
| 196 | rdstb <= 1'b1; |
| 197 | end |
| 198 | state <= `DATA; |
| 199 | end else begin |
| 200 | rdstb <= 1'b0; |
| 201 | end |
| 202 | end else if (state == `DATA) begin |
| 203 | predata <= {predata[6:0], SDI}; |
| 204 | count <= count + 1; |
| 205 | if (count == 3'b111) begin |
| 206 | if (fixed == 3'b001) begin |
| 207 | state <= `COMMAND; |
| 208 | end else if (fixed != 3'b000) begin |
| 209 | fixed <= fixed - 1; |
| 210 | addr <= addr + 1; // Auto increment address (fixed) |
| 211 | end else begin |
| 212 | addr <= addr + 1; // Auto increment address (streaming) |
| 213 | end |
| 214 | end else begin |
| 215 | rdstb <= 1'b0; |
| 216 | end |
| 217 | end // ! state `DATA |
| 218 | end // ! CSB |
| 219 | end // always @ SCK |
| 220 | |
| 221 | endmodule // caravel_spi_slave |