学习笔记之FPGA的SPI通讯

      FPGA由于 FPGA 是基于 SRAM 结构的,程序掉电后会丢失,所以需要一个外置 Flash 保存程序, FPGA 每次上电后去读取 Flash 中的配置程序,在 ALINX 开发板中,很多使用的是 SPI 接口的 nor flash,这种 flash 只需要 4 根 IO。FPGA 的配置 flash 是特殊的 IO,上电时工作,FPGA 要使用这些 IO 来读取 Flash,读取完成后释放这些 IO 交给用户使用。

1. SPI的基本知识

        SPI(Serial Peripheral Interface)是一种同步串行通信协议,通常用于在嵌入式系统中连接微控制器和外部设备。它允许微控制器与多个外设同时进行全双工通信,实现高速数据传输。SPI协议通常用于连接存储器芯片、传感器、显示屏、通信模块和其他外设。 

         SPI协议的基本工作原理如下:微控制器通过主设备选择(SS)引脚选择要与之通信的外设,并通过时钟信号(SCLK)同步数据传输。它还使用主输出主输入(MOSI)和主输入主输出(MISO)引脚进行双向数据传输。在传输过程中,主设备将数据位传输到外设的输入引脚,同时从外设的输出引脚读取数据位。

        SPI协议具有以下特点:

  • 简单灵活:SPI协议使用少量的引脚和简单的硬件电路,易于实现和调试。

  • 高速通信:由于采用同步传输方式,SPI协议可以实现高速数据传输,适用于实时应用。

  • 可靠性好:SPI协议具有错误检测和纠正机制,可以提供可靠的数据传输。

  • 多设备支持:SPI协议使用主从结构,一个主设备可以连接多个从设备,使得系统具备良好的扩展性。

        SPI协议的配置通常由以下参数确定:

  • 时钟极性(CPOL):确定时钟信号的空闲状态和活动状态。

  • 时钟相位(CPHA):确定数据采样的时机。

  • 数据位顺序:确定数据位的传输顺序,可以是最高位先传输(MSB-first)或最低位先传输(LSB-first)。

        在实际应用中,SPI协议根据不同的硬件平台和设备需求进行配置。通过正确配置参数,微控制器可以与外设进行可靠的通信和数据交换。

2. ADC读写实验

2.1 ADC128S022产品信息

        ADC128S022器件是一种低功耗、8通道CMOS 12位模数转换器,其转换吞吐量为50 ksps到200 ksps。该转换器是基于一个逐次逼近寄存器结构与一个内部跟踪和保持电路。它可以配置为在输入IN0到IN7接受多达8个输入信号。串行数据的输出是直接二进制,并兼容多种标准,如SPI, QSPI, MICROWIRE和许多常见的DSP串行接口。 ADC128S022可使用独立的模拟和数字电源。模拟电源(VA)的电压范围是2.7 V到5.25 V,数字电源(VD)的电压范围是2.7 V到V_{_{^{}}}A。使用3v或5v电源的正常功耗分别为1.2 mW和7.5 mW。当使用3v电源时,功耗为0.06µW;当使用5v电源时,功耗为0.25µW确保在-40°C至+105°C的扩展工业温度范围内运行。

 2.2 ADC128S022的时序图

        CS:片选信号,低电平表示选中。

        SCLK:输入时钟。

        DIN:串行输入,SCLK的上升沿进行采集。

        DOUT:转换输出,SCLK的下降沿进行数据输出。

2.3 实验代码

module SPI(
	input clk,
	input rst_n,
	input start,
	input [2:0]channel,
	
	//========ADC128S022===========//
	output reg SCLK,
	output reg DIN,
	output reg CS_N,
	input DOUT,
	
	output reg done,
	output reg [11:0]data
);

	reg en;
	reg [2:0]r_channel;
	reg [4:0]cnt;
	reg cnt_flag;
	reg [5:0]SCLK_CNT;
	reg [11:0]r_data;
	
//==============写入通道===================//  
	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)
			r_channel <= 'd0;
		else if(start)
			r_channel <= channel;
		else
			r_channel <= r_channel;
	end
	
//============转换使能信号==================//
	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)
			en <= 1'b0;
		else if(start)
			en <= 1'b1;
		else if(done)
			en <= 1'b0;
		else
			en <= en;
	end
	
