对RAM(随机存储器)进行读和写操作。
使用tool工具生成IP核RAM,8位读地址8位写地址。
`timescale 1ns/1ns
`define clk_period 20
module dpram_tb;
reg clock;
reg [7:0]data;
reg [7:0]rdaddress;
reg [7:0]wraddress;
reg wren;
wire [7:0]q;
integer n; //有符号数 寄存器类型
dpram dpram0(
.clock(clock),
.data(data),
.rdaddress(rdaddress), //读地址
.wraddress(wraddress), //写地址 写地址变化后慢一个时钟写入
.wren(wren), //写请求信号,高电平有效
.q(q) //读出来的结果 rdaddress地址后两个时钟出结果
);
initial clock = 1;
always #(`clk_period/2) clock = ~clock;
initial begin
data = 0;
rdaddress = 20;
wraddress = 0;
wren = 0;
#(`clk_period*20+1);
for( n=0;n<=15; n=n+1)
begin
wren = 1;
data = 255-n;
wraddress = n;
#`clk_period;
end
wren = 0;
#(`clk_period*50);
for( n=0;n<=15;n=n+1)
begin
rdaddress = n;
#`clk_period;
end
#(`clk_period*10);
$stop;
end
endmodule
当读地址为零时,两个周期后q信号输出数据。延迟两个周期。
将按键异步信号消抖处理,产生Key_flag和Key_state信号,处理按键按下事件。
module Key_shake(Clk,Rst_n,Key_in,Key_flag,Key_data);
input Clk;
input Rst_n;
input Key_in;
output reg Key_flag; //当按键按下20ms消除抖动成功后产生一个脉冲flag信号,检测到产生flag信号后产生按键按下低电平。
output reg Key_data;
reg [3:0]state;
reg [19:0]count; //寄存器计数
reg count_start;//时能计时
reg count_full;//计满脉冲信号
reg reg0,reg1;
reg key_in_now0,key_in_now1;
wire rise,fall;
localparam
High_pulse = 4'b0001, //高电平稳定状态
Low_eliminate = 4'b0010, //键下降沿稳定状态
Low_pulse = 4'b0100, //低电平稳定状态
High_eliminate = 4'b1000; //键上升沿稳定状态
always @(posedge Clk or negedge Rst_n) //对输入按键信号做同步处理,消除异步信号亚稳态的影响。
if(!Rst_n) begin
key_in_now0 <= 1'b0;
key_in_now1 <=1'b0;
end
else begin
key_in_now0 <= Key_in;
key_in_now1 <= key_in_now0;
end
always @(posedge Clk or negedge Rst_n) //脉冲边沿检测 两个寄存器
if(!Rst_n) begin
reg0 <= 1'b0;
reg1 <=1'b0;
end
else begin
reg0 <= key_in_now1;
reg1 <= reg0;
end
assign fall = !reg0 & reg1; //检测到下降沿
assign rise = reg0 & !reg1; //检测到上升沿
always @(posedge Clk or negedge Rst_n) //20ms计数器
if(!Rst_n)
count <= 20'b0;
else if(count_start)
count <= count + 20'b1;
else count <= 20'b0;
always @(posedge Clk or negedge Rst_n) //20ms计数器计数满标志脉冲信号
if(!Rst_n)
count_full <= 1'b0;
else if(count == 99_9999)
count_full <= 1'b1;
else count_full <= 1'b0;
always @(posedge Clk or negedge Rst_n)
if(!Rst_n) begin
Key_data <= 1'b1;
state <= High_pulse;
Key_flag <= 1'b0;
count_start <= 1'b0;
end
else case(state)
High_pulse : begin Key_flag <= 0;
Key_data <= 1'b1;
if(fall) begin
state <= Low_eliminate;
count_start <= 1'b1; //使能开始20ms计数
end
else state <= High_pulse;
end
Low_eliminate :
if(count_full) begin
state <= Low_pulse;
Key_flag <= 1'b1;
Key_data <= 1'b0;
count_start <= 1'b0; //关闭使能20ms计数
end
else begin
if(rise) begin
state <= High_pulse;
count_start <= 1'b0; //关闭使能20ms计数
end
else
state <= Low_eliminate;
end
Low_pulse: begin Key_flag <= 1'b0;
if(rise) begin
state <= High_eliminate;
count_start <= 1'b1; //开始计时
end
else state <= Low_pulse;
end
High_eliminate :
if(count_full) begin
Key_data <= 1'b1;
Key_flag <= 1'b1;
state <= High_pulse;
end
else begin
if(fall) begin
state <= Low_pulse;
count_start <= 1'b0;
end else
state <= High_eliminate;
end
default : begin
state <= High_pulse; //默认状态
count_start <= 1'b0;
Key_data <= 1'b1;
Key_flag <= 1'b0;
end
endcase
endmodule
接收外来信号并传输到dpram中。
module uart_data_rx( //串口接收模块
Clk,
Rst_n,
Rs232_rx,
Bps_set,
Data_out,
Done_rx
);
input Clk;
input Rst_n;
input Rs232_rx;
input [2:0]Bps_set;
output reg [7:0]Data_out;
output reg Done_rx;
reg [8:0]bps_max;//计数值
reg now_reg0,rs232_rx_now; //同步寄存器 消除亚稳态
reg data_reg0,data_reg1; //数据寄存器 判断高低电平
wire low;
reg en_data;
reg [8:0]bps_con; //bps_clk时钟计数器
reg bps_clk;
reg [7:0]bps_count;
reg [2:0]Data[7:0]; //共有8个Data数据每一个Data数据有3位 例如110
reg [2:0]start_bite;
reg [2:0]stop_bite;
//查找表选择计数值
always @(posedge Clk or negedge Rst_n)
if(!Rst_n)
bps_max <= 9'b0;
else begin
case(Bps_set)
0: bps_max <= 9'd324;
1: bps_max <= 9'd162;
2: bps_max <= 9'd80;
3: bps_max <= 9'd53;
4: bps_max <= 9'd26;
default : bps_max <= 9'd324;
endcase
end
//消除亚稳态
always @(posedge Clk or negedge Rst_n)
if(!Rst_n) begin
now_reg0 <= 0;
rs232_rx_now <= 0;
end
else begin
now_reg0 <= Rs232_rx;
rs232_rx_now <= now_reg0;
end
//判断低电平
always @(posedge Clk or negedge Rst_n)
if(!Rst_n) begin
data_reg0 <= 0;
data_reg1 <= 0;
end
else begin
data_reg0 <= rs232_rx_now;
data_reg1 <= data_reg0;
end
assign low = !data_reg0 & data_reg1;
//使能开始计数
always @(posedge Clk or negedge Rst_n)
if(!Rst_n)
bps_con <= 9'b0;
else
if(en_data) begin
if(bps_con == bps_max)
bps_con <= 9'b0;
else bps_con <= bps_con + 1'b1;
end else
bps_con <= 9'b0;
//产生波特率时钟
always @(posedge Clk or negedge Rst_n)
if(!Rst_n)
bps_clk <= 0;
else if(bps_con ==9'b1)
bps_clk <= 1'b1;
else bps_clk <= 1'b0;
//计数波特率时钟
always @(posedge Clk or negedge Rst_n)
if(!Rst_n)
bps_count <= 8'b0;
else if(Done_rx || ((bps_count == 8'd12) && (start_bite >2)))
bps_count <= 8'b0;
else if(bps_clk)
bps_count <= bps_count + 1'b1;
//产生Done_rx计数满信号
always @(posedge Clk or negedge Rst_n)
if(!Rst_n)
Done_rx <= 1'b0;
else if(bps_count == 8'd159)
Done_rx <= 1'b1;
else Done_rx <= 1'b0;
//将Data的值给Data_out输出查看
always @(posedge Clk or negedge Rst_n)
if(!Rst_n)
Data_out <= 8'b0;
else if(bps_count == 8'd159) begin
Data_out[0] <= Data[0][2]; //如果中间6位数值和加起来大于3(100.101.110)第3位数值将大于1,由此可以将第3位的值直接传给Data_out数据
Data_out[1] <= Data[1][2];
Data_out[2] <= Data[2][2];
Data_out[3] <= Data[3][2];
Data_out[4] <= Data[4][2];
Data_out[5] <= Data[5][2];
Data_out[6] <= Data[6][2];
Data_out[7] <= Data[7][2];
end
//将Data的中间6位数据累加
always @(posedge Clk or negedge Rst_n)
if(!Rst_n)begin
start_bite <= 3'b0;
Data[0] <= 3'b0;
Data[1] <= 3'b0;
Data[2] <= 3'b0;
Data[3] <= 3'b0;
Data[4] <= 3'b0;
Data[5] <= 3'b0;
Data[6] <= 3'b0;
Data[7] <= 3'b0;
stop_bite <= 3'b0;
end else if(bps_clk)
begin
case(bps_count)
0:begin
start_bite <= 3'b0;
Data[0] <= 3'b0;
Data[1] <= 3'b0;
Data[2] <= 3'b0;
Data[3] <= 3'b0;
Data[4] <= 3'b0;
Data[5] <= 3'b0;
Data[6] <= 3'b0;
Data[7] <= 3'b0;
stop_bite <= 3'b0;
end
6,7,8,9,10,11: start_bite <= start_bite + rs232_rx_now;
22,23,24,25,26,27: Data[0] <= Data[0] + rs232_rx_now;
38,39,40,41,42,43: Data[1] <= Data[1] + rs232_rx_now;
54,55,56,57,58,59: Data[2] <= Data[2] + rs232_rx_now;
70,71,72,73,74,75: Data[3] <= Data[3] + rs232_rx_now;
86,87,88,89,90,91: Data[4] <= Data[4] + rs232_rx_now;
102,103,104,105,106,107: Data[5] <= Data[5] + rs232_rx_now;
118,119,120,121,122,123: Data[6] <= Data[6] + rs232_rx_now;
134,135,136,137,138,139: Data[7] <= Data[7] + rs232_rx_now;
150,151,152,153,154,155: stop_bite <= stop_bite + rs232_rx_now;
default : begin
start_bite <= start_bite;
Data[0] <= Data[0];
Data[1] <= Data[1];
Data[2] <= Data[2];
Data[3] <= Data[3];
Data[4] <= Data[4];
Data[5] <= Data[5];
Data[6] <= Data[6];
Data[7] <= Data[7];
stop_bite <= stop_bite;
end
endcase
end
//判断是否完成接收或无开始错误,产生使能信号
always @(posedge Clk or negedge Rst_n)
if(!Rst_n)
en_data <= 1'b0;
else if(low)
en_data <= 1'b1;
else if(Done_rx ||((bps_count == 8'd12) && (start_bite >2)))
en_data <= 1'b0;
else en_data <= en_data;
endmodule
使用quartus2软件工具生成存储器IP核。
读取dpram中的值,con_en使能为高电平输出dapram中的值。
module UART_TX(
Clk,
Rst_n,
Bps_set,
Con_en,
Data_in,
Rs232_tx,
Done_tx,
Uart_state
);
input Clk;
input Rst_n;
input [2:0]Bps_set;
input Con_en;
input [7:0]Data_in;
output reg Rs232_tx;
output reg Done_tx;
output reg Uart_state;
reg [15:0]count; //计数器定值
reg [15:0]high_count; //计数值
reg [7:0]Data_in_r;
reg bps_clk;
reg [3:0]bps_count;
localparam start_bite = 1'b0;
localparam stop_bite = 1'b1;
//查找表输出计数值
always @(posedge Clk or negedge Rst_n)
if(!Rst_n)
count <= 16'd5207;
else begin
case(Bps_set)
0: count <= 16'd5207;
1: count <= 16'd2603;
2: count <= 16'd1301;
3: count <= 16'd865;
4: count <= 16'd433;
default : count <= 16'd5207;
endcase
end
//产生Uart_state使能信号
always @(posedge Clk or negedge Rst_n)
if(!Rst_n)
Uart_state <= 1'b0;
else if(Con_en)
Uart_state <= 1'b1;
else if(bps_count == 4'd11)
Uart_state <= 1'b0;
else Uart_state <= Uart_state;
//计数满产生bps_clk信号
always @(posedge Clk or negedge Rst_n)
if(!Rst_n)
high_count <= 0;
else if(Uart_state) begin
if(high_count == count)
high_count <= 0;
else high_count <= high_count + 1'b1;
end
else high_count <= 0;
always @(posedge Clk or negedge Rst_n)
if(!Rst_n)
bps_clk <= 1'b0;
else if(high_count == 16'b1)
bps_clk <= 1'b1;
else bps_clk <= 1'b0;
//计数bps_clk的高电平到11计数满为0 产生Done_tx高点平信号
always @(posedge Clk or negedge Rst_n)
if(!Rst_n)
bps_count <= 4'b0;
else if(bps_count == 4'd11)
bps_count <= 4'b0;
else if(bps_clk )
bps_count <= bps_count + 4'b1;
else bps_count <= bps_count;
always @(posedge Clk or negedge Rst_n)
if(!Rst_n)
Done_tx <= 1'b0;
else if(bps_count == 4'd11)
Done_tx <= 1;
else Done_tx <= 0;
always @(posedge Clk or negedge Rst_n )
if(!Rst_n)
Data_in_r <= 0;
else if(Con_en)
Data_in_r <= Data_in ;
else Data_in_r <= Data_in_r;
//十选一多路选择器
always @(posedge Clk or negedge Rst_n)
if(!Rst_n)
Rs232_tx <= 1'b1;
else begin
case(bps_count)
0: Rs232_tx <= 1'b1;
1: Rs232_tx <= start_bite;
2: Rs232_tx <= Data_in_r[0];
3: Rs232_tx <= Data_in_r[1];
4: Rs232_tx <= Data_in_r[2];
5: Rs232_tx <= Data_in_r[3];
6: Rs232_tx <= Data_in_r[4];
7: Rs232_tx <= Data_in_r[5];
8: Rs232_tx <= Data_in_r[6];
9: Rs232_tx <= Data_in_r[7];
10: Rs232_tx <= stop_bite;
default : Rs232_tx <= 1'b1;
endcase
end
endmodule
控制其他模块的输入输出。
module control(
Key_state,
Key_flag,
Clk,
Rst_n,
Done_rx,
Done_tx,
Rdaddress,
Wraddress,
Wren,
En_send
);
input Key_flag;
input Key_state;
input Clk, Rst_n;
input Done_rx,Done_tx;
output reg [7:0]Rdaddress;
output reg [7:0]Wraddress;
output Wren;
output reg En_send;
reg put_button;
reg delay_t0,delay_t1;
assign Wren = Done_rx;
always @(posedge Clk or negedge Rst_n) //写入地址
if(!Rst_n)
Wraddress <= 8'd0;
else if(Done_rx)
Wraddress <= Wraddress + 1'b1;
else Wraddress <= Wraddress ;
always @(posedge Clk or negedge Rst_n) //乒乓操作
if(!Rst_n)
put_button <= 1'b0;
else if(Key_flag && !Key_state)
put_button <= ~ put_button;
always @(posedge Clk or negedge Rst_n) //读操作
if(!Rst_n)
Rdaddress <= 8'b0;
else if(put_button && Done_tx)
Rdaddress <= Rdaddress + 1'b1;
else Rdaddress <= Rdaddress;
//将信号延迟两个周期
always @(posedge Clk or negedge Rst_n)
if(!Rst_n) begin
delay_t0 <= 1'b0;
delay_t1 <= 1'b0;
end
else begin
delay_t0 <= (put_button && Done_tx);
delay_t1 <= delay_t0;
end
//控制产生发送使能信号
always @(posedge Clk or negedge Rst_n)
if(!Rst_n)
En_send <= 1'b0;
else if(delay_t1)
En_send <= 1'b1;
else if(Key_flag && !Key_state)
En_send <= 1'b1;
else En_send <= 1'b0;
endmodule
//控制产生发送使能信号
always @(posedge Clk or negedge Rst_n)
if(!Rst_n)
En_send <= 1'b0;
else if(delay_t1)
En_send <= 1'b1;
else if(Key_flag && !Key_state)
En_send <= 1'b1;
else En_send <= 1'b0;
if(Key_flag && !Key_state)条件为第一次按下按键后的产生En_send使能信号发送地址为0的数据,if(delay_t1) 为按下按键后每当tx模块输出一次地址数据完成产生Done_tx信号自动输出下一个地址数据。
module Uart_dapram(
Clk,
Rs232_rx,
Rst_n,
Key_in,
Rs232_tx
);
input Clk;
input Rs232_rx;
input Rst_n;
input Key_in;
output Rs232_tx;
wire [7:0]Data_bite_rx,Data_bite_tx;
wire Done_tx,Done_rx;
wire Key_flag,Key_data;
wire Con_en;
wire [7:0]rdaddress,wraddress;
wire wren;
UART_TX UART_TX0( //串口发送模块
.Clk(Clk),
.Rst_n(Rst_n),
.Bps_set(3'b0),
.Con_en(Con_en),
.Data_in(Data_bite_tx),
.Rs232_tx(Rs232_tx),
.Done_tx(Done_tx), //一次发送完成标志信号
.Uart_state() //模块状态标志信号
);
uart_data_rx uart_data_rx0( //串口接收模块
.Clk(Clk),
.Rst_n(Rst_n),
.Rs232_rx(Rs232_rx),
.Bps_set(3'b0),
.Data_out(Data_bite_rx),
.Done_rx(Done_rx)
);
Key_shake Key_shake0(
.Clk(Clk),
.Rst_n(Rst_n),
.Key_in(Key_in),
.Key_flag(Key_flag),
.Key_data(Key_data)
);
dpram dpram0(
.clock(Clk),
.data(Data_bite_rx),
.rdaddress(rdaddress), //读地址
.wraddress(wraddress), //写地址
.wren(wren), //写请求信号,高电平有效
.q(Data_bite_tx)
);
control control0(
.Clk(Clk),
.Rst_n(Rst_n),
.Done_rx(Done_rx),
.Key_state(Key_data),
.Key_flag(Key_flag),
.Done_tx(Done_tx),
.Rdaddress(rdaddress),
.Wraddress(wraddress),
.Wren(wren),
.En_send(Con_en)
);
endmodule
`timescale 1ns/1ns
`define clk_period 20
module Uart_dapram_tb;
reg Clk,Con_en;
reg [7:0]Data_in;
wire Rs232_rx;
reg Rst_n;
wire Key_in;
reg press;
wire Rs232_tx;
wire [2:0]Bps_set;
wire Done_tx;
assign Bps_set = 3'd0;
Uart_dapram Uart_dapram1(
.Clk(Clk),
.Rs232_rx(Rs232_rx),
.Rst_n(Rst_n),
.Key_in(Key_in),
.Rs232_tx(Rs232_tx)
);
//用tx模块产生数据激励信号
UART_TX UART_TX0(
.Clk(Clk),
.Rst_n(Rst_n),
.Bps_set(Bps_set),
.Con_en(Con_en),
.Data_in(Data_in),
.Rs232_tx(Rs232_rx),
.Done_tx(Done_tx),
.Uart_state()
);
//产生按键信号 /按键模型
Key_model Key_model0(
.Press(press),
.Key(Key_in)
);
initial Clk = 1;
always#(`clk_period/2) Clk = ~Clk;
initial begin
Rst_n = 1'b0;
Data_in = 8'b0;
press = 1'b0;
Con_en = 1'b0;
#(`clk_period*20+1);
Rst_n = 1'b1;
#(`clk_period*20+1);
Con_en = 1'b1;
Data_in = 8'hae;
#(`clk_period);
Con_en = 1'b0;
@(posedge Done_tx)
#(`clk_period*3000);
Con_en = 1'b1;
Data_in = 8'h5b;
#(`clk_period);
Con_en = 1'b0;
@(posedge Done_tx)
#(`clk_period*3000);
Con_en = 1'b1;
Data_in = 8'hef;
#(`clk_period);
Con_en = 1'b0;
@(posedge Done_tx)
#(`clk_period*3000);
Con_en = 1'b1;
Data_in = 8'haa;
#(`clk_period);
Con_en = 1'b0;
@(posedge Done_tx)
#(`clk_period*3000);
press = 1;
#(`clk_period*5);
press = 0;
$stop;
end
endmodule
Key_model模块为按键事件模型,模拟实际中产生的按键抖动信号。
当读地址rdaddress变化后,dpram寄存器输出Data_bite_tx信号。