基于FPGA的实时图像处理系统(1)——SDRAM回环测试

SDRAM回环设计

文章目录

一、SDRAM简介

SDRAM英文全称"Synchronous Dynamic Random Access Memory",译为"同步动态随机存取内存"或"同步动态随机存储器",是动态随机存储器(Dynamic Random Access Memory,简称DRAM)家族的一份子。同步、动态、随机是其性能特点的外在说明,也是其区别其他存储器的特色标签。这三个概念性的标签,我们要好好理解掌握。

同步(Synchronous):与通常的异步DRAM不同, SDRAM存在一个同步接口,其工作时钟的时钟频率与对应控制器(CPU/FPGA)的时钟频率相同,并且SDRAM内部的命令发送与数据传输均以此时钟为基准,实现指令或数据的同步操作;

动态(Dynamic): SDRAM需要不断的刷新来保证存储阵列内数据不丢失;

随机(Random):数据在SDRAM中并不是按照线性依次存储,而是可以自由指定地址进行数据的读写。

空间存储量大、读写速度快以及价格相对便宜等优点使其在存储界屹立不倒、经久不衰,广泛应用在计算机中。随着时代的不断发展、技术的不断更新,SDRAM使用至今已过数十载,产品更新历经五代,分别是:第一代SDR SDRAM,第二代DDR SDRAM,第三代DDR2 SDRAM,第四代DDR3 SDRAM,第五代,DDR4 SDRAM。

第一代SDR SDRAM采用单端时钟信号,SDRAM只在时钟的上升沿进行数据采样;而后面的四代SDRAM由于工作频率比较快,所以采用可降低干扰的差分时钟信号作为同步时钟,双沿采样,速度更快,且功耗更低。同时技术的不断发展、制造工艺的不断提高,使得五代SDRAM的更新过程中,集成度越来越高、内核电压越来越低(SDR:3.3V 、DDR:2.5V、DDR2:1.8V、DDR3:1.5V、DDR4:1.2V),这也是SDRAM速度提高、功耗降低的重要原因。

本次运用的SDRAM为海力士公司的H57V2562GTR-75C,内存大小为256Mbit,有4个bank,每个bank有4M(8192行*512列)存储单元,每个单元可存16bit数据

1、引脚

信号 类型 描述
clk in 时钟,所有其他输入在CLK的上升沿被寄存到SDRAM
cke in 时钟使能,控制内部时钟信号,当禁用时,SDRAM将处于电源关闭、暂停或自刷新状态之一
cs_n in 片选,启用或禁用除CLK、CKE和DQM之外的所有输入
BA0,BA1 in bank地址线
A0-A12 in 行列地址线,行地址:RA0 ~ RA12,列地址:CA0 ~ CA8自动预充电标志:A10
RAS_n,CAS_n,WE_N in 行地址选通,列地址选通,写使能
LDQM,UDQM I/O 数据掩码,16位高低字节,可控制读写数据
DQ0-DQ15 I/O 数据输入输出

2、内部结构框图

由图可知,SDRAM内部包含一个状态机左上角state machine),外部通过控制CS_N、RAS_N、CAS_N、WE_N、A0-A12数据地址线以及BA0、BA1 BANK地址线来发送命令,命令经过解码器进行译码后,将控制参数保存到模式寄存器中,逻辑控制单元进而控制逻辑运行。

外部通过地址总线输入地址信息,地址信息在逻辑控制单元进行逻辑控制时起到辅助作用,除此之外,复用的地址总线与Bank控制逻辑、行地址复用器、列地址计数锁存器、列地址解码器等内部器件共同作用,精确选定存储阵列中与行列地址相对应的存储单元,进而进行数据存取操作。

DQM数据掩码线控制这数据是否有效,其又L低位与U高位两条线,每条控制着8bit数据。

DQ0-DQ15为数据线,其为双向,读写共用一条线。

3、操作指令

1、预充电(precharge)

预充电命令:关闭特定bank中激活的行,或关闭所有bank中激活的行。

A10决定预充电模式:

当预充电命令中的地址A10为高时,预充电所有bank;当预充电命令中A10为低时,预充电特定bank。

