Skip to content

Commit 195fc25

Browse files
committed
Add an SPD proxy mux
1 parent 2b6ac64 commit 195fc25

File tree

9 files changed

+649
-16
lines changed

9 files changed

+649
-16
lines changed

hdl/ip/vhd/i2c/controller/link_layer/i2c_ctrl_link_layer.vhd

+17-14
Original file line numberDiff line numberDiff line change
@@ -293,21 +293,11 @@ begin
293293
v.sda_oe := '0';
294294

295295
if tx_start then
296-
v.state := WAIT_TBUF;
297-
v.counter := STO_TO_STA_BUF_TICKS;
298-
v.count_load := '1';
299-
end if;
300-
301-
-- Wait out tbuf to ensure STOP/START spacing
302-
when WAIT_TBUF =>
303-
if sm_count_done then
304-
-- tbuf is always greater than or equal to the START setup requirement, skip to
305-
-- hold
296+
-- Coming back to IDLE after a transaction means we've waited out tbuf, and tbuf
297+
-- is always greater than or equal to the START setup requirement, skip to hold
306298
v.state := START_HOLD;
307299
v.counter := START_SETUP_HOLD_TICKS;
308300
v.count_load := '1';
309-
else
310-
v.count_decr := '1';
311301
end if;
312302

313303
when WAIT_REPEAT_START =>
@@ -369,7 +359,9 @@ begin
369359
v.state := ACK_RX;
370360
v.bits_shifted := 0;
371361
elsif v.stop_requested then
372-
v.state := STOP_SDA;
362+
-- this is a valid SDA transition cycle so drive SDA low and skip STOP_SDA
363+
v.state := STOP_SCL;
364+
v.sda_oe := '1';
373365
else
374366
v.sda_oe := not sm_reg.tx_data(7);
375367
v.tx_data := sm_reg.tx_data(sm_reg.tx_data'high-1 downto sm_reg.tx_data'low) & '1';
@@ -433,7 +425,18 @@ begin
433425

434426
when STOP_SETUP =>
435427
if sm_count_done then
436-
v := SM_REG_RESET;
428+
v.state := WAIT_TBUF;
429+
v.counter := STO_TO_STA_BUF_TICKS;
430+
v.count_load := '1';
431+
v.sda_oe := '0';
432+
else
433+
v.count_decr := '1';
434+
end if;
435+
436+
-- Wait out tbuf to ensure STOP/START spacing
437+
when WAIT_TBUF =>
438+
if sm_count_done then
439+
v := SM_REG_RESET;
437440
else
438441
v.count_decr := '1';
439442
end if;

hdl/ip/vhd/i2c/controller/txn_layer/sims/i2c_ctrl_txn_layer_th.vhd

-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ library vunit_lib;
1313
context vunit_lib.com_context;
1414
context vunit_lib.vc_context;
1515

16-
use work.i2c_common_pkg.all;
1716
use work.i2c_common_pkg.all;
1817
use work.tristate_if_pkg.all;
1918
use work.stream8_pkg;

hdl/ip/vhd/vunit_components/i2c_controller/i2c_ctrlr_vc.vhd

+1-1
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ begin
120120
sda <= 'Z';
121121
wait for 60 ns; -- need to be longer than 50ns glitch timing
122122
sda <= '0';
123-
wait for thd_sta * 4;
123+
wait for thd_sta;
124124
wait until falling_edge(aligner_int);
125125
scl <= '0'; -- scl is now low, ready for bits
126126
wait for 60 ns; -- need to be longer than 50ns glitch timing

