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

Subversion Repositories System09

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

Go to most recent revision | 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
--
94
--
95
 
96
library ieee;
97
  use ieee.std_logic_1164.all;
98
  use ieee.std_logic_unsigned.all;
99
 
100
entity spi_master is
101
  port (
102
    --
103
    -- CPU Interface Signals
104
    --
105
    clk                : in  std_logic;
106
    reset              : in  std_logic;
107
    cs                 : in  std_logic;
108
    rw                 : in  std_logic;
109
    addr               : in  std_logic_vector(1 downto 0);
110
    data_in            : in  std_logic_vector(7 downto 0);
111
    data_out           : out std_logic_vector(7 downto 0);
112
    irq                : out std_logic;
113
    --
114
    -- SPI Interface Signals
115
    --
116
    spi_miso           : in  std_logic;
117
    spi_mosi           : out std_logic;
118
    spi_clk            : out std_logic;
119
    spi_cs_n           : out std_logic_vector(7 downto 0)
120
    );
121
end;
122
 
123
architecture rtl of spi_master is
124
 
125
  -- State type of the SPI transfer state machine
126
  type   state_type is (s_idle, s_running);
127
  signal state           : state_type;
128
  -- Shift register
129
  signal shift_reg       : std_logic_vector(15 downto 0);
130
  -- Buffer to hold data to be sent
131
  signal spi_data_buf    : std_logic_vector(15 downto 0);
132
  -- Start transmission flag
133
  signal start           : std_logic;
134
  -- Number of bits transfered
135
  signal count           : std_logic_vector(3 downto 0);
136
  -- Buffered SPI clock
137
  signal spi_clk_buf     : std_logic;
138
  -- Buffered SPI clock output
139
  signal spi_clk_out     : std_logic;
140
  -- Previous SPI clock state
141
  signal prev_spi_clk    : std_logic;
142
  -- Number of clk cycles-1 in this SPI clock period
143
  signal spi_clk_count   : std_logic_vector(2 downto 0);
144
  -- SPI clock divisor
145
  signal spi_clk_divide  : std_logic_vector(1 downto 0);
146
  -- SPI transfer length
147
  signal transfer_length : std_logic_vector(1 downto 0);
148
  -- Flag to indicate that the SPI slave should be deselected after the current
149
  -- transfer
150
  signal deselect        : std_logic;
151
  -- Flag to indicate that an IRQ should be generated at the end of a transfer
152
  signal irq_enable      : std_logic;
153
  -- Internal chip select signal, will be demultiplexed through the cs_mux
154
  signal spi_cs          : std_logic;
155
  -- Current SPI device address
156
  signal spi_addr        : std_logic_vector(2 downto 0);
157
begin
158
 
159
  -- Read CPU bus into internal registers
160
  cpu_write : process(clk, reset)
161
  begin
162
    if reset = '1' then
163
      deselect        <= '0';
164
      irq_enable      <= '0';
165
      start           <= '0';
166
      spi_clk_divide  <= "11";
167
      transfer_length <= "11";
168
      spi_data_buf    <= (others => '0');
169
    elsif falling_edge(clk) then
170
      start <= '0';
171
      if cs = '1' and rw = '0' then
172
        case addr is
173
          when "00" =>
174
            spi_data_buf(7 downto 0) <= data_in;
175
          when "01" =>
176
            spi_data_buf(15 downto 8) <= data_in;
177
          when "10" =>
178
            start      <= data_in(0);
179
            deselect   <= data_in(1);
180
            irq_enable <= data_in(2);
181
            spi_addr   <= data_in(6 downto 4);
182
          when "11" =>
183
            spi_clk_divide  <= data_in(1 downto 0);
184
            transfer_length <= data_in(3 downto 2);
185
          when others =>
186
            null;
187
        end case;
188
      end if;
189
    end if;
190
  end process;
191
 
192
  -- Provide data for the CPU to read
193
  cpu_read : process(shift_reg, addr, state, deselect, start)
194
  begin
195
    data_out <= (others => '0');
196
    case addr is
197
      when "00" =>
198
        data_out <= shift_reg(7 downto 0);
199
      when "01" =>
200
        data_out <= shift_reg(15 downto 8);
201
      when "10" =>