当write/read命令中的A10为高时,自动预充电(auto-precharge)被使能;反之。

预充电命令发送之后,这些bank等待tRP才能接收命令

2、自动预充电(auto-precharge)

自动预充电是非显示命令,即使能自动预充电是需要发送write/read命令时将地址中A10拉高,在读写

突发结束后,立即预充电那个bank/row。

3、NOP

NOP 指令用以表明对 sdram 芯片(CS# == 0)进行空操作。NOP 指令的目的是在 sdram 在空闲

或者等待状态下,避免去执行一些潜在的不需要的指令。已经在执行过程中的指令不受影响。

4、自动刷新(auto-refresh)与自刷新(self-refresh)

为使数据不丢失,电容的两次刷新时间不能超过64ms,刷新都是针对行的。共性:都不需要外部提供地址信息,SDRAM内部有一个行地址生成器(刷新计数器)。刷新都是针对

一行的,不需要对列地址寻址,但也不需要对行进行寻址,因为内部有刷新计数器

自动刷新:SDRAM正常工作模式中为了数据不丢失进行的操作,需要外部时钟参与,刷新的行地址也

是由内部刷新计算器控制

自刷新:休眠模式低功耗状态下存储数据,不需要外部时钟参与,刷新的行地址内部刷新计算器控

制。

发送自动刷新命令需要的时间为tRRC(自动刷新周期),由于是对行操作,等效于行选通时间(RAS)

SDRAM中每次刷新操作所需要的时间为自动刷新周期(tRC),在自动刷新指令发出后需要等待tRC才能发

送其他指令。

5、行激活

行激活命令也叫做bank激活命令,作用是在指定bank中激活一行;

行激活后会一直处于激活状态(即列寻址处于激活状态),直到预充电命令被发送到这个bank;

行激活命令之后,需要延时tRCD(即发出行地址到发出列地址的时间间隔),才能发送READ/WRITE命令(读写操作必须先激活对应bank)

6、读操作

读数据命令用来开启数据的突发读,bank,row都可选,注意A10的值决定是否执行自动预充电操作,若执行自动预充电,突发读结束后就进行预充电,此行关闭,若不执行自动预充电,该行保持激活,仍能被访问。

CAS latency :读延迟(读潜伏周期,CL)Burst length:突发长度(BL)

读命令发出后,输出buffer(理解为SDRAM的dq_out)会在(CL-1)个时钟后期后变为低阻,然后会在突发读结束后重新变为高阻态,

7、写操作

DM(数据掩码)高有效,当为低时,数据能正确被写入DM;当为高时,数据将被忽略。

写突发时,第一个数据与写命令同步;

突发写-->预充电中间需要间隔tDPL。

二、系统设计


所实现功能 :pc发送数据,存入fifo的数据达到所设阈值,进行突发写操作(此处用的ip核不支持多bit突发读写操作,故在控制模块中实现突发读写操作),当按键(经过消抖)按下,进行突发读操作,将读取的数据发送回pc显示出来。

三、实现流程

1、SDRAM接口

SDRAM接口部分由ip核实现
1) 选择Platform Designer

2) 在左上角处搜索sdram,在下方双击选中ip添加

3) 配置各个参数

本次运用的SDRAM的数据宽度为16bit,有4个bank,每个bank有13行9列

参数 含义 典型值意义
CAS latency cycles 读命令发出后,到第一个有效数据出现在总线上的时钟周期数 设为 3 → 3 个时钟
Initialization refresh cycles 上电后、正式初始化前,控制器自动发出的 Auto-Refresh 命令次数 8 次
Issue one refresh command every 两次刷新命令之间的间隔时间 7.8 µs(SDRAM 要求 64 ms 内刷完所有行,因此 7.8 µs/行≈8192 行)
Delay after powerup, before initialization 芯片上电稳定后再开始初始化的等待时间 200 µs(JEDEC 规范建议 ≥100 µs)
Duration of refresh command (t_rfc) 单个 Auto-Refresh 命令占用总线的最短时间 70 ns
Duration of precharge command (t_rp) 关闭当前行(Precharge)所需时间 20 ns
ACTIVE to READ or WRITE delay (t_rcd) 行激活到列读/写命令之间的最小间隔 20 ns
Access time (t_ac) 时钟沿到数据有效输出的延迟(器件参数,非用户设) 5 ns
Write recovery time (t_wr) 写命令结束后到允许 Precharge 的最小间隔 20 ns

