CrazyBingo

8080时序的FPGA接收解码设计

1
阅读(10386)

1. 写在前面的话

很多人问我单片机和FPGA怎么通信,我告诉他,FPGA是万能的,你想怎么通信就怎么通信,或者你模拟UART,FSMC,I2C,自己看着办。呵呵。。。。。

纵然FPGA无所不能,也不能代替MCU的处理能力。很多系统中,都是FPGA与MCU的完美结合。因此难免有MCU与FPGA之间的数据交换。MCU与FPGA之间的通讯协议的稳定性,很大程序上决定了系统数据交换的成败率。在此设计了最基本的8080时序,并且通过STM32的FSMC模拟,借以奠定通讯的基础。

1. 8080时序详解

wps_clip_image-30268

如上所示,传说中的Intel 8080接口的通信协议!,左边Master,右边为Slave。数据通过一定的时序并行传输,实现片间通讯。既然FPGA是万能的,不妨设计一个基于FPGA的8080协议数据接收电路,通过STM32的FSMC模拟。

wps_clip_image-18401

注意,RS,WE,RD

CS(片选信号) :低电平片选有效,高电平失能(默认为高:失能)

RS(数据寄存器) :低电平写寄存器,高电平写数据(默认为高:写数据)

RD(读信号) :低电平有效,上升沿写入数据,高电平失能(默认为高,失能)

WR(写信号) :低电平有效,上升沿写入数据,高电平失能(默认为高,失能)

注意:FSMC中RS默认为低,其他为高

2. FSMC时序的FPGA接收解码设计

2.1. FSMC接收时序设计

wps_clip_image-5986

参照了ILI9325 LCD控制芯片的8080接口通信协议,如上图所示,STM32的FSMC硬件接口,设置成8080时序的输出时序。由于MCU与FPGA通信,设置为单向通信,因此省略了RD,Bingo版本的8080接口童鞋写入如下所示:

a) 写命令(单一或者连续写入)

wps_clip_image-1725

b) 写数据(单一或者连续写入)

wps_clip_image-23942

2.1.1. 异步数据同步化

由于MCU(或者其他外设)与FPGA是异步关系,为了达到数据的同步,以及边沿的采样,需要用几个寄存器,将异步数据同步化,实现时序系统稳定性。

其中片选,数据,以及时钟同步电路Verilog HDL代码如下所示:

//------------------------------------- //mcu data sync to fpga reg    mcu_cs_r0,    mcu_cs_r1, mcu_cs_r2;
reg    mcu_rs_r0,    mcu_rs_r1, mcu_rs_r2; //fsmc default 0; 8080 default 1; reg    mcu_we_r0,    mcu_we_r1; //lag one clk reg    [15:0]    mcu_data_r0, mcu_data_r1, mcu_data_r2;
always@(posedge clk or negedge rst_n)
begin if(!rst_n)
        begin
        mcu_cs_r0 <= 1; //chip enable mcu_cs_r1 <= 1;
        mcu_cs_r2 <= 1;
        mcu_rs_r0 <= 0; //record/statement mcu_rs_r1 <= 0;
        mcu_rs_r2 <= 0;    
        mcu_data_r0 <= 0; //data bus mcu_data_r1 <= 0;
        mcu_data_r2 <= 0;

        mcu_we_r0 <= 1; //write enable(rising)  mcu_we_r1 <= 1; //lag one clk end else begin
        mcu_cs_r0 <= mcu_cs;    
        mcu_cs_r1 <= mcu_cs_r0;
        mcu_cs_r2 <= mcu_cs_r1;
        mcu_rs_r0 <= mcu_rs;    
        mcu_rs_r1 <= mcu_rs_r0;
        mcu_rs_r2 <= mcu_rs_r1;
        mcu_data_r0 <= mcu_data;
        mcu_data_r1 <= mcu_data_r0;
        mcu_data_r2 <= mcu_data_r1;

        mcu_we_r0 <= mcu_we;    
        mcu_we_r1 <= mcu_we_r0;
        end
