FPGA学习笔记——简单的乒乓缓存(RAM)
一、任务
按键按下,将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;//300mselsecnt <= 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;
elsekey_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) beginif(!rst_n)en <= 0;else if (key_out)en <= ~en;elseen <= en;
endalways @(posedge clk) beginif(!rst_n)address <= 0;else if(en == 1)address <= address + 1;elseaddress <= address;
endalways @(posedge clk) beginif(!rst_n)rom_wren <= 0;else if ( key_out )rom_wren <= ~rom_wren;
endassign 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) beginif(!rst_n)cur_state <= IDLE;else cur_state <= next_state;
endalways @(*) beginif(!rst_n)next_state = IDLE;elsecase (cur_state)IDLE: beginnext_state = RAM1;endRAM1:beginif(wraddress1 == NUM - 1 )next_state = W2R1;elsenext_state = cur_state;endW2R1:beginif(wraddress2 == NUM - 1 && flag1 )next_state = W1R2;elsenext_state = cur_state;endW1R2: beginif(wraddress1 == NUM - 1 && flag2 )next_state = W2R1;elsenext_state = cur_state;enddefault: next_state = IDLE;endcase
end//ram1
always @(posedge clk) beginif(!rst_n) begindata1 <= 0;rdaddress1 <= 0;rden1 <= 0;wraddress1 <= 0;wren1 <= 0;flag1 <= 0;endelsecase (cur_state)IDLE:begin data1 <= 0;rdaddress1 <= 0;rden1 <= 0;wraddress1 <= 0;wren1 <= 0;flag1 <= 0;endRAM1:beginif(rom_wren) beginwraddress1 <= wraddress1 + 1;data1 <= data_rom;endwren1 <= rom_wren;flag1 <= 0;endW2R1:begindata1 <= 0;wren1 <= 0;wraddress1 <= 0;if(done_tx) beginrden1 <= 1;if(rdaddress1 == NUM - 1) beginrdaddress1 <= 0;flag1 <= 1;endelse beginrdaddress1 <= rdaddress1 + 1;flag1 <= 0;endendelserden1 <= 0;endW1R2: beginif(rom_wren) beginwraddress1 <= wraddress1 + 1;data1 <= data_rom;endwren1 <= rom_wren;flag1 <= 0;enddefault: begin data1 <= 0;rdaddress1 <= 0;rden1 <= 0;wraddress1 <= 0;wren1 <= 0;flag1 <= 0;endendcase
end//ram2
always @(posedge clk) beginif(!rst_n) begindata2 <= 0;rdaddress2 <= 0;rden2 <= 0;wraddress2 <= 0;wren2 <= 0;flag2 <=0;endelsecase (cur_state)IDLE:begindata2 <= 0;rdaddress2 <= 0;rden2 <= 0;wraddress2 <= 0;wren2 <= 0;flag2 <=0;endRAM1: ;W2R1: begindata2 <= data_rom;rdaddress2 <= 0;rden2 <= 0;wraddress2 <= wraddress2 + 1;wren2 <= rom_wren;flag2 <=0;endW1R2: begindata2 <= 0;wren2 <= 0;wraddress2 <= 0;if(done_tx) beginrden2 <= 1;if(rdaddress2 == NUM - 1) beginrdaddress2 <= 0;flag2 <= 1;endelse beginrdaddress2 <= rdaddress2 + 1;flag2 <= 0;endendelse rden2 <= 0;enddefault: begindata2 <= 0;rdaddress2 <= 0;rden2 <= 0;wraddress2 <= 0;wren2 <= 0;flag2 <=0;endendcase
end//start
always @(posedge clk) beginif(!rst_n) begindata_tx <= 0;start <= 0;endelsecase (cur_state)IDLE:begindata_tx <= 0;start <= 0;endRAM1:begindata_tx <= 0;if(wraddress1 == NUM - 1)start <= 1;elsestart <= 0;endW2R1:begindata_tx <= q1;if(rden1)start <= 1;elsestart <= 0;endW1R2: begindata_tx <= q2;if(rden2)start <= 1;elsestart <= 0;enddefault: begindata_tx <= 0;start <= 0;endendcase
endram 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 ,//系统时钟下:1sbps = 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) beginif(!rst_n)data_reg <= 0;else if(start)data_reg <= data_tx;elsedata_reg <= data_reg;end//产生使能信号always @(posedge clk) beginif(!rst_n)en_tx <= 0;else if (start)en_tx <= 1;else if ( cnt_bit == 9 && cnt == delay - 1)en_tx <= 0;elseen_tx <= en_tx;end//周期计数always @(posedge clk) beginif(!rst_n)cnt <= 0;else if ( en_tx ) beginif (cnt == delay - 1)cnt <= 0;else cnt <= cnt + 1;endelsecnt <= 0;end//bit 计数always @(posedge clk) beginif(!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;endelse //使能关闭cnt_bit <= 0;end//发送数据always @(posedge clk) beginif(!rst_n)tx_reg <= 1; //空闲else if( en_tx )beginif ( cnt_bit == 0 )tx_reg <= 0; //起始位 else if( cnt_bit > 0 && cnt_bit < 9 ) //数据位tx_reg <= data_reg[cnt_bit - 1]; //起始位 elsetx_reg <= 1; //停止位endelsetx_reg <= 1; //空闲endassign 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来实现乒乓缓存。