配置完如下


4) 点击右下角finish左边按钮出现以下界面

改为生成verilog代码,路径自行选择,更改完成点击生成,等待完成,等待时间因电脑配置有差异

生成完成,依次点击close、finish

5) finish后出现以下界面提示添加文件

添加完成在ip查看界面才会有显示

生成的例化模板:

c 复制代码
	sdram_ip u0 (
		.clk_clk           (), //   clk.clk
		.reset_reset_n     (), // reset.reset_n
		.sdram_addr        (), // sdram.addr
		.sdram_ba          (), //      .ba
		.sdram_cas_n       (), //      .cas_n
		.sdram_cke         (), //      .cke
		.sdram_cs_n        (), //      .cs_n
		.sdram_dq          (), //      .dq
		.sdram_dqm         (), //      .dqm
		.sdram_ras_n       (), //      .ras_n
		.sdram_we_n        (), //      .we_n
		.avs_address       (), //   avs.address
		.avs_byteenable_n  (), //      .byteenable_n
		.avs_chipselect    (), //      .chipselect
		.avs_writedata     (), //      .writedata
		.avs_read_n        (), //      .read_n
		.avs_write_n       (), //      .write_n
		.avs_readdata      (), //      .readdata
		.avs_readdatavalid (), //      .readdatavalid
		.avs_waitrequest   () //      .waitrequest
	);
端口名称 方向 位宽 含义说明
clk_clk input 1 全局系统时钟,用于 SDRAM 控制器内部逻辑以及 SDRAM 芯片。
reset_reset_n input 1 全局异步复位,低有效。
sdram_addr[12:0] output 13 SDRAM 地址总线,行列地址复用。
sdram_ba[1:0] output 2 Bank 地址,选择 SDRAM 的 4 个 bank。
sdram_cas_n output 1 列地址选通,低有效。
sdram_cke output 1 时钟使能,高时 SDRAM 响应时钟。
sdram_cs_n output 1 片选,低有效。
sdram_dq[15:0] inout 16 双向数据总线。
sdram_dqm[1:0] output 2 数据掩码/字节使能,对应 16bit 数据的高低字节。
sdram_ras_n output 1 行地址选通,低有效。
sdram_we_n output 1 写使能,低有效。
avs_address[21:0] input 22 Avalon-MM 总线地址(字节地址)。
avs_byteenable_n[1:0] input 2 字节使能,低有效,对应 16bit 数据高低字节。
avs_chipselect input 1 Avalon-MM 片选,高有效。
avs_writedata[15:0] input 16 写数据。
avs_read_n input 1 读请求,低有效。
avs_write_n input 1 写请求,低有效。
avs_readdata[15:0] output 16 读回数据。
avs_readdatavalid output 1 读数据有效指示,高有效。
avs_waitrequest output 1 控制器忙,拉低时表示需要等待。

sdram前缀为输出给sdram芯片的接口,avs前缀为内部的控制的Avalon协议接口。

2、FIFO设置

c 复制代码
//---------<写FIFO例化>------------------------------------------------- 

wr_fifo	wr_fifo_inst (
	.aclr       ( ~rst_n        ),
	.data       ( {2{rx_data}}  ),
	.rdclk      ( clk           ),
	.rdreq      ( wr_rden       ),
	.wrclk      ( clk_in        ),
	.wrreq      ( wr_wren       ),
	.q          ( wr_q          ),
	.rdempty    ( wr_rdempty    ),
	.rdusedw    ( wr_rdusedw    ),
	.wrfull     ( wr_wrfull     )
	);

assign  wr_wren = ~wr_wrfull && rx_vld;
assign  wr_rden = ~wr_rdempty && (state_c == WRITE) && ~avm_waitrequest;

