FPGA学习笔记——简单的乒乓缓存(RAM)

发布于:2025-08-05 ⋅ 阅读:(19) ⋅ 点赞:(0)

一、任务

按键按下,将ROM里面存储的数据进行乒乓缓存(我这里的数据是方波的数据),然后以串口的形式发送出去(串口可以看看我之前写的),以50MHz的速率输入,以50MHz的速率输出(这里没有涉及到速度---面积的换算)。


二、分析

首先,先要一个ROM IP核,将.mif文件.hex文件放进去,让它形成一个数据发送源,其次,还需要两个双端口的RAM将接收到的数据来进行乒乓缓存,这里就要用状态机来写(状态机写法更简单,逻辑清晰),

双端口RAM的状态可以有:

IDLE

RAM1

W2R1

W1R2

首先,RAM里面是没有数据的,当按键按下,ROM将不间断的发送数据,RAM1就开始写入数据,当RAM1的数据写满(地址达到最大),就跳入下一个状态,RAM1的读取RAM2的写入,当RAM1中的数据读完或者RAM2中的数据写满(地址达到最大),就跳回W2R1的状态,形成一个循环


三、需要用到的IP 核

单端口ROM配置

双端口RAM配置


四、Visio图


五、代码

key.v

module key(
input   wire    clk     ,
input   wire    rst_n   ,
input   wire    key     ,
output  reg     key_out
);
//消抖后的按键
parameter delay = 100_0000;//20ms
reg [20:0] cnt;

always@(posedge clk)//延时消抖的过程
if(!rst_n)
    cnt<=0;
else if(key == 0)begin//1.按键按下 2.抖动过程 3.中间稳定
    if(cnt == delay - 1) 
        cnt <= cnt;//300ms
    else
        cnt <= cnt + 1;//0--1--2  
end
else //1.抖动 2.没有按键
    cnt<=0;

always@(posedge clk)//产生消抖信号:1clk的持续时间
if(!rst_n)
    key_out<=0;
else if(cnt == delay - 2)
    key_out<=1;
else
    key_out<=0;

endmodule

rom_ctrl.v

module rom_ctrl (
input       wire                clk         ,
input       wire                rst_n       ,
input       wire                key_out     ,
output      wire      [7:0]     data_rom    ,
output      reg                 rom_wren            
);

reg     [7:0]   address;
wire     [7:0]   q      ;

reg     en;

always @(posedge clk) begin
    if(!rst_n)
        en <= 0;
    else if (key_out)
        en <= ~en;
    else
        en <= en;
end

always @(posedge clk) begin
    if(!rst_n)
        address <= 0;
    else if(en == 1)
        address <= address + 1;
    else
        address <= address;
end

always @(posedge clk) begin
    if(!rst_n)
        rom_wren <= 0;
    else if ( key_out )
        rom_wren <= ~rom_wren;
end

assign data_rom = q;

rom	rom_inst (
	.aclr ( !rst_n ),
	.address ( address ),
	.clock ( clk ),
	.q ( q )
	);



endmodule

ctrl.v

module ctrl (
input   wire            clk          ,
input   wire            rst_n        ,
input   wire    [7:0]   data_rom     ,//并行数据 ,其它模块用
input   wire            done_tx      ,    //字节传输完成
input   wire            rom_wren     ,
output  reg     [7:0]   data_tx      ,//并行输入 --- 变化
output  reg             start         //数据有效信号
);

//ram1
reg 	[7:0]    data1      ;
reg     [7:0]    rdaddress1 ;
reg              rden1      ;
reg  	[7:0]    wraddress1 ;
reg 	         wren1      ;
wire	[7:0]    q1         ;

//ram2
reg 	[7:0]    data2      ;
reg     [7:0]    rdaddress2 ;
reg              rden2      ;
reg  	[7:0]    wraddress2 ;
reg 	         wren2      ;
wire	[7:0]    q2         ;

reg flag1;
reg flag2;

parameter   NUM = 256;

localparam  IDLE = 4'b0001,
            RAM1 = 4'b0010,
            W2R1 = 4'b0100,
            W1R2 = 4'b1000;

reg [3:0] cur_state , next_state;

//描述状态转移:现态
always @(posedge clk) begin
   if(!rst_n)
       cur_state <= IDLE;
   else 
       cur_state <= next_state;
end

