OpenCores
URL https://opencores.org/ocsvn/System09/System09/trunk

Subversion Repositories System09

[/] [System09/] [trunk/] [rtl/] [VHDL/] [spi-master.vhd] - Blame information for rev 223

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 99 davidgb
--===========================================================================--
2
--                                                                           --
3
--             Synthesizable Serial Peripheral Interface Master              --
4
--                                                                           --
5
--===========================================================================--
6
--
7
--  File name      : spi-master.vhd
8
--
9
--  Entity name    : spi-master
10
--
11
--  Purpose        : Implements a SPI Master Controller
12
--                  
13
--  Dependencies   : ieee.std_logic_1164
14
--                   ieee.std_logic_unsigned
15
--
16
--  Author         : Hans Huebner
17
--
18
--  Email          : hans@huebner.org  
19
--
20
--  Web            : http://opencores.org/project,system09
21
--
22
--  Description    : This core implements a SPI master interface.  
23
--                   Transfer size is 4, 8, 12 or 16 bits.  
24
--                   The SPI clock is 0 when idle, sampled on 
25
--                   the rising edge of the SPI clock.  
26
--                   The SPI clock is derived from the bus clock input 
27
--                   divided by 2, 4, 8 or 16.
28
--
29
--                   clk, reset, cs, rw, addr, data_in, data_out and irq 
30
--                   represent the System09 bus interface. 
31
--                   spi_clk, spi_mosi, spi_miso and spi_cs_n are the 
32
--                   standard SPI signals meant to be routed off-chip.
33
--
34
--                   The SPI core provides for four register addresses 
35
--                   that the CPU can read or writen to:
36
--
37
--                   Base + $00 -> DL: Data Low LSB
38
--                   Base + $01 -> DH: Data High MSB
39
--                   Base + $02 -> CS: Command/Status
40
--                   Base + $03 -> CO: Config
41
--
42
--                   CS: Write bits:
43
--
44
--                   CS[0]   START : Start transfer
45
--                   CS[1]   END   : Deselect device after transfer 
46
--                                   (or immediately if START = '0')
47
--                   CS[2]   IRQEN : Generate IRQ at end of transfer
48
--                   CS[6:4] SPIAD : SPI device address
49
-- 
50
--                   CS: Read bits
51
--
52
--                   CS[0]   BUSY  : Currently transmitting data
53
--
54
--                   CO: Write bits
55
--
56
--                   CO[1:0] DIVIDE: SPI clock divisor, 
57
--                                   00=clk/2, 
58
--                                   01=clk/4,
59
--                                   10=clk/8,
60
--                                   11=clk/16
61
--                   CO[3:2] LENGTH: Transfer length, 
62
--                                   00= 4 bits, 
63
--                                   01= 8 bits,
64
--                                   10=12 bits,
65
--                                   11=16 bits
66
--
67
--  Copyright (C) 2009 - 2010 Hans Huebner
68
--
69
--  This program is free software: you can redistribute it and/or modify
70
--  it under the terms of the GNU General Public License as published by
71
--  the Free Software Foundation, either version 3 of the License, or
72
--  (at your option) any later version.
73
--
74
--  This program is distributed in the hope that it will be useful,
75
--  but WITHOUT ANY WARRANTY; without even the implied warranty of
76
--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
77
--  GNU General Public License for more details.
78
--
79
--  You should have received a copy of the GNU General Public License
80
--  along with this program.  If not, see <http://www.gnu.org/licenses/>.
81
--
82
--
83
--===========================================================================--
84
--                                                                           --
85
--                              Revision  History                            --
86
--                                                                           --
87
--===========================================================================--
88
--
89
-- Version  Author        Date               Description
90
--
91
-- 0.1      Hans Huebner  23 February 2009   SPI bus master for System09 
92
-- 0.2      John Kent     16 June 2010       Added GPL notice
93 223 davidgb
-- 0.3      David Burnette 8 April 2021      Added read-back of clk/transfer size
94
--                                           as well as a debug tag to confirm
95
--                                           read operation
96 99 davidgb
--
97
 
98
library ieee;
99
  use ieee.std_logic_1164.all;
100
  use ieee.std_logic_unsigned.all;
101
 