由于所设置SDRAM宽度为16,但uart_rx传入为8bit,所以拼两个传入的数据存入SDRAM。

写使能(wr_wren):fifo未满且rx传入的数据有效,就将数据写入fifo

读使能(wr_rden):fifo不空且处于写状态,且sdram处于不忙状态(waitrequest为低)将数据传给sdram

c 复制代码
//---------<读FIFO例化>------------------------------------------------- 

rd_fifo	rd_fifo_inst (
	.aclr       ( ~rst_n        ),
	.data       ( avm_readdata  ),
	.rdclk      ( clk_out       ),
	.rdreq      ( rd_rden       ),
	.wrclk      ( clk           ),
	.wrreq      ( rd_wren       ),
	.q          ( rd_q          ),
	.rdempty    ( rd_rdempty    ),
	.wrfull     ( rd_wrfull     )
);

assign  rd_wren = ~rd_wrfull && avm_readdatavalid;
assign  rd_rden = ~rd_rdempty && tx_done;

写使能(rd_wren):fifo未满且sdram处于不忙状态(waitrequest为低)将数据传给读fifo

读使能(rd_rden):fifo不空且tx发送模块不忙,将数据传给tx发送到pc

3、内部SDRAM的控制模块

由于uart时钟与sdram的时钟不同,这里用了两个异步fifo,一个用于写,一个用于读;wrfifo写侧为uart_rx输入的系统时钟,即为clk_in,读侧为输出到sdram的时钟,为clk_100m;rdfifo写侧为sdram传出的数据,时钟为clk_100m,读侧为通过uart_tx传回pc的数据接口,时钟为系统时钟。

在此设置一个简单的状态机实现读写操作,此处的burst_lenth为 10。

c 复制代码
//---------<突发计数器>------------------------------------------------- 

always @(posedge clk or negedge rst_n)begin 
   if(!rst_n)begin
        cnt_burst <= 'd0;
    end 
    else if(add_cnt_burst)begin 
        if(end_cnt_burst)begin 
            cnt_burst <= 'd0;
        end
        else begin 
            cnt_burst <= cnt_burst + 1'b1;
        end 
    end
end 

assign add_cnt_burst = ((state_c == READ) || (state_c == WRITE)) && ~avm_waitrequest;
assign end_cnt_burst = add_cnt_burst && cnt_burst == (BURST_LENTH-1);

当按键按下进入读数据状态,开始从sdram读取数据,当读fifo中读了10个数据时跳出。

当写fifo的数据大于所设突发操作的阈值,就进行写入操作,跳转到write状态,将10个数据写入sdram,写完10个后跳到空闲状态。

读写地址通过计数器实现

c 复制代码
//---------<读写地址>------------------------------------------------- 
//写地址计数
always @(posedge clk or negedge rst_n)begin 
   if(!rst_n)begin
        wr_addr <= 'd0;
    end 
    else if(add_wr_addr)begin 
        if(end_wr_addr)begin 
            wr_addr <= 'd0;
        end
        else begin 
            wr_addr <= wr_addr + 1'b1;
        end 
    end
end 

assign add_wr_addr = (state_c == WRITE) && ~avm_waitrequest;
assign end_wr_addr = add_wr_addr && wr_addr == 24'hff_ff_ff-1;

//读地址计数
always @(posedge clk or negedge rst_n)begin 
   if(!rst_n)begin
        rd_addr <= 'd0;
    end 
    else if(add_rd_addr)begin 
        if(end_rd_addr)begin 
            rd_addr <= 'd0;
        end
        else begin 
            rd_addr <= rd_addr + 1'b1;
        end 
    end
end 

assign add_rd_addr = (state_c == READ) && ~avm_waitrequest;
assign end_rd_addr = add_rd_addr && rd_addr == 24'hff_ff_ff-1;

由于地址线共用,所以要判断现在是读状态还是写状态

c 复制代码
//---------<状态判断>------------------------------------------------- 
assign  avm_address = (state_c == WRITE) ?  {wr_addr[23],wr_addr[21:9],wr_addr[22],wr_addr[8:0]} : 
                                            {rd_addr[23],rd_addr[21:9],rd_addr[22],rd_addr[8:0]} ;

