这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 嵌入式开发 » FPGA » FPGA驱动SRAM(转)

共2条 1/1 1 跳转至

FPGA驱动SRAM(转)

菜鸟
2015-03-19 20:15:03     打赏

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

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

首先肯定是先看datasheet。

clip_image002

首先看看sram的结构框图。

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

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

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

clip_image004

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

clip_image005

上图是管脚说明。

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

首先是读时序。

clip_image007

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

先解读下时序:

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

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

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

然后是写的时序,

clip_image009

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

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

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

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

首先是信号列表。

+ 查看代码

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

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

+ 查看代码

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

状态机采用二段式设计。

核心代码如下。

+ 查看代码

大致说明一下。

在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决定。

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

核心的测试代码:

+ 查看代码

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

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

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

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

clip_image011

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

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

然后来看看时序部分:

先看写数据。

clip_image013

这个时钟周期是20ns。

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

在看读数据:

clip_image015

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

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

驱动。

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

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 modulesram_control (    input               clk,     input              rst_n,            //sram interface      outputCE_N,      outputOE_N,      outputWE_N,      outputLB_N,      outputUB_N,      output[19:0] sram_address,      inout [15:0] sram_data,             output[15:0] led,             output[8:0]  led2 );     localparam  idle_state    = 'd1; localparam  write_state   = 'd2; localparam  read_state    = 'd4; localparam  finish_state  = 'd8;   regstart; wirefinish;   regcommand;   wire[1:0] byte_control; assignbyte_control =2'b00;   wire[19:0] address; wire[15:0] read_data;   reg[19:0] address_counter; reg[19:0] address_counter_next; always@(posedgeclkornegedgerst_n)begin    if(!rst_n)        address_counter <= 'd0;    else         address_counter <= address_counter_next; end   //reg [15:0] write_data_reg; //reg [15:0] write_data_reg_next; //always@(posedge clk or negedge rst_n) begin //   if(!rst_n) //     write_data_reg <= 'd0; //  else //     write_data_reg <= write_data_reg_next; //end   reg[23:0] counter_200ms;// 10000000 localparam counter_200ms_value =24'd1000_0000; //localparam counter_200ms_value = 24'd10; //for simulation always@(posedgeclkornegedgerst_n)begin    if(!rst_n)        counter_200ms <= 'd0;     elseif(counter_200ms >= counter_200ms_value)        counter_200ms <= 'd0;     else        counter_200ms <= counter_200ms +1'b1; end   reg[3:0] state; reg[3:0] state_next; always@(posedgeclkornegedgerst_n)begin    if(!rst_n)       state <= idle_state;     else        state <= state_next; end   always@(*)begin    state_next = state;    address_counter_next = address_counter;       command =1'b0; //default write     //write_data_reg_next = write_data_reg;     start =1'b0;    case(state)      idle_state:begin        address_counter_next = 'd0;         //write_data_reg_next = 'd0;         state_next = write_state;     end     write_state:begin        if(finish== 1)            begin              start =1'b0;               state_next = read_state;            end         else            start = 1;     end     read_state:begin        command =1'b1;//read         if(finish== 1)            begin               start =1'b0;               address_counter_next = address_counter +1'b1;               //write_data_reg_next = write_data_reg + 1'b1;               state_next = finish_state;            end         else            start =1'b1;     end     finish_state:begin       if(counter_200ms == counter_200ms_value)           state_next = write_state;     end     default:        state_next = idle_state;     endcase end   assignaddress = address_counter;   sram sram_inst (     .clk(clk) ,// input  clk     .rst_n(rst_n) ,// input  rst_n     .command(command) ,// input  command     .address(address) ,// input [19:0] address     .byte_control(byte_control) ,  // input [1:0] byte_control     .write_data(address_counter[15:0]) ,   // input [15:0] write_data     .read_data(read_data) ,// output [15:0] read_data     .start(start) ,// input  start     .finish(finish) ,  // output  finish     .CE_N(CE_N) ,  // output  CE_N     .OE_N(OE_N) ,  // output  OE_N     .WE_N(WE_N) ,  // output  WE_N     .LB_N(LB_N) ,  // output  LB_N     .UB_N(UB_N) ,  // output  UB_N     .sram_address(sram_address) ,  // output [19:0] sram_address     .sram_data(sram_data)  // inout [15:0] sram_data );   assignled = read_data; assignled2 = address_counter[8:0];   endmodul

然后分配管脚,编译生成sof文件,下到芯片中,发现,成功驱动了。led按照二进制加1的方式进行亮灭。


最后附上整个sram控制的代码:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 modulesram (     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      outputregCE_N,           //sram Chip Enable      outputregOE_N,           //sram Output Enable      outputregWE_N,           //sram Write Enable      outputLB_N,               //sram Lower-byte Control (I/O0-I/O7)      outputUB_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;     assignsram_data = command ?16'bz: write_data;     reg[4:0] state;  reg[4:0] state_next;     always@(posedgeclkornegedgerst_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@(posedgeclkornegedgerst_n)begin     if(!rst_n)         sram_data_reg <= 'd0;      else          sram_data_reg <= sram_data_reg_next;  end     assignread_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



关键词: FPGA     SRAM    

高工
2020-06-02 14:41:35     打赏
2楼

谢谢分享


共2条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]