一、任务
按键 按下,将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
cpp
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
cpp
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
cpp
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
cpp
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
cpp
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来实现乒乓缓存。