基于FPGA的多组SGPIO总线重组为单组SGPIO总线逻辑设计

        听说你还在搞什么原创,搞来搞去也就那样?

        自辞职读研后,已有一年多未踏足服务器FPGA固件开发行业了,本着怀旧心理,回忆下曾经的自己。本博客将介绍SGPIO总线的原理和如何使用FPGA实现其编码和解码。

        本文将介绍多组SGPIO总线输入到FPGA后,经过FPGA解析并重组成一组新的SGPIO总线发生至从机(LED或其他I/O口)。

1.SGPIO原理

        串行通用输入输出(SGPIO)是一种串行化通用IO信号的方法。通常用于电子设备或嵌入式系统中,尤其是在需要通过串行接口控制或监控多个I/O引脚的场景下。

        其SGPIO总线一般包括四条线:时钟CLK、数据输入DI、数据输出DO和使能LOAD。

其中:详细介绍请参考SFF-8485数据手册。

        在服务器系统中,BMC或CPU等主设备常使用SGPIO总线和CPLD/FPGA执行数据的交互。如:PCH或CPU使用SGPIO控制硬盘背板上HDD的状态LED,需要使用FPGA解析CPU发送的SGPIO总线数据,进而控制HDD背板上的LED。常见的SGPIO控制硬盘LED的拓补图如下图所示。

        基本知识就不赘述了,凡尔赛一下:很简单的。

        那么问题来了,如何使用FPGA准确的解析多组SGPIO总线上的数据并重组成一组新的SGPIO总线呢?且看本集后续。多组SGPIO输入FPGA后重组成一组SGPIO的系统框图如下图所示。

         若实现上述框图的FPGA逻辑,首先需要无误解析SGPIO总线的数据,那么FPGA的采样时钟必须满足奈奎斯特采样定理。即FPGA的采样时钟频率需要大于二倍的SGPIO时钟频率,才能保证FPGA解析SGPIO的数据不会出错。由SFF-8485数据手册可详细的得到SGPIO时钟频率的范围(32Hz~100KHz)。当然在编写FPGA固件时,其信号线DO/DI的建立时间和保持时间也必须符合SGPIO的规范。SGPIO通信时的波形图如下:

        LOAD置1后,LOAD、DO和DI上的位流会在时钟CLK上重启。比特流应至少包含4个驱动器的信息。比特流不必每次都是相同的长度,并且可以在从驱动器3位开始的任何位位置结束。通俗来讲,就是3个bit(0,1,2)位表示一组设备的数据信息。SGPIO 规范要求目标在 CLK、LOAD和 DO为高电平 64 ms 时关闭设备的所有指示器。

        根据波形示意图可以得知DI/DO是在CLK的上升沿接收/下降沿发送数据。那么,开始搓代码呗!

2.解码

       若想获取SGPIO上的数据,在此之前需要先对输入的SGPIO总线进行滤波处理,其目的是去除PCB走线或者线材引入的毛刺,这些毛刺对FPGA有很大的负面影响。其滤波固件本文不做阐述和编写。

        前戏结束,开始正题。

2.1 检测SGPIO总线状态

        由于SGPIO非活动状态下的CLK处于高电平状态(应该是高阻抗,其内部上拉电阻拉至高电平),先检测其状态并输出控制使能信号,保证编码逻辑块输出的I/O电平符合SGPIO规范。

always@(posedge i_sys_clk or negedge i_sys_rst_n)begin
	if(!i_sys_rst_n)
		sgpio_bit_cnt <= 24'h000000;
	else if(i_sample_clk)
		sgpio_bit_cnt <= {sgpio_bit_cnt[22:0] , sgpio_clk};
	else
		sgpio_bit_cnt <= sgpio_bit_cnt;
end

assign sgpio_act_flg = (sgpio_bit_cnt == 24'hffffff || sgpio_bit_cnt == 24'h000000) ? 1'b0 : 1'b1;

2.2 捕获LOAD信号

        LOAD从高电平变为低电平在变为高电平,其时间跨度表示SGPIO数据的一个周期,可以结合SGPIO的CLK频率得到SGPIO总线的位宽。

        LOAD边沿检测逻辑块:

	always@(posedge i_sys_clk or negedge i_sys_rst_n) begin
		if(!i_sys_rst_n)begin
			sgpio_load_d <= 1'b1;
            sgpio_load_q <= 1'b1;
		end
		else begin
			sgpio_load_d <= sgpio_load;
			sgpio_load_q <= sgpio_load_d;
		end
	end

	assign	sgpio_load_pose = (sgpio_load_d && ~sgpio_load_q);
	assign	sgpio_load_nege = (sgpio_load_q && ~sgpio_load_d);

        获取SGPIO位宽逻辑块(没啥用!)

