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
+ use work.i2c_base_types_pkg.all ;
12
+ use work.pca9506_pkg.all ;
13
+
14
+ entity pca9506ish_function is
15
+ generic (
16
+ -- i2c address of the mux
17
+ i2c_addr : std_logic_vector (6 downto 0 ) := 7x"70"
18
+ );
19
+ port (
20
+ clk : in std_logic ;
21
+ reset : in std_logic ;
22
+ -- PHY interface
23
+ -- instruction interface
24
+ inst_data : in std_logic_vector (7 downto 0 );
25
+ inst_valid : in std_logic ;
26
+ inst_ready : out std_logic ;
27
+ in_ack_phase: in std_logic ;
28
+ ack_next : out std_logic ;
29
+ txn_header : in i2c_header;
30
+ start_condition : in std_logic ;
31
+ stop_condition : in std_logic ;
32
+ -- response interface
33
+ resp_data : out std_logic_vector (7 downto 0 );
34
+ resp_valid : out std_logic ;
35
+ resp_ready : in std_logic ;
36
+
37
+ -- internal register interface
38
+ read_data : in std_logic_vector (7 downto 0 );
39
+ write_data : out std_logic_vector (7 downto 0 );
40
+ read_strobe : out std_logic ;
41
+ write_strobe : out std_logic ;
42
+ cmd_ptr : out cmd_t
43
+ );
44
+ end entity ;
45
+
46
+ architecture rtl of pca9506ish_function is
47
+
48
+ type state_t is (IDLE, WAIT_FOR_ACKNACK, WAIT_FOR_ACK_PHASE, ACK, NACK, COMMAND, DO_WRITE, DO_READ);
49
+
50
+ type reg_t is record
51
+ state : state_t;
52
+ post_ack_state : state_t;
53
+ read_strobe : std_logic ;
54
+ write_strobe : std_logic ;
55
+ cmd_reg : cmd_t;
56
+ data : std_logic_vector (7 downto 0 );
57
+ data_valid: std_logic ;
58
+ increment : std_logic ;
59
+ end record ;
60
+
61
+ constant rec_reset : reg_t := (
62
+ state => IDLE,
63
+ post_ack_state => IDLE,
64
+ read_strobe => '0' ,
65
+ write_strobe => '0' ,
66
+ cmd_reg => default_reset,
67
+ data => (others => '0' ),
68
+ data_valid => '0' ,
69
+ increment => '0'
70
+ );
71
+
72
+ signal r, rin : reg_t;
73
+
74
+ begin
75
+
76
+ -- assign some outputs
77
+ read_strobe <= r.read_strobe;
78
+ write_strobe <= r.write_strobe;
79
+ cmd_ptr <= r.cmd_reg;
80
+ resp_data <= r.data;
81
+ ack_next <= '1' when r.state = ACK else '0' ;
82
+ write_data <= r.data;
83
+ inst_ready <= '1' when r.state = COMMAND or r.state = DO_WRITE else '0' ;
84
+ resp_valid <= r.data_valid;
85
+
86
+ cm : process (all )
87
+ variable v : reg_t;
88
+ begin
89
+ v := r;
90
+
91
+ case r.state is
92
+ when IDLE =>
93
+ v.increment := '0' ;
94
+ if txn_header.valid = '1' and txn_header.tgt_addr = i2c_addr then
95
+ v.state := ACK;
96
+ if txn_header.read_write_n = '0' then
97
+ -- all writes go through command state after target
98
+ v.post_ack_state := COMMAND;
99
+ else
100
+ -- post repeated start, reads bypass command state
101
+ -- and immediately do reads
102
+ v.post_ack_state := DO_READ;
103
+ -- pointer is valid
104
+ end if ;
105
+ end if ;
106
+
107
+
108
+ when COMMAND =>
109
+ if inst_valid = '1' and inst_ready = '1' then
110
+ v.cmd_reg.ai := inst_data(7 );
111
+ v.cmd_reg.pointer := inst_data(5 downto 0 );
112
+ v.post_ack_state := DO_WRITE;
113
+ v.state := ACK;
114
+ end if ;
115
+
116
+ when DO_WRITE =>
117
+ if inst_valid = '1' and inst_ready = '1' then
118
+ v.data := inst_data;
119
+ v.state := ACK;
120
+ v.write_strobe := '1' ;
121
+ v.increment := v.cmd_reg.ai;
122
+ end if ;
123
+
124
+ when DO_READ =>
125
+ v.read_strobe := '1' ;
126
+ v.data_valid := '1' ;
127
+ v.data := read_data;
128
+ v.state := WAIT_FOR_ACK_PHASE;
129
+
130
+ when ACK =>
131
+ -- clear any single-cycle strobes
132
+ v.write_strobe := '0' ;
133
+ v.read_strobe := '0' ;
134
+ -- wait for ack time to finish
135
+ if in_ack_phase = '0' then
136
+ v.state := r.post_ack_state;
137
+ if r.increment then
138
+ -- do a category-wrapping increment
139
+ v.cmd_reg.pointer := category_wrapping_increment(r.cmd_reg.pointer);
140
+ end if ;
141
+ end if ;
142
+ when NACK =>
143
+ if in_ack_phase = '0' then
144
+ v.state := IDLE;
145
+ end if ;
146
+
147
+ when WAIT_FOR_ACK_PHASE =>
148
+ -- clear any single-cycle strobes
149
+ v.write_strobe := '0' ;
150
+ v.read_strobe := '0' ;
151
+ if resp_valid = '1' and resp_ready = '1' then
152
+ v.data_valid := '0' ;
153
+ v.increment := v.cmd_reg.ai;
154
+ end if ;
155
+ if in_ack_phase = '1' then
156
+ v.state := WAIT_FOR_ACKNACK;
157
+ end if ;
158
+
159
+ when WAIT_FOR_ACKNACK =>
160
+ -- clear any single-cycle strobes
161
+ v.write_strobe := '0' ;
162
+ v.read_strobe := '0' ;
163
+ if in_ack_phase = '0' then
164
+ v.state := DO_READ;
165
+ if r.increment then
166
+ -- do a category-wrapping increment
167
+ v.cmd_reg.pointer := category_wrapping_increment(r.cmd_reg.pointer);
168
+ end if ;
169
+ end if ;
170
+
171
+ when others =>
172
+ v.state := IDLE;
173
+
174
+ end case ;
175
+
176
+ if resp_valid = '1' and resp_ready = '1' then
177
+ v.data_valid := '0' ;
178
+ end if ;
179
+
180
+ -- No matter where we were, do cleanup if we see a stop
181
+ if start_condition = '1' or stop_condition = '1' then
182
+ v.state := IDLE;
183
+ v.write_strobe := '0' ;
184
+ v.read_strobe := '0' ;
185
+ end if ;
186
+
187
+ rin <= v;
188
+ end process ;
189
+
190
+
191
+ reg : process (clk, reset)
192
+ begin
193
+ if reset = '1' then
194
+ r <= rec_reset;
195
+ elsif rising_edge (clk) then
196
+ r <= rin;
197
+ end if ;
198
+ end process ;
199
+
200
+
201
+ end rtl ;
0 commit comments