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

Subversion Repositories tinyvliw8

[/] [tinyvliw8/] [trunk/] [src/] [vhdl/] [spiMaster.vhd] - Rev 5

Go to most recent revision | Compare with Previous | Blame | View Log

--
-- SPI master module for 32 bit processor		kth @ IHP Dec. 2006
--
-- This VHDL ENTITY implements an SPI interface master core to be connected 
-- to a microprocessor. It occupies 4 registers of 32 bit width in the address 
-- space for the following functions:
--
--	addr 0 write:	write data to SPI interface and read at the same time
--	addr 0 read:	get last received SPI data word
--	addr 1 write:	set number of data bits in SPI word (bit 4:0)
--	addr 1 read:	bit 4-0: number of data bits in SPI word -1
--	addr 2 write:	set clock prescaler max. value (bit 3:0)
--	addr 2 read:	bit 4-0: clock prescaler maximum value
--	addr 3 write:	bit 0: SCK clock mode selector (see below)
--	               bit 1: SPI shift+strobe edge selector (see below)
--	               bit 2: SS active level selector (see below)
--	addr 3 read:	bit 0: SCK clock mode selector (see below)
--	               bit 1: SPI shift+strobe edge selector (see below)
--	               bit 2: SS active level selector (see below)
--	               bit 13-8: current clock prescaler (counter) value
--	               bit 22-16: current state (shift) counter 
--	               bit 31: operation completed, high active
--
-- The output port "intr" is equivalent to status bit 31 and can be used for 
-- an interrupt to the processor on completion of a data transfer. 
-- The bit is cleared when a new transfer starts. 
-- The core averages all input (MISO) bits before shifting them into the input 
-- data register. For this purpose the design countains an accumulator, which 
-- is cleared on every "shifting" edge of the SPI clock SCK. Then, it strobes 
-- the MISO input on every rising edge og teh primary clock. Finally, the 
-- accumulated sum is compared with the clock prescaler value to make a 
-- majority decision whether the bit is high or low. 
--
-- Configurable parameters of SPI master core:
--
--   number of data bits in SPI word (addr 1, bit 4-0, default value = 7): 
--	The parameter gives the number of data bits - 1, which will be sent 
--	and received when writing a word to address 0. The parameter's range 
--	is 0 - 31, which corresponds to 1 - 32 bit. 
--   clock prescaler max. value (addr 2, bit 3-0, default value = 3): 
--	The parameter gives the max. count value of a prescaler to generate 
--	the SPI clock signal SCK from input clock. The prescaler completely 
--	expires in every half SCK cycle (high / low). The parameter's range 
--	is 1(!) - 15, which corresponds to a divider ratio of 1:4 - 1:32. 
--   SCK clock mode selector (addr 3, bit 0, default value = low): 
--	low : SCK is low  in idle phases, first edge is a rising  edge
--	high: SCK is high in idle phases, first edge is a falling edge
--   SPI shift+strobe edge selector (addr 3, bit 1, default value = low): 
--	low : first  (and all odd)  SCK edges strobe the MOSI and MISO values
--	      second (and all even) shift the data word to the next bit
--	high: first  (and all odd)  shift the data word to the next bit
--	      second (and all even) SCK edges strobe the MOSI and MISO values
--   SS active level selector (addr 3, bit 2, default value = low): 
--	low : SS (slave select) output is low active (high in idle phases)
--	high: SS (slave select) output is high active (low in idle phases)
 
LIBRARY ieee;
USE ieee.std_logic_1164.all;
USE	ieee.numeric_std.all;
 
ENTITY SPImaster IS
  PORT (
   	inclk : IN  STD_LOGIC;	-- system clock
	rst_n : IN  STD_LOGIC;	-- synchr. system reset, high active
 
-- processor interface
	we_n  : IN  STD_LOGIC;	-- write enable, low active
	re_n  : IN  STD_LOGIC;	-- read  enable, low active
	addr  : IN  STD_LOGIC_VECTOR(1 DOWNTO 0); -- address from processor
	din   : IN  STD_LOGIC_VECTOR(7 DOWNTO 0);-- data from processor
	dout  : OUT STD_LOGIC_VECTOR(7 DOWNTO 0);-- async. data to processor
	intr  : OUT STD_LOGIC;	-- interrupt to processor, high active
	intra : IN STD_LOGIC;	-- interrupt acknowledge
 
-- SPI interface
	SCK  : OUT STD_LOGIC;	-- SPI clock
	SS   : OUT STD_LOGIC;	-- SPI slave select, active level configurable
	MOSI : OUT STD_LOGIC;	-- SPI master output, slave input
	MISO : IN  STD_LOGIC  	-- SPI master input, slave output
  );