always @(*) begin
    if(!rst_n)
        next_state = IDLE;
    else
        case (cur_state)
            IDLE: begin
                next_state = RAM1;
            end
            RAM1:begin
                if(wraddress1 == NUM - 1 )
                    next_state = W2R1;
                else
                    next_state = cur_state;
            end
            W2R1:begin
                if(wraddress2 == NUM - 1 && flag1 )
                    next_state = W1R2;
                else
                    next_state = cur_state;
            end
            W1R2: begin
                if(wraddress1 == NUM - 1 && flag2 )
                    next_state = W2R1;
                else
                    next_state = cur_state;
            end
            default: next_state = IDLE;
        endcase
end

//ram1
always @(posedge clk) begin
    if(!rst_n) begin
        data1      <= 0;
        rdaddress1 <= 0;
        rden1      <= 0;
        wraddress1 <= 0;
        wren1      <= 0;
        flag1      <= 0;
    end
    else
        case (cur_state)
            IDLE:begin 
                data1      <= 0;
                rdaddress1 <= 0;
                rden1      <= 0;
                wraddress1 <= 0;
                wren1      <= 0;
                flag1      <= 0;
            end
            RAM1:begin
                if(rom_wren) begin
                    wraddress1 <= wraddress1 + 1;
                    data1      <= data_rom;
                end

                wren1      <= rom_wren;
                flag1      <= 0;
            end
            W2R1:begin
                data1      <= 0;
                wren1      <= 0;
                wraddress1 <= 0;

                if(done_tx) begin
                    rden1 <= 1;
                    if(rdaddress1 == NUM - 1) begin
                        rdaddress1 <= 0;
                        flag1      <= 1;
                    end
                    else begin
                        rdaddress1 <= rdaddress1 + 1;
                        flag1      <= 0;
                    end
                end
                else
                    rden1 <= 0;
            end
            W1R2: begin
                if(rom_wren) begin
                    wraddress1 <= wraddress1 + 1;
                    data1      <= data_rom;
                end

                wren1      <= rom_wren;
                flag1      <= 0;
            end
            default: begin 
                data1      <= 0;
                rdaddress1 <= 0;
                rden1      <= 0;
                wraddress1 <= 0;
                wren1      <= 0;
                flag1      <= 0;
            end
        endcase
end




//ram2
always @(posedge clk) begin
    if(!rst_n) begin
        data2      <= 0;
        rdaddress2 <= 0;
        rden2      <= 0;
        wraddress2 <= 0;
        wren2      <= 0;
        flag2      <=0;
    end
    else
        case (cur_state)
            IDLE:begin
                data2      <= 0;
                rdaddress2 <= 0;
                rden2      <= 0;
                wraddress2 <= 0;
                wren2      <= 0;
                flag2      <=0;
            end
            RAM1: ;
            W2R1: begin
                data2      <= data_rom;
                rdaddress2 <= 0;
                rden2      <= 0;
                wraddress2 <= wraddress2 + 1;
                wren2      <= rom_wren;
                flag2      <=0;
            end
            W1R2: begin
                data2      <= 0;
                wren2      <= 0;
                wraddress2 <= 0;
                if(done_tx) begin
                    rden2      <= 1;
                    if(rdaddress2 == NUM - 1) begin
                        rdaddress2 <= 0;
                        flag2      <= 1;
                    end
                    else begin
                        rdaddress2 <= rdaddress2 + 1;
                        flag2      <= 0;
                    end
                end
                else 
                    rden2      <= 0;
            end
            default: begin
                data2      <= 0;
                rdaddress2 <= 0;
                rden2      <= 0;
                wraddress2 <= 0;
                wren2      <= 0;
                flag2      <=0;
            end
        endcase
end

//start
always @(posedge clk) begin
    if(!rst_n) begin
        data_tx <= 0;
        start   <= 0;
    end
    else
        case (cur_state)
            IDLE:begin
                data_tx <= 0;
                start   <= 0;
            end
            RAM1:begin
                data_tx <= 0;
                if(wraddress1 == NUM - 1)
                    start   <= 1;
                else
                    start   <= 0;
            end
            W2R1:begin
                data_tx <= q1;
                if(rden1)
                    start   <= 1;
                else
                    start   <= 0;
            end
            W1R2: begin
                data_tx <= q2;
                if(rden2)
                    start   <= 1;
                else
                    start   <= 0;
            end
            default: begin
                data_tx <= 0;
                start   <= 0;
            end
        endcase
end



ram	ram_inst1 (
	.aclr ( !rst_n ),
	.clock ( clk ),
	.data ( data1 ),
	.rdaddress ( rdaddress1 ),
	.rden ( rden1 ),
	.wraddress ( wraddress1 ),
	.wren ( wren1 ),
	.q ( q1 )
	);

