├── .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