1
+ -- This Source Code Form is subject to the terms of the Mozilla Public
2
+ -- License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ -- file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
+ --
5
+ -- Copyright 2024 Oxide Computer Company
6
+
7
+ library ieee;
8
+ use ieee.std_logic_1164.all ;
9
+ use ieee.numeric_std.all ;
10
+ use ieee.numeric_std_unsigned.all ;
11
+
12
+ library vunit_lib;
13
+ context vunit_lib.com_context;
14
+ context vunit_lib.vunit_context;
15
+ context vunit_lib.vc_context;
16
+
17
+ use vunit_lib.spi_pkg.all ;
18
+
19
+ use work.spi_axi_tb_pkg.all ;
20
+ use work.spi_axi_pkg.all ;
21
+
22
+
23
+ entity spi_axi_tb is
24
+ generic (
25
+
26
+ runner_cfg : string
27
+ );
28
+ end entity ;
29
+
30
+ architecture tb of spi_axi_tb is
31
+
32
+ begin
33
+
34
+ th : entity work .spi_axi_th;
35
+
36
+ bench: process
37
+ alias reset is << signal th.reset : std_logic >> ;
38
+ alias csn is << signal th.csn : std_logic >> ;
39
+ variable buf : buffer_t;
40
+ variable buf2 : buffer_t;
41
+ variable data : std_logic_vector (7 downto 0 ) := (others => '0' );
42
+ variable expected_data : std_logic_vector (7 downto 0 ) := (others => '0' );
43
+ variable address : std_logic_vector (15 downto 0 ) := (others => '0' );
44
+ variable tx_queue : queue_t := new_queue;
45
+ begin
46
+ -- Always the first thing in the process, set up things for the VUnit test runner
47
+ test_runner_setup(runner, runner_cfg);
48
+ show_all(wr_logger, display_handler);
49
+ -- Reach into the test harness, which generates and de-asserts reset and hold the
50
+ -- test cases off until we're out of reset. This runs for every test case
51
+ wait until reset = '0' ;
52
+ wait for 500 ns ; -- let the resets propagate
53
+
54
+ while test_suite loop
55
+ if run(" single-read" ) then
56
+ -- set up the "memory to be read with a known value"
57
+ buf := allocate(rmemory, 1 * 64 , alignment => 32 );
58
+ -- Use the simulation interface to set the data we're going to read back
59
+ expected_data := X"AA" ;
60
+ write_word(rmemory, 0 , expected_data);
61
+ -- TB will fault if DUT tries to write to this memory
62
+ set_permissions(rmemory, 0 , read_only);
63
+ -- issue spi read command (data is dummy byte here since this is a)
64
+ spi_send_byte(net, spi_opcode_read, address, (others => '0' ), csn);
65
+ -- Read back word rx'd from DUT and check it matches expected.
66
+ -- -- we're going to have 3 dummy bytes here, skip them, keeping the 4th
67
+ for i in 0 to 3 loop
68
+ pop_stream(net, master_rstream, data);
69
+ end loop ;
70
+ check_equal(data, expected_data, " Read data did not match expected" );
71
+ elsif run(" single-write" ) then
72
+ -- set up the "memory to be read with a known value"
73
+ buf := allocate(wmemory, 1 * 64 , alignment => 32 );
74
+ -- Use the simulation interface to set the data we're expected to write
75
+ expected_data := X"AA" ;
76
+ set_expected_word(wmemory, 0 , expected_data);
77
+ -- issue spi write command
78
+ spi_send_byte(net, spi_opcode_write, address, expected_data, csn);
79
+ -- no reach into the ram and see what is there now
80
+ wait for 20 ns ;
81
+ check_expected_was_written(buf);
82
+ elsif run(" single-bit-set" ) then
83
+ -- Due to how the axi blocks by vunit work, we need to set up 2 buffers, one for the
84
+ -- read side and one for the write-side
85
+ -- READ SIDE is buf
86
+ -- set up the "memory to be read with a known value"
87
+ buf := allocate(rmemory, 1 * 64 , alignment => 32 );
88
+ -- WRITE SIDE is buf2
89
+ buf2 := allocate(wmemory, 1 * 64 , alignment => 32 );
90
+ -- Sick X"AA" into the read buffer, this is going to be our starting point for the bit-set
91
+ -- Use the simulation interface to set the data we're going to read back
92
+ expected_data := X"AA" ;
93
+ write_word(rmemory, 0 , expected_data);
94
+ -- Our Write address now has X"AA" in it.
95
+ data := X"05" ;
96
+ -- This is the expected write into the write-side after the read-modify-write bit-set operation
97
+ expected_data := expected_data or data;
98
+ set_expected_word(wmemory, 0 , expected_data);
99
+
100
+ -- issue spi write command for bit set with data bits
101
+ spi_send_byte(net, spi_opcode_bit_set, address, data, csn);
102
+
103
+ -- no reach into the ram and see what is there now
104
+ wait for 1 us ;
105
+ check_expected_was_written(buf2);
106
+ elsif run(" single-bit-clr" ) then
107
+ -- Due to how the axi blocks by vunit work, we need to set up 2 buffers, one for the
108
+ -- read side and one for the write-side
109
+ -- READ SIDE is buf
110
+ -- set up the "memory to be read with a known value"
111
+ buf := allocate(rmemory, 1 * 64 , alignment => 32 );
112
+ -- WRITE SIDE is buf2
113
+ buf2 := allocate(wmemory, 1 * 64 , alignment => 32 );
114
+ -- Sick X"AA" into the read buffer, this is going to be our starting point for the bit-set
115
+
116
+ -- Use the simulation interface to set the data we're going to read back
117
+ expected_data := X"AA" ;
118
+ write_word(rmemory, 0 , expected_data);
119
+ -- Our Write address now has X"AA" in it.
120
+ data := X"0A" ;
121
+ -- This is the expected write into the write-side after the read-modify-write bit-set operation
122
+ expected_data := expected_data and (not data);
123
+ set_expected_word(wmemory, 0 , expected_data);
124
+
125
+ -- issue spi write command for bit set with data bits
126
+ spi_send_byte(net, spi_opcode_bit_clr, address, data, csn);
127
+ -- no reach into the ram and see what is there now
128
+ wait for 20 ns ;
129
+ check_expected_was_written(buf2);
130
+ elsif run(" multi-read" ) then
131
+ -- set up the "memory to be read with a known value"
132
+ buf := allocate(rmemory, 4 * 32 , alignment => 32 );
133
+ -- Use the simulation interface to set the data we're going to read back
134
+ -- Load up 3 known bytes into the memory
135
+ expected_data := X"AA" ;
136
+ write_word(rmemory, 0 , X"ACABAA" );
137
+ -- TB will fault if DUT tries to write to this memory
138
+ set_permissions(rmemory, 0 , read_only);
139
+ -- issue spi read command for multiple bytes. No early abort
140
+ for i in 0 to 2 loop
141
+ push_byte(tx_queue, 0 ); -- push dummy data for the read
142
+ end loop ;
143
+ spi_send_stream(net, spi_opcode_read, address, tx_queue, csn);
144
+ -- Read back word rx'd from DUT and check it matches expected.
145
+ -- we're going to have 3 dummy bytes here, skip them, keeping the 4th
146
+ expected_data := X"AA" ;
147
+ for i in 0 to 4 loop
148
+ pop_stream(net, master_rstream, data);
149
+ -- only check past the dummy data
150
+ if i > 2 then
151
+ check_equal(data, expected_data, " Read data did not match expected, iteration " & to_string(i- 2 ));
152
+ expected_data := expected_data + 1 ;
153
+ end if ;
154
+ end loop ;
155
+ elsif run(" multi-write" ) then
156
+ -- set up the "memory to be read with a known value"
157
+ buf := allocate(wmemory, 1 * 64 , alignment => 32 );
158
+ -- Use the simulation interface to set the data we're expected to write
159
+ set_expected_word(wmemory, 0 , X"ACABAA" );
160
+ -- issue spi write command
161
+ expected_data := X"AA" ;
162
+ for i in 0 to 2 loop
163
+ push_byte(tx_queue, to_integer (expected_data)); -- push dummy data for the read
164
+ expected_data := expected_data + 1 ;
165
+ end loop ;
166
+ spi_send_stream(net, spi_opcode_write, address, tx_queue, csn);
167
+ -- no reach into the ram and see what is there now
168
+ wait for 20 ns ;
169
+ check_expected_was_written(buf);
170
+
171
+ elsif run(" ok-after-invalid-opcode" ) then
172
+ -- set up the "memory to be read with a known value"
173
+ buf := allocate(rmemory, 1 * 64 , alignment => 32 );
174
+ -- Use the simulation interface to set the data we're going to read back
175
+ expected_data := X"AA" ;
176
+ write_word(rmemory, 0 , expected_data);
177
+ -- TB will fault if DUT tries to write to this memory
178
+ set_permissions(rmemory, 0 , read_only);
179
+ -- issue invalid opcode.
180
+ spi_send_byte(net, "1001" , address, (others => '0' ), csn);
181
+ -- Read back word rx'd from DUT and check it matches expected.
182
+ -- we're going to have 3 dummy bytes here, skip them, keeping the 4th
183
+ for i in 0 to 3 loop
184
+ pop_stream(net, master_rstream, data);
185
+ end loop ;
186
+ wait for 20 ns ;-- same thing again with a read this time
187
+ -- issue spi read command (data is dummy byte here since this is a)
188
+ spi_send_byte(net, spi_opcode_read, address, (others => '0' ), csn);
189
+ -- Read back word rx'd from DUT and check it matches expected.
190
+ -- we're going to have 3 dummy bytes here, skip them, keeping the 4th
191
+ for i in 0 to 3 loop
192
+ pop_stream(net, master_rstream, data);
193
+ end loop ;
194
+ check_equal(data, expected_data, " Read data did not match expected" );
195
+ end if ;
196
+ end loop ;
197
+
198
+ wait for 2 us ;
199
+ test_runner_cleanup(runner);
200
+ wait ;
201
+ end process ;
202
+
203
+ -- Example total test timeout dog
204
+ test_runner_watchdog(runner, 10 ms );
205
+
206
+ end tb;
0 commit comments