END SPImaster;
 
 
ARCHITECTURE behav OF SPImaster IS
 
  SIGNAL busy_s  : STD_LOGIC;	         -- transfer active
  SIGNAL irqEn   : STD_LOGIC;	         -- interrupt enable
  SIGNAL enable  : STD_LOGIC;	         -- enable spi
  SIGNAL clkmode : STD_LOGIC;	         -- low: SCK starts with rising edge
  SIGNAL bitmode : STD_LOGIC;	         -- low: strobe at 1st SCK edge, shift at 2nd edge
  SIGNAL sslevel : STD_LOGIC;	         -- active level of SS output signal
  signal ssmode  : std_logic;            -- automatic SS output signal
  SIGNAL prsc    : UNSIGNED(4 DOWNTO 0); -- clock prescaler counter
  SIGNAL bits    : UNSIGNED(2 DOWNTO 0); -- number of bits in SPI word
  SIGNAL words   : unsigned(4 downto 0); -- number of SPI words in single transfer
 
  SIGNAL clkcnt : UNSIGNED(prsc'LENGTH-1 DOWNTO 0);-- clock counter within one bit
  SIGNAL bitcnt : UNSIGNED(bits'LENGTH+1 DOWNTO 0);-- state machine / bit counter
  SIGNAL data : STD_LOGIC_VECTOR(7 DOWNTO 0);	-- internal data shift register
  SIGNAL accu : STD_LOGIC_VECTOR(7 DOWNTO 0);   -- accumulator to MISO values
 
  SIGNAL bitcntmax : UNSIGNED(bitcnt'LENGTH-1 DOWNTO 0);-- tmp. variable
 
  signal fin_s  : std_logic;
  signal cs_s   : std_logic;
  signal mosi_s : std_logic;
 
  SIGNAL fin_clk : std_logic;
  SIGNAL clk : std_logic;
  SIGNAL sclk_s : std_logic;
 
BEGIN
 
	bitcntmax <= Shift_Left(RESIZE(bits, bitcntmax'LENGTH), 1) + 4;
 
	clk <= not(inclk) when enable = '1' and rst_n = '1' else
	       '0';
 
  PROCESS (rst_n, we_n)			-- SPI config. registers
    BEGIN
	IF rst_n = '0' THEN
	  clkmode <= '0';
	  bitmode <= '0';
	  sslevel <= '0';
	  ssmode  <= '0';
	  enable  <= '0';
	  irqEn   <= '0';
	  prsc    <= "00011";
	  bits    <= "111";
	  words   <= "00001";
	  data    <= (others => '0');
	ELSIF (we_n'event and we_n = '0') THEN
		if (re_n = '0') then
			if (addr = "00" and bitcnt = bitcntmax and clkcnt = prsc) then
				data <= din;
				if (words /= "00000") then
					words <= words - 1;
				end if;
			else
				CASE addr IS
				WHEN "11" => clkmode <= din(0);
	                      bitmode <= din(1);
	                      sslevel <= din(2);
				          ssmode  <= din(3);
	                      irqEn   <= din(6);
	                      enable  <= din(7);
				WHEN "10" => prsc <= UNSIGNED(din(4 DOWNTO 0));
				WHEN "01" => words <= UNSIGNED(din(7 DOWNTO 3));
			                 bits <= UNSIGNED(din(2 DOWNTO 0));
				WHEN OTHERS => NULL;
				END CASE;
			end if;
	  	end if;
	END IF;
  END PROCESS;
 
	fin_clk <= '1' when bitcnt = 0 and clkcnt = 0 else
	           '1' when re_n = '0' and we_n = '0' and addr = "00" else
	           '0';
 
	process (rst_n, fin_clk)
	begin
		if (rst_n = '0') then
			fin_s <= '1';
		else
			if (fin_clk'event and fin_clk = '1') then
				if (re_n = '0' and we_n = '0' and addr = "00") then
					fin_s <= '0';
				else
					fin_s <= '1';
				end if;
			end if;
		end if;
	end process;
 
	cnt_p : process (rst_n, clk)
	begin
		if (rst_n = '0') then
			bitcnt <= (others => '1');
			clkcnt <= (others => '1');
			-- bitcnt <= bitcntmax;
			-- clkcnt <= prsc;
		else
			IF clk'EVENT AND clk='0' THEN
				if (fin_s = '0') then
					IF clkcnt /= 0 THEN
						clkcnt <= clkcnt - 1;
					ELSIF bitcnt /= 0 THEN
						bitcnt <= bitcnt - 1;
					end if;
				else
					bitcnt <= bitcntmax;
					clkcnt <= prsc;
				end if;
			end if;
		end if;
	end process;
 
	busy_flag_p : PROCESS (rst_n, clk)			-- signals generated from state machine
	BEGIN
		IF rst_n = '0' THEN
			busy_s <= '0';
		ELSIF clk'EVENT AND clk = '1' THEN
			if (fin_s = '0') then
				IF clkcnt = 0 and bitcnt = 1 THEN
					busy_s <= '0';
				else
					busy_s <= '1';
				END IF;
			else
				busy_s <= '0';
			END IF;
		END IF;
	END PROCESS;
 
	cs_gen_p : process (rst_n, busy_s)
	begin
		if (rst_n = '0') then
			cs_s <= '1';
		else
			if (busy_s'event and busy_s = '1') then
				if (words = "00000") then
					cs_s <= '1';
				else
					cs_s <= '0';
				end if;
			end if;
		end if;
	end process;
 
	SS <= 'Z'          when enable = '0' else
	      not(sslevel) when ssmode = '1' and (cs_s = '1' and busy_s = '0') else
	      sslevel;
 
	PROCESS (rst_n, clk)
	BEGIN
		if (rst_n = '0') then
			sclk_s <= '0';
			accu   <= (others => '0');
			mosi_s   <= '0';
		ELSIF clk'EVENT AND clk='1' THEN
			IF clkcnt /= 0 THEN
				NULL;
			ELSIF bitcnt > 1 AND bitcnt < bitcntmax THEN
				sclk_s <= clkmode XOR bitcnt(0);
 
				if ((bitmode = '0' and sclk_s = not(clkmode)) or (bitmode = '1' and sclk_s = clkmode)) then
					if (bitmode = '0') then
						mosi_s <= accu(To_Integer(bits));
					else
						mosi_s <= accu(To_Integer(bits));
					end if;
				else
					accu <= accu(6 downto 0) & MISO;
				end if;
			ELSE
				sclk_s <= clkmode;
 
				if (fin_s = '0' and bitcnt = bitcntmax) then
					accu <= data;
					mosi_s <= data(To_Integer(bits));
				end if;
			END IF;
		END IF;
	END PROCESS;
 
	MOSI <= mosi_s when enable = '1' else
	        'Z';
	SCK  <= sclk_s when enable = '1' else
	        'Z';
 
	irq_gen : process(enable, irqEn, clk)
	begin
		IF (enable = '0' and irqEn = '0') THEN
			 intr <= '0';
		ELSE
			if (clk'EVENT AND clk = '0') THEN
				if (intra = '1') then
					intr <= '0';
				else
					if fin_s = '1' and bitcnt = 0 and clkcnt = 0 then
						intr <= '1';
					end if;
				end if;
			end if;
		end if;
	end process;
 
	WITH addr SELECT dout <= 
		accu                                                               WHEN "00",
		std_logic_vector(words) & STD_LOGIC_VECTOR(bits)                   WHEN "01",
		STD_LOGIC_VECTOR(RESIZE(prsc,dout'LENGTH))                         WHEN "10",
		enable & irqEn & '0' & busy_s & ssmode & sslevel & bitmode & clkmode WHEN OTHERS;
 
END behav;
 

Go to most recent revision | Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

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