URL
https://opencores.org/ocsvn/tinyvliw8/tinyvliw8/trunk
Subversion Repositories tinyvliw8
[/] [tinyvliw8/] [trunk/] [src/] [vhdl/] [spiMaster.vhd] - Rev 2
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;