//==================cnt=====================//
	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)
			cnt <= 'd0;
		else if(en)begin
			if(cnt == 'd10)
				cnt <= 'd0;
			else
				cnt <= cnt + 1'b1;
		end
	else
		cnt <= 'd0;
	end
	
//===================cnt_flag===============//
	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)
			cnt_flag <= 1'b0;
		else if(cnt == 'd10)
			cnt_flag <= 1'b1;
		else
			cnt_flag <= 1'b0;
	end

//===============SCLK_CNT===================//
	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)
			SCLK_CNT <= 'd0;
		else if(en)begin
			if(cnt_flag && (SCLK_CNT == 'd33))
				SCLK_CNT <= 'd0;
			else if(cnt_flag)
				SCLK_CNT <= SCLK_CNT + 1'b1;
			else
				SCLK_CNT <= SCLK_CNT;
		end
		else
			SCLK_CNT <= 'd0;
	end

//===========================================//
	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)begin
			SCLK <= 1'b1;
			CS_N <= 1'b1;
			DIN <= 1'b1;
		end
		else if(en)begin
			if(cnt_flag)begin
				case(SCLK_CNT)
					6'd0:begin CS_N <= 1'b0;end
					6'd1:begin SCLK <= 1'b0;DIN <= 1'b0;end
					6'd2:begin SCLK <= 1'b1;end
					6'd3:begin SCLK <= 1'b0;end
					6'd4:begin SCLK <= 1'b1;end
//写入通道
					6'd5:begin SCLK <= 1'b0;DIN <= r_channel[2];end
					6'd6:begin SCLK <= 1'b1;end
					6'd7:begin SCLK <= 1'b0;DIN <= r_channel[1];end
					6'd8:begin SCLK <= 1'b1;end
					6'd9:begin SCLK <= 1'b0;DIN <= r_channel[0];end        
//写入数据,在上升沿使数据逐次左移					6'd10,6'd12,6'd14,6'd16,6'd18,6'd20,6'd22,6'd24,6'd26,6'd28,6'd30,6'd32:
						begin SCLK <= 1'b1;r_data <= {r_data[10:0],DOUT};end
					6'd11,6'd13,6'd15,6'd17,6'd19,6'd21,6'd23,6'd25,6'd27,6'd29,6'd31:
						begin SCLK <= 1'b0;end
//传输结束,结束选中
					6'd33:begin CS_N <= 1'b1;end
					default:begin CS_N <= 1'b1;end
				endcase
			end
			else ;
		end
		else begin
			SCLK <= 1'b1;
			CS_N <= 1'b1;
			DIN <= 1'b1;
		end
	end
	
//================done=================//
	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)
			done <= 1'b0;
		else if(cnt_flag && (SCLK_CNT == 'd33))
			done <= 1'b1;
		else
			done <= 1'b0;
	end
	
//================data=================//
	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)
			data <= 'd0;
		else if(cnt_flag && (SCLK_CNT == 'd33))
			data <= r_data;
		else
			data <= data;
	end

endmodule

        注:这部分代码参考自b站FPGA小学生。 

2.4 测试文件

`timescale 1ns/1ns

module SPI_tb;

	reg clk;
	reg rst_n;
	reg start;
	reg [2:0]channel;
	
	wire SCLK;
	wire DIN;
	wire CS_N;
	reg DOUT;
	
	wire done;
	wire [11:0]data;

	reg i = 0;
	
	initial clk = 1'b1;
	always#10 clk = ~clk;
	
	SPI SPI_inst(
		.clk(clk),
		.rst_n(rst_n),
		.start(start),
		.channel(channel),
	
	//========ADC128S022===========//
		.SCLK(SCLK),
		.DIN(DIN),
		.CS_N(CS_N),
		.DOUT(DOUT),
	
		.done(done),
		.data(data)
);
	
	initial begin
		rst_n = 1'b0;
		channel = 'd0;
		start = 1'b0;
		DOUT = 1'b0;
		#100;
		rst_n = 1'b1;
		#100;
		channel = 3;
		for(i=0;i<3;i=i+1)begin
				start = 1;
				#20;
				start = 0;
				gene_DOUT(12'b0101_1100_1010);	//依次将存储器中存储的波形读出,按照ADC的转换结果输出方式送到DOUT信号线上
				@(posedge done);	//等待转换完成信号
				#200;
		end
		#20000;
		$stop;
	end	
				
	
	
	//将并行数据按照ADC的数据输出格式,送到DOUT信号线上,供控制模块采集读取
	task gene_DOUT;
		input [15:0]vdata;
		reg [4:0]cnt;
		begin
			cnt = 0;
			wait(!CS_N);
			while(cnt<16)begin
				@(negedge SCLK) DOUT = vdata[15-cnt];
				cnt = cnt + 1'b1;
			end
		end
	endtask

endmodule 

 

3. FLASH读写实验

3.1 FLASH的基本介绍

        FLASH闪存是一种非易失性存储器,常用于存储数据并替代传统的磁盘驱动器。它具有许多优点,例如速度快、耐用性高和体积小。其具有内部时钟,可确保数据在正确的时间段写入和读取,同时提供更高的数据传输速度和准确性,还有助于协调闪存与其他设备之间的数据传输。对其主要操作包括擦除、写入和读取。因为闪存内部的存储单元被分为多个块,每个块都可以独立操作,使得闪存在处理大量数据时表现出色。

图 FLASH的连接方式 

3.2 FLASH的基本指令

 

注:其他有关SPI和FLASH的基础内容请移步至前章的STM32通信总结。 

3.3 实验代码

3.3.1 SPI驱动代码

        值得注意的是这里需要区分delay_cnt、bit_cnt和clk_cnt,具体如下图所示。

         delay_cnt和clk_cnt均为1位,delay_cnt更多是作为延时开始的标志,而bit_clk会在clk_cnt高电平时加一,进而切换输出的位号。根据FLASH定义的时序,上电后会需要一段时间的延时等待,接着是8位的命令、24位的地址以及后续的数据,这也说明了在32位后才真正开始进行数据的传输。

module spi_drive(

	input             clk_100m      ,
	input             sys_rst_n     ,
	
	//user interface
	input             spi_start     ,//spi开启使能。
	input [7:0 ]      spi_cmd       ,//FLAH操作指令
	input [23:0]      spi_addr      ,//FLASH地址
	input [7:0 ]      spi_data      ,//FLASH写入的数据
	input [3:0 ]      cmd_cnt       ,
	
	output            idel_flag_r   ,//空闲状态标志的上升沿 
	output reg        w_data_req    ,//FLASH写数据请求 
	output reg [7:0]  r_data        ,//FLASH读出的数据
	output reg        erro_flag     ,//读出的数据错误标志
	
	//spi interface
	output reg        spi_cs        ,//SPI从机的片选信号,低电平有效。
	output reg        spi_clk       ,//主从机之间的数据同步时钟。
	output reg        spi_mosi      ,//数据引脚,主机输出,从机输入。
	input             spi_miso       //数据引脚,主机输入,从机输出。

);

//状态机
parameter IDLE         =4'd0;//空闲状态
parameter WEL          =4'd1;//写使能状态
parameter S_ERA        =4'd2;//扇区擦除状态
parameter C_ERA        =4'd3;//全局擦除
parameter READ         =4'd4;//读状态
parameter WRITE        =4'd5;//写状态
parameter R_STA_REG    =4'd6;

//指令集
parameter WEL_CMD      =8'h06;
parameter S_ERA_CMD    =8'h20;
parameter C_ERA_CMD    =8'hc7;
parameter READ_CMD     =8'h03;
parameter WRITE_CMD    =8'h02;
parameter R_STA_REG_CMD=8'h05;

//wire define
wire      idel_flag;

//reg define
reg[3:0]  current_state  ;
reg[3:0]  next_state     ;
reg[7:0 ] data_buffer    ;
reg[7:0 ] cmd_buffer     ;
reg[7:0 ] sta_reg        ;
reg[23:0] addr_buffer    ;
reg[31:0] bit_cnt        ;
reg       clk_cnt        ;
reg       dely_cnt       ;
reg[31:0] dely_state_cnt ;
reg[7:0 ] rd_data_buffer ;
reg       spi_clk0       ;
reg       stdone         ;
reg[7:0 ] data_check     ;
reg       idel_flag0     ;
reg       idel_flag1     ;

//*****************************************************
//**                    main code
//*****************************************************

assign idel_flag=(current_state==IDLE)?1:0;//空闲状态标志
assign idel_flag_r=idel_flag0&&(~idel_flag1);//空闲状态标志的上升沿

always @(posedge clk_100m or negedge sys_rst_n )begin
	if(!sys_rst_n)begin
		idel_flag0<=1'b1;
		idel_flag1<=1'b1;
	end
	else begin
		idel_flag0<=idel_flag;
		idel_flag1<=idel_flag0;
	end
end

always @(posedge clk_100m or negedge sys_rst_n )begin
	if(!sys_rst_n)
		w_data_req<=1'b0;
	else if((bit_cnt+2)%8==0&&bit_cnt>=30&&clk_cnt==0&&current_state==WRITE)
		w_data_req<=1'b1;
	else
		w_data_req<=1'b0;
end

always @(posedge clk_100m or negedge sys_rst_n )begin//读出的数据移位寄存
	if(!sys_rst_n)
		rd_data_buffer<=8'd0;
	else if(bit_cnt>=32&&bit_cnt<=2080&&clk_cnt==0&&current_state==READ)									
		rd_data_buffer<={rd_data_buffer[6:0],spi_miso};
	else
		rd_data_buffer<=rd_data_buffer;
end

always @(posedge clk_100m or negedge sys_rst_n )begin//检查读出的数据是否正确
	if(!sys_rst_n)
		data_check<=8'd0;
	else if(bit_cnt%8==0&&bit_cnt>=40&&clk_cnt==1&&current_state==READ)
		data_check<=data_check+1'd1;
	else
		data_check<=data_check;
end

always @(posedge clk_100m or negedge sys_rst_n )begin//读出的数据
	if(!sys_rst_n)
		r_data<=8'd0;
	else if(bit_cnt%8==0&&bit_cnt>38&&clk_cnt==1&&current_state==READ)
		r_data<=rd_data_buffer;
	else
		r_data<=r_data;
end

always @(posedge clk_100m or negedge sys_rst_n )begin//读出的数据错误标志
	if(!sys_rst_n)
		erro_flag<=1'd0;
	else if(bit_cnt>32&&bit_cnt<=2080&&current_state==READ&&cmd_cnt==6)begin
		if(data_check!=r_data)
			erro_flag<=1'd1;
		else
			erro_flag<=erro_flag;
		end
	else
		erro_flag<=erro_flag;
end
	
always @(posedge clk_100m or negedge sys_rst_n )begin
	if(!sys_rst_n)
		data_buffer<=8'd0;
	else if((bit_cnt+1)%8==0&&bit_cnt>30&&clk_cnt==1)
		data_buffer<=spi_data;
	else if(clk_cnt==1&&current_state==WRITE&&bit_cnt>=32)
		data_buffer<={data_buffer[6:0],data_buffer[7]};
	else
		data_buffer<=data_buffer;
end

always @(posedge clk_100m or negedge sys_rst_n )begin
	if(!sys_rst_n)
		cmd_buffer<=8'd0;
	else if(spi_cs==0&&dely_cnt==0)
		cmd_buffer<=spi_cmd;
	else if(clk_cnt==1&&(current_state==WEL||current_state==S_ERA||current_state==C_ERA
	       ||current_state==READ||current_state==WRITE||current_state==R_STA_REG)&&bit_cnt<8)
		cmd_buffer<={cmd_buffer[6:0],1'b1};
	else
		cmd_buffer<=cmd_buffer;
end

always @(posedge clk_100m or negedge sys_rst_n )begin
	if(!sys_rst_n)
		addr_buffer<=8'd0;
	else if(spi_cs==0&&dely_cnt==0)
		addr_buffer<=spi_addr;
	else if(clk_cnt==1&&(current_state==READ||current_state==WRITE)&&bit_cnt>=8&&bit_cnt<32)
		addr_buffer<={addr_buffer[22:0],addr_buffer[23]};
	else
		addr_buffer<=addr_buffer;
end

always @(posedge clk_100m or negedge sys_rst_n )begin
	if(!sys_rst_n)
		clk_cnt<=1'd0;
	else if(dely_cnt==1)
		clk_cnt<=clk_cnt+1'd1;
	else 
		clk_cnt<=1'd0;
end

always @(posedge clk_100m or negedge sys_rst_n )begin
	if(!sys_rst_n)
		dely_cnt<=1'd0;
	else if(spi_cs==0)begin
	    if(dely_cnt<1)
			dely_cnt<=dely_cnt+1'd1;
		else
			dely_cnt<=dely_cnt;
	end
	else
		dely_cnt<=1'd0;
end

always @(posedge clk_100m or negedge sys_rst_n )begin
	if(!sys_rst_n)
		dely_state_cnt<=1'd0;
	else if(spi_cs)begin
	    if(dely_state_cnt<400000000)
			dely_state_cnt<=dely_state_cnt+1'd1;
		else
			dely_state_cnt<=dely_state_cnt;
	end
	else
		dely_state_cnt<=1'd0;
end

always @(posedge clk_100m or negedge sys_rst_n )begin
	if(!sys_rst_n)
		bit_cnt<=11'd0;
	else if(dely_cnt==1)begin
			if(clk_cnt==1'b1)
				bit_cnt<=bit_cnt+1'd1;
			else
				bit_cnt<=bit_cnt;
	end
	else
		bit_cnt<=11'd0;
end
		
//三段式状态机
always @(posedge clk_100m or negedge sys_rst_n )begin
	if(!sys_rst_n)
		current_state<=IDLE;
	else
		current_state<=next_state;
end

always @(*)begin

	case(current_state)
	
	   IDLE: begin
	          if(spi_start&&spi_cmd==WEL_CMD)
				next_state=WEL;
			  else if(spi_start&&spi_cmd==C_ERA_CMD)
				next_state=C_ERA;
			  else if(spi_start&&spi_cmd==S_ERA_CMD)
				next_state=S_ERA;
			  else if(spi_start&&spi_cmd==READ_CMD)
				next_state=READ;
			  else if(spi_start&&spi_cmd==WRITE_CMD)
				next_state=WRITE;
			  else if(spi_start&&spi_cmd==R_STA_REG_CMD)
				next_state=R_STA_REG;
			  else
	            next_state=IDLE;
			end
	
		WEL: begin
			  if(stdone&&bit_cnt>=8)
				   next_state=IDLE;
			  else
		           next_state=WEL;
			  end
			 
		S_ERA: begin
				if(stdone)
					next_state=IDLE;
				else
					next_state=S_ERA;
				end
		C_ERA: begin		
				if(stdone)
					next_state=IDLE;
				else
					next_state=C_ERA;
				end
		READ: begin 		
				if(stdone&&bit_cnt>=8)
					next_state=IDLE;
				else
					next_state=READ;
				end
		WRITE: begin		
				 if(stdone&&bit_cnt>=8)
					next_state=IDLE;
				else
					next_state=WRITE;
				end
		R_STA_REG: begin		
				 if(stdone)
					next_state=IDLE;
				else
					next_state=R_STA_REG;
				end
		
	default: next_state=IDLE;			
	endcase				
end
									
always @(posedge clk_100m or negedge sys_rst_n )begin
	if(!sys_rst_n) begin
		spi_cs<=1'b1;
		spi_clk<=1'b0;
		spi_clk0<=1'b0;
		spi_mosi<=1'b0;	
		stdone<=1'b0;		
	end
	else begin
		case(current_state)
			IDLE: begin
				spi_cs<=1'b1;
				spi_clk<=1'b0;
				spi_mosi<=1'b0;				
			end
			
			WEL: begin
			     stdone<=1'b0;
				 spi_cs<=1'b0;
					 if(dely_cnt==1&&bit_cnt<8) begin						
						spi_clk0<=~spi_clk0;
						spi_clk<=spi_clk0;
						spi_mosi<=cmd_buffer[7];
						end
					 else if(bit_cnt==8&&clk_cnt==0)begin
					    stdone<=1'b1;
						spi_clk<=1'b0;						
						spi_mosi<=1'b0;						
					 end
					 else if(bit_cnt==8&&clk_cnt==1)begin
						spi_cs<=1'b1;						
				 end
				 end
			C_ERA: begin
					stdone<=1'b0;
			         if(dely_state_cnt==10)                
						spi_cs<=1'b0;
					 else if(dely_cnt==1&&bit_cnt<8) begin						
						spi_clk0<=~spi_clk0;
						spi_clk<=spi_clk0;
						spi_mosi<=cmd_buffer[7];
						end
					 else if(bit_cnt==8&&clk_cnt==0)begin
					    stdone<=1'b1;				    
						spi_clk<=1'b0;
						spi_mosi<=1'b0;	
					 end
					  else if(bit_cnt==8&&clk_cnt==1)begin
						spi_cs<=1'b1;						
				 end
				 end
			S_ERA: begin
			       stdone<=1'b0;				 
					if(dely_state_cnt==10)                
						spi_cs<=1'b0;
					 else if(dely_cnt==1&&bit_cnt<8) begin						
						spi_clk0<=~spi_clk0;
						spi_clk<=spi_clk0;
						spi_mosi<=cmd_buffer[7];
						end
					 else if(bit_cnt>=8&&bit_cnt<32&&spi_cs==0)begin
					    spi_cs<=1'b0;
						spi_clk0<=~spi_clk0;
						spi_clk<=spi_clk0;
						spi_mosi<=addr_buffer[23];
					 end
					 else if(bit_cnt==32&&clk_cnt==0) begin
						spi_cs<=1'b1;
						spi_clk<=1'b0;
						spi_mosi<=1'b0;
						stdone<=1'b1;
					 end
				 end
            READ: begin
			      stdone<=1'b0;
				  if(dely_state_cnt==10)                
						spi_cs<=1'b0;
					else if(dely_cnt==1&&bit_cnt<8) begin						
						spi_clk0<=~spi_clk0;
						spi_clk<=spi_clk0;
						spi_mosi<=cmd_buffer[7];
						end
					 else if(bit_cnt>=8&&bit_cnt<32&&spi_cs==0)begin					    
						spi_clk0<=~spi_clk0;
						spi_clk<=spi_clk0;
						spi_mosi<=addr_buffer[23];
					 end
					 else if(bit_cnt>=32&&bit_cnt<2080)begin						
						spi_clk0<=~spi_clk0;
						spi_clk<=spi_clk0;
						spi_mosi<=1'b0;						
					 end
					 else if(bit_cnt==2080&&clk_cnt==0) begin						
						spi_clk<=1'b0;
						spi_mosi<=1'b0;
						stdone<=1'b1;						
					 end
					  else if(bit_cnt==2080&&clk_cnt==1) begin
						spi_cs<=1'b1;
					 end
				 end
            WRITE: begin
			     stdone<=1'b0;
				  if(dely_state_cnt==10)                
						spi_cs<=1'b0;
					 else if(dely_cnt==1&&bit_cnt<8) begin						
						spi_clk0<=~spi_clk0;
						spi_clk<=spi_clk0;
						spi_mosi<=cmd_buffer[7];
						end
					 else if(bit_cnt>=8&&bit_cnt<32&&spi_cs==0)begin					   
						spi_clk0<=~spi_clk0;
						spi_clk<=spi_clk0;
						spi_mosi<=addr_buffer[23];
					 end
					 else if(bit_cnt>=32&&bit_cnt<2080)begin						
						spi_clk0<=~spi_clk0;
						spi_clk<=spi_clk0;
						spi_mosi<=data_buffer[7];
					 end
					 else if(bit_cnt==2080&&clk_cnt==0) begin
						
						spi_clk<=1'b0;
						spi_mosi<=1'b0;
						stdone<=1'b1;
					 end
					  else if(bit_cnt==2080&&clk_cnt==1) begin
						spi_cs<=1'b1;
					 end
                  end
			R_STA_REG:begin				              
						stdone<=1'b0;
				     if(dely_state_cnt==10)                
						spi_cs<=1'b0;
					else if(dely_cnt==1&&bit_cnt<8)begin						
						spi_clk0<=~spi_clk0;
						spi_clk<=spi_clk0;
						spi_mosi<=cmd_buffer[7];
						end
					 else if(bit_cnt==8)begin					   				    
						spi_clk0<=~spi_clk0;
						spi_clk<=spi_clk0;
						spi_mosi<=1'b0;						
					 end                      				 
					  else if(~spi_miso&&bit_cnt%8==0)begin
					    spi_clk<=1'b0;
						spi_cs<=1'b1;
						stdone<=1'b1;
				      end
					 else if(~spi_cs&&dely_cnt==1)begin
						spi_clk0<=~spi_clk0;
						spi_clk<=spi_clk0;
				 end	   			         	 
			  end 
             default: begin
			            stdone<=1'b0;
                        spi_cs<=1'b1;
				        spi_clk<=1'b0;
						spi_clk0<=1'b0;
				        spi_mosi<=1'b0;				        
			end
         endcase
	end
end

endmodule

3.3.2 FLASH读写代码

module flash_rw(

	input            sys_clk      ,
	input            sys_rst_n    ,
	
	input            idel_flag_r  ,//抓取空闲状态上升沿
	input            w_data_req   ,//写请求
	output reg[3:0 ] cmd_cnt      ,//计数器
	output reg       spi_start    ,//spi开启使能
	output reg[7:0 ] spi_cmd      ,//spi命令号
	output reg[7:0 ] spi_data      
   
);

//指令集
parameter WEL_CMD      =16'h06;//允许写
parameter S_ERA_CMD    =16'h20;//扇擦除
parameter C_ERA_CMD    =16'hc7;//块擦除
parameter READ_CMD     =16'h03;//读数据
parameter WRITE_CMD    =16'h02;//写数据
parameter R_STA_REG_CMD=8'h05 ;//读状态寄存器

//reg define
reg[3:0] flash_start;

//*****************************************************
//**                    main code
//*****************************************************

always @(posedge sys_clk or negedge sys_rst_n )begin //延时
	if(!sys_rst_n)
		flash_start<=0;
	else if(flash_start<=10)
	    flash_start<=flash_start+1;
	else
		flash_start<=flash_start;
end

always @(posedge sys_clk or negedge sys_rst_n )begin   
	if(!sys_rst_n)
		cmd_cnt<=0;
	else if(flash_start==9)     //生成第一次flash_start
	    spi_start<=1'b1;
	else if(idel_flag_r&&cmd_cnt<10)begin    //状态抓取idel_flag_r状态
	    cmd_cnt<=cmd_cnt+1;                  //切换下一次指令
		spi_start<=1'b1;
	end
	else begin
		cmd_cnt<=cmd_cnt;
		spi_start<=1'b0;
	end
end

always @(posedge sys_clk or negedge sys_rst_n )begin    //数据逐次加一
	if(!sys_rst_n)
		spi_data<=8'd0;
	else if(w_data_req)
		spi_data<=spi_data+1'b1;
	else
		spi_data<=spi_data;
end

always @(*)begin                                        //赋值命令
	case(cmd_cnt)
		0:spi_cmd=WEL_CMD;
		1:spi_cmd=C_ERA_CMD;
		2:spi_cmd=R_STA_REG_CMD;
		3:spi_cmd=WEL_CMD;
		4:spi_cmd=WRITE_CMD;
		5:spi_cmd=R_STA_REG_CMD;
		6:spi_cmd=READ_CMD;
		7:spi_cmd=WEL_CMD;
		8:spi_cmd=S_ERA_CMD;
		9:spi_cmd=R_STA_REG_CMD;
		10:spi_cmd=READ_CMD;
		
	default:;
	endcase
end

endmodule

免责声明:本文所引用的各种资料均用于自己学习使用,这里感谢黑金和正点原子官方的资料以及各位优秀的创作者。

  • 20
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
小梅哥在学习Xilinx FPGA期间进行了积极的探索,特别是对Cyclone V SOC的开发流程进行了学习。从裸机到基于Linux嵌入式系统,小梅哥实现了FPGA和ARM Cortex-A9 CPU之间的双向控制和数据传输。 在代码编写方面,小梅哥设计了一个名为mux2的模块,用于实现二选一多路器。模块包含了输入和输出端口,并通过assign语句实现了信号的赋值。 在激励文件中,小梅哥使用reg和wire定义了输入和输出信号,并通过mux2模块进行了实例化。然后,通过initial块生成了一系列的激励信号,对模块进行仿真。 总结来说,小梅哥在Xilinx FPGA学习笔记中记录了自己对Cyclone V SOC的学习、代码编写和激励文件的使用。这些学习内容对于理解FPGA开发流程以及实现特定功能非常有帮助。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [FPGA自学笔记——设计与验证VIP版.pdf](https://download.csdn.net/download/qq_30307853/11656682)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [小梅哥Xilinx FPGA学习笔记1——二选一多路器](https://blog.csdn.net/weixin_42454243/article/details/122026484)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值