202
        if state = s_idle then
203
          data_out(0) <= '0';
204
        else
205
          data_out(0) <= '1';
206
        end if;
207
        data_out(1) <= deselect;
208
      when others =>
209
        null;
210
    end case;
211
  end process;
212
 
213
  spi_cs_n <= "11111110" when spi_addr = "000" and spi_cs = '1' else
214
              "11111101" when spi_addr = "001" and spi_cs = '1' else
215
              "11111011" when spi_addr = "010" and spi_cs = '1' else
216
              "11110111" when spi_addr = "011" and spi_cs = '1' else
217
              "11101111" when spi_addr = "100" and spi_cs = '1' else
218
              "11011111" when spi_addr = "101" and spi_cs = '1' else
219
              "10111111" when spi_addr = "110" and spi_cs = '1' else
220
              "01111111" when spi_addr = "111" and spi_cs = '1' else
221
              "11111111";
222
 
223
  -- SPI transfer state machine
224
  spi_proc : process(clk, reset)
225
  begin
226
    if reset = '1' then
227
      count        <= (others => '0');
228
      shift_reg    <= (others => '0');
229
      prev_spi_clk <= '0';
230
      spi_clk_out  <= '0';
231
      spi_cs       <= '0';
232
      state        <= s_idle;
233
      irq          <= 'Z';
234
    elsif falling_edge(clk) then
235
      prev_spi_clk <= spi_clk_buf;
236
      irq          <= 'Z';
237
      case state is
238
        when s_idle =>
239
          if start = '1' then
240
            count     <= (others => '0');
241
            shift_reg <= spi_data_buf;
242
            spi_cs    <= '1';
243
            state     <= s_running;
244
          elsif deselect = '1' then
245
            spi_cs <= '0';
246
          end if;
247
        when s_running =>
248
          if prev_spi_clk = '1' and spi_clk_buf = '0' then
249
            spi_clk_out <= '0';
250
            count       <= count + "0001";
251
            shift_reg   <= shift_reg(14 downto 0) & spi_miso;
252
            if ((count = "0011" and transfer_length = "00")
253
                or (count = "0111" and transfer_length = "01")
254
                or (count = "1011" and transfer_length = "10")
255
                or (count = "1111" and transfer_length = "11")) then
256
              if deselect = '1' then
257
                spi_cs <= '0';
258
              end if;
259
              if irq_enable = '1' then
260
                irq <= '1';
261
              end if;
262
              state <= s_idle;
263
            end if;
264
          elsif prev_spi_clk = '0' and spi_clk_buf = '1' then
265
            spi_clk_out <= '1';
266
          end if;
267
        when others =>
268
          null;
269
      end case;
270
    end if;
271
  end process;
272
 
273
  -- Generate SPI clock
274
  spi_clock_gen : process(clk, reset)
275
  begin
276
    if reset = '1' then
277
      spi_clk_count <= (others => '0');
278
      spi_clk_buf   <= '0';
279
    elsif falling_edge(clk) then
280
      if state = s_running then
281
        if ((spi_clk_divide = "00")
282
            or (spi_clk_divide = "01" and spi_clk_count = "001")
283
            or (spi_clk_divide = "10" and spi_clk_count = "011")
284
            or (spi_clk_divide = "11" and spi_clk_count = "111")) then
285
          spi_clk_buf <= not spi_clk_buf;
286
          spi_clk_count <= (others => '0');
287
        else
288
          spi_clk_count <= spi_clk_count + "001";
289
        end if;
290
      else
291
        spi_clk_buf <= '0';
292
      end if;
293
    end if;
294
  end process;
295
 
296
  spi_mosi_mux : process(shift_reg, transfer_length)
297
  begin
298
    case transfer_length is
299
    when "00" =>
300
      spi_mosi <= shift_reg(3);
301
    when "01" =>
302
      spi_mosi <= shift_reg(7);
303
    when "10" =>
304
      spi_mosi <= shift_reg(11);
305
    when "11" =>
306
      spi_mosi <= shift_reg(15);
307
    when others =>
308
      null;
309
    end case;
310
  end process;
311
 
312
  spi_clk  <= spi_clk_out;
313
 
314
end rtl;

powered by: WebSVN 2.1.0

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