【FPGA零基础学习之旅#17】搭建串口收发与储存双口RAM系统
🎉欢迎来到FPGA专栏~搭建串口收发与储存双口RAM系统
- ☆* o(≧▽≦)o *☆嗨~我是小夏与酒🍹
- ✨博客主页:小夏与酒的博客
- 🎈该系列文章专栏:FPGA学习之旅
- 文章作者技术和水平有限,如果文中出现错误,希望大家能指正🙏
- 📜 欢迎大家关注! ❤️
🎉 目录-串口收发与储存双口RAM系统
- 一、效果演示
- 二、基础知识
- 2.1 实现目标
- 2.2 所需基础模块
- 三、系统分析
- 四、代码编写
- 4.1 控制模块
- 4.2 顶层模块
- 五、仿真测试激励文件
- 5.1 key_model
- 5.2 testbench编写
- 5.3 仿真结果
- 六、板级验证
一、效果演示
🥝输入数据:
🥝输出数据:
🥝串口助手分析:
按下第一次按键,FPGA开始连续发送数据,按下第二次按键,FPGA停止发送数据。
二、基础知识
2.1 实现目标
使用按键消抖、串口发送与接收模块,以及双端口RAM模块实现串口发送数据到FPGA中,FPGA接收到数据后将数据存储在双口RAM中,当按下按键时FPGA将RAM中存储的数据再通过串口发送出去,再次按下按键后,FPGA停止发送数据。
2.2 所需基础模块
相关模块学习文章:
🥝【FPGA零基础学习之旅#10】按键消抖模块设计与验证(一段式状态机实现);
🥝【FPGA零基础学习之旅#13】串口发送模块设计与验证;
🥝【FPGA零基础学习之旅#14】串口发送字符串;
🥝【FPGA零基础学习之旅#15】串口接收模块设计与验证(工业环境);
🥝【FPGA零基础学习之旅#16】嵌入式块RAM-双口ram的使用。
三、系统分析
参考小梅哥FPGA设计的系统框图:
通过串口发送数据到FPGA中,FPGA接收到数据后将数据存储在双口RAM的一段连续空间中。当需要时,按下按键S0,则FPGA将RAM中存储的数据通过串口发送出去;再次按下S0,则停止数据发送。
进行功能划分:串口接收模块、按键消抖模块、RAM模块、串口发送模块以及控制模块。
在此给出所需使用的模块代码:
按键消抖模块:
//
//模块:按键消抖模块
//key_state:输出消抖之后按键的状态
//key_flag:按键消抖结束时产生一个时钟周期的高电平脉冲
//
module KeyFilter(
input Clk,
input Rst_n,
input key_in,
output reg key_flag,
output reg key_state
);
//按键的四个状态
localparam
IDLE = 4'b0001,
FILTER1 = 4'b0010,
DOWN = 4'b0100,
FILTER2 = 4'b1000;
//状态寄存器
reg [3:0] curr_st;
//边沿检测输出上升沿或下降沿
wire pedge;
wire nedge;
//计数寄存器
reg [19:0]cnt;
//使能计数寄存器
reg en_cnt;
//计数满标志信号
reg cnt_full;//计数满寄存器
//------<边沿检测电路的实现>------
//边沿检测电路寄存器
reg key_tmp0;
reg key_tmp1;
//边沿检测
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)begin
key_tmp0 <= 1'b0;
key_tmp1 <= 1'b0;
end
else begin
key_tmp0 <= key_in;
key_tmp1 <= key_tmp0;
end
end
assign nedge = (!key_tmp0) & (key_tmp1);
assign pedge = (key_tmp0) & (!key_tmp1);
//------<状态机主程序>------
//状态机主程序
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)begin
curr_st <= IDLE;
en_cnt <= 1'b0;
key_flag <= 1'b0;
key_state <= 1'b1;
end
else begin
case(curr_st)
IDLE:begin
key_flag <= 1'b0;
if(nedge)begin
curr_st <= FILTER1;
en_cnt <= 1'b1;
end
else
curr_st <= IDLE;
end
FILTER1:begin
if(cnt_full)begin
key_flag <= 1'b1;
key_state <= 1'b0;
curr_st <= DOWN;
en_cnt <= 1'b0;
end
else if(pedge)begin
curr_st <= IDLE;
en_cnt <= 1'b0;
end
else
curr_st <= FILTER1;
end
DOWN:begin
key_flag <= 1'b0;
if(pedge)begin
curr_st <= FILTER2;
en_cnt <= 1'b1;
end
else
curr_st <= DOWN;
end
FILTER2:begin
if(cnt_full)begin
key_flag <= 1'b1;
key_state <= 1'b1;
curr_st <= IDLE;
en_cnt <= 1'b0;
end
else if(nedge)begin
curr_st <= DOWN;
en_cnt <= 1'b0;
end
else
curr_st <= FILTER2;
end
default:begin
curr_st <= IDLE;
en_cnt <= 1'b0;
key_flag <= 1'b0;
key_state <= 1'b1;
end
endcase
end
end
//------<20ms计数器>------
//20ms计数器
//Clk 50_000_000Hz
//一个时钟周期为20ns
//需要计数20_000_000 / 20 = 1_000_000次
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
cnt <= 20'd0;
else if(en_cnt)
cnt <= cnt + 1'b1;
else
cnt <= 20'd0;
end
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
cnt_full <= 1'b0;
else if(cnt == 999_999)
cnt_full <= 1'b1;
else
cnt_full <= 1'b0;
end
endmodule
串口接收模块:
//
//模块名称:串口接收模块(工业环境)
//
module uart_byte_rx(
input Clk,//50M
input Rst_n,
input [2:0] baud_set,
input data_rx,
output reg [7:0] data_byte,
output reg Rx_Done
);
reg s0_Rx,s1_Rx;//同步寄存器
reg tmp0_Rx,tmp1_Rx;//数据寄存器
reg [15:0]bps_DR;//分频计数器计数最大值
reg [15:0]div_cnt;//分频计数器
reg bps_clk;//波特率时钟
reg [7:0]bps_cnt;
reg uart_state;
reg [2:0] r_data_byte [7:0];
reg [2:0]START_BIT;
reg [2:0]STOP_BIT;
wire nedge;
//--------<同步寄存器处理>--------
//用于消除亚稳态
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)begin
s0_Rx <= 1'b0;
s1_Rx <= 1'b0;
end
else begin
s0_Rx <= data_rx;
s1_Rx <= s0_Rx;
end
end
//--------<数据寄存器处理>--------
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)begin
tmp0_Rx <= 1'b0;
tmp1_Rx <= 1'b0;
end
else begin
tmp0_Rx <= s1_Rx;
tmp1_Rx <= tmp0_Rx;
end
end
//--------<下降沿检测>--------
assign nedge = !tmp0_Rx & tmp1_Rx;
//--------<div_cnt模块>--------
//得到不同计数周期的计数器
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
div_cnt <= 16'd0;
else if(uart_state)begin
if(div_cnt == bps_DR)
div_cnt <= 16'd0;
else
div_cnt <= div_cnt + 1'b1;
end
else
div_cnt <= 16'd0;
end
//--------<bps_clk信号的产生>--------
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
bps_clk <= 1'b0;
else if(div_cnt == 16'd1)
bps_clk <= 1'b1;
else
bps_clk <= 1'b0;
end
//--------<bps_clk计数模块>--------
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
bps_cnt <= 8'd0;
else if(bps_cnt == 8'd159 || (bps_cnt == 8'd12 && (START_BIT > 2)))
bps_cnt <= 8'd0;
else if(bps_clk)
bps_cnt <= bps_cnt + 1'b1;
else
bps_cnt <= bps_cnt;
end
//--------<Rx_Done模块>--------
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
Rx_Done <= 1'b0;
else if(bps_cnt == 8'd159)
Rx_Done <= 1'b1;
else
Rx_Done <= 1'b0;
end
//--------<波特率查找表>--------
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
bps_DR <= 16'd324;
else begin
case(baud_set)
0:bps_DR <= 16'd324;
1:bps_DR <= 16'd162;
2:bps_DR <= 16'd80;
3:bps_DR <= 16'd53;
4:bps_DR <= 16'd26;
default:bps_DR <= 16'd324;
endcase
end
end
//--------<采样数据接收模块>--------
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)begin
START_BIT <= 3'd0;
r_data_byte[0] <= 3'd0;
r_data_byte[1] <= 3'd0;
r_data_byte[2] <= 3'd0;
r_data_byte[3] <= 3'd0;
r_data_byte[4] <= 3'd0;
r_data_byte[5] <= 3'd0;
r_data_byte[6] <= 3'd0;
r_data_byte[7] <= 3'd0;
STOP_BIT <= 3'd0;
end
else if(bps_clk)begin
case(bps_cnt)
0:begin
START_BIT <= 3'd0;
r_data_byte[0] <= 3'd0;
r_data_byte[1] <= 3'd0;
r_data_byte[2] <= 3'd0;
r_data_byte[3] <= 3'd0;
r_data_byte[4] <= 3'd0;
r_data_byte[5] <= 3'd0;
r_data_byte[6] <= 3'd0;
r_data_byte[7] <= 3'd0;
STOP_BIT <= 3'd0;
end
6,7,8,9,10,11:START_BIT <= START_BIT + s1_Rx;
22,23,24,25,26,27:r_data_byte[0] <= r_data_byte[0] + s1_Rx;
38,39,40,41,42,43:r_data_byte[1] <= r_data_byte[1] + s1_Rx;
54,55,56,57,58,59:r_data_byte[2] <= r_data_byte[2] + s1_Rx;
70,71,72,73,74,75:r_data_byte[3] <= r_data_byte[3] + s1_Rx;
86,87,88,89,90,91:r_data_byte[4] <= r_data_byte[4] + s1_Rx;
102,103,104,105,106,107:r_data_byte[5] <= r_data_byte[5] + s1_Rx;
118,119,120,121,122,123:r_data_byte[6] <= r_data_byte[6] + s1_Rx;
134,135,136,137,138,139:r_data_byte[7] <= r_data_byte[7] + s1_Rx;
150,151,152,153,154,155:STOP_BIT <= STOP_BIT + s1_Rx;
default:begin
START_BIT <= START_BIT;
r_data_byte[0] <= r_data_byte[0];
r_data_byte[1] <= r_data_byte[1];
r_data_byte[2] <= r_data_byte[2];
r_data_byte[3] <= r_data_byte[3];
r_data_byte[4] <= r_data_byte[4];
r_data_byte[5] <= r_data_byte[5];
r_data_byte[6] <= r_data_byte[6];
r_data_byte[7] <= r_data_byte[7];
STOP_BIT <= STOP_BIT;
end
endcase
end
end
//--------<数据状态判定模块>--------
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
data_byte <= 8'd0;
else if(bps_cnt == 8'd159)begin
data_byte[0] <= r_data_byte[0][2];
data_byte[1] <= r_data_byte[1][2];
data_byte[2] <= r_data_byte[2][2];
data_byte[3] <= r_data_byte[3][2];
data_byte[4] <= r_data_byte[4][2];
data_byte[5] <= r_data_byte[5][2];
data_byte[6] <= r_data_byte[6][2];
data_byte[7] <= r_data_byte[7][2];
end
else
;
end
//--------<uart_state模块>--------
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
uart_state <= 1'b0;
else if(nedge)
uart_state <= 1'b1;
else if(Rx_Done || (bps_cnt == 8'd12 && (START_BIT > 2)))
uart_state <= 1'b0;
else
uart_state <= uart_state;
end
endmodule
串口发送模块:
//
//模块名称:串口发送模块
//
module uart_byte_tx(
input Clk,
input Rst_n,
input [7:0] data_byte,
input send_en,
input [2:0] baud_set,
output reg uart_tx,
output reg Tx_Done,
output reg uart_state
);
reg bps_clk;//波特率时钟
reg [15:0]div_cnt;//分频计数器
reg [15:0]bps_DR;//分频计数最大值
reg [3:0]bps_cnt;//波特率计数时钟
//定义数据的起始位和停止位
localparam START_BIT = 1'b0;
localparam STOP_BIT = 1'b1;
reg [7:0]r_data_byte;//数据寄存器
//--------<uart状态模块>--------
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
uart_state <= 1'b0;
else if(send_en)
uart_state <= 1'b1;
else if(bps_cnt == 4'd11)//bps_cnt计数达到11次,即发送结束
uart_state <= 1'b0;
else
uart_state <= uart_state;
end
//--------<使能分频计数模块>-------
// assign en_cnt = uart_state;
//--------<寄存待发送的数据,使数据保持稳定>--------
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
r_data_byte <= 8'd0;
else if(send_en)
r_data_byte <= data_byte;
else
r_data_byte <= r_data_byte;
end
//--------<波特率查找表>--------
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
bps_DR <= 16'd5207;
else begin
case(baud_set)
0:bps_DR <= 16'd5207;
1:bps_DR <= 16'd2603;
2:bps_DR <= 16'd1301;
3:bps_DR <= 16'd867;
4:bps_DR <= 16'd433;
default:bps_DR <= 16'd5207;
endcase
end
end
//--------<Div_Cnt模块>--------
//得到不同计数周期的计数器
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
div_cnt <= 16'd0;
else if(uart_state)begin // assign en_cnt = uart_state;
if(div_cnt == bps_DR)
div_cnt <= 16'd0;
else
div_cnt <= div_cnt + 1'b1;
end
else
div_cnt <= 16'd0;
end
//--------<bps_clk信号的产生>--------
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
bps_clk <= 1'b0;
else if(div_cnt == 16'd1)
bps_clk <= 1'b1;
else
bps_clk <= 1'b0;
end
//--------<bps_cnt计数模块>--------
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
bps_cnt <= 4'd0;
else if(bps_cnt == 4'd11)//clr信号
bps_cnt <= 4'd0;
else if(bps_clk)
bps_cnt <= bps_cnt + 1'b1;
else
bps_cnt <= bps_cnt;
end
//--------<Tx_Done模块>--------
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
Tx_Done <= 1'b0;
else if(bps_cnt == 4'd11)
Tx_Done <= 1'b1;
else
Tx_Done <= 1'b0;
end
//--------<数据位输出模块-10选1多路器>--------
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
uart_tx <= 1'b1;
else begin
case(bps_cnt)
0:uart_tx <= 1'b1;
1:uart_tx <= START_BIT;
2:uart_tx <= r_data_byte[0];
3:uart_tx <= r_data_byte[1];
4:uart_tx <= r_data_byte[2];
5:uart_tx <= r_data_byte[3];
6:uart_tx <= r_data_byte[4];
7:uart_tx <= r_data_byte[5];
8:uart_tx <= r_data_byte[6];
9:uart_tx <= r_data_byte[7];
10:uart_tx <= STOP_BIT;
default:uart_tx <= 1'b1;
endcase
end
end
endmodule
四、代码编写
上述已给出按键消抖模块和串口收发模块,在本文中主要编写控制模块和顶层模块。
4.1 控制模块
为了实现FPGA将接收到的数据存储到双口RAM的一段连续空间中,就需要设计一个可以实现写地址数据自加的控制逻辑,且其控制信号为串口接收模块输出的Rx_Done信号。每来一个Rx_Done就表明接收成功一字节数,地址数进行加一:
assign wren = Rx_Done;
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
wraddress <= 8'd0;
else if(Rx_Done)
wraddress <= wraddress + 1'b1;
else
wraddress <= wraddress;
end
当按下按键S0,FPGA将RAM中存储的数据通过串口发送出去。需要实现按键按下即启动连续读操作,再次按下可暂停读操作:
reg do_send;
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
do_send <= 1'd0;
else if(Key_flag && !Key_state)
do_send <= ~do_send;
end
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
rdaddress <= 8'd0;
else if(do_send && Tx_Done)
rdaddress <= rdaddress + 8'd1;
else
rdaddress <= rdaddress;
end
在仿真双端口RAM时发现其输出会延迟两个系统时钟周期。这是为了保证数据变化稳定之后才进行数据输出,所以在此将驱动Send_en的信号接两级寄存器进行延迟两拍。当按键按下后启动一次发送,然后判断上一字节是否发送结束,是则进行下一字节发送否则不进行下一次发送:
reg r0_send_done,r1_send_done;
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)begin
r0_send_done <= 1'b0;
r1_send_done <= 1'b0;
end
else begin
r0_send_done <= (do_send && Tx_Done);
r1_send_done <= r0_send_done;
end
end
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
Send_en <= 1'b0;
else if(Key_flag && !Key_state)
Send_en <= 1'b1;
else if(r1_send_done)
Send_en <= 1'b1;
else
Send_en <= 1'b0;
end
为了保证RAM地址操作的有效性,在写地址和读地址代码部分加上范围限制:
//--------<dpram写地址加1>--------
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
wraddress <= 8'd0;
else if(Rx_Done)
wraddress <= wraddress + 1'b1;
else if(wraddress > 8'd255) //当写地址大于配置ip核时的值时,返回到0地址;
wraddress <= 8'd0;
else
wraddress <= wraddress;
end
//--------<dpram读地址加1>--------
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
rdaddress <= 8'd0;
else if(do_send && Tx_Done)
rdaddress <= rdaddress + 8'd1;
else if(rdaddress > 8'd255) //当读地址大于255时,返回到0地址;
rdaddress <= 8'd0;
else
rdaddress <= rdaddress;
end
完整的控制模块代码:system_ctrl.v
module system_ctrl(
input Clk,
input Rst_n,
input Key_flag,
input Key_state,
input Rx_Done,
input Tx_Done,
output wren,
output reg Send_en,
output reg [7:0] rdaddress,
output reg [7:0] wraddress
);
assign wren = Rx_Done;
reg do_send;
reg r0_send_done;
reg r1_send_done;
//--------<dpram写地址加1>--------
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
wraddress <= 8'd0;
else if(Rx_Done)
wraddress <= wraddress + 1'b1;
else if(wraddress > 8'd255) //当写地址大于配置ip核时的值时,返回到0地址;
wraddress <= 8'd0;
else
wraddress <= wraddress;
end
//--------<翻转标志信号>--------
//按下一次按键开始连续发送数据,再按一次按键停止发送;
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
do_send <= 1'b0;
else if(Key_flag && !Key_state)
do_send <= ~do_send;
end
//--------<dpram读地址加1>--------
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
rdaddress <= 8'd0;
else if(do_send && Tx_Done)
rdaddress <= rdaddress + 8'd1;
else if(rdaddress > 8'd255) //当读地址大于255时,返回到0地址;
rdaddress <= 8'd0;
else
rdaddress <= rdaddress;
end
//--------<RAM的两拍延迟>--------
//双端口RAM的输出延迟两个系统时钟周期;
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)begin
r0_send_done <= 1'b0;
r1_send_done <= 1'b1;
end
else begin
r0_send_done <= (do_send && Tx_Done);
r1_send_done <= r0_send_done;
end
end
//--------<按键控制与连续读操作>--------
//Send_en由按键信号和r1_send_done信号同时控制;
//r1_send_done信号使得串口连续读取dpram的数据;
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
Send_en <= 1'b0;
else if(Key_flag && !Key_state)
Send_en <= 1'b1;
else if(r1_send_done)
Send_en <= 1'b1;
else
Send_en <= 1'b0;
end
endmodule
控制模块的RTL视图:
4.2 顶层模块
将串口接收模块、按键消抖模块、RAM模块、串口发送模块以及控制模块例化到顶层模块中。
uart_system_top.v:
module uart_system_top(
input Clk,
input Rst_n,
input key_in,
input uart_rx,
output uart_tx
);
wire [7:0]rx_data;
wire [7:0]tx_data;
wire Key_flag;
wire Key_state;
wire Rx_Done;
wire Tx_Done;
wire wren;
wire Send_en;
wire [7:0]rdaddress;
wire [7:0]wraddress;
uart_byte_rx uart_byte_rx(
.Clk(Clk),
.Rst_n(Rst_n),
.baud_set(3'd0),
.data_rx(uart_rx),
.data_byte(rx_data),
.Rx_Done(Rx_Done)
);
dpram dpram(
.clock(Clk),
.data(rx_data),
.rdaddress(rdaddress),
.wraddress(wraddress),
.wren(wren),
.q(tx_data)
);
KeyFilter KeyFilter(
.Clk(Clk),
.Rst_n(Rst_n),
.key_in(key_in),
.key_flag(Key_flag),
.key_state(Key_state)
);
uart_byte_tx uart_byte_tx(
.Clk(Clk),
.Rst_n(Rst_n),
.data_byte(tx_data),
.send_en(Send_en),
.baud_set(3'd0),
.uart_tx(uart_tx),
.Tx_Done(Tx_Done),
.uart_state()
);
system_ctrl system_ctrl(
.Clk(Clk),
.Rst_n(Rst_n),
.Key_flag(Key_flag),
.Key_state(Key_state),
.Rx_Done(Rx_Done),
.Tx_Done(Tx_Done),
.wren(wren),
.Send_en(Send_en),
.rdaddress(rdaddress),
.wraddress(wraddress)
);
endmodule
顶层模块的RTL视图:
五、仿真测试激励文件
5.1 key_model
key_model仿真模型用于有按键控制信号的项目进行仿真测试,模拟实际情况中的按键抖动。 在仿真时将该模型也添加到工程中使用。
key_model.v:
`timescale 1ns/1ns
module key_model(press,key);
input press;
output reg key;
reg [15:0]myrand;
initial begin
key = 1'b1;
end
always@(posedge press)
press_key;
task press_key;
begin
repeat(50)begin
myrand = {$random}%65536;//0~65535;
#myrand key = ~key;
end
key = 0;
#25000000;
repeat(50)begin
myrand = {$random}%65536;//0~65535;
#myrand key = ~key;
end
key = 1;
#25000000;
end
endtask
endmodule
5.2 testbench编写
完整的仿真测试激励文件:
uart_system_top_tb.v:
`timescale 1ns/1ns
`define clock_period 20
module uart_system_top_tb;
reg Clk;
reg Rst_n;
wire Key_in;
wire uart_rx;
wire uart_tx;
reg [7:0]data_byte_t;
reg send_en;
wire [2:0]baud_set;
wire Tx_Done;
reg press;
assign baud_set = 3'd0;
uart_system_top uart_system_top(
.Clk(Clk),
.Rst_n(Rst_n),
.key_in(Key_in),
.uart_rx(uart_tx),
.uart_tx(uart_rx)
);
uart_byte_tx uart_byte_tx(
.Clk(Clk),
.Rst_n(Rst_n),
.data_byte(data_byte_t),
.send_en(send_en),
.baud_set(baud_set),
.uart_tx(uart_tx),
.Tx_Done(Tx_Done),
.uart_state()
);
key_model key_model(
.press(press),
.key(Key_in)
);
initial Clk = 1;
always#(`clock_period / 2) Clk = ~Clk;
initial begin
Rst_n = 1'b0;
press = 0;
data_byte_t = 8'd0;
send_en = 1'd0;
#(`clock_period*20 + 1 );
Rst_n = 1'b1;
#(`clock_period*50);
data_byte_t = 8'haa;
send_en = 1'd1;
#`clock_period;
send_en = 1'd0;
@(posedge Tx_Done)
#(`clock_period*5000);
data_byte_t = 8'h55;
send_en = 1'd1;
#`clock_period;
send_en = 1'd0;
@(posedge Tx_Done)
#(`clock_period*5000);
data_byte_t = 8'h33;
send_en = 1'd1;
#`clock_period;
send_en = 1'd0;
@(posedge Tx_Done)
#(`clock_period*5000);
data_byte_t = 8'haf;
send_en = 1'd1;
#`clock_period;
send_en = 1'd0;
@(posedge Tx_Done)
#(`clock_period*5000);
press = 1;
#(`clock_period*3)
press = 0;
#(`clock_period*2000000)
$stop;
end
endmodule
5.3 仿真结果
六、板级验证
🥝输入数据储存到双口RAM中:
🥝输出RAM中的数据:
🧸结尾
- ❤️ 感谢您的支持和鼓励! 😊🙏
- 📜您可能感兴趣的内容:
- 【FPGA零基础学习之旅#14】串口发送字符串
- 【Python】串口通信-与FPGA、蓝牙模块实现串口通信(Python+FPGA)
- 【Arduino TinyGo】【最新】使用Go语言编写Arduino-环境搭建和点亮LED灯
- 【全网首发开源教程】【Labview机器人仿真与控制】Labview与Solidworks多路支配关系-四足爬行机器人仿真与控制