hdl/projects/cosmo_seq/spd_proxy/BUCK

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
load("//tools:hdl.bzl", "vhdl_unit", "vunit_sim")
2+
3+
vhdl_unit(
4+
name = "spd_proxy_top",
5+
srcs = glob(["*.vhd"]),
6+
deps = [
7+
"//hdl/ip/vhd/common:tristate_if_pkg",
8+
"//hdl/ip/vhd/i2c/common:i2c_common_pkg",
9+
"//hdl/ip/vhd/i2c/common:i2c_glitch_filter",
10+
"//hdl/ip/vhd/i2c/controller:i2c_ctrl_txn_layer",
11+
],
12+
standard = "2019",
13+
visibility = ['PUBLIC']
14+
)
15+
16+
vunit_sim(
17+
name = "spd_proxy_top_tb",
18+
srcs = glob(["sims/*.vhd"]),
19+
deps = [
20+
":spd_proxy_top",
21+
"//hdl/ip/vhd/vunit_components:basic_stream",
22+
"//hdl/ip/vhd/vunit_components:i2c_cmd_vc",
23+
"//hdl/ip/vhd/vunit_components:i2c_target_vc",
24+
"//hdl/ip/vhd/vunit_components:i2c_controller_vc"
25+
],
26+
visibility = ['PUBLIC'],
27+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
[*]
2+
[*] GTKWave Analyzer v3.3.104 (w)1999-2020 BSI
3+
[*] Wed Feb 19 22:29:57 2025
4+
[*]
5+
[dumpfile] "/home/aaron/Oxide/git/quartz/vunit_out/test_output/lib.spd_proxy_top_tb.cpu_with_simulated_start_009e7a6b9dbd8a7136824e0237951cc80f6be48b/nvc/spd_proxy_top_tb.fst"
6+
[dumpfile_mtime] "Wed Feb 19 22:23:27 2025"
7+
[dumpfile_size] 5095
8+
[savefile] "/home/aaron/Oxide/git/quartz/hdl/projects/cosmo_seq/spd_proxy/sims/spd_proxy_top_tb.gtkw"
9+
[timestart] 0
10+
[size] 2816 1283
11+
[pos] 1074 -22
12+
*-34.132660 19580000000 -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+
[treeopen] spd_proxy_top_tb.
14+
[treeopen] spd_proxy_top_tb.th.
15+
[treeopen] spd_proxy_top_tb.th.spd_proxy_top_inst.
16+
[treeopen] spd_proxy_top_tb.th.spd_proxy_top_inst.i2c_ctrl_txn_layer_inst.
17+
[treeopen] spd_proxy_top_tb.th.spd_proxy_top_inst.i2c_ctrl_txn_layer_inst.i2c_ctrl_link_layer_inst.
18+
[sst_width] 281
19+
[signals_width] 337
20+
[sst_expanded] 1
21+
[sst_vpaned_height] 382
22+
@200
23+
-CPU Bus
24+
@28
25+
spd_proxy_top_tb.th.i2c_controller_vc_inst.state
26+
spd_proxy_top_tb.th.cpu_scl
27+
spd_proxy_top_tb.th.cpu_sda
28+
@200
29+
-
30+
-DIMM Bus
31+
@28
32+
spd_proxy_top_tb.th.i2c_target_vc_inst.state
33+
spd_proxy_top_tb.th.dimm_scl
34+
spd_proxy_top_tb.th.dimm_sda
35+
@200
36+
-
37+
-FPGA Bus
38+
@28
39+
spd_proxy_top_tb.th.spd_proxy_top_inst.fpga_scl_if.i
40+
spd_proxy_top_tb.th.spd_proxy_top_inst.fpga_sda_if.i
41+
@200
42+
-
43+
-SPD Proxy Logic
44+
@28
45+
spd_proxy_top_tb.th.spd_proxy_top_inst.cpu_start_detected
46+
spd_proxy_top_tb.th.spd_proxy_top_inst.cpu_stop_detected
47+
spd_proxy_top_tb.th.spd_proxy_top_inst.cpu_busy
48+
spd_proxy_top_tb.th.spd_proxy_top_inst.cpu_has_mux
49+
spd_proxy_top_tb.th.spd_proxy_top_inst.ctrlr_has_int_mux
50+
@200
51+
-
52+
-Simulated START State
53+
@28
54+
spd_proxy_top_tb.th.spd_proxy_top_inst.scl_sim
55+
spd_proxy_top_tb.th.spd_proxy_top_inst.sda_sim
56+
@29
57+
spd_proxy_top_tb.th.spd_proxy_top_inst.sda_sim_fedge
58+
@28
59+
spd_proxy_top_tb.th.spd_proxy_top_inst.need_start
60+
spd_proxy_top_tb.th.spd_proxy_top_inst.start_simulated
61+
@200
62+
-
63+
-Internal I2C Controller
64+
-Transaction Layer
65+
@28
66+
spd_proxy_top_tb.th.spd_proxy_top_inst.i2c_ctrl_txn_layer_inst.abort
67+
spd_proxy_top_tb.th.spd_proxy_top_inst.i2c_ctrl_txn_layer_inst.sm_reg.do_stop
68+
spd_proxy_top_tb.th.spd_proxy_top_inst.i2c_ctrl_txn_layer_inst.sm_reg.state
69+
@200
70+
-Link Layer
71+
@28
72+
spd_proxy_top_tb.th.spd_proxy_top_inst.i2c_ctrl_txn_layer_inst.i2c_ctrl_link_layer_inst.sm_reg.stop_requested
73+
spd_proxy_top_tb.th.spd_proxy_top_inst.i2c_ctrl_txn_layer_inst.i2c_ctrl_link_layer_inst.sm_reg.state
74+
[pattern_trace] 1
75+
[pattern_trace] 0
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
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_unsigned.all;
10+
11+
library osvvm;
12+
use osvvm.RandomPkg.RandomPType;
13+
14+
library vunit_lib;
15+
context vunit_lib.com_context;
16+
context vunit_lib.vunit_context;
17+
context vunit_lib.vc_context;
18+
19+
-- VCs
20+
use work.basic_stream_pkg.all;
21+
use work.i2c_cmd_vc_pkg.all;
22+
use work.i2c_common_pkg.all;
23+
use work.i2c_ctrl_vc_pkg.all;
24+
use work.i2c_target_vc_pkg.all;
25+
26+
use work.spd_proxy_top_tb_pkg.all;
27+
28+
entity spd_proxy_top_tb is
29+
generic (
30+
runner_cfg : string
31+
);
32+
end entity;
33+
34+
architecture tb of spd_proxy_top_tb is
35+
begin
36+
37+
th: entity work.spd_proxy_top_th;
38+
39+
bench: process
40+
alias reset is << signal th.reset : std_logic >>;
41+
variable rnd : RandomPType;
42+
variable i2c_ctrlr_msg : msg_t;
43+
44+
variable command : cmd_t;
45+
variable ack : boolean := false;
46+
47+
variable data : std_logic_vector(7 downto 0);
48+
variable exp_addr : std_logic_vector(7 downto 0);
49+
variable exp_data : std_logic_vector(7 downto 0);
50+
variable byte_len : natural;
51+
variable byte_idx : natural;
52+
53+
variable cpu_tx_q : queue_t := new_queue;
54+
variable cpu_ack_q : queue_t := new_queue;
55+
variable fpga_tx_q : queue_t := new_queue;
56+
variable fpga_exp_q : queue_t := new_queue;
57+
58+
-- helper to get the internal FPGA controller doing _something_ before we have the CPU
59+
-- attempting to interrupt
60+
procedure init_controller is
61+
begin
62+
-- arbitrary for the test
63+
exp_addr := X"00";
64+
byte_len := 8;
65+
for i in 0 to byte_len - 1 loop
66+
push_byte(fpga_tx_q, rnd.RandInt(0, 255));
67+
end loop;
68+
fpga_exp_q := copy(fpga_tx_q);
69+
70+
-- write some data in
71+
command := (
72+
op => WRITE,
73+
addr => address(I2C_TGT_VC),
74+
reg => std_logic_vector(exp_addr),
75+
len => to_std_logic_vector(byte_len, command.len'length)
76+
);
77+
issue_i2c_cmd(net, command, fpga_tx_q);
78+
end procedure;
79+
begin
80+
-- Always the first thing in the process, set up things for the VUnit test runner
81+
test_runner_setup(runner, runner_cfg);
82+
-- Reach into the test harness, which generates and de-asserts reset and hold the
83+
-- test cases off until we're out of reset. This runs for every test case
84+
wait until reset = '0';
85+
wait for 500 ns; -- let the resets propagate
86+
87+
while test_suite loop
88+
if run("no_cpu_transaction") then
89+
init_controller;
90+
91+
byte_idx := to_integer(exp_addr);
92+
while not is_empty(fpga_exp_q) loop
93+
data := to_std_logic_vector(pop_byte(fpga_exp_q), data'length);
94+
exp_addr := to_std_logic_vector(byte_idx, exp_addr'length);
95+
check_written_byte(net, I2C_TGT_VC, data, exp_addr);
96+
byte_idx := byte_idx + 1;
97+
end loop;
98+
99+
expect_stop(net, I2C_TGT_VC);
100+
elsif run("cpu_transaction") then
101+
-- Get the FPGA controller started on a transaction
102+
init_controller;
103+
104+
-- At some point into the transaction, have the CPU start its own
105+
wait for rnd.RandInt(500, 4000) * 1 ns;
106+
107+
push_byte(cpu_tx_q, to_integer(rnd.RandSlv(0, 255, 8)));
108+
i2c_write_txn(net, address(I2C_TGT_VC), cpu_tx_q, cpu_ack_q, I2C_CTRL_VC.p_actor);
109+
110+
elsif run("cpu_with_simulated_start") then
111+
-- Get the FPGA controller started on a transaction
112+
init_controller;
113+
114+
-- At some point into the transaction, have the CPU start its own
115+
wait for 9500 ns;
116+
117+
push_byte(cpu_tx_q, to_integer(rnd.RandSlv(0, 255, 8)));
118+
i2c_write_txn(net, address(I2C_TGT_VC), cpu_tx_q, cpu_ack_q, I2C_CTRL_VC.p_actor);
119+
end if;
120+
end loop;
121+
122+
wait for 2 us;
123+
test_runner_cleanup(runner);
124+
wait;
125+
end process;
126+
127+
test_runner_watchdog(runner, 10 ms);
128+
129+
end architecture;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
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_unsigned.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.i2c_cmd_vc_pkg.all;
17+
use work.i2c_ctrl_vc_pkg.all;
18+
use work.i2c_target_vc_pkg.all;
19+
use work.basic_stream_pkg.all;
20+
21+
use work.i2c_common_pkg.all;
22+
23+
package spd_proxy_top_tb_pkg is
24+
-- Constants
25+
constant CLK_PER_NS : positive := 8;
26+
27+
-- Verification Components
28+
constant I2C_CTRL_VC : i2c_ctrl_vc_t := new_i2c_ctrl_vc("cpu_i2c_vc");
29+
constant I2C_TGT_VC : i2c_target_vc_t := new_i2c_target_vc("dimm_i2c_vc");
30+
constant I2C_CMD_VC : i2c_cmd_vc_t := new_i2c_cmd_vc;
31+
constant TX_DATA_SOURCE_VC : basic_source_t := new_basic_source(8);
32+
constant RX_DATA_SINK_VC : basic_sink_t := new_basic_sink(8);
33+
34+
procedure issue_i2c_cmd (
35+
signal net : inout network_t;
36+
constant command : cmd_t;
37+
constant tx_data : queue_t;
38+
);
39+
40+
end package;
41+
42+
package body spd_proxy_top_tb_pkg is
43+
44+
procedure issue_i2c_cmd (
45+
signal net : inout network_t;
46+
constant command : cmd_t;
47+
constant tx_data : queue_t;
48+
) is
49+
variable ack : boolean := FALSE;
50+
begin
51+
push_i2c_cmd(net, I2C_CMD_VC, command);
52+
start_byte_ack(net, I2C_TGT_VC, ack);
53+
check_true(ack, "Peripheral did not ACK correct address");
54+
while not is_empty(tx_data) loop
55+
push_basic_stream(net, TX_DATA_SOURCE_VC, to_std_logic_vector(pop_byte(tx_data), 8));
56+
end loop;
57+
end procedure;
58+
59+
end package body;

0 commit comments

Comments
 (0)