二、VHDL实现SPI协议代码

1、spi.vhd

LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.STD_LOGIC_ARITH.ALL;
USE IEEE.STD_LOGIC_UNSIGNED.ALL;
USE IEEE.STD_LOGIC_signed.ALL;
-------------------------------------------------------------------------------------------
ENTITY spi IS
	GENERIC(CPHA: INTEGER := 1;CPOL: INTEGER := 1);--模式3空闲高电平,前沿输出后沿采样
	PORT(
		--系统时钟与复位
		clk		: IN 	STD_LOGIC;--50MHZ
		rst_n	: IN 	STD_LOGIC;
		--与主机的接口
		din		: IN 	STD_LOGIC_VECTOR(7 DOWNTO 0);
		req		: IN 	STD_LOGIC;
		dout    : OUT 	STD_LOGIC_VECTOR(7 DOWNTO 0);
		dout_vld	: OUT 	STD_LOGIC;
		--SPI物理接口
		cs_n	: OUT 	STD_LOGIC;--片选信号
		sclk	: BUFFER 	STD_LOGIC;--串行时钟
		miso	: IN 	STD_LOGIC;--主机输入从机输出
		mosi	: OUT 	STD_LOGIC --主机输出从机输入
	);
END spi;
--------------------------------------------------------------------------------------------
ARCHITECTURE spi OF spi IS 
	CONSTANT SCLK_NUM: INTEGER := 16;--对系统时钟进行16分频变为3125KHZ
	CONSTANT SCLK_BEFORE: INTEGER := SCLK_NUM/4;
	CONSTANT SCLK_AFTER: INTEGER := SCLK_NUM*3/4;
	TYPE state IS(IDLE,WAIT_L,DATA,DONE);
	SIGNAL state_c,state_n: state;
	SIGNAL bit_cnt: INTEGER RANGE 0 TO 7;--bit计数器
	SIGNAL sclk_cnt: INTEGER RANGE 0 TO 15;--时钟计数器
	SIGNAL tx_data: STD_LOGIC_VECTOR(7 DOWNTO 0);
	SIGNAL rx_data: STD_LOGIC_VECTOR(7 DOWNTO 0);
	signal t,t_l,tmp:STD_LOGIC;