always@(negedge w_sgpio_clk or negedge i_sys_rst_n)begin
	if(!i_sys_rst_n || w_sgpio_load)
		sgpio_bitw_d <= {24'h0000000};
	else if(!w_sgpio_load)
		sgpio_bitw_d <= sgpio_bitw_d + 1'b1;
	else
		sgpio_bitw_d <= sgpio_bitw_d;
end

2.3 采样并获取DI的数据

       结合 CLK和LOAD捕获并解析DI上的数据信息。高位在前,低位在后。解码后的数据传送至FIFO中。

always@(negedge w_sgpio_clk or negedge i_sys_rst_n) begin
	if(!i_sys_rst_n)begin
		receive_data_d <= {SGPIO_WIDTH{1'b0}};
		receive_data_q <= {SGPIO_WIDTH{1'b0}};
	end
	else begin
		if(!w_sgpio_load) begin 
			receive_data_d <= {sgpio_data_in , receive_data_d[SGPIO_WIDTH - 1'b1 : 1]}; 
		end
		else begin
			receive_data_q <= {sgpio_data_in , receive_data_d[SGPIO_WIDTH - 1'b1 : 1]}; 
		end	
	end
end

assign	o_data_out = sgpio_act_flg ? receive_data_q : {SGPIO_WIDTH{1'b0}};

3.编码

       3.1 生成SGPIO的时钟CLK

        其实在频率需要自己设定,但需要满足SGPIO规范的范围(32Hz~100KHz)同样可以使用FPGA的IP核生成所需的时钟(CLK IP + PLL IP)。

always @(posedge isys_clk or negedge isys_rst_n)begin
	if (!isys_rst_n || state == IDLE) begin
		sgpio_clk_out <= 1'b1; 							
		clk_div_count <= {(CNT_SIZE + 1'b1){1'b0}};				
	end
	else begin
		if (clk_div_count == (SGPIO_CLK_DIV - 1'b1)) begin	
			sgpio_clk_out <= ~sgpio_clk_out ;      	
			clk_div_count <= {(CNT_SIZE + 1'b1){1'b0}};          
		end
		else begin
			sgpio_clk_out <= sgpio_clk_out;      			
			clk_div_count <= clk_div_count + 1'b1;         
		end
	end 
end 

assign	osgpio_clk_out = sgpio_clk_out ;

3.2 LOAD逻辑块

always@(posedge osgpio_clk_out or negedge isys_rst_n)begin
	if(!isys_rst_n || state == IDLE || (state == CYCLE_END && orcrtl_flg == 1'b0) ) begin
		sgpio_load_count 	<= {(SGPIO_CYCLE_BIT + 1'b1){1'b0}};
	end
	else if(sgpio_load_count == (SGPIO_BIT_WIDTH - 1'b1))begin
		sgpio_load_count 	<= {(SGPIO_CYCLE_BIT + 1'b1){1'b0}};
	end
	else begin
		sgpio_load_count <= sgpio_load_count + 1'b1;
	end	
end


always@(posedge isys_clk or negedge isys_rst_n)begin
	if(!isys_rst_n || state == IDLE || state == CYCLE_STOP) begin
		sgpio_load_n <= 1'b1;
	end
	else if(sgpio_load_count == 8'd23)begin
		sgpio_load_n <= 1'b1;
	end
	else begin
		sgpio_load_n <= 1'b0;
	end	
end

3.3 SGPIO状态机

	always@(posedge isys_clk or negedge isys_rst_n)begin
		if(!isys_rst_n)
			state <= IDLE;
		else
			state <= next_state;
	end

	always@(*)begin
		case(state)
		
			IDLE : begin
				if(iwr_enable == 1'b1)
					next_state <= CYCLE_START;
				else
					next_state <= IDLE;
			end
			
			CYCLE_START : begin
				if(sgpio_load_count == (SGPIO_BIT_WIDTH - 1'b1))
					next_state <= CYCLE_VALID;
				else
					next_state <= CYCLE_START;
			end
	
			CYCLE_VALID : begin
				if(iwcrtl_flg == 1'b1)
					next_state <= CYCLE_VALID;
				else
					next_state <= CYCLE_END;		
			end
			
			CYCLE_END : begin
				if(orcrtl_flg == 1'b1)
					next_state <= CYCLE_END;
				else
					next_state <= CYCLE_STOP;			
			end
			
			CYCLE_STOP : begin
				if(sgpio_load_count == (SGPIO_BIT_WIDTH - 1'b1))
					next_state <= IDLE;
				else
					next_state <= CYCLE_STOP;
			end
			
			default : begin
				next_state <= IDLE;
			end	
	
		endcase
	end

	always@(posedge isys_clk or negedge isys_rst_n)begin
		if(!isys_rst_n || state == IDLE)
			orcrtl_flg <= 1'b0;
		else if(state == CYCLE_END || state == CYCLE_STOP)begin
			if(sgpio_load_count == (SGPIO_BIT_WIDTH - 1'b1))
				orcrtl_flg <= 1'b0;
			else
				orcrtl_flg <= 1'b1;
		end
		else
			orcrtl_flg <= 1'b1;
	end

3.4 DO输出数据

always@(negedge osgpio_clk_out or negedge isys_rst_n)begin
	if(!isys_rst_n) begin
		data_out1 <= {SGPIO_BIT_WIDTH{1'b0}};
		data_out2 <= {SGPIO_BIT_WIDTH{1'b0}};
	end
	else if(!osgpio_load_n)begin
		data_out1 <= {data_out1[SGPIO_BIT_WIDTH - 2 : 0] , sgpio_data_in};
	end
	else begin
		data_out2 <= {data_out1[SGPIO_BIT_WIDTH - 1 : 1] , sgpio_data_in};
	end	
end	

4.FIFO

       测试时使用的是ALTERA的IP核,这里不做阐述。很简单的啦!

5.仿真验证        

        比较懒,不想写testbench文件。

6.测试的波形(未发送数据)

7.参考资料

  1. 脑袋瓜
  2. Intel FPGA逻辑设计指南
  3. SGPIO Spec
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值