├── .gitignore ├── README.md ├── bench └── formal │ ├── .gitignore │ ├── Makefile │ ├── rawslave.gtkw │ └── rawslave.sby ├── doc ├── critical-cdc.dia ├── critical-cdc.png ├── rawspislave-equal.png ├── rawspislave-highspeed.png ├── rawspislave-traditional.png ├── uctrlr-isa.dia ├── uctrlr-isa.md └── uctrlr-isa.png ├── rtl ├── README.md ├── rawslave.v └── spicpu.v └── sw ├── .gitignore ├── Makefile ├── README.md └── spiasm.l /.gitignore: -------------------------------------------------------------------------------- 1 | legal.txt 2 | .svn 3 | xilinx 4 | obj_dir 5 | obj-pc 6 | obj-zip 7 | *.o 8 | *.a 9 | *.vcd 10 | .swp 11 | .*.swp 12 | .*.swo 13 | svn-commit* 14 | *_tb 15 | *_tb.dbl 16 | *dbg.txt 17 | *dump.txt 18 | *debug.txt 19 | tags 20 | cpudefs.h 21 | design.h 22 | octave-workspace 23 | core 24 | *.aux 25 | *.log 26 | *.out 27 | *.ps 28 | *.yslog 29 | *.smt2 30 | lex.yy.c 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## (Wishbone accessible) SPI Cores 2 | 3 | This repository will eventually contain some SPI cores with a Wishbone 4 | interface. 5 | 6 | For now, it only contains a [SPI slave with a raw interface](rtl/rawslave.v). 7 | This slave core will send signals, `o_frame` at the end of a SPI transaction, 8 | `o_rd` when the core wishes to read a byte from the local interface that will 9 | then be sent over MISO, and `o_wr` when it wishes to write a byte to the local 10 | interface once it has been read from the MOSI channel. The first byte sent 11 | over `MISO` is always undefined. 12 | 13 | This core was really designed to be high speed. By that I mean that the slave 14 | can handle any SPI clock relationship to the system clock up to just less than 15 | 33% faster than the system clock. A sad consequence of this is that it might 16 | be difficult to reply to an incoming data request without losing an additional 17 | 8-bits of `MISO`. So ... make certain this works in your environment before 18 | and if you should try to use it. 19 | 20 | Feel free to read more information about this core in the documentation of the 21 | core itself. 22 | 23 | ## License 24 | 25 | The core as written is licensed under GPL. Feel free to contact me if this 26 | license is not sufficient for your needs and we can discuss other options. 27 | -------------------------------------------------------------------------------- /bench/formal/.gitignore: -------------------------------------------------------------------------------- 1 | rawslave_prf/ 2 | rawslave_cvr/ 3 | -------------------------------------------------------------------------------- /bench/formal/Makefile: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | ## 3 | ## Filename: bench/formal/Makefile 4 | ## 5 | ## Project: wbspi, a set of Serial Peripheral Interface cores 6 | ## 7 | ## Purpose: Only runs proofs when files have changed 8 | ## 9 | ## Creator: Dan Gisselquist, Ph.D. 10 | ## Gisselquist Technology, LLC 11 | ## 12 | ################################################################################ 13 | ## 14 | ## Copyright (C) 2020, Gisselquist Technology, LLC 15 | ## 16 | ## This program is free software (firmware): you can redistribute it and/or 17 | ## modify it under the terms of the GNU General Public License as published 18 | ## by the Free Software Foundation, either version 3 of the License, or (at 19 | ## your option) any later version. 20 | ## 21 | ## This program is distributed in the hope that it will be useful, but WITHOUT 22 | ## ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or 23 | ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 24 | ## for more details. 25 | ## 26 | ## You should have received a copy of the GNU General Public License along 27 | ## with this program. (It's in the $(ROOT)/doc directory. Run make with no 28 | ## target there if the PDF file isn't present.) If not, see 29 | ## for a copy. 30 | ## 31 | ## License: GPL, v3, as defined and found on www.gnu.org, 32 | ## http://www.gnu.org/licenses/gpl.html 33 | ## 34 | ## 35 | ################################################################################ 36 | ## 37 | ## 38 | .PHONY: all 39 | all: rawslave 40 | 41 | rawslave: rawslave_prf/PASS rawslave_cvr/PASS 42 | rawslave_prf/PASS: rawslave.sby ../../rtl/rawslave.v 43 | sby -f rawslave.sby prf 44 | rawslave_cvr/PASS: rawslave.sby ../../rtl/rawslave.v 45 | sby -f rawslave.sby cvr 46 | 47 | .PHONY: clean 48 | clean: 49 | rm -rf rawslave_*/ 50 | -------------------------------------------------------------------------------- /bench/formal/rawslave.gtkw: -------------------------------------------------------------------------------- 1 | [*] 2 | [*] GTKWave Analyzer v3.3.86 (w)1999-2017 BSI 3 | [*] Sat Jul 13 09:29:37 2019 4 | [*] 5 | [dumpfile] "/home/dan/bizcopy/wbspi/bench/formal/rawslave_cvr/engine_0/trace10.vcd" 6 | [dumpfile_mtime] "Wed Jul 10 20:26:47 2019" 7 | [dumpfile_size] 37231 8 | [savefile] "/home/dan/bizcopy/wbspi/bench/formal/rawslave.gtkw" 9 | [timestart] 0 10 | [size] 1918 866 11 | [pos] -453 -1 12 | *-6.957687 270 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 13 | [sst_width] 270 14 | [signals_width] 220 15 | [sst_expanded] 1 16 | [sst_vpaned_height] 248 17 | @28 18 | rawslave.f_past_valid_clk 19 | rawslave.f_past_valid_gbl 20 | rawslave.f_past_valid_spi 21 | rawslave.i_reset_clk 22 | @200 23 | - 24 | @28 25 | [color] 3 26 | rawslave.i_clk 27 | [color] 3 28 | rawslave.i_reset 29 | @200 30 | - 31 | @28 32 | [color] 2 33 | rawslave.o_frame 34 | [color] 2 35 | rawslave.o_rd 36 | @22 37 | [color] 3 38 | rawslave.i_byte[7:0] 39 | @28 40 | [color] 2 41 | rawslave.o_we 42 | @22 43 | [color] 2 44 | rawslave.o_byte[7:0] 45 | @200 46 | - 47 | @28 48 | [color] 3 49 | rawslave.i_spi_csn 50 | [color] 3 51 | rawslave.i_spi_sck 52 | [color] 3 53 | rawslave.i_spi_mosi 54 | [color] 2 55 | rawslave.o_spi_miso 56 | @200 57 | - 58 | @28 59 | rawslave.f_active 60 | rawslave.f_clkd_while_idle 61 | rawslave.f_fault 62 | rawslave.f_past_csn 63 | rawslave.f_past_sck 64 | rawslave.pre_stb 65 | @200 66 | - 67 | @28 68 | rawslave.spi_bitcount[2:0] 69 | @22 70 | rawslave.spi_byte[7:0] 71 | rawslave.spi_output[7:0] 72 | rawslave.spi_sreg[6:0] 73 | @28 74 | rawslave.sync_spi_stb 75 | rawslave.sync_stb_pipe[1:0] 76 | @22 77 | rawslave.xck_oreg[7:0] 78 | @28 79 | rawslave.xck_stb 80 | @22 81 | rawslave.f_txseq[7:0] 82 | @28 83 | rawslave.f_bytes_rcvd[2:0] 84 | rawslave.f_bytes_sent[2:0] 85 | @201 86 | - 87 | @28 88 | rawslave.f_cvrspd_slow 89 | rawslave.f_cvrspd_equal 90 | rawslave.f_cvrspd_fast 91 | @200 92 | - 93 | [pattern_trace] 1 94 | [pattern_trace] 0 95 | -------------------------------------------------------------------------------- /bench/formal/rawslave.sby: -------------------------------------------------------------------------------- 1 | [tasks] 2 | prf 3 | cvr 4 | 5 | [options] 6 | prf: mode prove 7 | prf: depth 98 8 | cvr: mode cover 9 | cvr: depth 240 10 | 11 | multiclock on 12 | 13 | [engines] 14 | smtbmc 15 | 16 | [script] 17 | read -formal rawslave.v 18 | prep -top rawslave 19 | 20 | [files] 21 | ../../rtl/rawslave.v 22 | -------------------------------------------------------------------------------- /doc/critical-cdc.dia: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZipCPU/wbspi/bab5284a30979516aed8498b22961aba7ab5aa88/doc/critical-cdc.dia -------------------------------------------------------------------------------- /doc/critical-cdc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZipCPU/wbspi/bab5284a30979516aed8498b22961aba7ab5aa88/doc/critical-cdc.png -------------------------------------------------------------------------------- /doc/rawspislave-equal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZipCPU/wbspi/bab5284a30979516aed8498b22961aba7ab5aa88/doc/rawspislave-equal.png -------------------------------------------------------------------------------- /doc/rawspislave-highspeed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZipCPU/wbspi/bab5284a30979516aed8498b22961aba7ab5aa88/doc/rawspislave-highspeed.png -------------------------------------------------------------------------------- /doc/rawspislave-traditional.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZipCPU/wbspi/bab5284a30979516aed8498b22961aba7ab5aa88/doc/rawspislave-traditional.png -------------------------------------------------------------------------------- /doc/uctrlr-isa.dia: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZipCPU/wbspi/bab5284a30979516aed8498b22961aba7ab5aa88/doc/uctrlr-isa.dia -------------------------------------------------------------------------------- /doc/uctrlr-isa.md: -------------------------------------------------------------------------------- 1 | # SPI MicroController ISA 2 | 3 | ## Instructions 4 | 5 | All instructions are 8bits. Some instructions, such as `SEND` or `TXRX` will 6 | be followed by subsequent bytes containing values to be sent over the interface. 7 | 8 | - **START #**: Activates the CS# pin, deactivating all other pins. Attempts 9 | to activate a non-existent device will quietly be treated as STOP commands. 10 | 11 | Note that there is no means of activating multiple CS# signals at once. 12 | 13 | - **STOP**: Deactivates all CS# pins. This is encoded as a START command with 14 | an all ones argument--i.e., the maximum device number. 15 | 16 | - **READ #**: Reads `#` octets from the SPI interface, and forwards those 17 | octets to the AXI stream. This will involve clocking the SPI interface `8*#` 18 | times. The minimum read is 1 octet, and the maximum is 16. The actual 19 | encoding is off by one, so that 0-15 encodes a read of between 1-16 values, 20 | even though the assembler will attempt to hide this reality. The value sent 21 | across the SPI interface during this time is a don't care. If the value on 22 | the MOSI pin is important, use a TXRX command. 23 | 24 | - **SEND #,V1**,V2,V3,...: Sends between 1 and 16 bytes across the SPI 25 | interface, clocking it `8*#` times in the process. The values transmitted 26 | are given as arguments to the command in subsequent bytes: V1, V2, V3, etc. 27 | As with the read, the minimum number of items to send is one and the maximum 28 | number is 16. Likewise with the read, the encoded number of items to be sent 29 | is off by one. Any value(s) returned on the MISO pin during these commands 30 | will be ignored. 31 | 32 | - **TXRX #,V1**,V2,V3,...: This is a combination of the READ and SEND commands. 33 | Values to be sent are provided as arguments to this command. Values read 34 | from the device will be fed to the AXI stream (external) interface. 35 | 36 | - **LAST**: Sets an internal "LAST" flag. If set, the following READ or TXRX 37 | command will set the AXI stream LAST flag on the last beat of the read. 38 | 39 | - **HALT**: Halts the script. Sets a CPU interrupt. 40 | 41 | - **WAIT**: Deactivates all CS# signals and halts the processing of further 42 | commands. Commands will begin again following the next synchronization 43 | signal. 44 | 45 | - **TICK**: Issues a single clock tick to the SPI interface. The return value 46 | is ignored. 47 | 48 | The argument is reserved for either a number of clock ticks, or the value 49 | to be sent over MOSI, or ... something still to be determined. 50 | 51 | - **TARGET**: Sets the address of where to jump to. 52 | 53 | This permits simple looping. More complicating looping will require CPU 54 | intervention. 55 | 56 | - **JUMP**: Jumps to the address of the last `TARGET` command. 57 | 58 | - **CHAN #**: Specifies that any bytes read following this command will be 59 | forwarded to the given AXI stream channel. (`TID` will be set to `#`.) 60 | Only relevant if/when the number of TID bits is non-zero. In all other 61 | cases, this command operates as a NOOP. 62 | 63 | - **NOOP**: Does nothing. 64 | 65 | -------------------------------------------------------------------------------- /doc/uctrlr-isa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZipCPU/wbspi/bab5284a30979516aed8498b22961aba7ab5aa88/doc/uctrlr-isa.png -------------------------------------------------------------------------------- /rtl/README.md: -------------------------------------------------------------------------------- 1 | ## Files 2 | 3 | - [rawslave.v](rawslave.v): A raw SPI slave. Notable for the fact that it 4 | allows the SPI interface to operate on one clock (SCK) and the rest of the 5 | design on another. As a result, SCK can run (slightly) faster than the 6 | rest of the design. 7 | 8 | - [spicpu.v](spicpu.v): A memory controlled SPI master. This has been 9 | designed for controlling a telemetry stream. Once configured with 10 | a memory address for its script, and possibly a (repeating) interrupt 11 | for a sync, this can run its script repeatedly outputting its results to an 12 | AXI stream. 13 | 14 | ## Status 15 | 16 | Neither of these designs have ever seen the light of hardware. 17 | -------------------------------------------------------------------------------- /rtl/rawslave.v: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // Filename: rawslave.v 4 | // {{{ 5 | // Project: wbspi, a set of Serial Peripheral Interface cores 6 | // 7 | // Purpose: This design implements a raw SPI slave. Typically, were I to 8 | // implement a raw SPI slave, I'd make some assumptions about 9 | // the speed of the SPI clock being 3x slower than the system clock, and 10 | // then I'd synchronize all of the inputs to the system clock. Not for 11 | // this design. This design was created under the assumption that the 12 | // SPI SCK (clock) signal is up to 2x as fast as the system clock. This 13 | // leads to some difficult clock domain crossing issues, solved herein. 14 | // 15 | // Interface: 16 | // 17 | // Upon receiving 8-bits from the MOSI pin on the SCK, the data will be 18 | // transferred from the SPI clock domain to a surrounding system clock 19 | // domain. Once done, the o_we signal will go high, and o_byte will 20 | // contain that byte. 21 | // 22 | // Any time the SPI interface becomes idle (CSN goes inactive/high), the 23 | // o_frame signal will be set--allowing interacting software to know 24 | // when the frame is starting. 25 | // 26 | // o_frame will remain set until o_rd goes high. The i_byte value present 27 | // when o_rd is high will be sent to the SPI port. It will not be the 28 | // first 8-bits sent, but rather the second. To avoid metastability 29 | // issues, this value can only be changed when o_rd is high. 30 | // 31 | // Creator: Dan Gisselquist, Ph.D. 32 | // Gisselquist Technology, LLC 33 | // 34 | //////////////////////////////////////////////////////////////////////////////// 35 | // }}} 36 | // Copyright (C) 2019-2022, Gisselquist Technology, LLC 37 | // {{{ 38 | // This program is free software (firmware): you can redistribute it and/or 39 | // modify it under the terms of the GNU General Public License as published 40 | // by the Free Software Foundation, either version 3 of the License, or (at 41 | // your option) any later version. 42 | // 43 | // This program is distributed in the hope that it will be useful, but WITHOUT 44 | // ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or 45 | // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 46 | // for more details. 47 | // 48 | // You should have received a copy of the GNU General Public License along 49 | // with this program. (It's in the $(ROOT)/doc directory. Run make with no 50 | // target there if the PDF file isn't present.) If not, see 51 | // for a copy. 52 | // 53 | // License: GPL, v3, as defined and found on www.gnu.org, 54 | // http://www.gnu.org/licenses/gpl.html 55 | // 56 | // 57 | //////////////////////////////////////////////////////////////////////////////// 58 | // 59 | // 60 | 61 | `default_nettype none 62 | // 63 | // }}} 64 | module rawslave #( 65 | // {{{ 66 | // Default to a 2-clock synchronizer FF chain 67 | localparam NFF = 2 // FF's in synchronizer set 68 | // }}} 69 | ) ( 70 | // {{{ 71 | input wire i_clk, i_reset, 72 | // 73 | // Verilator lint_off SYNCASYNCNET 74 | input wire i_spi_csn, 75 | // Verilator lint_on SYNCASYNCNET 76 | input wire i_spi_sck, i_spi_mosi, 77 | output wire o_spi_miso, 78 | // 79 | output reg o_frame, 80 | output reg o_we, 81 | output reg [7:0] o_byte, 82 | output wire o_rd, 83 | input reg [7:0] i_byte 84 | // }}} 85 | ); 86 | 87 | //////////////////////////////////////////////////////////////////////// 88 | // 89 | // SPI clock domain 90 | // {{{ 91 | //////////////////////////////////////////////////////////////////////// 92 | // 93 | // 94 | 95 | // Registers used in the SPI clock domain 96 | // {{{ 97 | reg [2:0] spi_bitcount, spi_bitcount_n; 98 | reg spi_rdreq; 99 | reg [7:0] spi_byte, xck_oreg; 100 | reg [6:0] spi_sreg; 101 | reg xck_stb; 102 | reg [7:0] spi_output; 103 | // }}} 104 | 105 | // spi_bitcount 106 | // {{{ 107 | initial spi_bitcount = 0; 108 | always @(posedge i_spi_sck, posedge i_spi_csn) 109 | if (i_spi_csn) 110 | spi_bitcount <= 0; 111 | else 112 | spi_bitcount <= spi_bitcount + 1; 113 | // }}} 114 | 115 | // MOSI -> spi_sreg - the SPI bit-wise shift register 116 | // {{{ 117 | always @(posedge i_spi_sck) 118 | spi_sreg <= { spi_sreg[5:0], i_spi_mosi }; 119 | // }}} 120 | 121 | // { sreg, MOSI } -> spi_byte at the end of each byte 122 | // {{{ 123 | always @(posedge i_spi_sck) 124 | if (!i_spi_csn && spi_bitcount[2:0] == 3'b111) 125 | spi_byte <= { spi_sreg[6:0], i_spi_mosi }; 126 | // }}} 127 | 128 | // xck_stb - the cross clock strobe indicating we have a byte available 129 | // {{{ 130 | initial xck_stb = 0; 131 | always @(posedge i_spi_sck) 132 | if (spi_bitcount[2:0] == 3'h7) 133 | xck_stb <= 1'b1; 134 | else if ((spi_bitcount[2:0] >= 3'h3)&&(spi_bitcount[2:0] < 3'h7)) 135 | xck_stb <= 1'b0; 136 | // }}} 137 | 138 | // 139 | // Negative edge of the clock 140 | // 141 | // This is where the master reads from the SPI port 142 | // 143 | 144 | // spi_bitcount_n - bit counting on the negative edge of the clock 145 | // {{{ 146 | initial spi_bitcount_n = 0; 147 | always @(negedge i_spi_sck, posedge i_spi_csn) 148 | if (i_spi_csn) 149 | spi_bitcount_n <= 0; 150 | else 151 | spi_bitcount_n <= spi_bitcount_n + 1; 152 | // }}} 153 | 154 | // spi_rdreq - requesting a read from the other clock domain 155 | // {{{ 156 | initial spi_rdreq = 0; 157 | always @(negedge i_spi_sck) 158 | if (spi_bitcount_n[2:0] < 3'h4) 159 | spi_rdreq <= !i_spi_csn; 160 | else 161 | spi_rdreq <= 0; 162 | // }}} 163 | 164 | // spi_output 165 | // {{{ 166 | // Output data can only change on the negative edge of the clock 167 | initial spi_output = 0; 168 | always @(negedge i_spi_sck, posedge i_spi_csn) 169 | if (i_spi_csn) 170 | spi_output <= 0; 171 | else if (spi_bitcount_n[2:0] == 3'h7) 172 | spi_output <= xck_oreg; 173 | else 174 | spi_output <= spi_output << 1; 175 | // }}} 176 | 177 | // MISO 178 | // {{{ 179 | // We might want if (CSN) 1'bz else spi_output[7]. However, few 180 | // synthesizers can handle 'zs within a design, and most 'z definitions 181 | // require special output control. Therefore we'll leave 'z handling 182 | // for the top-level. This does create a risk that the 'z I/O handling 183 | // won't be fast enough, since it isn't properly registered. May need 184 | // to come back to this later. 185 | assign o_spi_miso = spi_output[7]; 186 | // }}} 187 | 188 | // }}} 189 | //////////////////////////////////////////////////////////////////////// 190 | // 191 | // System clock domain 192 | // {{{ 193 | //////////////////////////////////////////////////////////////////////// 194 | // 195 | // 196 | 197 | // Registers used in the system clock domain 198 | // {{{ 199 | reg last_spi_rd, last_spi_stb; 200 | reg sync_spi_rd, sync_spi_stb, sync_spi_frame; 201 | reg [NFF-2:0] sync_rd_pipe, sync_stb_pipe, sync_frame_pipe; 202 | wire pre_stb; 203 | // }}} 204 | 205 | // last_spi_rd, sync_spi_rd, sync_rd_pipe -- clock synchronizers 206 | // {{{ 207 | initial { last_spi_rd, sync_spi_rd, sync_rd_pipe } = 0; 208 | always @(posedge i_clk) 209 | if (i_reset) 210 | { last_spi_rd, sync_spi_rd, sync_rd_pipe } <= 3'h0; 211 | else 212 | { last_spi_rd, sync_spi_rd, sync_rd_pipe } <= { sync_spi_rd, 213 | sync_rd_pipe, spi_rdreq&&!i_spi_csn }; 214 | // }}} 215 | 216 | // o_rd 217 | // {{{ 218 | assign o_rd = !last_spi_rd && sync_spi_rd; 219 | // }}} 220 | 221 | // { sync_spi_frame, sync_frame_pipe } - frame synchronization 222 | // {{{ 223 | initial { sync_spi_frame, sync_frame_pipe } = -1; 224 | always @(posedge i_clk) 225 | if (i_reset) 226 | { sync_spi_frame, sync_frame_pipe } <= -1; 227 | else 228 | { sync_spi_frame, sync_frame_pipe } 229 | <= { sync_frame_pipe, i_spi_csn }; 230 | // }}} 231 | 232 | // o_frame 233 | // {{{ 234 | initial o_frame = 1'b1; 235 | always @(posedge i_clk) 236 | if (i_reset) 237 | o_frame <= 1'b1; 238 | else if (o_rd) 239 | o_frame <= 0; 240 | else if (sync_spi_frame) 241 | o_frame <= 1; 242 | // }}} 243 | 244 | // xck_oreg -- Byte-wise data headed back to the master 245 | // {{{ 246 | assign xck_oreg = i_byte; 247 | // }}} 248 | 249 | // { last_spi_stb, sync_spi_stb, sync_stb_pipe } from xck_stb 250 | // {{{ 251 | initial { last_spi_stb, sync_spi_stb, sync_stb_pipe } = -1; 252 | always @(posedge i_clk) 253 | if (i_reset) 254 | { last_spi_stb, sync_spi_stb, sync_stb_pipe } <= -1; 255 | else 256 | { last_spi_stb, sync_spi_stb, sync_stb_pipe } 257 | <= { sync_spi_stb, sync_stb_pipe, xck_stb }; 258 | 259 | assign pre_stb = !last_spi_stb && sync_spi_stb; 260 | // }}} 261 | 262 | // o_we 263 | // {{{ 264 | initial o_we = 0; 265 | always @(posedge i_clk) 266 | o_we <= pre_stb && !i_reset; 267 | // }}} 268 | 269 | // o_byte 270 | // {{{ 271 | always @(posedge i_clk) 272 | if (pre_stb) 273 | o_byte <= spi_byte; 274 | // }}} 275 | 276 | // }}} 277 | // Make verilator happy 278 | // {{{ 279 | // Verilator lint_off UNUSED 280 | wire unused; 281 | assign unused = &{ 1'b0 }; // Not currently needed 282 | // Verilator lint_on UNUSED 283 | // }}} 284 | ////////////////////////////////////////////////////////////////////////////// 285 | //////////////////////////////////////////////////////////////////////////////// 286 | //////////////////////////////////////////////////////////////////////////////// 287 | // 288 | // Formal properties 289 | // {{{ 290 | //////////////////////////////////////////////////////////////////////////////// 291 | //////////////////////////////////////////////////////////////////////////////// 292 | //////////////////////////////////////////////////////////////////////////////// 293 | `ifdef FORMAL 294 | // Formal related register/net/param declarations 295 | // {{{ 296 | localparam F_CKBITS=5; 297 | (* anyconst *) reg [F_CKBITS-1:0] f_sysck_step, 298 | f_spick_step; 299 | reg [F_CKBITS-1:0] f_sysck_counter, f_sysck_next, 300 | f_spick_counter, f_spick_next; 301 | reg f_will_rise_clk, f_will_rise_sck, 302 | f_will_fall_sck; 303 | 304 | (* gclk *) reg gbl_clk; 305 | reg f_past_valid_gbl, f_past_valid_spi, f_past_valid_clk; 306 | reg f_past_csn, f_past_sck; //, f_clkd_while_idle; 307 | wire f_active, f_fault; 308 | reg [3:0] f_csn_hi_count, f_csn_to_csn_count, 309 | f_csn_lo_count; 310 | reg last_spi_frame; 311 | reg [8:0] f_rxseq; 312 | reg [3:0] f_rxcdc; 313 | reg [7:0] f_data, f_rcvd; 314 | wire [2:0] f_sync_frame; 315 | reg f_pending_rdstb; 316 | reg [7:0] f_txmit, f_txseq; 317 | reg f_pending_wrstb; 318 | reg f_latter_byte_n; 319 | // }}} 320 | 321 | //////////////////////////////////////////////////////////////////////// 322 | // 323 | // Generate some (assumed) clocking signals 324 | // {{{ 325 | //////////////////////////////////////////////////////////////////////// 326 | // 327 | // 328 | 329 | always @(*) 330 | begin 331 | assume(f_sysck_step > 0); 332 | assume(f_sysck_step <= { 1'b1, {(F_CKBITS-1){1'b0}} }); 333 | 334 | assume(f_spick_step > 0); 335 | assume(f_spick_step <= { 1'b1, {(F_CKBITS-1){1'b0}} }); 336 | end 337 | 338 | always @(posedge gbl_clk) 339 | begin 340 | f_sysck_counter <= f_sysck_counter + f_sysck_step; 341 | f_spick_counter <= f_spick_counter + f_spick_step; 342 | end 343 | 344 | always @(*) 345 | begin 346 | assume(i_clk == f_sysck_counter[F_CKBITS-1]); 347 | 348 | if (!i_spi_csn) 349 | assume(i_spi_sck == f_spick_counter[F_CKBITS-1]); 350 | end 351 | 352 | always @(posedge gbl_clk) 353 | if ($fell(i_spi_csn)) 354 | assume(i_spi_sck); 355 | 356 | // always @(posedge gbl_clk) 357 | // if ($stable(f_spick_counter[F_CKBITS-1])) 358 | // assume($stable(i_spi_sck)); 359 | 360 | always @(*) 361 | begin 362 | f_sysck_next = f_sysck_counter + f_sysck_step; 363 | f_spick_next = f_spick_counter + f_spick_step; 364 | 365 | f_will_rise_clk = !i_clk && f_sysck_next[F_CKBITS-1]; 366 | f_will_rise_sck = !i_spi_sck && f_spick_next[F_CKBITS-1]; 367 | f_will_fall_sck = !i_spi_csn && i_spi_sck && !f_spick_next[F_CKBITS-1]; 368 | end 369 | // }}} 370 | //////////////////////////////////////////////////////////////////////// 371 | // 372 | // f_past_valid 373 | // {{{ 374 | //////////////////////////////////////////////////////////////////////// 375 | // 376 | // 377 | 378 | initial f_past_valid_gbl = 1'b0; 379 | always @(posedge gbl_clk) 380 | f_past_valid_gbl <= 1'b1; 381 | 382 | initial f_past_valid_spi = 1'b0; 383 | always @(posedge i_spi_sck) 384 | f_past_valid_spi <= 1'b1; 385 | 386 | initial f_past_valid_clk = 1'b0; 387 | always @(posedge i_clk) 388 | f_past_valid_clk <= 1'b1; 389 | 390 | always @(*) 391 | assume(i_reset == !f_past_valid_clk); 392 | // }}} 393 | //////////////////////////////////////////////////////////////////////// 394 | // 395 | // Basic SPI protocol rules 396 | // {{{ 397 | //////////////////////////////////////////////////////////////////////// 398 | // 399 | // 400 | 401 | // 402 | // Always start de-selected 403 | always @(*) 404 | if (!f_past_valid_gbl) 405 | assume(i_spi_csn); 406 | 407 | initial { f_past_csn, f_past_sck } = 2'b11; 408 | always @(posedge gbl_clk) 409 | { f_past_csn, f_past_sck } <= { i_spi_csn, i_spi_sck }; 410 | 411 | assign f_active = !i_spi_csn && !f_past_csn; 412 | assign f_fault = i_spi_csn && !f_past_sck; 413 | 414 | // 415 | // When i_spi_csn falls (i.e. activates), the clock must be stable 416 | // and *high* 417 | always @(*) 418 | if (i_spi_csn || f_past_csn) 419 | assume(i_spi_sck); 420 | 421 | // 422 | // The incoming data line from the master changes on a falling edge 423 | // *only*. 424 | always @(posedge gbl_clk) 425 | if (f_past_valid_gbl && f_active && !$fell(i_spi_sck)) 426 | assume($stable(i_spi_mosi)); 427 | 428 | always @(posedge gbl_clk) 429 | if ($fell(i_spi_csn)) 430 | assert(o_frame); 431 | 432 | initial last_spi_frame = 1'b1; 433 | always @(posedge i_clk) 434 | if (i_reset) 435 | last_spi_frame <= 1'b1; 436 | else 437 | last_spi_frame <= sync_spi_frame; 438 | 439 | always @(*) 440 | if (last_spi_frame) 441 | assert(o_frame); 442 | 443 | // 444 | // The outgoing data line changes on a rising edge *only*. However, 445 | // any time i_spi_csn is high, it's a don't care. 446 | always @(posedge gbl_clk) 447 | if (f_past_valid_gbl && !i_spi_csn && $past(!i_spi_csn) 448 | && !$past(i_spi_sck) && !$rose(i_spi_sck)) 449 | assert($stable(o_spi_miso)); 450 | 451 | /* 452 | initial f_clkd_while_idle <= 1'b1; 453 | always @(posedge gbl_clk) 454 | if (!i_spi_csn) 455 | f_clkd_while_idle <= 1'b0; 456 | else if ($rose(i_clk)) 457 | f_clkd_while_idle <= 1'b1; 458 | 459 | always @(posedge gbl_clk) 460 | if (!f_clkd_while_idle && f_past_csn) 461 | assume(i_spi_csn); 462 | */ 463 | // }}} 464 | //////////////////////////////////////////////////////////////////////// 465 | // 466 | // Synchronous Input assumptions 467 | // {{{ 468 | //////////////////////////////////////////////////////////////////////// 469 | // 470 | // 471 | 472 | always @(posedge gbl_clk) 473 | if (f_past_valid_gbl && !$rose(i_clk)) 474 | begin 475 | assume($stable(i_reset)); 476 | assume($stable(i_byte)); 477 | // 478 | // Synchronous output assertions 479 | assert($stable(o_we)); 480 | assert($stable(o_byte)); 481 | assert($stable(o_frame)); 482 | assert($stable(o_rd)); 483 | end 484 | // }}} 485 | //////////////////////////////////////////////////////////////////////// 486 | // 487 | // Basic CDC rules 488 | // {{{ 489 | //////////////////////////////////////////////////////////////////////// 490 | // 491 | // 492 | 493 | // If ever pre_stb is rises, spi_byte must be stable 494 | always @(posedge gbl_clk) 495 | if (f_past_valid_gbl && $rose(pre_stb) && !f_fault) 496 | assert($stable(spi_byte)); 497 | 498 | `ifdef VERIFIC 499 | always @(*) 500 | case({ last_spi_stb, sync_spi_stb, sync_stb_pipe }) 501 | 3'b000: begin end 502 | 3'b001: begin end 503 | 3'b011: begin end 504 | 3'b100: begin end 505 | 3'b110: begin end 506 | 3'b111: begin end 507 | default: assert(0); 508 | endcase 509 | `endif 510 | 511 | always @(posedge gbl_clk) 512 | if (f_past_valid_gbl && spi_bitcount_n == 3'h7) 513 | assert($stable(xck_oreg)); 514 | // }}} 515 | //////////////////////////////////////////////////////////////////////// 516 | // 517 | // Formal contract: Receive sequence checking 518 | // {{{ 519 | //////////////////////////////////////////////////////////////////////// 520 | // 521 | // 522 | 523 | initial f_rxseq = 1; 524 | always @(posedge gbl_clk) 525 | if (f_past_valid_gbl && f_will_rise_sck) 526 | begin 527 | f_rxseq <= f_rxseq << 1; 528 | if (f_rxseq[7]) 529 | f_rxseq[0] <= 1; 530 | if (f_rxseq[8] && !f_will_rise_clk) 531 | f_rxseq[8] <= 1; 532 | assert(f_rxseq != 0); 533 | f_data <= { f_data, i_spi_mosi }; 534 | if (f_rxseq[7]) 535 | f_rcvd <= { f_data[6:0], i_spi_mosi }; 536 | end else if (i_spi_csn) 537 | begin 538 | f_rxseq[8:0] <= 1; 539 | if (f_rxseq[8] && !f_will_rise_clk) 540 | f_rxseq[8] <= 1; 541 | end else if (f_will_rise_clk && f_rxseq[8]) 542 | f_rxseq[8] <= 0; 543 | 544 | always @(*) 545 | if (!i_spi_csn) 546 | begin 547 | if (f_rxseq[1]) 548 | begin 549 | assert(spi_bitcount == 1); 550 | assert(f_data[0] == spi_sreg[0]); 551 | end 552 | if (f_rxseq[2]) 553 | begin 554 | assert(spi_bitcount == 2); 555 | assert(f_data[1:0] == spi_sreg[1:0]); 556 | end 557 | if (f_rxseq[3]) 558 | begin 559 | assert(spi_bitcount == 3); 560 | assert(f_data[2:0] == spi_sreg[2:0]); 561 | end 562 | if (f_rxseq[4]) 563 | begin 564 | assert(spi_bitcount == 4); 565 | assert(f_data[3:0] == spi_sreg[3:0]); 566 | end 567 | if (f_rxseq[5]) 568 | begin 569 | assert(spi_bitcount == 5); 570 | assert(f_data[4:0] == spi_sreg[4:0]); 571 | end 572 | if (f_rxseq[6]) 573 | begin 574 | assert(spi_bitcount == 6); 575 | assert(f_data[5:0] == spi_sreg[5:0]); 576 | end 577 | if (f_rxseq[7]) 578 | begin 579 | assert(spi_bitcount == 7); 580 | assert(f_data[6:0] == spi_sreg[6:0]); 581 | end 582 | end 583 | 584 | initial f_rxcdc = 0; 585 | always @(posedge gbl_clk) 586 | if (f_past_valid_gbl && f_will_rise_clk) 587 | begin 588 | f_rxcdc <= f_rxcdc << 1; 589 | if (f_rxseq[8]) 590 | f_rxcdc[0] <= 1; 591 | end 592 | 593 | always @(*) 594 | if (f_rxseq[8]) 595 | assert(xck_stb); 596 | 597 | always @(*) 598 | if (f_rxcdc[0]) 599 | assert(!sync_spi_stb && sync_stb_pipe && !o_we); 600 | else if (f_rxcdc[1]) 601 | assert(sync_spi_stb && sync_stb_pipe && !o_we); 602 | else if (f_rxcdc[2]) 603 | assert(sync_spi_stb && o_we); 604 | else 605 | assert(!o_we && (sync_spi_stb || !sync_stb_pipe)); 606 | 607 | always @(posedge gbl_clk) 608 | if (|f_rxcdc) 609 | begin 610 | assert($stable(spi_byte)); 611 | assert(f_rcvd == spi_byte); 612 | end 613 | 614 | assign f_sync_frame = { last_spi_frame, 615 | sync_spi_frame, sync_frame_pipe }; 616 | 617 | always @(posedge gbl_clk) 618 | case(f_sync_frame) 619 | 3'b000: begin end 620 | 3'b001: begin end 621 | 3'b011: begin end 622 | 3'b111: begin end 623 | 3'b110: begin end 624 | 3'b100: begin end 625 | default: assert(0); 626 | endcase 627 | 628 | always @(posedge gbl_clk) 629 | if (pre_stb) 630 | begin 631 | assert($stable(f_rcvd)); 632 | assert(f_rcvd == spi_byte); 633 | end 634 | 635 | always @(*) 636 | cover(f_rxseq[8] && !i_spi_csn); 637 | 638 | always @(*) 639 | assume(!f_fault); 640 | // }}} 641 | //////////////////////////////////////////////////////////////////////// 642 | // 643 | // Formal contract: Transmitter sequence checking 644 | // {{{ 645 | //////////////////////////////////////////////////////////////////////// 646 | // 647 | // 648 | always @(posedge gbl_clk) 649 | if (f_past_valid_gbl && (!$past(o_frame) && !$fell(o_rd))) 650 | assume($stable(i_byte)); 651 | 652 | initial f_txseq = 0; 653 | always @(posedge gbl_clk) 654 | if (i_spi_csn) 655 | f_txseq <= 0; 656 | else if (!i_spi_csn && f_will_fall_sck) 657 | begin 658 | f_txseq <= f_txseq << 1; 659 | if (spi_bitcount_n == 3'h7) 660 | begin 661 | f_txseq[0] <= 1'b1; 662 | f_txmit <= i_byte; 663 | end 664 | end 665 | 666 | always @(*) 667 | if (!i_spi_csn && |f_txseq) 668 | begin 669 | if (f_txseq[0]) 670 | begin 671 | assert(spi_bitcount_n == 3'b000); 672 | assert(f_txmit[7:0] == spi_output); 673 | end 674 | if (f_txseq[1]) 675 | begin 676 | assert(spi_bitcount_n == 3'b001); 677 | assert(f_txmit[6:0] == spi_output[7:1]); 678 | end 679 | if (f_txseq[2]) 680 | begin 681 | assert(spi_bitcount_n == 3'b010); 682 | assert(f_txmit[5:0] == spi_output[7:2]); 683 | end 684 | if (f_txseq[3]) 685 | begin 686 | assert(spi_bitcount_n == 3'b011); 687 | assert(f_txmit[4:0] == spi_output[7:3]); 688 | end 689 | if (f_txseq[4]) 690 | begin 691 | assert(spi_bitcount_n == 3'b100); 692 | assert(f_txmit[3:0] == spi_output[7:4]); 693 | end 694 | if (f_txseq[5]) 695 | begin 696 | assert(spi_bitcount_n == 3'b101); 697 | assert(f_txmit[2:0] == spi_output[7:5]); 698 | end 699 | if (f_txseq[6]) 700 | begin 701 | assert(spi_bitcount_n == 3'b110); 702 | assert(f_txmit[1:0] == spi_output[7:6]); 703 | end 704 | if (f_txseq[7]) 705 | begin 706 | assert(spi_bitcount_n == 3'b111); 707 | assert(f_txmit[0] == spi_output[7]); 708 | end 709 | end 710 | 711 | //////////////////////////////////////////////////////////////////////// 712 | // 713 | 714 | always @(posedge i_clk) 715 | if (f_past_valid_clk && $past(o_we)) 716 | assert(!o_we); 717 | 718 | always @(posedge i_clk) 719 | if (f_past_valid_clk && $past(pre_stb)) 720 | assert(o_we && !pre_stb); 721 | 722 | // 723 | // Insist that, for every rise of the xck_stb, that there's one and 724 | // only one corresponding strobe on the system clock frequency 725 | initial f_pending_wrstb = 1'b0; 726 | always @(posedge gbl_clk) 727 | if ($rose(xck_stb)) 728 | f_pending_wrstb <= 1'b1; 729 | else if (o_we) 730 | f_pending_wrstb <= 1'b0; 731 | 732 | always @(*) 733 | if (!i_spi_csn && xck_stb) 734 | assert(spi_bitcount == 3'h7 || spi_bitcount == 0 || spi_bitcount <= 3'h3); 735 | 736 | always @(posedge gbl_clk) 737 | if(f_pending_wrstb) 738 | begin 739 | assert($stable(spi_byte)); 740 | assert(xck_stb || sync_spi_stb || o_we); 741 | end else 742 | assert(!pre_stb); 743 | 744 | always @(posedge i_clk) 745 | if(f_pending_wrstb && $past(sync_spi_stb && sync_stb_pipe)) 746 | assert(o_we || pre_stb); 747 | // }}} 748 | //////////////////////////////////////////////////////////////////////// 749 | // 750 | // Random induction properties 751 | // {{{ 752 | //////////////////////////////////////////////////////////////////////// 753 | // 754 | // 755 | always @(negedge i_spi_sck, posedge i_spi_csn) 756 | if (i_spi_csn) 757 | f_latter_byte_n <= 1'b0; 758 | else if (&spi_bitcount_n) 759 | f_latter_byte_n <= 1'b1; 760 | 761 | always @(posedge gbl_clk) 762 | if ($rose(i_spi_sck)) 763 | assert(spi_bitcount == spi_bitcount_n); 764 | 765 | always @(*) 766 | if (spi_bitcount_n == 3'h7) 767 | assert(!o_frame); 768 | 769 | always @(*) 770 | if (f_latter_byte_n) 771 | assert(!o_frame); 772 | 773 | always @(*) 774 | if (sync_spi_rd && sync_rd_pipe) 775 | assert(o_rd || !o_frame); 776 | // }}} 777 | //////////////////////////////////////////////////////////////////////// 778 | // 779 | // Cover properties 780 | // {{{ 781 | //////////////////////////////////////////////////////////////////////// 782 | // 783 | // 784 | reg [2:0] f_bytes_sent, f_bytes_rcvd; 785 | reg f_cvrspd_equal, f_cvrspd_fast, f_cvrspd_slow; 786 | (* anyconst *) reg f_cvrprop; 787 | 788 | initial f_bytes_rcvd = 0; 789 | always @(posedge i_clk) 790 | if (o_we && (!(&f_bytes_rcvd))) 791 | f_bytes_rcvd <= f_bytes_rcvd + 1; 792 | 793 | initial f_bytes_sent = 0; 794 | always @(posedge gbl_clk) 795 | if (f_past_valid_gbl && $fell(f_txseq[7]) && (!(&f_bytes_sent))) 796 | f_bytes_sent <= f_bytes_sent + 1; 797 | 798 | always @(*) 799 | f_cvrspd_fast = (f_spick_step == { 1'b1, {(F_CKBITS-1){1'b0}} }) 800 | && (f_sysck_step == (f_spick_step >> 1) 801 | + (f_spick_step >> 2) + 1); 802 | 803 | always @(*) 804 | f_cvrspd_equal = (f_sysck_step == f_spick_step); 805 | 806 | always @(*) 807 | f_cvrspd_slow = (f_sysck_step == { 1'b1, {(F_CKBITS-1){1'b0}} }) 808 | && (f_spick_step < (f_sysck_step >> 1)); 809 | 810 | always @(*) 811 | cover(o_we && o_byte == 8'hda); 812 | 813 | always @(posedge gbl_clk) 814 | if (f_cvrprop && $changed(i_byte)) 815 | assume((i_byte == $past(i_byte) + 8'h11) 816 | &&(i_byte[7:4] != i_byte[3:0])); 817 | 818 | always @(posedge gbl_clk) 819 | if (f_cvrprop && $changed(o_byte)) 820 | assume((o_byte == $past(o_byte) + 8'h11) 821 | &&(o_byte[7:4] != o_byte[3:0])); 822 | 823 | always @(posedge gbl_clk) 824 | if (f_past_valid_gbl) 825 | begin 826 | cover($changed(i_byte)); 827 | cover($changed(o_byte)); 828 | if ($rose(o_we)) 829 | begin 830 | cover($stable(o_byte)); 831 | cover($changed(o_byte)); 832 | end 833 | if ($fell(o_rd)) 834 | begin 835 | cover($stable(i_byte)); 836 | cover($changed(i_byte)); 837 | end 838 | end 839 | 840 | // 841 | // 842 | // 843 | always @(posedge gbl_clk) 844 | // 44 steps 845 | cover(f_bytes_rcvd == 2 && $fell(i_spi_csn) && f_cvrspd_fast); 846 | always @(posedge gbl_clk) 847 | // 42 steps 848 | cover(f_bytes_sent == 2 && $fell(i_spi_csn) && f_cvrspd_fast); 849 | 850 | always @(posedge gbl_clk) 851 | // 44 steps 852 | cover(f_bytes_rcvd == 2 && $fell(i_spi_csn) && f_cvrspd_equal); 853 | always @(posedge gbl_clk) 854 | // 40 steps 855 | cover(f_bytes_sent == 2 && $fell(i_spi_csn) && f_cvrspd_equal); 856 | 857 | always @(posedge gbl_clk) 858 | // 83 clocks 859 | cover(f_bytes_rcvd == 2 && $fell(i_spi_csn) && f_cvrspd_slow); 860 | always @(posedge gbl_clk) 861 | // 110 steps 862 | cover(f_bytes_sent == 2 && $fell(i_spi_csn) && f_cvrspd_slow); 863 | // }}} 864 | //////////////////////////////////////////////////////////////////////// 865 | // 866 | // "Careless" (i.e. constraining) assumptions 867 | // {{{ 868 | //////////////////////////////////////////////////////////////////////// 869 | // 870 | // 871 | localparam F_CK_MIN_CSN_TO_CSN = 4'h8; 872 | localparam F_CK_MIN_CSN_RECOVERY = 4'h3; 873 | localparam F_CK_MIN_CSN_ACTIVE = 4'h2; 874 | 875 | // Clock speed assumptions 876 | // {{{ 877 | always @(*) 878 | begin 879 | // If the SPI clock is too fast, we cannot support an 8b 880 | // interface. If it's too slow--no one cares. 881 | // 882 | // The following isn't quite tight enough: 883 | // SPI clock must be less than 1.14x system clock rate 884 | // assume(f_sysck_step > f_spick_step - (f_spick_step>>3)); 885 | // 886 | // We can go faster than that. Let's instead insist that ... 887 | // SPI clock * 0.75 must be < System clock rate, or ... 888 | // SPI clock must be < 1.333x System clock rate 889 | assume(f_sysck_step > (f_spick_step >> 1) 890 | + (f_spick_step >> 2)); 891 | // 892 | // If we try to run the SPI clock even faster, the design will 893 | // fail. For example, the design fails if the SPI clock is 894 | // greater than 1.3333x the system clock rate, but less than 895 | // 1.39x the system clock rate (the assumption below) then the 896 | // proof fails. 897 | // 898 | // assume(f_sysck_step > (f_spick_step >> 1) 899 | // + (f_spick_step >> 3) 900 | // + (f_spick_step >> 4) 901 | // + (f_spick_step >> 5)); 902 | // 903 | 904 | // While the following isn't strictly necessary, it helps 905 | // keep the proof moving along 906 | assume(f_sysck_step >= { 3'b001, {(F_CKBITS-3){1'b0}} }); 907 | // assume(f_spick_step > { 3'b001, {(F_CKBITS-3){1'b0}} }); 908 | end 909 | // }}} 910 | 911 | // f_csn_to_csn_count -- system clocks between CSNs 912 | // {{{ 913 | initial f_csn_to_csn_count = -1; 914 | always @(posedge gbl_clk) 915 | if ($fell(i_spi_csn)) 916 | f_csn_to_csn_count <= 0; 917 | else if ($rose(i_clk) && (!(&f_csn_to_csn_count))) 918 | f_csn_to_csn_count <= f_csn_to_csn_count + 1; 919 | // }}} 920 | 921 | // f_csn_hi_count -- number of system clocks where CSN is inactive (hi) 922 | // {{{ 923 | initial f_csn_hi_count = -1; 924 | always @(posedge gbl_clk) 925 | if (!i_spi_csn) 926 | f_csn_hi_count <= 0; 927 | else if ($rose(i_clk) && (!(&f_csn_hi_count))) 928 | f_csn_hi_count <= f_csn_hi_count + 1; 929 | // }}} 930 | 931 | // f_csn_hi_count -- number of system clocks where CSN is active (lo) 932 | // {{{ 933 | initial f_csn_lo_count = 0; 934 | always @(posedge gbl_clk) 935 | if (i_spi_csn) 936 | f_csn_lo_count <= 0; 937 | else if ($rose(i_clk) && (!(&f_csn_lo_count))) 938 | f_csn_lo_count <= f_csn_lo_count + 1; 939 | // }}} 940 | 941 | always @(posedge gbl_clk) 942 | if (f_csn_to_csn_count <= F_CK_MIN_CSN_TO_CSN) 943 | assume(!$fell(i_spi_csn)); 944 | 945 | always @(posedge gbl_clk) 946 | if (f_csn_hi_count <= F_CK_MIN_CSN_RECOVERY) 947 | assume(!$fell(i_spi_csn)); 948 | 949 | always @(posedge gbl_clk) 950 | if (f_csn_lo_count <= F_CK_MIN_CSN_ACTIVE) 951 | assume(!$rose(i_spi_csn)); 952 | 953 | // }}} 954 | `endif 955 | // }}} 956 | endmodule 957 | -------------------------------------------------------------------------------- /rtl/spicpu.v: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // Filename: spicpu.v 4 | // {{{ 5 | // Project: wbspi, a set of Serial Peripheral Interface cores 6 | // 7 | // Purpose: To make it easy to script SPI commands. 8 | // 9 | // Registers: 10 | // {{{ 11 | // 0,0: Control/status 12 | // (&CSN),5b idx, SCK, MISO[idx], 4b current channel, 13 | // AXI stream stall, 14 | // busy (needed for manual cmd interface) 15 | // Waiting on an immediate 16 | // Active (i.e. running a script) 17 | // ABORT (stop the command interpreter, and raise CS#) 18 | // 0,1: Override 19 | // Provide a means of sending commands directly, without scripting 20 | // them. Commands may be written to bits [7:0] as thought they 21 | // were issued from memory. 22 | // Reading from this register will return a shift (left) register 23 | // of all that's been read, with the following exceptions: 24 | // - A START instruction clears the register. 25 | // - [31]: LAST bit was set on the last one 26 | // - [30:28]: Bit field indicating if bytes [23:16], [15:8], [7:0] 27 | // have valid data in them respectively. 28 | // [31:24] -- Penultimate byte received 29 | // [23:16] -- Last byte received 30 | // [15: 8] -- Manual override controls 31 | // [15:12] control which CS# to activate 32 | // [11] Enables manual mode (and hence activates CS#) 33 | // [10] Maps to SCK 34 | // [ 9] Controls MOSI 35 | // [ 8] Read only, returns the current MISO[Channel] 36 | // [ 7: 0] -- Command override 37 | // Writes with bit[11] clear will send commands to the 38 | // device, one byte at a time. 39 | // This mode is cleared on any write to the address 40 | // register 41 | // 0,2: Address 42 | // Any write to this address will turn on autonomous mode and 43 | // instruct the command interpreter to jump to the given address. 44 | // 0,3: Clock divider 45 | // (??) 1,x: Sets/reads the internal memory of this peripheral. 46 | // }}} 47 | // 48 | // Commands: All instructions are 8-bits. Many support immediates following. 49 | // {{{ 50 | // 00011111 STOP Raise all CSn pins to deactivate the interface. 51 | // STOP commands may also be implied by other 52 | // commands. 53 | // 000iiiii START ID Drop CSn[iii] to activate (implies STOP for 54 | // anything else that's active.) If we are only 55 | // configured for a single CSN, then any START 56 | // command will activate (lower) that CSN. 57 | // 001nmbr READ #NMBR Read #NMBR+1 bytes. No immediates follow. 58 | // #NMBR ranges from 0--15, for a READ of 1--16 59 | // bytes. Data will be read from the selected CSN. 60 | // 010nmbr SEND #NMBR Send #NMBR bytes 61 | // This command is followed by the bytes to send. 62 | // Any return responses are ignored. 63 | // 011nmbr TXRX #NMBR Send #NMBR bytes. 64 | // #NMBR immediates follow. 65 | // Any return responses are sent up the chain. 66 | // 100xxxx LAST The next item read will have the AXI stream 67 | // TLAST flag bit set when sent across the stream. 68 | // 1010xxx HALT No further instructions accepted until reset 69 | // or CPU override/restart. Implies STOP. 70 | // 1011xxx WAIT Just like HALT, but will also restart on an 71 | // interrupt. Implies STOP if active. 72 | // 1100xxx TARGET Sets the target address for a future JUMP 73 | // instruction. Implies STOP if active. 74 | // 1101xxx JUMP Jumps to the jump target address. Implies STOP. 75 | // 111xxxx NOP No operation. Reserved for future instructions. 76 | // 77 | // 'x' bits are reserved and should be set to zero. 78 | // }}} 79 | // Decoded commands: 80 | // {{{ 81 | // These are the ones sent downstream. Since they are internal, they 82 | // don't need to fit within 8-bits. 83 | // 84 | // CSN, (&CSN), LAST, KEEP, 8'bSEND 85 | // - CSN : The CSN bits to be set 86 | // - &CSN : True if all CSN bits are high and the channel is to be 87 | // made idle 88 | // - LAST : True if the LAST flag is to be set when the result is placed 89 | // into the output AXI stream 90 | // - KEEP : True if the result of this operation is to be sent to the 91 | // output AXI stream. 92 | // - 8bSEND: An 8-bit value to output over the SPI channel. Always set. 93 | // 94 | // No command implies keeping CSN,&CSN as they are, and the clock off. 95 | // }}} 96 | // Creator: Dan Gisselquist, Ph.D. 97 | // Gisselquist Technology, LLC 98 | // 99 | //////////////////////////////////////////////////////////////////////////////// 100 | // }}} 101 | // Copyright (C) 2022, Gisselquist Technology, LLC 102 | // {{{ 103 | // This program is free software (firmware): you can redistribute it and/or 104 | // modify it under the terms of the GNU General Public License as published 105 | // by the Free Software Foundation, either version 3 of the License, or (at 106 | // your option) any later version. 107 | // 108 | // This program is distributed in the hope that it will be useful, but WITHOUT 109 | // ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or 110 | // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 111 | // for more details. 112 | // 113 | // You should have received a copy of the GNU General Public License along 114 | // with this program. (It's in the $(ROOT)/doc directory. Run make with no 115 | // target there if the PDF file isn't present.) If not, see 116 | // for a copy. 117 | // }}} 118 | // License: GPL, v3, as defined and found on www.gnu.org, 119 | // {{{ 120 | // http://www.gnu.org/licenses/gpl.html 121 | // 122 | //////////////////////////////////////////////////////////////////////////////// 123 | // 124 | `default_nettype none 125 | // }}} 126 | module spicpu #( 127 | // {{{ 128 | parameter ADDRESS_WIDTH = 25, 129 | parameter DATA_WIDTH = 32, 130 | parameter NCE = 1, // Number of CS# sigs to control 131 | // parameter LGMEM = 5, // Address width of internal mem 132 | parameter [ADDRESS_WIDTH+$clog2(DATA_WIDTH/8)-1:0] RESET_ADDRESS 133 | = 0, 134 | // localparam AW = LGMEM, 135 | parameter AXIS_ID_WIDTH = 0, 136 | parameter [0:0] OPT_MANUAL = 0, 137 | // Verilator lint_off UNUSED 138 | parameter MANUAL_CSN = 12, 139 | parameter MANUAL_BIT = 11, 140 | parameter MANUAL_SCK = 10, 141 | parameter MANUAL_MOSI = 9, 142 | parameter MANUAL_MISO = 8, 143 | // Verilator lint_on UNUSED 144 | parameter [0:0] OPT_LITTLE_ENDIAN = 0, 145 | parameter [0:0] OPT_LOWPOWER = 0, 146 | parameter [0:0] OPT_START_HALTED= 0, 147 | parameter [0:0] OPT_SHARED_MISO = 0, 148 | parameter [0:0] DEF_CPOL = 1, 149 | // DEF_CKCOUNT -- Default number of system clocks per SCK 150 | // {{{ 151 | // This number gets divided by two, so *DON'T* set it to 152 | // anything less than 2. 153 | parameter [11:0] DEF_CKCOUNT = 20 // Divide clk by 20 154 | // Dividing by 20 should yield a clock rate of SYSCLK/20 155 | // or 100MHz / 20 = 5MHz 156 | // }}} 157 | // }}} 158 | ) ( 159 | // {{{ 160 | input wire i_clk, i_reset, 161 | // 162 | // WISHBONE/slave control interface 163 | // {{{ 164 | input wire i_wb_cyc, i_wb_stb, i_wb_we, 165 | input wire [1:0] i_wb_addr, 166 | input wire [32-1:0] i_wb_data, 167 | input wire [32/8-1:0] i_wb_sel, 168 | output wire o_wb_stall, 169 | output reg o_wb_ack, 170 | output reg [32-1:0] o_wb_data, 171 | // }}} 172 | // WISHBONE/master control interface 173 | // {{{ 174 | output wire o_pf_cyc, o_pf_stb, o_pf_we, 175 | output wire [ADDRESS_WIDTH-1:0] o_pf_addr, 176 | output wire [DATA_WIDTH-1:0] o_pf_data, 177 | output wire [DATA_WIDTH/8-1:0] o_pf_sel, 178 | input wire i_pf_stall, i_pf_ack, 179 | input wire [DATA_WIDTH-1:0] i_pf_data, 180 | input wire i_pf_err, 181 | // }}} 182 | // SPI interface 183 | // {{{ 184 | output reg [NCE-1:0] o_spi_csn, 185 | output reg o_spi_sck, 186 | output reg o_spi_mosi, 187 | input wire [(OPT_SHARED_MISO ? 0:(NCE-1)):0] i_spi_miso, 188 | // }}} 189 | // Outgoing AXI-Stream interface 190 | // {{{ 191 | output reg M_AXIS_TVALID, 192 | input wire M_AXIS_TREADY, 193 | output reg [7:0] M_AXIS_TDATA, 194 | output reg M_AXIS_TLAST, 195 | output reg [((AXIS_ID_WIDTH > 0)? AXIS_ID_WIDTH:1)-1:0] 196 | M_AXIS_TID, 197 | // output reg M_AXIS_TABORT, 198 | // }}} 199 | input wire i_sync_signal, 200 | output wire o_interrupt 201 | // }}} 202 | ); 203 | 204 | // Local declarations 205 | // {{{ 206 | localparam LGNCHAN = ((AXIS_ID_WIDTH > 0)? AXIS_ID_WIDTH:1); 207 | localparam [1:0] ADR_CONTROL = 2'b00, 208 | ADR_OVERRIDE = 2'b01, 209 | ADR_ADDRESS = 2'b10, 210 | ADR_CKCOUNT = 2'b11; 211 | 212 | localparam BAW = ADDRESS_WIDTH 213 | + $clog2(DATA_WIDTH/8); // Byte addr wid 214 | 215 | localparam [2:0] CMD_START = 3'h0, // CMD_STOP = 3'h0, 216 | CMD_READ = 3'h1, 217 | CMD_SEND = 3'h2, 218 | CMD_TXRX = 3'h3, 219 | CMD_LAST = 3'h4; 220 | localparam [3:0] CMD_TICK = 4'hb, 221 | CMD_CHANNEL= 4'he, 222 | CMD_NOOP = 4'hf; 223 | localparam [4:0] CMD_HALT = { 4'ha, 1'b0 }, 224 | CMD_WAIT = { 4'ha, 1'b1 }, 225 | CMD_TARGET = { 4'hc, 1'b0 }, 226 | CMD_JUMP = { 4'hc, 1'b1 }; 227 | 228 | wire bus_write, bus_read, bus_jump, bus_override, bus_manual; 229 | wire [1:0] bus_write_addr, bus_read_addr; 230 | wire [31:0] bus_write_data; 231 | wire [3:0] bus_write_strb; 232 | 233 | reg [31:0] w_control_word; 234 | 235 | reg [15:0] ovw_data; 236 | reg [7:0] ovw_cmd; 237 | 238 | wire cpu_reset, cpu_clear_cache; 239 | reg cpu_new_pc; 240 | 241 | reg next_valid, next_illegal, next_cpu; 242 | wire next_ready; 243 | reg [7:0] next_insn; 244 | reg [BAW-1:0] next_insn_addr; 245 | 246 | reg dcd_stop, dcd_valid; 247 | wire dcd_ready; 248 | reg dcd_active, dcd_last, dcd_send, dcd_keep, 249 | dcd_byte; 250 | reg r_stopped, r_wait, r_err; 251 | reg [NCE-1:0] dcd_csn; 252 | reg [9:0] dcd_command; 253 | reg [LGNCHAN-1:0] dcd_channel; 254 | 255 | reg imm_cycle; 256 | reg [5:0] imm_count; 257 | 258 | 259 | wire pf_valid, pf_ready, pf_illegal; 260 | wire [7:0] pf_insn; 261 | reg [BAW-1:0] pf_jump_addr; 262 | wire [BAW-1:0] pf_insn_addr; 263 | 264 | reg [11:0] edge_counter, ckcount; 265 | reg spi_ckedge; 266 | 267 | wire spi_stall; 268 | reg spi_active, spi_last, spi_keep; 269 | reg [6:0] spi_srout, spi_srin; 270 | reg [4:0] spi_count; 271 | reg [LGNCHAN-1:0] spi_channel; 272 | 273 | wire miso; 274 | 275 | wire set_tvalid; 276 | 277 | wire manual_mode, manual_sck, manual_mosi; 278 | wire [NCE-1:0] manual_csn; 279 | wire [7:0] manual_data; 280 | 281 | // }}} 282 | //////////////////////////////////////////////////////////////////////// 283 | // 284 | // Bus handling 285 | // {{{ 286 | //////////////////////////////////////////////////////////////////////// 287 | // 288 | // 289 | 290 | assign bus_write = i_wb_stb && i_wb_we && !o_wb_stall; 291 | assign bus_write_addr = i_wb_addr; 292 | assign bus_write_data = i_wb_data; 293 | assign bus_write_strb = i_wb_sel; 294 | 295 | assign bus_read = i_wb_stb && !i_wb_we && !o_wb_stall; 296 | assign bus_read_addr = i_wb_addr; 297 | 298 | assign o_wb_stall = 1'b0; 299 | 300 | // o_wb_ack 301 | // {{{ 302 | initial o_wb_ack = 1'b0; 303 | always @(posedge i_clk) 304 | if (i_reset) 305 | o_wb_ack <= 1'b0; 306 | else 307 | o_wb_ack <= i_wb_stb && !o_wb_stall; 308 | // }}} 309 | 310 | always @(*) 311 | begin 312 | w_control_word = 0; 313 | // 314 | w_control_word[23:16] = next_insn; 315 | // 316 | w_control_word[10] = M_AXIS_TVALID; 317 | w_control_word[ 9] = M_AXIS_TVALID && M_AXIS_TREADY; 318 | w_control_word[ 8] = M_AXIS_TLAST; 319 | // 320 | w_control_word[7] = next_valid; 321 | w_control_word[5] = dcd_send; 322 | w_control_word[4] = imm_cycle; 323 | w_control_word[3] = manual_mode; 324 | w_control_word[2] = r_err; 325 | w_control_word[1] = r_wait; 326 | w_control_word[0] = r_stopped && !next_valid; 327 | end 328 | 329 | // o_interrupt is true if the CPU may send a command w/o interrupting 330 | // anything 331 | assign o_interrupt = r_stopped && !next_valid; 332 | 333 | // o_wb_data 334 | // {{{ 335 | always @(posedge i_clk) 336 | if (OPT_LOWPOWER && i_reset) 337 | o_wb_data <= 0; 338 | else if (bus_read || !OPT_LOWPOWER) 339 | begin 340 | o_wb_data <= 0; 341 | case(bus_read_addr) 342 | ADR_CONTROL: o_wb_data <= w_control_word; 343 | ADR_OVERRIDE: o_wb_data <= { ovw_data, manual_data, ovw_cmd }; 344 | ADR_ADDRESS: o_wb_data[BAW-1:0] <= pf_insn_addr; 345 | ADR_CKCOUNT: o_wb_data[12:1] <= ckcount; 346 | default: begin end 347 | endcase 348 | 349 | end else if (OPT_LOWPOWER) 350 | o_wb_data <= 0; 351 | // }}} 352 | 353 | assign bus_override = r_stopped && bus_write 354 | && bus_write_addr == ADR_OVERRIDE 355 | && bus_write_strb[0] 356 | && (!OPT_MANUAL || !bus_write_data[MANUAL_BIT] 357 | || !bus_write_strb[MANUAL_BIT/8]); 358 | 359 | assign bus_manual = OPT_MANUAL && bus_write 360 | && bus_write_addr == ADR_OVERRIDE 361 | && bus_write_data[MANUAL_BIT] 362 | && bus_write_strb[MANUAL_BIT/8]; 363 | 364 | assign bus_jump = bus_write && bus_write_addr == ADR_ADDRESS 365 | && (&bus_write_strb) && r_stopped; 366 | // }}} 367 | //////////////////////////////////////////////////////////////////////// 368 | // 369 | // Instruction fetch 370 | // {{{ 371 | //////////////////////////////////////////////////////////////////////// 372 | // 373 | // 374 | 375 | assign cpu_reset = r_stopped; 376 | assign cpu_clear_cache = 1'b0; 377 | 378 | `ifndef FORMAL 379 | dblfetch #( 380 | .ADDRESS_WIDTH(BAW), .DATA_WIDTH(DATA_WIDTH), .INSN_WIDTH(8), 381 | .OPT_LITTLE_ENDIAN(OPT_LITTLE_ENDIAN) 382 | ) u_fetch ( 383 | // {{{ 384 | .i_clk(i_clk), .i_reset(i_reset || cpu_reset), 385 | // 386 | .i_new_pc(cpu_new_pc), .i_clear_cache(cpu_clear_cache), 387 | .i_ready(pf_ready), .i_pc(pf_jump_addr), 388 | .o_valid(pf_valid), .o_illegal(pf_illegal), 389 | .o_insn(pf_insn), .o_pc(pf_insn_addr), 390 | // 391 | .o_wb_cyc(o_pf_cyc), .o_wb_stb(o_pf_stb), .o_wb_we(o_pf_we), 392 | .o_wb_addr(o_pf_addr), .o_wb_data(o_pf_data), 393 | .i_wb_stall(i_pf_stall), 394 | .i_wb_ack(i_pf_ack), .i_wb_data(i_pf_data), 395 | .i_wb_err(i_pf_err) 396 | // }}} 397 | ); 398 | `endif 399 | 400 | assign o_pf_sel = -1; 401 | assign pf_ready = !r_stopped && (!next_valid || next_ready); 402 | 403 | // next_valid 404 | // {{{ 405 | always @(posedge i_clk) 406 | if (i_reset) 407 | next_valid <= 0; 408 | else if (bus_override || (pf_valid && pf_ready)) 409 | next_valid <= 1; 410 | else if (next_ready || (r_stopped && !next_cpu)) 411 | next_valid <= 0; 412 | 413 | `ifdef FORMAL 414 | always (*) 415 | if (pf_ready) 416 | assert(!r_stopped); 417 | 418 | always (*) 419 | if (pf_ready) 420 | assert(!next_valid || next_ready); 421 | `endif 422 | // }}} 423 | 424 | // next_illegal 425 | // {{{ 426 | always @(posedge i_clk) 427 | if (i_reset) 428 | next_illegal <= 0; 429 | else if (r_stopped) 430 | next_illegal <= 0; 431 | else if (pf_valid && pf_ready) 432 | next_illegal <= pf_illegal; 433 | // }}} 434 | 435 | // next_cpu 436 | // {{{ 437 | always @(posedge i_clk) 438 | if (i_reset) 439 | next_cpu <= 1'b0; 440 | else if (bus_override) 441 | next_cpu <= 1'b1; 442 | else if (pf_valid && pf_ready) 443 | next_cpu <= 1'b0; 444 | // }}} 445 | 446 | // next_insn, ovw_cmd 447 | // {{{ 448 | always @(posedge i_clk) 449 | if (r_stopped && bus_write && bus_write_addr == ADR_OVERRIDE 450 | && bus_write_strb[0] 451 | && (!OPT_MANUAL || !bus_write_data[MANUAL_BIT] 452 | || !bus_write_strb[MANUAL_BIT/8])) 453 | begin 454 | next_insn <= bus_write_data[7:0]; 455 | end else if (pf_valid && pf_ready) 456 | next_insn <= pf_insn; 457 | 458 | always @(posedge i_clk) 459 | if (r_stopped && bus_write && bus_write_addr == ADR_OVERRIDE 460 | && bus_write_strb[0] 461 | && (!OPT_MANUAL || !bus_write_data[MANUAL_BIT] 462 | || !bus_write_strb[MANUAL_BIT/8])) 463 | ovw_cmd <= bus_write_data[7:0]; 464 | // }}} 465 | 466 | // next_insn_addr 467 | // {{{ 468 | always @(posedge i_clk) 469 | if (!next_valid || next_ready) 470 | begin 471 | if (r_stopped) 472 | next_insn_addr <= 0; 473 | else if (pf_valid) 474 | next_insn_addr <= pf_insn_addr; 475 | end 476 | // }}} 477 | 478 | // }}} 479 | //////////////////////////////////////////////////////////////////////// 480 | // 481 | // Instruction decode 482 | // {{{ 483 | //////////////////////////////////////////////////////////////////////// 484 | // 485 | // 486 | 487 | assign next_ready = (!dcd_valid || !spi_stall) 488 | && (!imm_cycle || dcd_send) 489 | && (next_cpu || (!r_stopped && (!r_wait || i_sync_signal))); 490 | assign dcd_ready = !spi_stall; 491 | 492 | // cpu_new_pc, pf_jump_addr 493 | // {{{ 494 | always @(posedge i_clk) 495 | begin 496 | cpu_new_pc <= 1'b0; 497 | 498 | if (bus_jump) 499 | cpu_new_pc <= 1'b1; 500 | if (next_valid && next_ready 501 | && !imm_cycle && next_insn[7:3] == CMD_JUMP) 502 | cpu_new_pc <= 1'b1; 503 | 504 | // pf_jump_addr 505 | // {{{ 506 | if (next_valid && next_ready 507 | && !imm_cycle && next_insn[7:3] == CMD_TARGET) 508 | pf_jump_addr <= next_insn_addr + 1; // TARGET 509 | if (bus_jump) 510 | pf_jump_addr <= bus_write_data[BAW-1:0]; 511 | // }}} 512 | 513 | if (i_reset) 514 | begin 515 | cpu_new_pc <= 1'b0; 516 | pf_jump_addr <= RESET_ADDRESS; 517 | end 518 | end 519 | // }}} 520 | 521 | // r_stopped 522 | // {{{ 523 | always @(posedge i_clk) 524 | begin 525 | if (next_valid && next_ready 526 | && !imm_cycle && next_insn[7:3] == CMD_HALT) 527 | r_stopped <= 1'b1; 528 | if (next_valid && next_ready && next_cpu) 529 | r_stopped <= 1'b1; 530 | if ((!imm_cycle || dcd_send) && next_valid && next_illegal) 531 | r_stopped <= 1'b1; 532 | if (bus_jump) 533 | r_stopped <= 1'b0; 534 | 535 | if (i_reset) 536 | r_stopped <= OPT_START_HALTED; 537 | end 538 | // }}} 539 | 540 | // r_err 541 | // {{{ 542 | always @(posedge i_clk) 543 | begin 544 | if (!r_stopped) 545 | begin 546 | r_err <= 1'b0; 547 | if (next_valid && next_illegal) 548 | r_err <= 1'b1; 549 | end else if (bus_jump) 550 | r_err <= 1'b0; 551 | 552 | if (i_reset) 553 | r_err <= 1'b0; 554 | end 555 | // }}} 556 | 557 | // r_wait 558 | // {{{ 559 | always @(posedge i_clk) 560 | begin 561 | if (i_sync_signal) 562 | r_wait <= 1'b0; 563 | 564 | if (next_valid && next_ready && !imm_cycle 565 | && next_insn[7:3] == CMD_WAIT) 566 | r_wait <= 1'b1; 567 | if ((!imm_cycle || dcd_send)&&next_valid&& next_illegal) 568 | r_wait <= 1'b0; 569 | if (bus_jump) 570 | r_wait <= 1'b0; 571 | 572 | if (i_reset) 573 | r_wait <= 1'b0; 574 | end 575 | // }}} 576 | 577 | // Massive decoder state machine 578 | always @(posedge i_clk) 579 | begin 580 | if (dcd_ready) 581 | begin 582 | dcd_valid <= 1'b0; 583 | if (OPT_LOWPOWER) 584 | dcd_command[9:0] <= 10'h00; 585 | end 586 | 587 | if (dcd_stop) 588 | dcd_active <= 1'b0; 589 | 590 | if (imm_cycle && !dcd_send) 591 | begin // READ # in progress 592 | // {{{ 593 | dcd_command[9:0] <= { 594 | (dcd_last && imm_count == 1), 595 | dcd_keep, 8'h00 }; 596 | imm_count <= imm_count - 1; 597 | imm_cycle <= (imm_count > 1); 598 | if (imm_count == 1) 599 | dcd_last <= 1'b0; 600 | // }}} 601 | end else if (next_valid && next_ready) 602 | begin 603 | // {{{ 604 | dcd_stop <= 1'b0; 605 | dcd_valid <= next_valid || dcd_stop; 606 | dcd_byte <= 1'b0; 607 | dcd_command[9:0] <= { 2'h00, 8'h0 }; 608 | if (imm_cycle) 609 | begin // SEND # 610 | // {{{ 611 | dcd_byte <= 1'b1; 612 | dcd_valid <= dcd_active; 613 | dcd_command[9:0] <= { 1'b0, 614 | dcd_keep, next_insn[7:0] }; 615 | imm_count <= imm_count - 1; 616 | imm_cycle <= (imm_count > 1); 617 | if (!dcd_keep || imm_count == 1) 618 | begin 619 | dcd_last <= 1'b0; 620 | dcd_command[9] <= 1'b1; 621 | end 622 | // }}} 623 | end else if (next_valid) casez(next_insn[7:4]) 624 | { CMD_START, 1'b? }: begin // START #ID 625 | // {{{ 626 | dcd_valid <= 1'b1; 627 | dcd_byte <= 1'b0; 628 | dcd_last <= 1'b0; 629 | { dcd_keep, dcd_send } <= 2'b00; 630 | 631 | if (next_insn[4:0] >= NCE) 632 | begin 633 | dcd_csn <= -1; 634 | dcd_active <= 1'b0; 635 | dcd_stop <= (dcd_active); 636 | if (!dcd_active) 637 | dcd_valid <= 1'b0; 638 | end else begin 639 | dcd_csn <= ~(1<< next_insn[4:0]); 640 | dcd_active <= 1'b1; 641 | end end 642 | // }}} 643 | { CMD_READ, 1'b? }: begin // READ #NMBR 644 | // {{{ 645 | dcd_valid <= dcd_active;// Start immediately 646 | dcd_byte <= 1'b1; // Need 8 clocks 647 | dcd_send <= 1'b0; // Not sending data 648 | dcd_keep <= 1'b1; // but we are keeping results 649 | imm_cycle <= (next_insn[4:0] > 0); 650 | imm_count <= { 1'b0, next_insn[4:0] }; // Remaining items 651 | dcd_last <= dcd_last && (next_insn[4:0] > 0); 652 | dcd_command[9:0] <= { 653 | (dcd_last && (next_insn[4:0] == 5'h0)), 654 | 1'b1, 8'h0 }; 655 | end 656 | // }}} 657 | { CMD_SEND, 1'b? }: begin // SEND #NMBR 658 | // {{{ 659 | dcd_valid <= 1'b0; // No data(yet), so not valid 660 | dcd_send <= 1'b1; // Sending data 661 | dcd_keep <= 1'b0; // Not keeping the results 662 | dcd_byte <= 1'b1; 663 | // dcd_last --- no change 664 | 665 | imm_cycle <= 1'b1; 666 | imm_count <= next_insn[4:0] + 1; // items 667 | end 668 | // }}} 669 | { CMD_TXRX, 1'b? }: begin // TXRX #NMBR 670 | // {{{ 671 | dcd_valid <= 1'b0; // No data(yet) to send 672 | dcd_send <= 1'b1; // We're sending data 673 | dcd_keep <= 1'b1; // Keep the results 674 | dcd_byte <= 1'b1; 675 | // dcd_last --- no change 676 | imm_cycle <= 1'b1; 677 | imm_count <= next_insn[4:0] + 1; 678 | end 679 | // }}} 680 | { CMD_LAST, 1'b? }: begin 681 | { dcd_keep, dcd_send } <= 2'b00; 682 | { dcd_valid, dcd_last } <= 2'b01; // LAST insn 683 | end 684 | CMD_HALT[4:1]: begin // WAIT, HALT 685 | // {{{ 686 | dcd_valid <= (dcd_active); 687 | dcd_csn <= -1; 688 | dcd_active <= 1'b0; 689 | dcd_byte <= 1'b0; 690 | dcd_last <= 1'b0; 691 | { dcd_keep, dcd_send } <= 2'b00; 692 | 693 | // { r_stopped, r_wait } <= { !next_insn[4], 694 | // next_insn[4] }; 695 | if (dcd_active) 696 | begin 697 | // Implied stop 698 | dcd_stop <= 1'b1; 699 | end end 700 | // }}} 701 | CMD_TICK: begin // TICK 702 | // {{{ 703 | dcd_valid <= dcd_active; 704 | dcd_keep <= 1'b0; 705 | dcd_byte <= 1'b0; 706 | { dcd_keep, dcd_send } <= 2'b00; 707 | end 708 | // }}} 709 | CMD_TARGET[4:1]: begin // TARGET || JUMP 710 | // {{{ 711 | dcd_valid <= (dcd_active); 712 | dcd_csn <= -1; 713 | dcd_active <= 1'b0; 714 | dcd_byte <= 1'b0; 715 | dcd_last <= 1'b0; 716 | { dcd_keep, dcd_send } <= 2'b00; 717 | 718 | if (dcd_active) 719 | begin 720 | // Implied stop 721 | dcd_stop <= 1'b1; 722 | end end 723 | // }}} 724 | CMD_CHANNEL: begin // Channel # 725 | // {{{ 726 | dcd_valid <= 1'b0; 727 | dcd_channel<= next_insn[LGNCHAN-1:0]; 728 | { dcd_keep, dcd_send } <= 2'b00; 729 | end 730 | // }}} 731 | CMD_NOOP: // dcd_valid <= 1'b0; 732 | { dcd_keep, dcd_send } <= 2'b00; 733 | default: 734 | { dcd_valid, dcd_keep, dcd_send } <= 3'b00; 735 | endcase 736 | 737 | if (next_illegal) 738 | begin 739 | // ILLEGAL instruction!!! Deactivate all 740 | dcd_valid <= dcd_active; 741 | dcd_active <= 1'b0; 742 | dcd_csn <= -1; 743 | dcd_byte <= 1'b0; 744 | { dcd_keep, dcd_send } <= 2'b00; 745 | end 746 | // }}} 747 | end 748 | 749 | if (i_reset) 750 | begin 751 | // {{{ 752 | dcd_valid <= 0; 753 | dcd_active <= 0; 754 | dcd_csn <= -1; 755 | dcd_stop <= 0; 756 | dcd_channel<= 0; 757 | imm_cycle <= 0; 758 | imm_count <= 0; 759 | // }}} 760 | end 761 | end 762 | // }}} 763 | //////////////////////////////////////////////////////////////////////// 764 | // 765 | // (Optional) Manual SPI control 766 | // {{{ 767 | //////////////////////////////////////////////////////////////////////// 768 | // 769 | // 770 | 771 | // Offers a simple bit-banging interface, should it be required 772 | generate if (OPT_MANUAL) 773 | begin : GEN_MANUAL 774 | // {{{ 775 | reg r_manual, r_sck, r_mosi; 776 | reg [NCE-1:0] r_csn; 777 | reg [7:0] r_data; 778 | 779 | // r_manual 780 | // {{{ 781 | initial r_manual = 0; 782 | always @(posedge i_clk) 783 | if (i_reset || !r_stopped || bus_jump) 784 | r_manual <= 1'b0; 785 | else if (bus_write && bus_write_addr == ADR_OVERRIDE 786 | && bus_write_strb[MANUAL_BIT/8]) 787 | r_manual <= bus_write_data[MANUAL_BIT]; 788 | // }}} 789 | 790 | // r_csn 791 | // {{{ 792 | initial r_csn = -1; 793 | always @(posedge i_clk) 794 | begin 795 | if (bus_write && bus_write_addr == ADR_OVERRIDE 796 | && bus_write_strb[MANUAL_BIT/8]) 797 | begin 798 | r_csn <= ~(1 << bus_write_data[15:12]); 799 | 800 | if (!bus_write_data[MANUAL_BIT] 801 | || (&bus_write_data[15:12])) 802 | r_csn <= -1; 803 | end 804 | 805 | if (i_reset || !r_stopped) 806 | r_csn <= -1; 807 | end 808 | // }}} 809 | 810 | // r_sck, r_mosi 811 | // {{{ 812 | always @(posedge i_clk) 813 | begin 814 | if (bus_manual) 815 | begin 816 | r_sck <= bus_write_data[MANUAL_SCK]; 817 | r_mosi <= bus_write_data[MANUAL_MOSI]; 818 | end 819 | 820 | if (i_reset) 821 | r_sck <= DEF_CPOL; 822 | end 823 | // }}} 824 | 825 | always @(posedge i_clk) 826 | if (OPT_LOWPOWER && i_reset) 827 | r_data <= 8'h0; 828 | else if ((!M_AXIS_TVALID || M_AXIS_TREADY) && set_tvalid) 829 | r_data <= { spi_srin, miso }; 830 | 831 | assign manual_mode = r_manual; 832 | assign manual_csn = r_csn; 833 | assign manual_sck = r_sck; 834 | assign manual_mosi = r_mosi; 835 | assign manual_data = r_data; 836 | // }}} 837 | end else begin : NO_MANUAL_CONTROL 838 | // {{{ 839 | assign manual_mode = 1'b0; 840 | assign manual_csn = -1; 841 | assign manual_sck = DEF_CPOL; 842 | assign manual_mosi = 0; 843 | assign manual_data = 0; 844 | 845 | // Keep Verilator happy 846 | // {{{ 847 | // Verilator lint_off UNUSED 848 | wire unused_manual; 849 | assign unused_manual = &{ 1'b0, bus_manual }; 850 | // Verilator lint_on UNUSED 851 | // }}} 852 | // }}} 853 | end endgenerate 854 | 855 | // }}} 856 | //////////////////////////////////////////////////////////////////////// 857 | // 858 | // SPI clock generation 859 | // {{{ 860 | //////////////////////////////////////////////////////////////////////// 861 | // 862 | // 863 | 864 | // ckcount 865 | // {{{ 866 | always @(posedge i_clk) 867 | if (i_reset) 868 | ckcount <= DEF_CKCOUNT/2; 869 | else if (bus_write && bus_write_addr == ADR_CKCOUNT) 870 | begin 871 | // ckcount = divisor / 2, since we count half edges 872 | if (bus_write_strb[1]) 873 | ckcount[11:7] <= bus_write_data[12:8]; 874 | if (bus_write_strb[0]) 875 | ckcount[6:0] <= bus_write_data[7:1]; 876 | end 877 | // }}} 878 | 879 | // edge_counter 880 | // {{{ 881 | always @(posedge i_clk) 882 | if (i_reset) 883 | edge_counter <= 0; 884 | else if (edge_counter > 0) 885 | edge_counter <= edge_counter - 1; 886 | else if (dcd_valid || spi_count > 0) 887 | edge_counter <= ckcount - 1; 888 | // }}} 889 | 890 | // spi_ckedge -- the one bit signal that causes everything below to mv 891 | // {{{ 892 | always @(posedge i_clk) 893 | if (i_reset) 894 | spi_ckedge <= 1; 895 | else 896 | spi_ckedge <= (edge_counter == 1); 897 | // }}} 898 | 899 | // }}} 900 | //////////////////////////////////////////////////////////////////////// 901 | // 902 | // SPI execution 903 | // {{{ 904 | //////////////////////////////////////////////////////////////////////// 905 | // 906 | // 907 | 908 | assign spi_stall = !spi_ckedge || spi_count > 0; 909 | 910 | // o_spi_csn, spi_active, spi_last, spi_keep 911 | // {{{ 912 | always @(posedge i_clk) 913 | if (i_reset) 914 | begin 915 | o_spi_csn <= -1; 916 | spi_active <= 1'b0; 917 | end else begin 918 | if (dcd_valid && !spi_stall) 919 | begin 920 | o_spi_csn <= dcd_csn; 921 | spi_active <= dcd_active && (dcd_keep || dcd_send); 922 | end 923 | 924 | if (manual_mode) 925 | o_spi_csn <= manual_csn; 926 | end 927 | 928 | always @(posedge i_clk) 929 | if (dcd_valid && !spi_stall) 930 | begin 931 | spi_last <= dcd_command[9]; 932 | spi_keep <= dcd_command[8]; 933 | spi_channel<= dcd_channel; 934 | end 935 | // }}} 936 | 937 | // o_spi_sck 938 | // {{{ 939 | initial o_spi_sck = DEF_CPOL; 940 | always @(posedge i_clk) 941 | if (i_reset) 942 | o_spi_sck <= DEF_CPOL; 943 | else begin 944 | if (!spi_stall || r_stopped) 945 | o_spi_sck <= DEF_CPOL; 946 | else if (spi_ckedge && spi_active && (!M_AXIS_TVALID || M_AXIS_TREADY 947 | || !spi_keep || spi_count > 1)) 948 | o_spi_sck <= !o_spi_sck; 949 | 950 | if (manual_mode) 951 | o_spi_sck <= manual_sck; 952 | end 953 | // }}} 954 | 955 | // o_spi_mosi, spi_srout 956 | // {{{ 957 | always @(posedge i_clk) 958 | if (i_reset) 959 | { o_spi_mosi, spi_srout } <= 0; 960 | else begin 961 | if (dcd_valid && !spi_stall) 962 | { o_spi_mosi, spi_srout } <= dcd_command[7:0]; 963 | else if (spi_ckedge && (o_spi_sck ^ DEF_CPOL)) 964 | { o_spi_mosi, spi_srout } <= { spi_srout, 1'b0 }; 965 | 966 | if (manual_mode) 967 | o_spi_mosi <= manual_mosi; 968 | end 969 | // }}} 970 | 971 | // spi_srin -- incoming shift register 972 | // {{{ 973 | generate if (OPT_SHARED_MISO) 974 | begin : GEN_SHARED_MISO 975 | assign miso = i_spi_miso; 976 | end else begin : MISO_SELECT 977 | assign miso = |(i_spi_miso & ~o_spi_csn); 978 | end endgenerate 979 | 980 | always @(posedge i_clk) 981 | if (OPT_LOWPOWER && i_reset) 982 | spi_srin <= 0; 983 | else if (OPT_LOWPOWER && dcd_valid && !spi_stall) 984 | spi_srin <= 0; 985 | else if (spi_ckedge && !(o_spi_sck ^ DEF_CPOL)) 986 | spi_srin <= { spi_srin[5:0], miso }; 987 | // }}} 988 | 989 | // spi_count -- a bit counter, not to be confused with the edge counter 990 | // {{{ 991 | always @(posedge i_clk) 992 | if (i_reset) 993 | spi_count <= 0; 994 | else if (dcd_valid && !spi_stall) 995 | spi_count <= dcd_byte ? 16 : 2; 996 | else if (spi_ckedge && spi_count > 0 997 | && (!M_AXIS_TVALID || M_AXIS_TREADY 998 | || !spi_keep || !spi_active || spi_count > 1)) 999 | spi_count <= spi_count - 1; 1000 | // }}} 1001 | 1002 | // }}} 1003 | //////////////////////////////////////////////////////////////////////// 1004 | // 1005 | // Outgoing AXI Stream 1006 | // {{{ 1007 | //////////////////////////////////////////////////////////////////////// 1008 | // 1009 | // 1010 | 1011 | assign set_tvalid = spi_keep && spi_active && spi_ckedge 1012 | && spi_count == 1 && !manual_mode; 1013 | 1014 | // M_AXIS_TVALID 1015 | // {{{ 1016 | always @(posedge i_clk) 1017 | if (i_reset) 1018 | M_AXIS_TVALID <= 1'b0; 1019 | else if (!M_AXIS_TVALID || M_AXIS_TREADY) 1020 | M_AXIS_TVALID <= set_tvalid; 1021 | // }}} 1022 | 1023 | // M_AXIS_TDATA 1024 | // {{{ 1025 | always @(posedge i_clk) 1026 | if (OPT_LOWPOWER && i_reset) 1027 | M_AXIS_TDATA <= 8'h0; 1028 | else if (!M_AXIS_TVALID || M_AXIS_TREADY) 1029 | begin 1030 | if (!OPT_LOWPOWER || set_tvalid) 1031 | begin 1032 | M_AXIS_TDATA <= { spi_srin, miso }; 1033 | ovw_data <= { ovw_data[7:0], spi_srin, miso }; 1034 | end else if (OPT_LOWPOWER) 1035 | M_AXIS_TDATA <= 8'b0; 1036 | end 1037 | // }}} 1038 | 1039 | // M_AXIS_TID 1040 | // {{{ 1041 | always @(posedge i_clk) 1042 | if (!M_AXIS_TVALID || M_AXIS_TREADY) 1043 | begin 1044 | if (!OPT_LOWPOWER || set_tvalid) 1045 | M_AXIS_TID <= spi_channel; 1046 | else 1047 | M_AXIS_TID <= 0; 1048 | end 1049 | // }}} 1050 | 1051 | 1052 | // M_AXIS_TLAST 1053 | // {{{ 1054 | always @(posedge i_clk) 1055 | if (!M_AXIS_TVALID || M_AXIS_TREADY) 1056 | begin 1057 | if (!OPT_LOWPOWER || set_tvalid) 1058 | M_AXIS_TLAST <= spi_last; 1059 | else 1060 | M_AXIS_TLAST <= 1'b0; 1061 | end 1062 | // }}} 1063 | 1064 | // }}} 1065 | 1066 | // Keep Verilator happy 1067 | // {{{ 1068 | // Verilator lint_off UNUSED 1069 | wire unused; 1070 | assign unused = &{ 1'b0, i_wb_cyc }; 1071 | // Verilator lint_on UNUSED 1072 | // }}} 1073 | //////////////////////////////////////////////////////////////////////////////// 1074 | //////////////////////////////////////////////////////////////////////////////// 1075 | //////////////////////////////////////////////////////////////////////////////// 1076 | // 1077 | // Formal properties 1078 | // {{{ 1079 | //////////////////////////////////////////////////////////////////////////////// 1080 | //////////////////////////////////////////////////////////////////////////////// 1081 | //////////////////////////////////////////////////////////////////////////////// 1082 | `ifdef FORMAL 1083 | `endif 1084 | // }}} 1085 | endmodule 1086 | -------------------------------------------------------------------------------- /sw/.gitignore: -------------------------------------------------------------------------------- 1 | *.bin 2 | spiasm 3 | -------------------------------------------------------------------------------- /sw/Makefile: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | ## 3 | ## Filename: sw/Makefile 4 | ## {{{ 5 | ## Project: wbspi, a set of Serial Peripheral Interface cores 6 | ## 7 | ## Purpose: 8 | ## 9 | ## Creator: Dan Gisselquist, Ph.D. 10 | ## Gisselquist Technology, LLC 11 | ## 12 | ################################################################################ 13 | ## }}} 14 | ## Copyright (C) 2022, Gisselquist Technology, LLC 15 | ## {{{ 16 | ## This program is free software (firmware): you can redistribute it and/or 17 | ## modify it under the terms of the GNU General Public License as published 18 | ## by the Free Software Foundation, either version 3 of the License, or (at 19 | ## your option) any later version. 20 | ## 21 | ## This program is distributed in the hope that it will be useful, but WITHOUT 22 | ## ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or 23 | ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 24 | ## for more details. 25 | ## 26 | ## You should have received a copy of the GNU General Public License along 27 | ## with this program. (It's in the $(ROOT)/doc directory. Run make with no 28 | ## target there if the PDF file isn't present.) If not, see 29 | ## for a copy. 30 | ## }}} 31 | ## License: GPL, v3, as defined and found on www.gnu.org, 32 | ## {{{ 33 | ## http://www.gnu.org/licenses/gpl.html 34 | ## 35 | ################################################################################ 36 | ## 37 | ## }}} 38 | all: spiasm 39 | 40 | ## Build spiasm 41 | ## {{{ 42 | lex.yy.c: spiasm.l 43 | flex spiasm.l 44 | 45 | spiasm: lex.yy.c 46 | g++ lex.yy.c -o spiasm 47 | ## }}} 48 | 49 | ## A "test" target 50 | ## {{{ 51 | dump.bin: testfil.s spiasm 52 | ./spiasm testfil.s -o dump.bin 53 | 54 | .PHONY: test 55 | test: dump.bin 56 | ./spiasm -d dump.bin 57 | ## }}} 58 | 59 | .PHONY: clean 60 | ## {{{ 61 | clean: 62 | rm -f spiasm lex.yy.c 63 | ## }}} 64 | -------------------------------------------------------------------------------- /sw/README.md: -------------------------------------------------------------------------------- 1 | # SPI CPU Assembler 2 | 3 | The main component of this software directory is the SPI CPU assembler. This 4 | assembler takes files, such as [testfil.s](testfil.s), containing 5 | assembler commands, and it generates a binary file which can then be fed to 6 | command the [SPICPU](../rtl/spicpu.v). 7 | 8 | Assembler commands are: 9 | 10 | - STOP will cause the SPI controller to raise CSN, and thereby clear the 11 | interface 12 | 13 | - START : Will cause the SPI controller to lower CSN[ID]. ID's can range 14 | from 0 to 30, subject to the hardware the controller was built to support. 15 | If ID is greater than the number of chip enables supported by the SPI 16 | controller hardware, then this instruction becomes the equivalent of a 17 | STOP command and will raise all CSN lines. 18 | 19 | - READ : Reads NMBR bytes from the SPI interface. The outgoing MOSI 20 | lines will be set to zero during this time. All bytes will be sent to the 21 | outgoing stream port. If given sufficient backpressure, the SPI clock 22 | and hence the whole interface will stall. 23 | 24 | - SEND [, ]`*`: Sends the given bytes across the SPI interface. 25 | Any return values contained in the MISO lines will be ignored, and the 26 | outgoing AXI stream interface will thus become idle. Immediate values 27 | can be of any form accepted by `strtoul()`. 28 | 29 | - TXRX [, ]`*`: This is the same as the `SEND` comand above, save 30 | that the receive values will be sent across the outgoing AXI stream interface 31 | instead of being ignored. 32 | 33 | - LAST: This command is issued as a prefix command to any `READ` or `TXRX` 34 | commands. If issued, then the last value read as part of those commands 35 | will have the TLAST flag set, to indicate that said value is the last value 36 | in a packet. 37 | 38 | Those are the commands that will be used to control the SPI interface. Another 39 | 4 commands exist as well, which are handled prior to the SPI interface: 40 | 41 | - HALT: Once received, no further commands will be issued to the SPI 42 | interface without CPU intervention. This instruction implies a STOP if CSn 43 | has not been deactivated. 44 | 45 | - WAIT: Will pause all instructions to the SPI interface until an external 46 | synchronization signal has been received. This instruction also implies a 47 | STOP if CSn has not yet been deactivated. 48 | 49 | - TARGET: Sets the address for a future JUMP instruction to return to. 50 | 51 | The SPI controller does not support conditional jumps or halts. Therefore, 52 | it can only support one of two control structures: Run from a start to a 53 | completion, or run from a start to a `TARGET` command followed by an infinite 54 | loop from the last `TARGET` command to the final `JUMP` command. 55 | 56 | - JUMP: This is the other half of the `TARGET` loop structure. Once `JUMP` 57 | is received, the CPU will `JUMP` to the `TARGET` instruction. If CSn is 58 | active at this time, then this instruction will also deactivate all CSn 59 | signals (i.e. raise them). 60 | 61 | Note that all logic is address independent: any jump address is defined by 62 | the location of the last `TARGET` instructions. This is designed to make it 63 | easy to install the script anywhere in memory at a later time. 64 | 65 | ## Testing 66 | 67 | (Current test comes from a different project.) 68 | 69 | 84 | -------------------------------------------------------------------------------- /sw/spiasm.l: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | ** 3 | ** Filename: spiasm.l 4 | ** {{{ 5 | ** Project: wbspi, a set of Serial Peripheral Interface cores 6 | ** 7 | ** Purpose: Contains flex instructions to build a basic assembler for the 8 | ** SPIASM language used by our SPI CPU (in ../rtl). 9 | ** 10 | ** Creator: Dan Gisselquist, Ph.D. 11 | ** Gisselquist Technology, LLC 12 | ** 13 | ******************************************************************************** 14 | ** }}} 15 | ** Copyright (C) 2022, Gisselquist Technology, LLC 16 | ** {{{ 17 | ** This program is free software (firmware): you can redistribute it and/or 18 | ** modify it under the terms of the GNU General Public License as published 19 | ** by the Free Software Foundation, either version 3 of the License, or (at 20 | ** your option) any later version. 21 | ** 22 | ** This program is distributed in the hope that it will be useful, but WITHOUT 23 | ** ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or 24 | ** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 25 | ** for more details. 26 | ** 27 | ** You should have received a copy of the GNU General Public License along 28 | ** with this program. (It's in the $(ROOT)/doc directory. Run make with no 29 | ** target there if the PDF file isn't present.) If not, see 30 | ** for a copy. 31 | ** }}} 32 | ** License: GPL, v3, as defined and found on www.gnu.org, 33 | * {{{ 34 | * http://www.gnu.org/licenses/gpl.html 35 | * 36 | ******************************************************************************** 37 | * 38 | * }}} 39 | */ 40 | 41 | %{ 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | 48 | typedef struct SYMBOL_S { 49 | unsigned addr; 50 | char *str; 51 | } SYMBOL; 52 | 53 | typedef struct EQDEFN_S { 54 | unsigned val; 55 | char *str; 56 | } EQDEFN; 57 | 58 | #define MAX_SYMBOLS 512 59 | SYMBOL symlist[MAX_SYMBOLS]; 60 | unsigned nsyms = 0; 61 | 62 | EQDEFN defnlist[MAX_SYMBOLS]; 63 | unsigned ndefns = 0; 64 | 65 | extern "C" int yylex(); 66 | extern "C" int yywrap() { return 1;} 67 | 68 | #define I_START 0 69 | // #define I_STOP 1 70 | #define I_READ 2 71 | #define I_SEND 4 72 | #define I_TXRX 6 73 | #define I_LAST 8 74 | #define I_HALT 10 75 | #define I_TICK 11 76 | #define I_TARGET 12 77 | #define I_CHANNEL 14 78 | #define I_NOOP 15 79 | // 80 | #define I_STOP 16 81 | #define I_JUMP 17 82 | #define I_WAIT 18 83 | 84 | int posn = 0; 85 | bool m_debug = false; 86 | 87 | void addinsn(int); 88 | void addimm_lbl(const char *str); 89 | void addimm(int); 90 | void adddefn(const char *str); 91 | void label(const char *); 92 | %} 93 | 94 | %option yylineno 95 | %option warn 96 | 97 | %% 98 | 99 | (?i:START) { addinsn(I_START); } 100 | (?i:STOP) { addinsn(I_STOP); } 101 | (?i:READ) { addinsn(I_READ); } 102 | (?i:SEND) { addinsn(I_SEND); } 103 | (?i:TXRX) { addinsn(I_TXRX); } 104 | (?i:LAST) { addinsn(I_LAST); } 105 | (?i:WAIT) { addinsn(I_WAIT); } 106 | (?i:HALT) { addinsn(I_HALT); } 107 | (?i:TICK) { addinsn(I_TICK); } 108 | (?i:TGT) { addinsn(I_TARGET); } 109 | (?i:TARGET) { addinsn(I_TARGET); } 110 | (?i:JUMP) { addinsn(I_JUMP); } 111 | (?i:CHAN) { addinsn(I_CHANNEL); } 112 | (?i:CHANNEL) { addinsn(I_CHANNEL); } 113 | (?i:NOP) { addinsn(I_NOOP); } 114 | (?i:NOOP) { addinsn(I_NOOP); } 115 | [A-Za-z_][A-Za-z_0-9]*[ \t]*=[ \t]*0[xX][0-9A-Fa-f] { adddefn(yytext);} 116 | [A-Za-z_][A-Za-z_0-9]*[ \t]*=[ \t]*0[0-7]* { adddefn(yytext);} 117 | [A-Za-z_][A-Za-z_0-9]*[ \t]*=[ \t]*[1-9][0-9]* { adddefn(yytext);} 118 | [A-Za-z_][A-Za-z_0-9]*: { label(yytext); } 119 | [A-Za-z_][A-Za-z_0-9]* { addimm_lbl(yytext); } 120 | [,]?[\s]*0[xX][0-9A-Fa-f]+ { addimm(strtoul(yytext,NULL,16));} 121 | [,]?[\s]*0[0-7]+ { addimm(strtoul(yytext,NULL, 8));} 122 | [,]?[\s]*[1-9][0-9]* { addimm(strtoul(yytext,NULL,10));} 123 | 0 { addimm(0); } 124 | "," { } 125 | [ \t]+ { } 126 | ";".*\n { } 127 | "#".*\n { } 128 | "//".*\n { } 129 | \n { } 130 | %% 131 | 132 | bool verbose_flag = false; 133 | FILE *hfile = NULL; // C++ data file 134 | // FILE *dbgfp = NULL; // Debug file 135 | 136 | const char *INSN[] = { 137 | "START", "READ", "SEND", "TXRX", 138 | "HALT", "TARGET","JUMP", "NOOP"}; 139 | 140 | int m_binsz, m_pos = 0, m_numimm = 0, m_lastpos = 0; 141 | char *m_binary = NULL; 142 | int m_last_insn = I_NOOP; 143 | bool m_imm = false, m_halted = false, m_active =false; 144 | 145 | void init_buffer(void) { 146 | // {{{ 147 | m_binsz = 512; 148 | m_binary = new char[m_binsz]; 149 | m_pos = 0; 150 | m_lastpos = 0; 151 | m_numimm = 0; 152 | m_imm = false; 153 | m_halted = false; 154 | m_active = false; 155 | } 156 | // }}} 157 | 158 | void grow_buffer(void) { 159 | // {{{ 160 | char *newbuf; 161 | 162 | assert(m_binsz >= 512); 163 | newbuf = new char[m_binsz * 2]; 164 | memcpy(newbuf, m_binary, m_pos); 165 | m_binsz *= 2; 166 | delete[] m_binary; 167 | m_binary = newbuf; 168 | } 169 | // }}} 170 | 171 | void addinsn(int i) { 172 | // {{{ 173 | if (verbose_flag) { 174 | // {{{ 175 | fprintf(stderr, "Lastpos = %d\n", m_lastpos); 176 | if (m_pos > m_lastpos) 177 | switch((m_binary[m_lastpos] >> 4) & 0x0e) { 178 | case I_START: 179 | // {{{ 180 | if (m_binary[m_lastpos] == 0x01f) 181 | fprintf(stderr, "> STOP\n"); 182 | else 183 | fprintf(stderr, "> START %d\n", m_binary[m_lastpos] & 0x01f); 184 | break; 185 | // }}} 186 | case I_READ: 187 | fprintf(stderr, "> READ %2d\n", 1+(m_binary[m_lastpos] & 0x01f)); 188 | break; 189 | case I_SEND: 190 | // {{{ 191 | fprintf(stderr, "> SEND\t"); 192 | for(unsigned k=0; k < (m_binary[m_lastpos] & 0x1f)+1; 193 | k++) { 194 | fprintf(stderr, " 0x%02x", 195 | m_binary[m_lastpos+k+1] & 0x0ff); 196 | if (k < (m_binary[m_lastpos] & 0x1f)) { 197 | fprintf(stderr, ","); 198 | if ((k & 7)==7) 199 | fprintf(stderr, "\n\t"); 200 | } 201 | } fprintf(stderr, "\n"); 202 | break; 203 | // }}} 204 | case I_TXRX: 205 | // {{{ 206 | fprintf(stderr, "> TXRX\t"); 207 | for(unsigned k=0; k < (m_binary[m_lastpos] & 0x1f)+1; 208 | k++) { 209 | fprintf(stderr, " 0x%02x", 210 | m_binary[m_lastpos+k+1] & 0x0ff); 211 | if (k < (m_binary[m_lastpos] & 0x1f)) { 212 | fprintf(stderr, ","); 213 | if ((k & 7)==7) 214 | fprintf(stderr, "\n\t"); 215 | } 216 | } fprintf(stderr, "\n"); 217 | break; 218 | // }}} 219 | case I_LAST: 220 | fprintf(stderr, "> LAST\n"); 221 | break; 222 | case 0xa: // HALT/WAIT / TICK 223 | // {{{ 224 | if (0xa0 == (m_binary[m_lastpos] & 0x0f8)) 225 | fprintf(stderr, "> HALT\n"); 226 | else if (0xa8 == (m_binary[m_lastpos] & 0x0f8)) 227 | fprintf(stderr, "> WAIT\n"); 228 | else 229 | fprintf(stderr, "> TICK\n"); 230 | break; 231 | // }}} 232 | case 0xc: // TARGET/JUMP 233 | // {{{ 234 | if (0xc0 == (m_binary[m_lastpos] & 0x0f8)) 235 | fprintf(stderr, "> TARGET\n"); 236 | else if (0xc8 == (m_binary[m_lastpos] & 0x0f8)) 237 | fprintf(stderr, "> JUMP\n"); 238 | else 239 | fprintf(stderr, "> ILL!\n"); 240 | break; 241 | // }}} 242 | case 0xe: // CHAN / NOOP 243 | // {{{ 244 | if (0xe0 == (m_binary[m_lastpos] & 0x0f0)) 245 | fprintf(stderr, "> CHANNEL %d\n", 246 | m_binary[m_lastpos] & 0x0f); 247 | else 248 | fprintf(stderr, "> NOOP\n"); 249 | break; 250 | // }}} 251 | default: break; // Should never get here 252 | } 253 | 254 | fprintf(stderr, "%02d: ", m_pos); 255 | switch(i & 0x0f) { 256 | case I_START: 257 | fprintf(stderr, "Insn #%2d: START\n", i); break; 258 | case I_STOP: 259 | fprintf(stderr, "Insn #%2d: STOP\n", i); break; 260 | case I_READ: 261 | fprintf(stderr, "Insn #%2d: READ\n", i); break; 262 | case I_SEND: 263 | fprintf(stderr, "Insn #%2d: SEND\n", i); break; 264 | case I_TXRX: 265 | fprintf(stderr, "Insn #%2d: TXRX\n", i); break; 266 | case I_LAST: 267 | fprintf(stderr, "Insn #%2d: LAST\n", i); break; 268 | case I_HALT: 269 | fprintf(stderr, "Insn #%2d: HALT\n", i); break; 270 | case I_WAIT: 271 | fprintf(stderr, "Insn #%2d: WAIT\n", i); break; 272 | case I_TICK: 273 | fprintf(stderr, "Insn #%2d: TICK\n", i); break; 274 | case I_TARGET: 275 | fprintf(stderr, "Insn #%2d: TARGET\n", i); break; 276 | case I_JUMP: 277 | fprintf(stderr, "Insn #%2d: JUMP\n", i); break; 278 | case I_CHANNEL: 279 | fprintf(stderr, "Insn #%2d: CHANNEL\n", i); break; 280 | case I_NOOP: 281 | fprintf(stderr, "Insn #%2d: NOOP\n", i); break; 282 | default: 283 | fprintf(stderr, "Insn #%2d: (Unknwon)\n", i); break; 284 | } 285 | // }}} 286 | } 287 | 288 | if (i == I_STOP) { 289 | if (!m_active) 290 | // Two stops in a row are pointless 291 | return; 292 | m_binary[m_pos] = 0x01f; 293 | m_active = false; 294 | } else if (m_imm && ( ((m_binary[m_lastpos] & 0x0e0) >> 4) == i) 295 | && m_numimm < 32 && (i== I_TXRX || i == I_SEND)) { 296 | // Do nothing. No new instruction, we just continue the last 297 | // one 298 | if (verbose_flag) { 299 | fprintf(stderr, "Continuing I_TXRX|I_SEND\n"); 300 | } 301 | return; 302 | } else { 303 | switch(i) { 304 | case I_START: 305 | if (m_active) { 306 | addinsn(I_STOP); 307 | } m_binary[m_pos] = 0; 308 | m_active = true; 309 | break; 310 | case I_READ: 311 | assert(m_active); 312 | m_binary[m_pos] = 0x20; 313 | break; 314 | case I_SEND: 315 | assert(m_active); 316 | m_binary[m_pos] = 0x40; 317 | break; 318 | case I_TXRX: 319 | assert(m_active); 320 | m_binary[m_pos] = 0x60; 321 | break; 322 | case I_LAST: 323 | m_binary[m_pos] = 0x80; 324 | break; 325 | case I_HALT: 326 | if (m_halted) 327 | return; 328 | m_binary[m_pos] = 0xa0; 329 | break; 330 | case I_TICK: 331 | m_binary[m_pos] = 0xb0; 332 | break; 333 | case I_TARGET: 334 | m_binary[m_pos] = 0xc0; 335 | break; 336 | case I_CHANNEL: 337 | assert(!m_active); 338 | m_binary[m_pos] = 0xe0; 339 | break; 340 | case I_NOOP: 341 | m_binary[m_pos] = 0xf0; 342 | break; 343 | case I_STOP: 344 | // Handled above, so should never come here 345 | m_binary[m_pos] = 0x1f; 346 | m_active = false; 347 | break; 348 | case I_JUMP: 349 | m_binary[m_pos] = 0xc8; 350 | break; 351 | case I_WAIT: 352 | m_binary[m_pos] = 0xa8; 353 | break; 354 | default: m_binary[m_pos] = 0x1f; 355 | m_active = false; 356 | } 357 | 358 | if (i == I_WAIT || i == I_HALT || i == I_JUMP) 359 | m_active = false; 360 | } 361 | 362 | m_numimm = 0; 363 | m_imm = (i == I_TXRX) || (i == I_SEND); 364 | m_lastpos= m_pos; 365 | m_last_insn = i; 366 | 367 | m_pos ++; 368 | m_halted = (i== I_HALT); 369 | 370 | if (m_pos >= m_binsz) 371 | grow_buffer(); 372 | } 373 | // }}} 374 | 375 | void addimm_lbl(const char *id) { 376 | // {{{ 377 | unsigned jk; 378 | 379 | for(jk=0; jk 32) { 428 | m_binary[m_lastpos] &= 0xe0; 429 | m_binary[m_lastpos] |= (31 & 0x1f); 430 | 431 | while(imm >= 32) { 432 | addinsn(I_READ); 433 | addimm(32); 434 | imm -= 32; 435 | } if (imm > 0) { 436 | addinsn(I_READ); 437 | m_binary[m_lastpos] &= 0xe0; 438 | m_binary[m_lastpos] |= ((imm-1) & 0x1f); 439 | } 440 | } else { 441 | m_binary[m_lastpos] &= 0xe0; 442 | m_binary[m_lastpos] |= ((imm-1) & 0x1f); 443 | } 444 | m_imm = true; 445 | break; 446 | case I_LAST: 447 | errcode = "LAST"; 448 | break; 449 | case I_WAIT: 450 | if (m_binary[m_lastpos] & 0x07) 451 | errcode = "WAIT"; 452 | m_binary[m_lastpos] &= 0xf8; 453 | m_binary[m_lastpos] |= imm; 454 | break; 455 | case I_HALT: 456 | errcode = "HALT"; 457 | break; 458 | case I_TARGET: 459 | errcode = "TARGET"; 460 | break; 461 | case I_JUMP: 462 | errcode = "JUMP"; 463 | break; 464 | case I_NOOP: 465 | errcode = "NOOP"; 466 | break; 467 | default: 468 | errcode = "ILL"; 469 | break; 470 | } if (errcode) { 471 | fprintf(stderr, "ERR: Command %s takes no immediates!\n", 472 | errcode); 473 | } 474 | // }}} 475 | } 476 | 477 | if (m_pos >= m_binsz) 478 | grow_buffer(); 479 | } 480 | // }}} 481 | 482 | void dump(FILE *fp) { 483 | // {{{ 484 | for(int p=0; p> 4) & 0x0f; 486 | 487 | printf("%02x: ", p); 488 | 489 | switch(op) { 490 | case I_START: case 1: 491 | if (m_binary[p] == 0x01f) 492 | fprintf(fp, " STOP\n"); 493 | else 494 | fprintf(fp, " START %d\n", (m_binary[p] & 0x1f)); 495 | break; 496 | case I_READ: case I_READ+1: 497 | fprintf(fp, " READ %d\n", 1+(m_binary[p] & 0x01f)); 498 | break; 499 | case I_SEND: case I_SEND+1: 500 | fprintf(fp, " SEND"); 501 | for(unsigned k=0; k < (m_binary[p] & 0x01f)+1 502 | && (k + p + 1 < m_pos); k++) { 503 | printf(" 0x%02x", m_binary[p+k+1] & 0x0ff); 504 | if (k < (m_binary[p] & 0x1f)) { 505 | printf(","); 506 | if ((k & 7)==7) 507 | printf("\n\t"); 508 | } 509 | } printf("\n"); 510 | p += (m_binary[p] & 0x01f)+1; 511 | break; 512 | case I_TXRX: case I_TXRX+1: 513 | fprintf(fp, " TXRX"); 514 | for(unsigned k=0; k < (m_binary[p] & 0x01f)+1 515 | && (k + p + 1 < m_pos); k++) { 516 | printf(" 0x%02x", m_binary[p+k+1] & 0x0ff); 517 | if (k < (m_binary[p] & 0x1f)) { 518 | printf(","); 519 | if ((k & 7)==7) 520 | printf("\n\t"); 521 | } 522 | } 523 | printf("\n"); 524 | p += (m_binary[p] & 0x01f)+1; 525 | break; 526 | case I_LAST: 527 | printf(" LAST\n"); break; 528 | case I_HALT: 529 | if (0x08 == (m_binary[p] & 0x18)) { 530 | if (m_binary[p] & 0x07) { 531 | printf(" WAIT %d\n", m_binary[p] & 7); 532 | } else 533 | printf(" WAIT\n"); 534 | } else if (0x00 == (m_binary[p] & 0x18)) { 535 | printf(" HALT\n"); break; 536 | } else 537 | printf(" TICK\n"); break; 538 | break; 539 | case I_TARGET: if (m_binary[p] & 0x10) 540 | printf(" ILLEGAL!!! 0x%02x\n", 541 | m_binary[p]); 542 | else if(m_binary[p] & 0x08) 543 | printf(" JUMP\n"); 544 | else 545 | printf(" TARGET\n"); 546 | break; 547 | case I_CHANNEL: if (0x0f0 == (m_binary[p] & 0x0f0)) 548 | printf(" NOOP\n"); 549 | else 550 | printf(" CHANNEL %d\n", 551 | m_binary[p] & 0x0f); 552 | break; 553 | default: printf(" ILL\t(0x%x)\n", op); break; 554 | } 555 | } 556 | } 557 | // }}} 558 | 559 | void adddefn(const char *str) { 560 | // {{{ 561 | char *cpy, *ptr, *ptreq; 562 | unsigned val; 563 | 564 | cpy = strdup(str); 565 | ptreq = ptr = strchr(cpy,'='); 566 | if (ptr == NULL) { 567 | free(cpy); return; 568 | } 569 | 570 | ptr++; 571 | while(*ptr && isspace(*ptr)) 572 | ptr++; 573 | 574 | val = strtoul(ptr, NULL, 0); 575 | 576 | *ptreq = '\0'; 577 | ptreq--; 578 | while(cpy < ptreq && isspace(*ptreq)) 579 | *ptreq-- = '\0'; 580 | 581 | defnlist[ndefns].val = val; 582 | defnlist[ndefns].str = strdup(cpy); 583 | ndefns++; 584 | assert(ndefns < MAX_SYMBOLS); 585 | 586 | free(cpy); 587 | } 588 | // }}} 589 | 590 | void label(const char *str) { 591 | // {{{ 592 | if (m_pos > 0 && !m_halted) { 593 | addinsn(I_HALT); 594 | } 595 | 596 | if (verbose_flag) { 597 | fprintf(stderr, "New-Label: %s at %d\n", str, m_pos); 598 | } 599 | 600 | symlist[nsyms].addr = m_pos; 601 | symlist[nsyms].str = strdup(str); 602 | 603 | unsigned slen = strlen(symlist[nsyms].str); 604 | if (symlist[nsyms].str[slen-1] == ':') 605 | // This should always be true 606 | symlist[nsyms].str[slen-1] = '\0'; 607 | nsyms++; 608 | 609 | assert(nsyms < MAX_SYMBOLS); 610 | } 611 | // }}} 612 | 613 | unsigned filesz(FILE *fp) { 614 | // {{{ 615 | unsigned long here = ftell(fp), endp; 616 | 617 | if (0 != fseek(fp, 0l, SEEK_END)) { 618 | fprintf(stderr, "ERR: FILESZ(FSEEK)\n"); 619 | perror("O/S Err:"); 620 | exit(EXIT_FAILURE); 621 | } 622 | endp = ftell(fp); 623 | if (0 != fseek(fp, here, SEEK_SET)) { 624 | fprintf(stderr, "ERR: FILESZ(FSEEK return)\n"); 625 | perror("O/S Err:"); 626 | exit(EXIT_FAILURE); 627 | } 628 | return (unsigned)(endp-here); 629 | } 630 | // }}} 631 | 632 | void usage(void) { 633 | // {{{ 634 | fprintf(stderr, "" 635 | "Usage: spiasm [-hdv] [-o ] [infiles ...]\n" 636 | "\n" 637 | "\t-h\tThis usage statement\n" 638 | "\t-c\tProduce a C file output, declaring a variable array\n" 639 | "\t-d\tDisassemble the given file, rather than assembling it\n" 640 | "\t-v\tVerbose mode (may or may not do anything)\n" 641 | "\t-o \tWrite the results to . If is not" 642 | "\t\tgiven, results will be written to standard out.\n" 643 | "\t\tA set of filenames, separated by spaces, to be either\n" 644 | "\t\tassembled or (in the case of -d) disassembled.\n"); 645 | } 646 | // }}} 647 | 648 | int main(int argc, char **argv) { 649 | bool dump_flag = false, cpp_flag = false; 650 | int opt; 651 | // dbgfp = fopen("dump.txt", "w"); 652 | 653 | init_buffer(); 654 | 655 | int nfiles = 0, argn; 656 | FILE *finp, *fout = stdout; 657 | while(-1 != (opt = getopt(argc, argv, "hcdvo:"))) { 658 | // {{{ 659 | switch(opt) { 660 | case 'h': 661 | usage(); 662 | exit(EXIT_SUCCESS); 663 | break; 664 | case 'c': 665 | cpp_flag = true; 666 | dump_flag = false; 667 | break; 668 | case 'd': 669 | dump_flag = true; 670 | cpp_flag = false; 671 | break; 672 | case 'v': 673 | fprintf(stderr, "Verbose mode enabled\n"); 674 | verbose_flag = true; 675 | break; 676 | case 'o': 677 | // output_file = strcpy(optarg); 678 | fout = fopen(optarg, "w"); 679 | break; 680 | } 681 | } 682 | // }}} 683 | 684 | if (verbose_flag) { 685 | if (cpp_flag) 686 | fprintf(stderr, "Attempting to output in C++ format\n"); 687 | if (dump_flag) 688 | fprintf(stderr, "Attempting to dump input files\n"); 689 | } 690 | 691 | // Process individual files 692 | // {{{ 693 | for(argn=optind; argn 0); 745 | assert(m_binary != NULL); 746 | assert(m_pos == 0); 747 | 748 | while((nr = fread(&m_binary[m_pos], 1, m_binsz-m_pos, stdin)) 749 | == m_binsz-m_pos) { 750 | 751 | if (m_binsz > 65536) { 752 | fprintf(stderr, "ERR: File size exceeds artificial 64kB limit!\n"); 753 | exit(EXIT_FAILURE); 754 | } 755 | 756 | m_pos += nr; 757 | grow_buffer(); 758 | } 759 | 760 | fprintf(fout, "DUMP: (stdin)\n====================\n"); 761 | dump(fout); 762 | fprintf(fout, "\n"); 763 | fclose(finp); 764 | delete[] m_binary; 765 | m_binary = NULL; 766 | // }}} 767 | } else { 768 | yylex(); // Use stdin 769 | } 770 | // }}} 771 | 772 | // Write the file out 773 | // {{{ 774 | if (!dump_flag) { 775 | if (cpp_flag) { 776 | unsigned sympos = 0, tabstart = 0; 777 | 778 | if (nsyms > 0 && symlist[0].addr == 0) 779 | fprintf(fout, "const char %s[] = {\n\t", symlist[sympos++].str); 780 | else 781 | fprintf(fout, "const char spiasm[] = {\n\t"); 782 | for(int p=0; p