其他输出

c 复制代码
assign  tx_data = rd_q [7:0];
assign  tx_vld  = rd_rden;
assign  avm_writedata = wr_q;
assign  avm_read_n = ~(state_c == READ);
assign  avm_write_n = ~(state_c == WRITE);

4、其他

其他模块复用之前的。

四、实现效果

直接按下按键读出错乱的数据,按下复位,发送数据,将数据写入sdram

按下两次按键读出数据

读出的20个数据与发送的前20个相同,验证成功。

五、总结

相较于之前的几个存储器来说,sdram是并行的,且有其他的各种信号,端口较多比较复杂,且内部要用avalon协议传输数据,连线和数据之间的传输较为复杂,需要认真理解,本实验主要目的为理解sdram的操作和工作原理。

六、代码

1、top

c 复制代码
/*******************************************************************
Engineer: Turing_kun
Create Date: 2025-07-30 14:03:14
Description: 项目顶层
********************************************************************/

module top (
    input               clk             ,
    input               rst_n           ,
    input    [1:0]      sw              ,
//---------<sdram>------------------------------------------------- 
    output   [12:0]     sdram_addr      ,
    output   [1:0]      sdram_ba        ,
    output              sdram_cas_n     ,
    output              sdram_cke       ,
    output              sdram_cs_n      ,
    inout    [15:0]     sdram_dq        ,
    output   [1:0]      sdram_dqm       ,
    output              sdram_ras_n     ,
    output              sdram_we_n      ,
    output              sdram_clk       ,
//---------<uart>------------------------------------------------- 
    input               rx              ,
    output              tx              ,
//---------<key>------------------------------------------------- 
    input    [0:0]      key_in
);

wire            clk_100ms;
wire            clk_100ms_s;
wire            locked;

//---------<uart_rx>------------------------------------------------- 
wire    [7:0]   rx_data;
wire            rx_vld;
//---------<uart_tx>------------------------------------------------- 
wire    [7:0]   tx_data;
wire            tx_vld;
wire            tx_done;
//---------<key>-------------------------------------------------
wire            key_down; 

assign sdram_clk = clk_100ms_s;
sdram_top   inst_sdram_top(
    .clk        (clk_100ms  ),
    .clk_in     (clk        ),
    .clk_out    (clk        ),
    .rst_n      (rst_n      ),
    .rx_data    (rx_data    ),
    .rx_vld     (rx_vld     ),
    .tx_done    (tx_done    ),
    .tx_data    (tx_data    ),
    .tx_vld     (tx_vld     ),
    .key_down   (key_down   ),
    .sdram_addr (sdram_addr ),
    .sdram_ba   (sdram_ba   ),
    .sdram_cas_n(sdram_cas_n),
    .sdram_cke  (sdram_cke  ),
    .sdram_cs_n (sdram_cs_n ),
    .sdram_dq   (sdram_dq   ),
    .sdram_dqm  (sdram_dqm  ),
    .sdram_ras_n(sdram_ras_n),
    .sdram_we_n (sdram_we_n )
);

sdram_pll	sdram_pll_inst (
	.areset ( ~rst_n ),
	.inclk0 ( clk ),
	.c0     ( clk_100ms    ),
	.c1     ( clk_100ms_s   ),
	.locked ( locked )
	);

uart_rx     inst_uart_rx(
    .clk     (clk     ),
    .rst_n   (rst_n   ),
    .rx      (rx      ),
    .sw      (sw      ),
    .rx_data (rx_data ),
    .rx_done (rx_vld  )
);

uart_tx     inst_uart_tx(
    .clk     (clk     ),
    .rst_n   (rst_n   ),
    .tx_data (tx_data ),
    .tx_start(tx_vld  ),
    .sw      (sw      ),
    .tx      (tx      ),
    .tx_done (tx_done )
);

fsm_key     inst_fsm_key(
    .clk		(clk    ),
    .rst_n	    (rst_n	),
    .key_in	    (key_in	),
    .key_down   (key_down)
);
endmodule

