~ruther/vhdl-i2c

ref: 872331db79186465cf26769169370e381cb80ab1 vhdl-i2c/src/i2c/tx.vhd -rw-r--r-- 6.8 KiB
872331db — Rutherther feat: add clear_buffer input to tx 1 year, 3 months ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
-- i2c interface
    -- scl_pulse_i
    -- scl_stretch_o
    -- sda_o

-- control interface
    -- clk_i
    -- rst_in
    -- start_write_i

-- write interface
-- ready_o
-- valid_i
-- write_data

library ieee;
use ieee.std_logic_1164.all;

library utils;

entity tx is
  port (
    clk_i               : in  std_logic;
    rst_in              : in  std_logic;
    start_write_i       : in  std_logic;
    ss_condition_i      : in std_logic;    -- Reset rx circuitry
    clear_buffer_i      : in std_logic;

    expect_ack_i        : in std_logic;
    err_noack_o         : out std_logic;

    scl_rising_pulse_i  : in  std_logic;
    scl_falling_delayed_i : in  std_logic;

    scl_stretch_o       : out std_logic;
    sda_i               : in  std_logic;
    sda_enable_o        : out std_logic;

    ready_o             : out std_logic;
    valid_i             : in  std_logic;
    write_data_i        : in  std_logic_vector(7 downto 0));

end entity tx;

architecture a1 of tx is
  -- IDLE - not doing anything
  -- SENDING - data are in the buffer, being sent
  -- WAITING_FOR_SEND there were data written to the buffer, but cannot send now
  -- WAITING_FOR_DATA should be sending, but there are no data - stretching
  type tx_state_t is (IDLE, WAITING_FOR_FALLING_EDGE, SENDING, ACK, WAITING_FOR_DATA);
  signal curr_state : tx_state_t;
  signal next_state : tx_state_t;

  type tx_buffers is array (0 to 1) of std_logic_vector(8 downto 0);
  signal curr_tx_buffers : tx_buffers;
  signal next_tx_buffers : tx_buffers;

  -- Index to save next new data to.
  signal curr_saving_buffer_index : integer range 0 to 1;
  signal next_saving_buffer_index : integer range 0 to 1;

  signal curr_tx_buffer_index : integer range 0 to 1;
  signal next_tx_buffer_index : integer range 0 to 1;

  signal curr_tx_buffers_filled : std_logic_vector(1 downto 0);
  signal next_tx_buffers_filled : std_logic_vector(1 downto 0);

  signal tx_buffer : std_logic_vector(8 downto 0);
  signal tx_buffer_filled : std_logic;

  signal curr_scl : std_logic;
  signal next_scl : std_logic;

  signal curr_err_noack : std_logic;
  signal next_err_noack : std_logic;

  signal ready : std_logic;