ram	ram_inst2 (
	.aclr ( !rst_n ),
	.clock ( clk ),
	.data ( data2 ),
	.rdaddress ( rdaddress2 ),
	.rden ( rden2 ),
	.wraddress ( wraddress2 ),
	.wren ( wren2 ),
	.q ( q2 )
	);



endmodule

tx.v

 module tx (
 input   wire            clk       ,
 input   wire            rst_n     ,
 input   wire    [7:0]   data_tx   ,//并行输入 --- 变化
 input   wire            start     ,//数据有效信号
 output  wire            tx        ,  //串行输出
 output  wire            done_tx     //字节传输完成
 );
 parameter   sysclk = 50_000_000  ,//系统时钟下:1s
             bps    = 115200      , //波特率
             delay  = sysclk / bps;//1bit工作周期

 reg  [7:0]   data_reg;
 reg  [12:0]  cnt;//周期计数
 reg  [3:0]   cnt_bit;//bit计数
 reg en_tx ;
 reg tx_reg;//发送数据寄存器

 //寄存数据
 always @(posedge clk) begin
     if(!rst_n)
         data_reg <= 0;
     else if(start)
         data_reg <= data_tx;
     else
         data_reg <= data_reg;
 end

 //产生使能信号
 always @(posedge clk) begin
     if(!rst_n)
         en_tx <= 0;
     else if (start)
         en_tx <= 1;
     else if ( cnt_bit == 9  && cnt == delay - 1)
         en_tx <= 0;
     else
         en_tx <= en_tx;
 end

 //周期计数
 always @(posedge clk) begin
     if(!rst_n)
         cnt <= 0;
     else if (  en_tx ) begin
         if (cnt == delay - 1)
             cnt <= 0;
         else 
             cnt <= cnt + 1;
     end
     else
         cnt <= 0;
 end

 //bit 计数
 always @(posedge clk) begin
     if(!rst_n)
         cnt_bit <= 0;
     else if (en_tx == 1) begin //使能打开
         if( cnt == delay - 1 ) begin // 周期计数最大值
             if(cnt_bit == 9) // bit最大值
                 cnt_bit <= 0;
             else 
                 cnt_bit <= cnt_bit + 1;
         end else 
             cnt_bit <= cnt_bit;
     end
     else //使能关闭
         cnt_bit <= 0;
 end

 //发送数据
 always @(posedge clk) begin
     if(!rst_n)
         tx_reg <= 1; //空闲
     else if( en_tx )begin
         if ( cnt_bit == 0 )
             tx_reg <= 0; //起始位 
         else if( cnt_bit > 0 && cnt_bit < 9 ) //数据位
             tx_reg <= data_reg[cnt_bit - 1]; //起始位 
         else
             tx_reg <= 1; //停止位
     end
     else
         tx_reg <= 1; //空闲
 end

 assign tx      = tx_reg;
 assign done_tx = (cnt_bit == 9 && cnt == delay - 1) ? 1 : 0;//长 or 短

 endmodule

top.v

module top (
input   wire    clk     ,
input   wire    rst_n   ,
input   wire    key     ,
output  wire    tx          
);
//rom
wire      [7:0]     data_rom;
wire                rom_wren;
//key
wire    key_out;
//ctrl

//tx
wire    [7:0]   data_tx;
wire            start  ;
wire            done_tx;


ctrl ctrl_u(
.     clk        (clk     )  ,
.     rst_n      (rst_n   )  ,
.     data_rom   (data_rom)  ,//并行数据 ,其它模块用
.     done_tx    (done_tx )  ,    //字节传输完成
.     rom_wren   (rom_wren)  ,
.     data_tx    (data_tx )  ,//并行输入 --- 变化
.     start      (start   )   //数据有效信号
);


rom_ctrl rom_ctrl_u(
.     clk       (clk     )  ,
.     rst_n     (rst_n   )  ,
.     key_out   (key_out )  ,
.     data_rom  (data_rom)  ,
.     rom_wren  (rom_wren)          
);

tx tx_u(
.   clk      (clk    ) ,
.   rst_n    (rst_n  ) ,
.   data_tx  (data_tx) ,//并行输入 --- 变化
.   start    (start  ) ,//数据有效信号
.   tx       (tx     ) ,  //串行输出
.   done_tx  (done_tx)   //字节传输完成
);

key key_u(
.   clk    (clk    ) ,
.   rst_n  (rst_n  ) ,
.   key    (key    ) ,
.   key_out(key_out)
);



endmodule


六、现象


以上就是用RAM来实现乒乓缓存。


网站公告

今日签到

点亮在社区的每一天
去签到