BEGIN 
	PROCESS(clk,rst_n)
	BEGIN
		IF(rst_n = '0') THEN
			state_c <= IDLE;
		ELSIF(clk'EVENT AND clk = '1') THEN 
			state_c <= state_n;
		END IF;
	END PROCESS;
	-------------------------------------------------------------------------------------------
	PROCESS(state_c,req,bit_cnt)
	BEGIN
		CASE state_c IS 
			WHEN IDLE =>
				IF(req = '1')THEN 
					state_n <= WAIT_L;
				END IF;
			WHEN WAIT_L => 
				state_n <= DATA;
			WHEN DATA =>
				IF(tmp = '1')THEN--() AND 
					state_n <= DONE;
				END IF;
			WHEN DONE =>
				state_n <= IDLE;
			WHEN OTHERS =>
				state_n <= IDLE;
		END CASE;
	END PROCESS;
	-----------------------------------bit_cnt ---------------------------------------------------------
	PROCESS(clk,rst_n)
	BEGIN
		IF(rst_n = '0') THEN 
			bit_cnt <= 0;
		ELSIF(clk'EVENT AND clk = '1')THEN 
			IF((state_c = DATA) AND (sclk_cnt = SCLK_NUM-1)) THEN 
				IF(bit_cnt = 7)THEN
					bit_cnt <= 0;
				ELSE
					bit_cnt <= bit_cnt + 1;
				END IF;
			ELSE 
				bit_cnt <= bit_cnt;
			END IF;
		END IF;
	END PROCESS;
	----------------------------------sclk计数器---------------------------------------------------------
	PROCESS(clk,rst_n)
	BEGIN
		IF(rst_n = '0') THEN 
			sclk_cnt <= 0;
		ELSIF(clk'EVENT AND clk = '1')THEN 
			IF(state_c = DATA) THEN
				IF(sclk_cnt = SCLK_NUM-1)THEN 
					sclk_cnt <= 0;
				ELSE 
					sclk_cnt <= sclk_cnt + 1;
				END IF;
			ELSE 
				sclk_cnt <= sclk_cnt;
			END IF;
		END IF;
	END PROCESS;
	----------------------------------16分频串行时钟 CPHA=1 CPOL=1-----------------------------------------------------------
	PROCESS(clk,rst_n)
	BEGIN 
		IF(rst_n = '0') THEN 
			IF(CPHA = 0)THEN 
				sclk <= '0';--空闲低电平
			ELSIF(CPHA = 1)THEN
				sclk <= '1';
			END IF;
		ELSIF(clk'EVENT AND clk = '1')THEN 
			IF((state_c = DATA)AND(sclk_cnt = SCLK_BEFORE-1))THEN 
				sclk <= NOT sclk;
			ELSIF((state_c = DATA)AND(sclk_cnt = SCLK_AFTER-1))THEN 
				sclk <= NOT sclk;
			END IF;
		END IF;
	END PROCESS;
	---------------------------------发送的数据mosi,高位先行------------------------------------------------------------------
	PROCESS(clk,rst_n)
	BEGIN
		IF(rst_n = '0')THEN 
			tx_data <= "00000000";
		ELSIF(clk'EVENT AND clk = '1')THEN 
			IF(CPOL = 0)THEN 
				IF((state_c = DATA)AND(sclk_cnt = SCLK_AFTER-1))THEN --后沿输出
					tx_data <= din;
				END IF;
			ELSIF(CPOL = 1)THEN 
				IF((state_c = DATA)AND(sclk_cnt = SCLK_BEFORE-1))THEN 
					tx_data <= din;
				END IF;
			END IF;
		END IF;
	END PROCESS;
	----------------------------------接收的数据miso-----------------------------------------------------------------------------
	PROCESS(clk,rst_n)
	BEGIN
		IF(rst_n = '0')THEN 
			rx_data <= "00000000";
		ELSIF(clk'EVENT AND clk = '1')THEN 
			IF(CPOL = 0)THEN 
				IF((state_c = DATA)AND(sclk_cnt = SCLK_BEFORE-1))THEN --前沿采样
					rx_data(7-bit_cnt) <= miso;
				END IF;
			ELSIF(CPOL = 1)THEN 
				IF((state_c = DATA)AND(sclk_cnt = SCLK_AFTER-1))THEN 
					rx_data(7-bit_cnt) <= miso;
				END IF;
			END IF;
		END IF;
	END PROCESS;
	----------------------------------------出bug后加的--(sclk_cnt=15)AND(bit_cnt = 7)出现状态机不跳转---------------------------------
	PROCESS(sclk_cnt)BEGIN
		IF((sclk_cnt=15))THEN 
			t<='1';
		ELSE 
			t <= '0';
		END IF;
	END PROCESS;
	PROCESS(bit_cnt)BEGIN
		IF(bit_cnt = 7)THEN 
			t_l<='1';
		ELSE 
			t_l <= '0';
		END IF;
	END PROCESS;
	tmp <= t and t_l;
------------------------------------------------------------------------------------------------------------------------------------	
	mosi <= tx_data(7-bit_cnt);
	cs_n <= NOT req;
	dout <= rx_data;
	dout_vld <= tmp;
	
END spi;
	

2、spi_tb.v

`timescale 1ns/1ps
module spi_tb();
	reg			clk;
	reg 		rst_n;
	reg [7:0]   din;
	reg         req;
	wire [7:0]  dout;
	wire        dout_vld;
	wire        cs_n;
	wire        sclk;
	reg         miso;
	wire        mosi;
	
	initial begin
		clk <= 1'b0;
		rst_n = 1'b0;
		din <= 8'd0;
		req <= 1'b0;
		miso <= 1'b0;
		#50
		rst_n = 1'b1;
		din <= 8'd8;
		req <= 1'b1;
		#20
		req <= 1'b0;
		#80000
		din <= 8'd50;
		req <= 1'b1;
		#20
		req <= 1'b0;
		#10000
		$stop;
	end 
	
	always #10 clk <= ~clk;
	always #10 miso <= {$random}%2;
	
	spi u_spi(
		.clk		(clk    ),		
		.rst_n		(rst_n	),		
				
		.din		(din	),		
		.req		(req	),		
		.dout   	(dout   ),	 
		.dout_vld	(dout_vld),		
				
		.cs_n		(cs_n	),	
		.sclk		(sclk	),	
		.miso		(miso	),		
		.mosi		(mosi	)	
		);
		
endmodule

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值