~ruther/vhdl-i2c

ref: d2d01a3c5b4e59284c32b402a7c1c288853c767d vhdl-i2c/src/i2c/rx.vhd -rw-r--r-- 5.9 KiB
d2d01a3c — Rutherther feat: handle ack in rx, 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
-- i2c interface
    -- scl_pulse_i
    -- scl_stretch_o
    -- sda_o

-- control interface
    -- clk_i
    -- rst_in
    -- start_read_i

-- read interface
    -- read_valid_o
    -- read_ready_o
    -- read_data_o
    -- confirm_read_i
--
library ieee;
use ieee.std_logic_1164.all;

entity rx is

  port (
    -- control part
    clk_i          : in  std_logic;     -- Clock
    rst_in         : in  std_logic;     -- Reset (asynchronous)
    start_read_i   : in  std_logic;     -- Start reading with next scl_pulse
    ss_condition_i : in  std_logic;     -- Reset rx circuitry

    scl_pulse_i    : in  std_logic;     -- SCL rising edge pulse
    scl_falling_delayed_i    : in  std_logic;     -- SCL rising edge pulse
    scl_stretch_o  : out std_logic;     -- Stretch SCL (keep SCL 0)
    sda_i          : in  std_logic;     -- SDA data line state
    sda_enable_o   : out std_logic;     -- SDA data line state

    generate_ack_i : in std_logic;

    read_valid_o   : out std_logic;     -- Are there any data on read_data_o?
    read_ready_o   : out std_logic;     -- Is it possible to read anymore, or
                                        -- does data have to be read to flush buffer?
    read_data_o    : out std_logic_vector(7 downto 0);  -- The received data
    confirm_read_i : in  std_logic);    -- Confirm that data have been read

end entity rx;

architecture a1 of rx is
  -- IDLE - not doing anythign
  -- RECEIVING - currently receiving data to the buffer
  -- ACK - going to generate acknowledge
  -- ACK_ON - sda should be enabled, to signal ack
  -- STRETCHING -
  -- read if cannot save
  -- be sending already, but cannot, since the data are not read,
  -- so stretching SCL
  type rx_state_t is (IDLE, RECEIVING, ACK, ACK_ON, STRETCHING);
  signal curr_state : rx_state_t;
  signal next_state : rx_state_t;

  -- Whether state = RECEIVING
  signal curr_receiving : std_logic;

  -- Whether data are being saved (could be just one cycle if not filled, or
  -- more if filled)
  signal curr_saving : std_logic;
  signal next_saving : std_logic;

  -- Whether the read data output is filled
  -- already (it's a register)
  signal curr_read_data_filled : std_logic;
  signal next_read_data_filled : std_logic;

  -- The received data
  signal curr_rx_buffer : std_logic_vector(7 downto 0);
  signal next_rx_buffer : std_logic_vector(7 downto 0);

  signal curr_read_data : std_logic_vector(7 downto 0);
  signal next_read_data : std_logic_vector(7 downto 0);
begin  -- architecture a1
  read_ready_o <= '1' when curr_saving = '0' or curr_read_data_filled = '0' or confirm_read_i = '1' else
                  '0';

  scl_stretch_o <= '1' when curr_state = STRETCHING else '0';

  read_data_o <= curr_read_data;

  sda_enable_o <= '1' when curr_state = ACK_ON and generate_ack_i = '1' else
                  '0';

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

    if curr_state = IDLE then
      if start_read_i = '1' then
        start_receive := '1';
      end if;
    elsif curr_state = RECEIVING then
      if curr_rx_buffer(7) = '1' and scl_pulse_i = '1' then
        next_state <= ACK;
      end if;
    elsif curr_state = ACK then
      if scl_falling_delayed_i = '1' then
        next_state <= ACK_ON;
      end if;
    elsif curr_state = ACK_ON then
      if scl_falling_delayed_i = '1' then
        if start_read_i = '1' then
          start_receive := '1';
        else
          next_state <= IDLE;
        end if;
      end if;
    elsif curr_state = STRETCHING then
      if confirm_read_i = '1' then
        next_state <= RECEIVING;
      end if;
    end if;

    if start_receive = '1' then
      if curr_read_data_filled = '0' or confirm_read_i = '1' or curr_saving = '0' then
        next_state <= RECEIVING;
      else
        next_state <= STRETCHING;
      end if;
    end if;

    if ss_condition_i = '1' then
      next_state <= IDLE;
    end if;
  end process set_next_state;

  curr_receiving <= '1' when curr_state = RECEIVING else '0';

  next_saving <= '1' when curr_rx_buffer(7) = '1' and scl_pulse_i = '1' and curr_state = RECEIVING else
                 '1' when curr_saving = '1' and curr_read_data_filled = '1' and confirm_read_i = '0' else
                 '0';

  read_valid_o <= curr_read_data_filled;

  -- TODO: (speedup by one cycle when saving?)
  next_read_data <= curr_rx_buffer when next_read_data_filled = '1' and curr_read_data_filled = '0' else
                    curr_rx_buffer when curr_read_data_filled = '1' and curr_saving = '1' and confirm_read_i = '1' else
                    curr_read_data when curr_read_data_filled = '1' else
                    (others => '0');

  next_read_data_filled <= '1' when curr_read_data_filled = '1' and confirm_read_i = '0' else
                           curr_saving when curr_read_data_filled = '1' and confirm_read_i = '1' else
                           '1' when curr_saving = '1' else
                           '0';

  next_rx_buffer <= curr_rx_buffer(6 downto 0) & sda_i when curr_receiving = '1' and scl_pulse_i = '1' else
                    curr_rx_buffer when curr_receiving = '1' else
                    curr_rx_buffer when curr_saving = '1' and confirm_read_i = '0' else
                    "00000001";

  set_regs: process (clk_i) is
  begin  -- process set_regs
    if rising_edge(clk_i) then          -- rising clock edge
      if rst_in = '0' then              -- synchronous reset (active low)
        curr_read_data <= (others => '0');
        curr_rx_buffer <= (others => '0');
        curr_read_data_filled <= '0';
        curr_state <= IDLE;
        curr_saving <= '0';
      else
        curr_read_data <= next_read_data;
        curr_rx_buffer <= next_rx_buffer;
        curr_read_data_filled <= next_read_data_filled;
        curr_state <= next_state;
        curr_saving <= next_saving;
      end if;
    end if;
  end process set_regs;

end architecture a1;
Do not follow this link