end
wire    fpga_cs = mcu_cs_r2; //lead one clk than fpga_we_flag wire    fpga_rs = mcu_rs_r2; //lead one clk than fpga_we_flag wire    [15:0]    fpga_data = mcu_data_r2; //lead one clk than fpga_we_flag //To sure when fpga_we_flag = 1, cs & rs is valid, we lag one clk wire    fpga_we_flag = (~mcu_we_r1 & mcu_we_r0) ? 1'b1 : 1'b0;        //rising edge of we
2.1.2. FSMC数据接受解码

8080协议是为了实现命令以及数据的发送,因此接收数据的同时,需要进行数据的分配,以实现数据的解码电路,如下所示。

Reg_addr为接收到的命令。

Reg_data为接收到的数据。

Update_flag为接收到数据的标志位,便于在后续模块中的接收。

//------------------------------------- reg    [7:0]    reg_addr;
reg    [15:0]    reg_data;
reg            update_flag;
always@(posedge clk or negedge rst_n)
begin if(!rst_n)
        begin
        reg_addr <= 0;
        reg_data <= 0;
        update_flag <= 0;
        end else if(~fpga_cs) //chip enable begin if(~fpga_rs & fpga_we_flag) //write register begin
            reg_addr <= fpga_data[7:0];
            reg_data <= reg_data;
            update_flag <= 0;
            end else if(fpga_rs & fpga_we_flag) //write data begin
            reg_addr <= reg_addr;
            reg_data <= fpga_data;
            update_flag <= 1;
            end else begin
            reg_addr <= reg_addr;
            reg_data <= reg_data;
            update_flag <= 0;
            end
        end else begin
        reg_addr <= reg_addr;
        reg_data <= reg_data;
        update_flag <= 0;
        end    
en
2.1.3. 命令数据解码电路

其实这一部也很重要,通过reg_addr以及reg_data,实现命令与数据的解码电路,也就是相当于模拟ILI9325内部的初始化协议。FPGA是万能的,这个功能用简单的case语句便能实现,如下以16h'FF的时候配置led_data为例。

//-------------------------------------//register and data trans always@(posedge clk or negedge rst_n)
begin if(!rst_n)
        begin
        led_data <= 0;
        end else if(update_flag) //updata data flag begin case(reg_addr)        
        16'hFF    :    led_data <= reg_data[7:0]; default:;
        endcase
        end
end
2.2. FSMC接收解码的Modelsim仿真
2.2.1. Testbench FSMC发送的设计

为了仿真电路的正确性,通过testbench模拟了STM32-FSMC的命令以及数据的发送时序,如下所示:

//--------------------------------------------- //mcu fsmc write cmd task task_write_cmd;
input    [15:0] fsmc_cmd;
begin
    mcu_cs = 0; #100;
    mcu_rs = 0; #100;
    mcu_data = fsmc_cmd;
    mcu_we = 0; #100;
    mcu_we = 1; #100;
    mcu_rs = 1; #100;
    mcu_cs = 1; #100;
end
endtask //--------------------------------------------- //mcu fsmc write data task task_write_data;
input    [15:0] fsmc_data;
begin
    mcu_cs = 0; #100;
    mcu_rs = 1; #100;
    mcu_data = fsmc_data;
    mcu_we = 0; #100;
    mcu_we = 1; #100;
    mcu_rs = 1; #100;
    mcu_cs = 1; #100;
end
endtask
2.2.2. FSMC时序仿真电路
initial
begin
    task_sysinit;
    task_reset;
    
    task_write_cmd(16'hFF);    task_write_data(16'h58);
    task_write_cmd(16'hFF);    task_write_data(16'hA9);    
    task_write_data(16'h6E);    task_write_data(16'h8F);

end

模拟MCU发送FSMC数据,经过FPGA的接收解码电路,最后的Modelsim仿真图如下所示:

wps_clip_image-28494

该代码已经应用在MCU2FPGA的VGA显示驱动中应用,经过长时间的测试校验,目前稳定可靠,希望能给大家在平时的设计中,带来一定的启发与帮助。我的设计永远是我的,除非你已经掌握了精髓,行云流水而自如。