102
entity spi_master is
103
  port (
104
    --
105
    -- CPU Interface Signals
106
    --
107
    clk                : in  std_logic;
108
    reset              : in  std_logic;
109
    cs                 : in  std_logic;
110
    rw                 : in  std_logic;
111
    addr               : in  std_logic_vector(1 downto 0);
112
    data_in            : in  std_logic_vector(7 downto 0);
113
    data_out           : out std_logic_vector(7 downto 0);
114
    irq                : out std_logic;
115
    --
116
    -- SPI Interface Signals
117
    --
118
    spi_miso           : in  std_logic;
119
    spi_mosi           : out std_logic;
120
    spi_clk            : out std_logic;
121
    spi_cs_n           : out std_logic_vector(7 downto 0)
122
    );
123
end;
124
 
125
architecture rtl of spi_master is
126
 
127
  -- State type of the SPI transfer state machine
128
  type   state_type is (s_idle, s_running);
129
  signal state           : state_type;
130
  -- Shift register
131
  signal shift_reg       : std_logic_vector(15 downto 0);
132
  -- Buffer to hold data to be sent
133
  signal spi_data_buf    : std_logic_vector(15 downto 0);
134
  -- Start transmission flag
135
  signal start           : std_logic;
136
  -- Number of bits transfered
137
  signal count           : std_logic_vector(3 downto 0);
138
  -- Buffered SPI clock
139
  signal spi_clk_buf     : std_logic;
140
  -- Buffered SPI clock output
141
  signal spi_clk_out     : std_logic;
142
  -- Previous SPI clock state
143
  signal prev_spi_clk    : std_logic;
144
  -- Number of clk cycles-1 in this SPI clock period
145
  signal spi_clk_count   : std_logic_vector(2 downto 0);
146
  -- SPI clock divisor
147
  signal spi_clk_divide  : std_logic_vector(1 downto 0);
148
  -- SPI transfer length
149
  signal transfer_length : std_logic_vector(1 downto 0);
150
  -- Flag to indicate that the SPI slave should be deselected after the current
151
  -- transfer
152
  signal deselect        : std_logic;
153
  -- Flag to indicate that an IRQ should be generated at the end of a transfer
154
  signal irq_enable      : std_logic;
155
  -- Internal chip select signal, will be demultiplexed through the cs_mux
156
  signal spi_cs          : std_logic;
157
  -- Current SPI device address
158
  signal spi_addr        : std_logic_vector(2 downto 0);
159
begin
160
 
161
  -- Read CPU bus into internal registers
162
  cpu_write : process(clk, reset)
163
  begin
164
    if reset = '1' then
165
      deselect        <= '0';
166
      irq_enable      <= '0';
167
      start           <= '0';
168
      spi_clk_divide  <= "11";
169
      transfer_length <= "11";
170
      spi_data_buf    <= (others => '0');
171
    elsif falling_edge(clk) then
172
      start <= '0';
173
      if cs = '1' and rw = '0' then
174
        case addr is
175
          when "00" =>
176
            spi_data_buf(7 downto 0) <= data_in;
177
          when "01" =>
178
            spi_data_buf(15 downto 8) <= data_in;
179
          when "10" =>
180
            start      <= data_in(0);
181
            deselect   <= data_in(1);
182
            irq_enable <= data_in(2);
183
            spi_addr   <= data_in(6 downto 4);
184
          when "11" =>
185
            spi_clk_divide  <= data_in(1 downto 0);
186
            transfer_length <= data_in(3 downto 2);
187
          when others =>
188
            null;
189
        end case;
190
      end if;
191
    end if;
192
  end process;
193
 
194
  -- Provide data for the CPU to read
195
  cpu_read : process(shift_reg, addr, state, deselect, start)
196
  begin
197
    data_out <= (others => '0');
198
    case addr is
199
      when "00" =>
200
        data_out <= shift_reg(7 downto 0);
201
      when "01" =>
202
        data_out <= shift_reg(15 downto 8);
203
      when "10" =>
204
        if state = s_idle then
205
          data_out(0) <= '0';
206
        else
207
          data_out(0) <= '1';
208
        end if;
209
        data_out(1) <= deselect;
210 223 davidgb
                when "11" =>
211
                  data_out(1 downto 0) <= spi_clk_divide; -- allow read back of config
