Skip to content

Commit 3a3d3ed

Browse files
SPI<->AXI interface
1 parent 8187700 commit 3a3d3ed

File tree

10 files changed

+1048
-0
lines changed

10 files changed

+1048
-0
lines changed

hdl/ip/vhd/spi/axi_controller/BUCK

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
load("//tools:hdl.bzl", "vhdl_unit", "vunit_sim")
2+
load("//tools:rdl.bzl", "rdl_file")
3+
4+
5+
vhdl_unit(
6+
name = "spi_axi_controller",
7+
srcs = glob(["*.vhd",]),
8+
deps = [
9+
"//hdl/ip/vhd/spi/spi_target_phy:spi_target_phy",
10+
],
11+
visibility = ['PUBLIC']
12+
)
13+
14+
vunit_sim(
15+
name = "spi_axi_tb",
16+
srcs = glob(["sims/**/*.vhd"]),
17+
deps = [
18+
":spi_axi_controller",
19+
"//hdl/ip/vhd/vunit_components:spi_vcs",
20+
"//hdl/ip/vhd/i2c/target:i2c_phy_consolidator",
21+
],
22+
visibility = ['PUBLIC'],
23+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
It supports Mode 0 (0,0) and mode3 (1,1) transfers. It is expected that there are fewer than 256 addressable registers, but we’re picking 16bit addressing for future extensibility without changing the protocol and to support a circular buffer for storing event history.
2+
3+
SPI instructions
4+
[cols=4,options="header"]
5+
|===
6+
|Opcode| Instruction Name | Description| Notes
7+
| 0x0 | Write byte(s) | Write one or more contiguous bytes |
8+
| 0x1| Read byte(s) | Read one or more contiguous bytes|
9+
| 0x2| Bit set | hardware does a bit-wise OR with data and current register state | new_reg = old_reg \|\| data
10+
| 0x3| Bit clr | hardware does a bit-wise clear with data and current register state |new_reg = old_reg && !data
11+
|===
12+
13+
Note that for the bit-set and bit-clear spi instructions, the AXI controller internally does a read-modify-write operation, resulting
14+
in a read of the current register state, a bitwise operation with the data, and a write back of the new register state. These are
15+
not good operations to use if the registers you're modifiying have read-side effects.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
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 data : std_logic_vector(7 downto 0) := (others => '0');
41+
variable expected_data : std_logic_vector(7 downto 0) := (others => '0');
42+
variable address : std_logic_vector(15 downto 0) := (others => '0');
43+
alias addr_h : std_logic_vector(7 downto 0) is address(15 downto 8);
44+
alias addr_l : std_logic_vector(7 downto 0) is address(7 downto 0);
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(rd_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 => 8);
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+
-- Read back written word via sim interface and check it matches
64+
data := read_word(rmemory, 0, 1);
65+
check_equal(data, expected_data, "AXI ram setup didn't work correctly");
66+
-- issue spi read command
67+
csn <= '0';
68+
-- read opcode
69+
push_stream(net, master_wstream, resize(spi_opcode_read, 8));
70+
-- addr h
71+
push_stream(net, master_wstream, addr_h);
72+
-- addr l
73+
push_stream(net, master_wstream, addr_l);
74+
-- data
75+
push_stream(net, master_wstream, x"00");
76+
wait_until_idle(net, as_sync(master_spi));
77+
csn <= '1';
78+
-- Read back word rx'd from DUT and check it matches expected.
79+
-- -- we're going to have 3 dummy bytes here, skip them, keeping the 4th
80+
for i in 0 to 3 loop
81+
pop_stream(net, master_rstream, data);
82+
end loop;
83+
check_equal(data, expected_data, "Read data did not match expected");
84+
85+
86+
-- get response back
87+
-- check against known value
88+
89+
-- single write
90+
-- single bit-set
91+
-- single bit-clear
92+
-- multi-read
93+
-- multi-write
94+
-- Note: by and large, multi-bit-set/clr doesn't really make sense over this interface
95+
-- it may still work, do we need to test it?
96+
-- invalid transaction, ok after
97+
-- early termination of transaction, ok after
98+
end if;
99+
end loop;
100+
101+
wait for 2 us;
102+
test_runner_cleanup(runner);
103+
wait;
104+
end process;
105+
106+
-- Example total test timeout dog
107+
test_runner_watchdog(runner, 10 ms);
108+
109+
end tb;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
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 2025 Oxide Computer Company
6+
7+
library ieee;
8+
use ieee.std_logic_1164.all;
9+
use ieee.numeric_std.all;
10+
11+
library vunit_lib;
12+
context vunit_lib.vunit_context;
13+
context vunit_lib.com_context;
14+
context vunit_lib.vc_context;
15+
16+
use vunit_lib.spi_pkg.all;
17+
18+
package spi_axi_tb_pkg is
19+
20+
constant rd_logger : logger_t := get_logger("axi_rd");
21+
constant rmemory : memory_t := new_memory;
22+
constant axi_read_target : axi_slave_t := new_axi_slave(address_fifo_depth => 1,
23+
memory => rmemory,
24+
logger => rd_logger);
25+
26+
constant wmemory : memory_t := new_memory;
27+
constant axi_write_target : axi_slave_t := new_axi_slave(memory => wmemory);
28+
29+
30+
constant master_spi : spi_master_t := new_spi_master;
31+
constant master_wstream : stream_master_t := as_stream(master_spi);
32+
constant master_rstream : stream_slave_t := as_stream(master_spi);
33+
34+
end package;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
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+
11+
library vunit_lib;
12+
context vunit_lib.vunit_context;
13+
context vunit_lib.com_context;
14+
context vunit_lib.vc_context;
15+
16+
use work.spi_axi_tb_pkg.all;
17+
18+
entity spi_axi_th is
19+
end entity;
20+
21+
architecture th of spi_axi_th is
22+
23+
signal clk : std_logic := '0';
24+
signal reset : std_logic := '1';
25+
26+
signal csn : std_logic := '1';
27+
signal sclk : std_logic;
28+
signal copi : std_logic;
29+
signal cipo : std_logic;
30+
signal awvalid : std_logic;
31+
signal awready : std_logic;
32+
signal awaddr : std_logic_vector(15 downto 0);
33+
signal wvalid : std_logic;
34+
signal wready : std_logic;
35+
signal wdata : std_logic_vector(7 downto 0);
36+
signal wstrb : std_logic_vector(3 downto 0);
37+
signal bvalid : std_logic;
38+
signal bready : std_logic;
39+
signal bresp : std_logic_vector(1 downto 0);
40+
signal arvalid : std_logic;
41+
signal arready : std_logic;
42+
signal araddr : std_logic_vector(15 downto 0);
43+
signal rvalid : std_logic;
44+
signal rready : std_logic;
45+
signal rdata : std_logic_vector(7 downto 0);
46+
signal rresp : std_logic_vector(1 downto 0);
47+
48+
signal arid : std_logic_vector(3 downto 0) := std_logic_vector(to_unsigned(0, 4));
49+
signal bid : std_logic_vector(3 downto 0);
50+
signal awid : std_logic_vector(3 downto 0) := std_logic_vector(to_unsigned(0, 4));
51+
signal rid : std_logic_vector(3 downto 0);
52+
53+
begin
54+
55+
-- set up a fastish clock for the sim env
56+
-- and release reset after a bit of time
57+
clk <= not clk after 4 ns;
58+
reset <= '0' after 200 ns;
59+
60+
61+
DUT: entity work.spi_axi_controller
62+
port map(
63+
clk => clk,
64+
reset => reset,
65+
csn => csn,
66+
sclk => sclk,
67+
copi => copi,
68+
cipo => cipo,
69+
awvalid => awvalid,
70+
awready => awready,
71+
awaddr => awaddr,
72+
wvalid => wvalid,
73+
wready => wready,
74+
wdata => wdata,
75+
wstrb => wstrb,
76+
bvalid => bvalid,
77+
bready => bready,
78+
bresp => bresp,
79+
arvalid => arvalid,
80+
arready => arready,
81+
araddr => araddr,
82+
rvalid => rvalid,
83+
rready => rready,
84+
rdata => rdata,
85+
rresp => rresp
86+
);
87+
88+
-- sim infrastructure from VUnit
89+
axi_read_sim_infra: entity vunit_lib.axi_read_slave
90+
generic map (
91+
axi_slave => axi_read_target
92+
)
93+
port map (
94+
aclk => clk,
95+
96+
arvalid => arvalid,
97+
arready => arready,
98+
arid => arid,
99+
araddr => araddr,
100+
arlen => "00000000",
101+
arsize => "000",
102+
arburst => "00",
103+
104+
rvalid => rvalid,
105+
rready => rready,
106+
rid => rid,
107+
rdata => rdata,
108+
rresp => rresp,
109+
rlast => open
110+
);
111+
112+
axi_write_sim_infra: entity vunit_lib.axi_write_slave
113+
generic map (
114+
axi_slave => axi_write_target
115+
)
116+
port map (
117+
aclk => clk,
118+
awvalid => awvalid,
119+
awready => awready,
120+
awid => awid,
121+
awaddr => awaddr,
122+
awlen => "00000000",
123+
awsize => "000",
124+
awburst => "00",
125+
wvalid => wvalid,
126+
wready => wready,
127+
wdata => wdata,
128+
wstrb => wstrb,
129+
wlast => '1',
130+
bvalid => bvalid,
131+
bready => bready,
132+
bid => bid,
133+
bresp => open
134+
);
135+
136+
spi_controller: entity vunit_lib.spi_master
137+
generic map(
138+
spi => master_spi
139+
)
140+
port map (
141+
sclk => sclk,
142+
mosi => copi,
143+
miso => cipo
144+
);
145+
146+
147+
end th;

0 commit comments

Comments
 (0)