打印

FPGA驱动SRAM(转)

[复制链接]
1298|2
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
风魔小象|  楼主 | 2015-3-19 20:10 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

准备把de2-115上面的存储器外设都给驱动一下,首先就先从简单的sram开始。

Sram的驱动比较简单,和FPGA内部的ram差不多,只不过不是由时钟来控制读写,而是由控制信号来控制读写,读写都很快,基本上一个时钟就可以读取和写入数据,当然这时钟不能太快,不能超过芯片规定的最小时间。

首先肯定是先看datasheet。

首先看看sram的结构框图。

有20位地址总线,对地址进行译码,得到存储器的单元,然后根据控制电路,判断各个控制信号,从而判定是读还是写操作。读的话,数据线16位就是输出,输出数据。如果是写的话,数据线就是输入,从外部接收数据。

这里数据线高8位和低8位是分开的,因为这里有一个位屏蔽功能,可以屏蔽高8位数据线或者是屏蔽低8位数据线。

具体看芯片的真值表就知道了。

从真值表就可以看出芯片的功能了。

上图是管脚说明。

下面就是关键的时序图了。因为要驱动它的话,是肯定要按照规定的时序来的。

首先是读时序。

考虑通用的方式,即CE和OE来控制。而不是直接将OE和CE使能。

先解读下时序:

在地址数据发送后,要过10ns时间后,但是OE拉低6.5ns时间,并且CE拉低10ns时间,数据数据才是有效数据,当然要读数据的话,需要将OE和CE拉低,同时不要忘记WE要拉高,表示是读数据。

第二次读数据,要10ns之后,所以这个10ns就是读取数据的最小时间,才能发送地址。然后OE和CE要满足上图的保持时间。

总体来说,这个时序图还是很简单的。

然后是写的时序,

这时序图也是很简单的。写时候,OE为高为低都是可以的。

写数据间隔也是10ns,在写控制发出后,数据在5ns后为输入有效,且数据要有6ns的建立时间,保持时间可以为0.

搞定时序图后,就开始进行程序的编写了。。

首先是搞定sram的底层驱动。

首先是信号列表。

[color=blue !important][size=1em]
[color=blue !important]+ 查看代码



从注释可以看出各个信号的作用。

这里采用状态机来进行控制,首先是定义状态

[color=blue !important][size=1em]
[color=blue !important]+ 查看代码



定义了5个状态。看名字就知道是干什么的了。。。

状态机采用二段式设计。

核心代码如下。

[color=blue !important][size=1em]
[color=blue !important]+ 查看代码



大致说明一下。

在idle_state:如果判断start信号有效的话,就跳转到发送地址状态。

在send_address_state:这个状态就是将OE和CE给拉低。然后依据命令是读还是写,进行判断跳转到读还是写状态。

在write_state:保持OE,CE为低,同时拉低WE。然后状态跳转到finish_state。

在read_state:保持OE,CE为低,把sram_data上的数据给读取进来。然后状态跳转到finish_state.

在finish_state:就将finish信号给置1 ,然后状态跳转。

整个状态机设计是很简单的。就是要注意各个信号的时序,满足手册上要求的时序,因为系统时钟很慢,只有50M,周期为20ns,完全满足手册上要求的时序。

用状态机设计很简单,但是也有一个问题就是读写太慢了。。手册上写的是10ns就可以进行一次读写,但是这里设计的需要4个时钟,即80ns才能进行一次读写。

程序在说明一下,关于sram_data的处理,因为这个信号是双向的。

                    assign sram_data = command ? 16'bz : write_data;

这里使用的是双向口的通用处理方法,使用asignn赋值,因为command为1的时候,操作是读数据,所以这个时候赋值为高阻z,就表明是输入了。为0表示写数据,那么就赋值为write_data,作为输出,输出的值由write_data决定。

下面来对这个模块进行仿真看看。

核心的测试代码:

[color=blue !important][size=1em]
[color=blue !important]+ 查看代码



第一个repeat是写数据,这里就观测写的数据是不是和sram_data的值一样的,这个时候sram_data是作为输出的。

第二个repeat是读数据,这里就观测读的数据是不是和sram_data_reg一样的,这个时候sram_data是作为输入的,而输入的值由sram_data_reg决定的。

对于testbench,最后结束的时候,最好用$stop,暂停仿真,不然仿真就一直继续下去了。。

使用modelsim仿真,因为quartus只能用modelsim仿真。