2、sdram_top

c 复制代码
/*******************************************************************
Engineer: Turing_kun
Create Date: 2025-07-30 14:02:40
Description: SDRAM子顶层
********************************************************************/
    
module sdram_top( 
    input               clk             ,   //100mHz
    input               clk_in          ,   //50mHz
    input               clk_out         ,   //50mHz
    input               rst_n           ,
//---------<uart_rx>------------------------------------------------- 
    input    [7:0]      rx_data         ,
    input               rx_vld          ,
//---------<uart_tx>------------------------------------------------- 
    input               tx_done         ,
    output   [7:0]      tx_data         ,
    output              tx_vld          ,
//---------<key>------------------------------------------------- 
    input               key_down        ,
//---------<sdram_ip>-------------------------------------------------      
    output   [12:0]     sdram_addr      ,
    output   [1:0]      sdram_ba        ,
    output              sdram_cas_n     ,
    output              sdram_cke       ,
    output              sdram_cs_n      ,
    inout    [15:0]     sdram_dq        ,
    output   [1:0]      sdram_dqm       ,
    output              sdram_ras_n     ,
    output              sdram_we_n      
);

wire    [23:0]    avm_address           ;
wire    [15:0]    avm_writedata         ;
wire              avm_read_n            ;
wire              avm_write_n           ;
wire    [15:0]    avm_readdata          ;
wire              avm_readdatavalid     ;
wire              avm_waitrequest       ;

sdram_ctrl      inst_sdram_ctrl(
    .clk              (clk              ),
    .clk_in           (clk_in           ),
    .clk_out          (clk_out          ),
    .rst_n            (rst_n            ),
    .rx_data          (rx_data          ),
    .rx_vld           (rx_vld           ),
    .tx_done          (tx_done          ),
    .tx_data          (tx_data          ),
    .tx_vld           (tx_vld           ),
    .key_down         (key_down         ),
    .avm_address      (avm_address      ),
    .avm_writedata    (avm_writedata    ),
    .avm_read_n       (avm_read_n       ),
    .avm_write_n      (avm_write_n      ),
    .avm_readdata     (avm_readdata     ),
    .avm_readdatavalid(avm_readdatavalid),
    .avm_waitrequest  (avm_waitrequest  )
);