212
                  data_out(3 downto 2) <= transfer_length;
213
                  data_out(7 downto 4) <= "1010";  -- debug tag
214 99 davidgb
      when others =>
215
        null;
216
    end case;
217
  end process;
218
 
219
  spi_cs_n <= "11111110" when spi_addr = "000" and spi_cs = '1' else
220
              "11111101" when spi_addr = "001" and spi_cs = '1' else
221
              "11111011" when spi_addr = "010" and spi_cs = '1' else
222
              "11110111" when spi_addr = "011" and spi_cs = '1' else
223
              "11101111" when spi_addr = "100" and spi_cs = '1' else
224
              "11011111" when spi_addr = "101" and spi_cs = '1' else
225
              "10111111" when spi_addr = "110" and spi_cs = '1' else
226
              "01111111" when spi_addr = "111" and spi_cs = '1' else
227
              "11111111";
228
 
229
  -- SPI transfer state machine
230
  spi_proc : process(clk, reset)
231
  begin
232
    if reset = '1' then
233
      count        <= (others => '0');
234
      shift_reg    <= (others => '0');
235
      prev_spi_clk <= '0';
236
      spi_clk_out  <= '0';
237
      spi_cs       <= '0';
238
      state        <= s_idle;
239
      irq          <= 'Z';
240
    elsif falling_edge(clk) then
241
      prev_spi_clk <= spi_clk_buf;
242
      irq          <= 'Z';
243
      case state is
244
        when s_idle =>
245
          if start = '1' then
246
            count     <= (others => '0');
247
            shift_reg <= spi_data_buf;
248
            spi_cs    <= '1';
249
            state     <= s_running;
250
          elsif deselect = '1' then
251
            spi_cs <= '0';
252
          end if;
253
        when s_running =>
254
          if prev_spi_clk = '1' and spi_clk_buf = '0' then
255
            spi_clk_out <= '0';
256
            count       <= count + "0001";
257
            shift_reg   <= shift_reg(14 downto 0) & spi_miso;
258
            if ((count = "0011" and transfer_length = "00")
259
                or (count = "0111" and transfer_length = "01")
260
                or (count = "1011" and transfer_length = "10")
261
                or (count = "1111" and transfer_length = "11")) then
262
              if deselect = '1' then
263
                spi_cs <= '0';
264
              end if;
265
              if irq_enable = '1' then
266
                irq <= '1';
267
              end if;
268
              state <= s_idle;
269
            end if;
270
          elsif prev_spi_clk = '0' and spi_clk_buf = '1' then
271
            spi_clk_out <= '1';
272
          end if;
273
        when others =>
274
          null;
275
      end case;
276
    end if;
277
  end process;
278
 
279
  -- Generate SPI clock
280
  spi_clock_gen : process(clk, reset)
281
  begin
282
    if reset = '1' then
283
      spi_clk_count <= (others => '0');
284
      spi_clk_buf   <= '0';
285
    elsif falling_edge(clk) then
286
      if state = s_running then
287
        if ((spi_clk_divide = "00")
288
            or (spi_clk_divide = "01" and spi_clk_count = "001")
289
            or (spi_clk_divide = "10" and spi_clk_count = "011")
290
            or (spi_clk_divide = "11" and spi_clk_count = "111")) then
291
          spi_clk_buf <= not spi_clk_buf;
292
          spi_clk_count <= (others => '0');
293
        else
294
          spi_clk_count <= spi_clk_count + "001";
295
        end if;
296
      else
297
        spi_clk_buf <= '0';
298
      end if;
299
    end if;
300
  end process;
301
 
302
  spi_mosi_mux : process(shift_reg, transfer_length)
303
  begin
304
    case transfer_length is
305
    when "00" =>
306
      spi_mosi <= shift_reg(3);
307
    when "01" =>
308
      spi_mosi <= shift_reg(7);
309
    when "10" =>
310
      spi_mosi <= shift_reg(11);
311
    when "11" =>
312
      spi_mosi <= shift_reg(15);
313
    when others =>
314
      null;
315
    end case;
316
  end process;
317
 
318
  spi_clk  <= spi_clk_out;
319
 
320
end rtl;

powered by: WebSVN 2.1.0

© copyright 1999-2022 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.