先观察黄线左边的部分,这部分是写数据。就看写的数据和sram_data是不是一样的,发现是一样的。说明功能正确。

然后观察黄线右边的部分,这部分是读数据。就看读的数据是不是和sram_data_reg一样的。发现sram_data_reg和sram_data值一样,这个肯定是当然的,然后有和read_data一样的。说明功能正确。

然后来看看时序部分:

先看写数据。

这个时钟周期是20ns。

Start为高,表明操作开始。这个时候地址数据已经送出,CE,OE,WE持续20ns的高电平,然后持续40ns的低电平,最后才拉高,是符合写的时序的,而且余量还很多。

在看读数据:

Start为高,表明操作开始。这个时候地址数据已经送出,CE,OE持续20ns的高电平,然后拉低,持续40ns的低电平,最后才拉高。也是符合读的时序的,而且余量也很多。

按照仿真的情况来看的话,似乎功能是正确的。那么就写个简单的顶层代码,对sram

驱动。

代码功能也很简单,每隔200ms对sram写一个数据,然后将刚写数据读出来,然后赋值给led。然后sram地址加1,写的数据也加1.



评论
820632 2023-2-14 12:20 回复TA
有这个完整文档吗?有的话,麻烦发下820632815@qq.com 

相关帖子

沙发
风魔小象|  楼主 | 2015-3-19 20:10 | 只看该作者
module sram
(
    input           clk,     //input 50M clock
     input          rst_n,   //reset signal ,avtive low
      
     input         command, //1 read 0 write
     input  [19:0] address, // address to send sram
      
     input  [1:0]  byte_control, //00  all select
                                          //10  selcet low byte
                                          //01  select high byte
                                          //11  select no
     input  [15:0] write_data,   //data to write sram
      
     output [15:0] read_data,    //read data from sram
      
     input         start,        //start signal , if 1 ,begin to operate sram
     output   reg  finish,       //finish signal, if 1 ,mean operate sram finish
      
     //sram interface
     output reg CE_N,            //sram Chip Enable
     output reg OE_N,            //sram Output Enable
     output reg WE_N,            //sram Write Enable
     output LB_N,                //sram Lower-byte Control (I/O0-I/O7)
     output UB_N,                //sram Upper-byte Control (I/O8-I/O15)
     output [19:0] sram_address, //sram Address
     inout  [15:0] sram_data     //sram Data Inputs/Outputs
);


localparam idle_state           =   'd1;
localparam send_address_state   =   'd2;
localparam read_state           =   'd4;
localparam write_state          =   'd8;
localparam finish_state         =   'd16;
  
  
assign {UB_N,LB_N} = byte_control;
assign  sram_address = address;
  
assign sram_data = command ? 16'bz : write_data;
  
reg [4:0] state;
reg [4:0] state_next;
  
always@(posedge clk or negedge rst_n)  begin
    if(!rst_n)
         state <= idle_state;
     else
         state <= state_next;
end
  
//save the read data from sram
reg [15:0] sram_data_reg;
reg [15:0] sram_data_reg_next;
always@(posedge clk or negedge rst_n) begin
    if(!rst_n)
        sram_data_reg <= 'd0;
     else
        sram_data_reg <= sram_data_reg_next;
end
  
assign read_data = sram_data_reg;
  
always@(*) begin
    state_next = state;
     sram_data_reg_next = sram_data_reg;
     CE_N = 1'b1;
     OE_N = 1'b1;
     WE_N = 1'b1;
     finish = 1'b0;
     case(state)
     idle_state: begin
        if(start)
              state_next = send_address_state;
     end
     send_address_state: begin
        OE_N = 1'b0;
         CE_N = 1'b0;
         if(command == 1) //read
            begin
                state_next = read_state;
             end
         else  //write
            begin
                state_next = write_state;
                 WE_N = 1'b0;
             end
     end
     write_state: begin
        OE_N = 1'b0;
         CE_N = 1'b0;
         WE_N = 1'b0;
         state_next = finish_state;
     end
     read_state: begin
        OE_N = 1'b0;
         CE_N = 1'b0;
         sram_data_reg_next = sram_data;
         state_next = finish_state;
     end
     finish_state:begin
        finish = 1'b1;
         state_next = idle_state;
     end
     default:
        state_next = idle_state;
     endcase
end
  
endmodule

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

9

主题

80

帖子

1

粉丝