begin  -- architecture a1
  scl_stretch_o <= '1' when curr_state = WAITING_FOR_DATA else '0';
  ready_o <= ready and not curr_err_noack;
  sda_enable_o <= not tx_buffer(8) when curr_state = SENDING else '0';
  err_noack_o <= curr_err_noack;

  ready <= '0' when curr_tx_buffers_filled(curr_saving_buffer_index) = '1' or curr_err_noack = '1' else '1';
  tx_buffer <= curr_tx_buffers(curr_tx_buffer_index);
  tx_buffer_filled <= curr_tx_buffers_filled(curr_tx_buffer_index);

  next_scl <= '1' when scl_rising_pulse_i = '1' else
              '0' when scl_falling_delayed_i = '1' else
              curr_scl;

  next_tx_buffer_index <= (curr_tx_buffer_index + 1) mod 2 when tx_buffer(7 downto 0) = "10000000" and scl_falling_delayed_i = '1' else
                          curr_tx_buffer_index;

  next_saving_buffer_index <= (curr_saving_buffer_index + 1) mod 2 when ready = '1' and valid_i = '1' else
                              curr_saving_buffer_index;

  next_err_noack <= '0' when expect_ack_i = '0' else
                    '1' when curr_err_noack = '1' else
                    '1' when curr_state = ACK and scl_rising_pulse_i = '1' and sda_i = '1' else
                    '0';

  set_next_tx_buffers: process(all) is
  begin  -- process set_next_tx_buffer
    next_tx_buffers <= curr_tx_buffers;
    if ready = '1' and valid_i = '1' then
      next_tx_buffers(curr_saving_buffer_index) <= write_data_i & '1';
    end if;

    if curr_state = SENDING and scl_falling_delayed_i = '1' then
      next_tx_buffers(curr_tx_buffer_index) <= tx_buffer(7 downto 0) & '0';
    end if;
  end process set_next_tx_buffers;

  set_next_buffer_filled: process(all) is
  begin  -- process set_next_buffer_filled
    next_tx_buffers_filled <= curr_tx_buffers_filled;
    if tx_buffer(7 downto 0) = "10000000" and scl_falling_delayed_i = '1' then
      next_tx_buffers_filled(curr_tx_buffer_index) <= '0';
    end if;

    if ready = '1' and valid_i = '1' then
      next_tx_buffers_filled(curr_saving_buffer_index) <= '1';
    end if;
  end process set_next_buffer_filled;

  set_next_state: process(all) is
    variable start_sending : std_logic := '0';
    variable override_tx_buffer_filled : std_logic;
  begin  -- process set_next_state
    start_sending := '0';
    next_state <= curr_state;
    override_tx_buffer_filled := tx_buffer_filled;

    if curr_state = IDLE then
      if start_write_i = '1' then
        start_sending := '1';
      end if;
    elsif curr_state = WAITING_FOR_FALLING_EDGE then
      if scl_falling_delayed_i = '1' then
        next_state <= SENDING;
      end if;
    elsif curr_state = SENDING then
      if tx_buffer(7 downto 0) = "10000000" and scl_falling_delayed_i = '1' then
        next_state <= ACK;
      end if;
    elsif curr_state = ACK then
      if scl_rising_pulse_i = '1' then
        if start_write_i = '1' then
          override_tx_buffer_filled := curr_tx_buffers_filled(next_tx_buffer_index);
          start_sending := '1';
        else
          next_state <= IDLE;
        end if;
      end if;
    elsif curr_state = WAITING_FOR_DATA then
      if valid_i = '1' then
        start_sending := '1';
      end if;
    end if;

    if start_sending = '1' then
      if override_tx_buffer_filled = '0' and valid_i = '0' then
        next_state <= WAITING_FOR_DATA;
      elsif curr_scl = '0' and scl_rising_pulse_i = '0' then
        next_state <= SENDING;
      else
        next_state <= WAITING_FOR_FALLING_EDGE;
      end if;
    end if;

    if curr_err_noack = '1' then
      next_state <= IDLE;               -- not doing anything after an error.
                                        -- Waiting for ss condition or reset
    end if;
  end process set_next_state;

  set_regs: process (clk_i) is
  begin  -- process set_next
    if rising_edge(clk_i) then          -- rising clock edge
      if rst_in = '0' or clear_buffer_i = '1' then  -- synchronous reset (active low)
        curr_state <= IDLE;
        curr_tx_buffers <= (others => (others => '0'));
        curr_tx_buffer_index <= 0;
        curr_tx_buffers_filled <= "00";
        curr_saving_buffer_index <= 0;
        curr_scl <= '1';                -- assume 1 (the default, no one transmitting)
        curr_err_noack <= '0';
      elsif ss_condition_i = '1' then
        curr_state <= IDLE;
        curr_scl <= '1';                -- assume 1 (the default, no one transmitting)
        curr_err_noack <= '0';
      else
        curr_state <= next_state;
        curr_tx_buffers <= next_tx_buffers;
        curr_tx_buffer_index <= next_tx_buffer_index;
        curr_tx_buffers_filled <= next_tx_buffers_filled;
        curr_saving_buffer_index <= next_saving_buffer_index;
        curr_scl <= next_scl;
        curr_err_noack <= next_err_noack;
      end if;
    end if;
  end process set_regs;

end architecture a1;
Do not follow this link