sdram_ip        inst_sdram_ip(
    .avs_address      (avm_address      ),      
    .avs_byteenable_n (2'b00            ), 
    .avs_chipselect   (1'b1             ),   
    .avs_writedata    (avm_writedata    ),    
    .avs_read_n       (avm_read_n       ),       
    .avs_write_n      (avm_write_n      ),      
    .avs_readdata     (avm_readdata     ),     
    .avs_readdatavalid(avm_readdatavalid),
    .avs_waitrequest  (avm_waitrequest  ),  
    .clk_clk          (clk              ),          
    .reset_reset_n    (rst_n            ),    
    .sdram_addr       (sdram_addr       ),       
    .sdram_ba         (sdram_ba         ),         
    .sdram_cas_n      (sdram_cas_n      ),      
    .sdram_cke        (sdram_cke        ),        
    .sdram_cs_n       (sdram_cs_n       ),       
    .sdram_dq         (sdram_dq         ),         
    .sdram_dqm        (sdram_dqm        ),        
    .sdram_ras_n      (sdram_ras_n      ),      
    .sdram_we_n       (sdram_we_n       ) 
);    
    
endmodule

3、sdram_ctrl

c 复制代码
/*******************************************************************
Engineer: Turing_kun
Create Date: 2025-07-30 14:05:40
Description: SDRAM控制模块
********************************************************************/

module sdram_ctrl #(parameter BURST_LENTH = 10)(
    input               clk                 ,   //100mHz
    input               clk_in              ,   //50mHz
    input               clk_out             ,   //50mHz
    input               rst_n               ,
//---------<uart_rx>---------------------------------
    input    [7:0]      rx_data             ,
    input               rx_vld              ,
//---------<uart_tx>---------------------------------
    input               tx_done             ,
    output   [7:0]      tx_data             ,
    output              tx_vld              ,
//---------<key>-------------------------------------
    input               key_down            ,
//---------<sdram_ip>------------------------------------------------- 
    output    [23:0]    avm_address         ,      
    output    [15:0]    avm_writedata       ,    
    output              avm_read_n          ,
    output              avm_write_n         ,
    input     [15:0]    avm_readdata        ,
    input               avm_readdatavalid   ,
    input               avm_waitrequest     
);

//rd_fifo
wire                    rd_rden     ;
wire                    rd_wren     ;
wire        [15:0]      rd_q        ;
wire                    rd_rdempty  ;
wire                    rd_wrfull   ;

//wr_fifo
wire                    wr_rden     ;
wire                    wr_wren     ;
wire        [15:0]      wr_q        ;
wire                    wr_rdempty  ;
wire                    wr_wrfull   ;
wire        [8:0]       wr_rdusedw  ;

//---------<状态参数>------------------------------------------------- 

reg     [1:0]   state_c ;
reg     [1:0]   state_n ;

localparam              IDLE    =       2'd0,
                        READ    =       2'd1,
                        WRITE   =       2'd2,
                        DONE    =       2'd3;

wire                    IDLE_2_READ;
wire                    READ_2_DONE;
wire                    IDLE_2_WRITE;
wire                    WRITE_2_DONE;

reg		[8:0]	cnt_burst	    ;
wire			add_cnt_burst   ;
wire			end_cnt_burst   ;

//写地址
reg		[23:0]	wr_addr	   ;
wire			add_wr_addr;
wire			end_wr_addr;
//读地址
reg		[23:0]	rd_addr	   ;
wire			add_rd_addr;
wire			end_rd_addr;

//---------<读FIFO例化>------------------------------------------------- 

rd_fifo	rd_fifo_inst (
	.aclr       ( ~rst_n        ),
	.data       ( avm_readdata  ),
	.rdclk      ( clk_out       ),
	.rdreq      ( rd_rden       ),
	.wrclk      ( clk           ),
	.wrreq      ( rd_wren       ),
	.q          ( rd_q          ),
	.rdempty    ( rd_rdempty    ),
	.wrfull     ( rd_wrfull     )
);

assign  rd_wren = ~rd_wrfull && avm_readdatavalid;
assign  rd_rden = ~rd_rdempty && tx_done;

//---------<写FIFO例化>------------------------------------------------- 

wr_fifo	wr_fifo_inst (
	.aclr       ( ~rst_n        ),
	.data       ( {2{rx_data}}  ),
	.rdclk      ( clk           ),
	.rdreq      ( wr_rden       ),
	.wrclk      ( clk_in        ),
	.wrreq      ( wr_wren       ),
	.q          ( wr_q          ),
	.rdempty    ( wr_rdempty    ),
	.rdusedw    ( wr_rdusedw    ),
	.wrfull     ( wr_wrfull     )
	);

assign  wr_wren = ~wr_wrfull && rx_vld;
assign  wr_rden = ~wr_rdempty && (state_c == WRITE) && ~avm_waitrequest;

//---------<突发计数器>------------------------------------------------- 

always @(posedge clk or negedge rst_n)begin 
   if(!rst_n)begin
        cnt_burst <= 'd0;
    end 
    else if(add_cnt_burst)begin 
        if(end_cnt_burst)begin 
            cnt_burst <= 'd0;
        end
        else begin 
            cnt_burst <= cnt_burst + 1'b1;
        end 
    end
end 

assign add_cnt_burst = ((state_c == READ) || (state_c == WRITE)) && ~avm_waitrequest;
assign end_cnt_burst = add_cnt_burst && cnt_burst == (BURST_LENTH-1);


//---------<state>------------------------------------------------- 

//第一段:同步时序描述状态转移
always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        state_c <= IDLE;
    end 
    else begin 
        state_c <= state_n;
    end 
end
    
//第二段:组合逻辑判断状态转移条件,描述状态转移规律
always @(*) begin
    case(state_c)
        IDLE  : begin
            if(IDLE_2_READ)
                state_n = READ;
            else if(IDLE_2_WRITE)
                state_n = WRITE;
            else 
                state_n = state_c;
        end
        READ  : state_n = (READ_2_DONE ) ? DONE : state_c;
        WRITE : state_n = (WRITE_2_DONE) ? DONE : state_c;
        DONE  : state_n = IDLE;
        default : state_n = IDLE;
    endcase
end

assign      IDLE_2_READ     =   (state_c == IDLE ) && key_down;
assign      READ_2_DONE     =   (state_c == READ ) && end_cnt_burst;
assign      IDLE_2_WRITE    =   (state_c == IDLE ) && wr_rdusedw >= BURST_LENTH;
assign      WRITE_2_DONE    =   (state_c == WRITE) && end_cnt_burst;

//---------<读写地址>------------------------------------------------- 
//写地址计数
always @(posedge clk or negedge rst_n)begin 
   if(!rst_n)begin
        wr_addr <= 'd0;
    end 
    else if(add_wr_addr)begin 
        if(end_wr_addr)begin 
            wr_addr <= 'd0;
        end
        else begin 
            wr_addr <= wr_addr + 1'b1;
        end 
    end
end 

assign add_wr_addr = (state_c == WRITE) && ~avm_waitrequest;
assign end_wr_addr = add_wr_addr && wr_addr == 24'hff_ff_ff-1;

//读地址计数
always @(posedge clk or negedge rst_n)begin 
   if(!rst_n)begin
        rd_addr <= 'd0;
    end 
    else if(add_rd_addr)begin 
        if(end_rd_addr)begin 
            rd_addr <= 'd0;
        end
        else begin 
            rd_addr <= rd_addr + 1'b1;
        end 
    end
end 

assign add_rd_addr = (state_c == READ) && ~avm_waitrequest;
assign end_rd_addr = add_rd_addr && rd_addr == 24'hff_ff_ff-1;

//---------<tx_data tx_vld>------------------------------------------------- 
assign  tx_data = rd_q [7:0];
assign  tx_vld  = rd_rden;
 
//---------<状态判断>------------------------------------------------- 
assign  avm_address = (state_c == WRITE) ?  {wr_addr[23],wr_addr[21:9],wr_addr[22],wr_addr[8:0]} : 
                                            {rd_addr[23],rd_addr[21:9],rd_addr[22],rd_addr[8:0]} ;

assign  avm_writedata = wr_q;
assign  avm_read_n = ~(state_c == READ);
assign  avm_write_n = ~(state_c == WRITE);
endmodule

其他复用之前的项目

相关推荐
I'm a winner1 天前
新手入门Makefile:FPGA项目实战教程(二)
笔记·fpga开发
我爱C编程1 天前
基于FPGA的8PSK+卷积编码Viterbi译码通信系统,包含帧同步,信道,误码统计,可设置SNR
fpga开发·通信·8psk·帧同步·snr·卷积编码·维特比译码
I'm a winner1 天前
新手入门 Makefile:FPGA 项目实战教程(三)
fpga开发
范纹杉想快点毕业1 天前
嵌入式 C 语言编程规范个人学习笔记,参考华为《C 语言编程规范》
linux·服务器·数据库·笔记·单片机·嵌入式硬件·fpga开发
lazyduck2 天前
从半年到一年的 bug 往事:TCP modbus的卡死与补救
fpga开发·modbus
范纹杉想快点毕业2 天前
《嵌入式 C 语言编码规范与工程实践个人笔记》参考华为C语言规范标准
服务器·c语言·stm32·单片机·华为·fpga开发·51单片机
Chipi Chipi3 天前
FPGA即插即用Verilog驱动系列——串口数据、命令解析
fpga开发
FPGA_ADDA3 天前
基于 AMDXCVU47P HBM2 FPGA 的 2 路 100G 光纤 PCIe 高性能计算加速卡
fpga开发·vu47p·100g光纤pcie·高性能计算加速卡
霖003 天前
高级项目——基于FPGA的串行FIR滤波器
人工智能·经验分享·matlab·fpga开发·信息与通信·信号处理