概述
本学习笔记主要参考小梅哥B站教学视频,网址如下:
https://www.bilibili.com/video/BV1va411c7Dz?p=1
使用的编译器为Vivado,HDL语言为verilog
一、串口接收原理与思路
二、串口接收程序设计与调试
设计文件uart_byte_rx:
module uart_byte_rx(
Clk,
Reset_n,
Baud_Set,
uart_rx,
Data,
Rx_Done
);
input Clk;
input Reset_n;
input [2:0]Baud_Set;
input uart_rx;
output reg[7:0]Data;
output reg Rx_Done;
//边沿检测
//存储数据
reg [1:0]uart_rx_r; //定义两个D触发器,判断前后的值
always@(posedge Clk)begin
uart_rx_r[0] <= uart_rx;
uart_rx_r[1] <= uart_rx_r[0]; //[1]是前一刻的值
end
wire pedge_uart_rx;//上升沿检测
//assign pedge_uart_rx = (uart_rx_r[1] == 0) && (uart_rx_r[0] == 1);
assign pedge_uart_rx = (uart_rx_r == 2'b01);
wire nedge_uart_rx;//下降沿检测
assign nedge_uart_rx = (uart_rx_r == 2'b10);
//波特率设置
//1位数据分成了16小段,9600的波特率:1_000_000_000/9600/16/20=325
reg[8:0] bps_DR;
always@(*)
case(Baud_Set)
0:bps_DR = 1_000_000_000/9600/16/20 -1;
1:bps_DR = 1_000_000_000/19200/16/20 -1;
2:bps_DR = 1_000_000_000/38400/16/20 -1;
3:bps_DR = 1_000_000_000/57600/16/20 -1;
4:bps_DR = 1_000_000_000/115200/16/20 -1;
default:bps_DR = 1_000_000_000/9600/16/20 -1;
endcase
/
//产生bps_CLK
wire bps_clk_16x;
assign bps_clk_16x = (div_cnt == bps_DR / 2); //16段的每一小段也要在中心处采样
reg RX_EN;
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
RX_EN <= 0;
else if(nedge_uart_rx)
RX_EN <= 1;
else if(Rx_Done || (sta_bit >= 4)) //sta_bit >= 4是错误的起始位,是一个干扰
RX_EN <= 0;
reg [8:0] div_cnt; //分频计数器
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
div_cnt <= 0;
else if(RX_EN)begin //nedge_uart_rx仅一个脉冲,不足以使得div_cnt一直加,所以这里用前面定义的RX_EN。
if(div_cnt == bps_DR)
div_cnt <= 0;
else
div_cnt <= div_cnt + 1'b1;
end
//采样数据
reg[7:0] bps_cnt; //16*10=160个bps_clk,10是10位(包括起始和终止)
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
bps_cnt <= 0;
else if(RX_EN)begin
if(bps_clk_16x)begin
if(bps_cnt == 160 - 1)
bps_cnt <= 0;
else
bps_cnt <= bps_cnt + 1'b1;
end
else
bps_cnt <= bps_cnt;
end
else
bps_cnt <= 0;
//接收部分
reg [2:0] r_data[7:0]; //累加每一位处获得的值,8位;(二维数组) reg width name number
reg [2:0] sta_bit; //单独定义起始位
reg [2:0] sto_bit; //停止位
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)begin
sta_bit <= 0;
sto_bit <= 0;
r_data[0] <= 0; //只能一个个的赋值
r_data[1] <= 0;
r_data[2] <= 0;
r_data[3] <= 0;
r_data[4] <= 0;
r_data[5] <= 0;
r_data[6] <= 0;
r_data[7] <= 0;
end
else if(bps_clk_16x)begin
case(bps_cnt)
0:begin //等于零的时刻 把这些清零,防止出错
sta_bit <= 0;
sto_bit <= 0;
r_data[0] <= 0;
r_data[1] <= 0;
r_data[2] <= 0;
r_data[3] <= 0;
r_data[4] <= 0;
r_data[5] <= 0;
r_data[6] <= 0;
r_data[7] <= 0;
end
5,6,7,8,9,10,11:sta_bit <= sta_bit + uart_rx; //7次采样,16次中,主要是中间的几次有效和稳定
21,22,23,24,25,26,27:r_data[0] <= r_data[0] + uart_rx;
37,38,39,40,41,42,43:r_data[1] <= r_data[1] + uart_rx;
53,54,55,56,57,58,59:r_data[2] <= r_data[2] + uart_rx;
69,70,71,72,73,74,75:r_data[3] <= r_data[3] + uart_rx;
85,86,87,88,89,90,91:r_data[4] <= r_data[4] + uart_rx;
101,102,103,104,105,106,107:r_data[5] <= r_data[5] + uart_rx;
117,118,119,120,121,122,123:r_data[6] <= r_data[6] + uart_rx;
133,134,135,136,137,138,139:r_data[7] <= r_data[7] + uart_rx;
149,150,151,152,153,154,155:sto_bit <= sto_bit + uart_rx;
default:; //什么都不做
endcase
end
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
Data <= 0;
else if(bps_clk_16x && (bps_cnt == 159))begin //bps_cnt为159的时候就可以输出结果了
Data[0] <= (r_data[0] >= 4)?1'b1:1'b0;
Data[1] <= (r_data[1] >= 4)?1'b1:1'b0;
Data[2] <= (r_data[2] >= 4)?1'b1:1'b0;
Data[3] <= (r_data[3] >= 4)?1'b1:1'b0;
Data[4] <= (r_data[4] >= 4)?1'b1:1'b0;
Data[5] <= (r_data[5] >= 4)?1'b1:1'b0;
Data[6] <= (r_data[6] >= 4)?1'b1:1'b0;
Data[7] <= (r_data[7] >= 4)?1'b1:1'b0;
end
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
Rx_Done <= 0;
else if(bps_clk_16x && (bps_cnt == 159))
Rx_Done <= 1;
else
Rx_Done <= 0;
endmodule
测试文件uart_byte_rx_tb:
`timescale 1ns / 1ps
module uart_byte_rx_tb();
reg Clk;
reg Reset_n;
wire [2:0]Baud_Set;
reg uart_rx;
wire [7:0]Data;
wire Rx_Done;
assign Baud_Set = 4;
uart_byte_rx uart_byte_rx(
Clk,
Reset_n,
Baud_Set,
uart_rx,
Data,
Rx_Done
);
initial Clk = 1;
always#10 Clk = ~Clk;
initial begin
Reset_n = 0;
uart_rx = 1;
#201;
Reset_n = 1;
#200;
uart_tx_byte(8'h5a); //调用task
@(posedge Rx_Done);
#5000;
uart_tx_byte(8'ha5);
@(posedge Rx_Done);
#5000;
uart_tx_byte(8'h86);
@(posedge Rx_Done);
#5000;
$stop;
end
task uart_tx_byte; //创建任务
input [7:0]tx_data;
begin
uart_rx = 1;
#20;
uart_rx = 0;
#8680; //一位的发送时间
uart_rx = tx_data[0];
#8680;
uart_rx = tx_data[1];
#8680;
uart_rx = tx_data[2];
#8680;
uart_rx = tx_data[3];
#8680;
uart_rx = tx_data[4];
#8680;
uart_rx = tx_data[5];
#8680;
uart_rx = tx_data[6];
#8680;
uart_rx = tx_data[7];
#8680;
uart_rx = 1;
#8680;
end
endtask
endmodule
仿真结果:
发现仅仅接受了一位,也就是5a,而没有进行到下一步。进一步观察:
可知uart_rx最后一位开始到Rx_Done之间的时间为8.059us,而测试文件最后一位要延迟8680ns之后,才结束一个task,这个时候才会执行@(posedge Rx_Done),而这时早已过了Rx_Done的脉冲。
回到代码,寻找有关Rx_Done的原因
其中bps_clk_16x的部分
是说在每一小段的中间就已经产生了Rx_Done,早了一点。
修改一下,当div_cnt计满时,才拉高Rx_Done.
再次仿真,观察仿真结果,发现Rx_Done更加提前了,仍然是不对的
加入子模块的信号深入探究:
初步看,没啥毛病
细看最后一位,只有15位
观察前面的位数,是16个
应该计数到160,修改原程序试试。
观察仿真结果,发现还是不够长,而且160的宽度要比其他的窄。bps_cnt为159时,div_cnt从14到下一个13;而bps_cnt为160时,div_cnt从14到下一个1.
下面看看div_cnt的结束条件:
再次修改
为
重新仿真
发现时长还是有一些不够
verilog中只要整数,有可能实际不是测试文件中写的8680us了。
看仿真结果
一位是540ns,540*16=8640ns<8680ns,导致Rx_Done提前到来。
因此,@(posedge Rx_Done);的方法可能不行了,直接采用延时。
将tb中部分代码修改为:
观察仿真结果:
从Data来看,初步正确
然后Rx_Done也在Data之后
完整修改后的代码:
uart_byte_rx:
module uart_byte_rx(
Clk,
Reset_n,
Baud_Set,
uart_rx,
Data,
Rx_Done
);
input Clk;
input Reset_n;
input [2:0]Baud_Set;
input uart_rx;
output reg[7:0]Data;
output reg Rx_Done;
//边沿检测
//存储数据
reg [1:0]uart_rx_r; //定义两个D触发器,判断前后的值
always@(posedge Clk)begin
uart_rx_r[0] <= uart_rx;
uart_rx_r[1] <= uart_rx_r[0]; //[1]是前一刻的值
end
wire pedge_uart_rx;//上升沿检测
//assign pedge_uart_rx = (uart_rx_r[1] == 0) && (uart_rx_r[0] == 1);
assign pedge_uart_rx = (uart_rx_r == 2'b01);
wire nedge_uart_rx;//下降沿检测
assign nedge_uart_rx = (uart_rx_r == 2'b10);
//波特率设置
//1位数据分成了16小段,9600的波特率:1_000_000_000/9600/16/20=325
reg[8:0] bps_DR;
always@(*)
case(Baud_Set)
0:bps_DR = 1_000_000_000/9600/16/20 -1;
1:bps_DR = 1_000_000_000/19200/16/20 -1;
2:bps_DR = 1_000_000_000/38400/16/20 -1;
3:bps_DR = 1_000_000_000/57600/16/20 -1;
4:bps_DR = 1_000_000_000/115200/16/20 -1;
default:bps_DR = 1_000_000_000/9600/16/20 -1;
endcase
/
//产生bps_CLK
wire bps_clk_16x;
assign bps_clk_16x = (div_cnt == bps_DR / 2); //16段的每一小段也要在中心处采样
reg RX_EN;
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
RX_EN <= 0;
else if(nedge_uart_rx)
RX_EN <= 1;
else if(Rx_Done || (sta_bit >= 4)) //sta_bit >= 4是错误的起始位,是一个干扰
RX_EN <= 0;
reg [8:0] div_cnt; //分频计数器
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
div_cnt <= 0;
else if(RX_EN)begin //nedge_uart_rx仅一个脉冲,不足以使得div_cnt一直加,所以这里用前面定义的RX_EN。
if(div_cnt == bps_DR)
div_cnt <= 0;
else
div_cnt <= div_cnt + 1'b1;
end
else
div_cnt <= 0;
//采样数据
reg[7:0] bps_cnt; //16*10=160个bps_clk,10是10位(包括起始和终止)
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
bps_cnt <= 0;
else if(RX_EN)begin
if(bps_clk_16x)begin
if(bps_cnt == 160)
bps_cnt <= 0;
else
bps_cnt <= bps_cnt + 1'b1;
end
else
bps_cnt <= bps_cnt;
end
else
bps_cnt <= 0;
//接收部分
reg [2:0] r_data[7:0]; //累加每一位处获得的值,8位;(二维数组) reg width name number
reg [2:0] sta_bit; //单独定义起始位
reg [2:0] sto_bit; //停止位
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)begin
sta_bit <= 0;
sto_bit <= 0;
r_data[0] <= 0; //只能一个个的赋值
r_data[1] <= 0;
r_data[2] <= 0;
r_data[3] <= 0;
r_data[4] <= 0;
r_data[5] <= 0;
r_data[6] <= 0;
r_data[7] <= 0;
end
else if(bps_clk_16x)begin
case(bps_cnt)
0:begin //等于零的时刻 把这些清零,防止出错
sta_bit <= 0;
sto_bit <= 0;
r_data[0] <= 0;
r_data[1] <= 0;
r_data[2] <= 0;
r_data[3] <= 0;
r_data[4] <= 0;
r_data[5] <= 0;
r_data[6] <= 0;
r_data[7] <= 0;
end
5,6,7,8,9,10,11:sta_bit <= sta_bit + uart_rx; //7次采样,16次中,主要是中间的几次有效和稳定
21,22,23,24,25,26,27:r_data[0] <= r_data[0] + uart_rx;
37,38,39,40,41,42,43:r_data[1] <= r_data[1] + uart_rx;
53,54,55,56,57,58,59:r_data[2] <= r_data[2] + uart_rx;
69,70,71,72,73,74,75:r_data[3] <= r_data[3] + uart_rx;
85,86,87,88,89,90,91:r_data[4] <= r_data[4] + uart_rx;
101,102,103,104,105,106,107:r_data[5] <= r_data[5] + uart_rx;
117,118,119,120,121,122,123:r_data[6] <= r_data[6] + uart_rx;
133,134,135,136,137,138,139:r_data[7] <= r_data[7] + uart_rx;
149,150,151,152,153,154,155:sto_bit <= sto_bit + uart_rx;
default:; //什么都不做
endcase
end
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
Data <= 0;
else if(bps_clk_16x && (bps_cnt == 159))begin //bps_cnt为159的时候就可以输出结果了
Data[0] <= (r_data[0] >= 4)?1'b1:1'b0;
Data[1] <= (r_data[1] >= 4)?1'b1:1'b0;
Data[2] <= (r_data[2] >= 4)?1'b1:1'b0;
Data[3] <= (r_data[3] >= 4)?1'b1:1'b0;
Data[4] <= (r_data[4] >= 4)?1'b1:1'b0;
Data[5] <= (r_data[5] >= 4)?1'b1:1'b0;
Data[6] <= (r_data[6] >= 4)?1'b1:1'b0;
Data[7] <= (r_data[7] >= 4)?1'b1:1'b0;
end
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
Rx_Done <= 0;
else if((div_cnt == bps_DR/2) && (bps_cnt == 160))
Rx_Done <= 1;
else
Rx_Done <= 0;
endmodule
uart_byte_rx_tb:
`timescale 1ns / 1ps
module uart_byte_rx_tb();
reg Clk;
reg Reset_n;
wire [2:0]Baud_Set;
reg uart_rx;
wire [7:0]Data;
wire Rx_Done;
assign Baud_Set = 4;
uart_byte_rx uart_byte_rx(
Clk,
Reset_n,
Baud_Set,
uart_rx,
Data,
Rx_Done
);
initial Clk = 1;
always#10 Clk = ~Clk;
initial begin
Reset_n = 0;
uart_rx = 1;
#201;
Reset_n = 1;
#200;
uart_tx_byte(8'h5a); //调用task
#90000;
uart_tx_byte(8'ha5);
#90000;
uart_tx_byte(8'h86);
#90000;
$stop;
end
task uart_tx_byte; //创建任务
input [7:0]tx_data;
begin
uart_rx = 1;
#20;
uart_rx = 0;
#8680; //一位的发送时间
uart_rx = tx_data[0];
#8680;
uart_rx = tx_data[1];
#8680;
uart_rx = tx_data[2];
#8680;
uart_rx = tx_data[3];
#8680;
uart_rx = tx_data[4];
#8680;
uart_rx = tx_data[5];
#8680;
uart_rx = tx_data[6];
#8680;
uart_rx = tx_data[7];
#8680;
uart_rx = 1;
#8680;
end
endtask
endmodule
二、巧用位操作优化串口接受逻辑设计
三、串口接收模块的项目应用案例
中间控制单元
八位数据与协议进行比较
程序文件
uart_rx_ctrl_led
module uart_rx_ctrl_led(
Clk,
Reset_n,
Led,
uart_rx
);
input Clk;
input Reset_n;
output Led;
input uart_rx;
wire [7:0] ctrl;//模块连接模块,中间只需要导线
wire [31:0] time_set;
wire [7:0] rx_data;
wire rx_done;
counter_led_3 counter_led(
.Clk(Clk),
.Reset_n(Reset_n),
.Ctrl(ctrl),
.Time(time_set),
.Led(Led)
);
uart_byte_rx uart_byte_rx(
.Clk(Clk),
.Reset_n(Reset_n),
.Baud_Set(4),
.uart_rx(uart_rx),
.Data(rx_data),
.Rx_Done(rx_done)
);
uart_cmd uart_cmd(
.Clk(Clk),
.Reset_n(Reset_n),
.rx_data(rx_data),
.rx_done(rx_done),
.ctrl(ctrl),
.time_set(time_set)
);
endmodule
counter_led_3
module counter_led_3(
Clk,
Reset_n,
Ctrl,
Time,
Led
);
input Clk;
input Reset_n;
input [7:0]Ctrl;
input [31:0]Time;
output reg Led;
reg [31:0] counter;
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
counter <= 0;
else if(counter == Time-1)
counter <=0;
else
counter <= counter + 1'b1;
reg [2:0] counter2;
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
counter2 <= 0;
else if(counter == Time - 1)
counter2 <= counter2 + 1'b1;
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
Led <= 0;
else case(counter2)
0:Led <= Ctrl[0];
1:Led <= Ctrl[1];
2:Led <= Ctrl[2];
3:Led <= Ctrl[3];
4:Led <= Ctrl[4];
5:Led <= Ctrl[5];
6:Led <= Ctrl[6];
7:Led <= Ctrl[7];
default Led <= Led;
endcase
endmodule
uart_byte_rx
module uart_byte_rx(
Clk,
Reset_n,
Baud_Set,
uart_rx,
Data,
Rx_Done
);
input Clk;
input Reset_n;
input [2:0]Baud_Set;
input uart_rx;
output reg[7:0]Data;
output reg Rx_Done;
//边沿检测
//存储数据
reg [1:0]uart_rx_r; //定义两个D触发器,判断前后的值
always@(posedge Clk)begin
uart_rx_r[0] <= uart_rx;
uart_rx_r[1] <= uart_rx_r[0]; //[1]是前一刻的值
end
wire pedge_uart_rx;//上升沿检测
//assign pedge_uart_rx = (uart_rx_r[1] == 0) && (uart_rx_r[0] == 1);
assign pedge_uart_rx = (uart_rx_r == 2'b01);
wire nedge_uart_rx;//下降沿检测
assign nedge_uart_rx = (uart_rx_r == 2'b10);
//波特率设置
//1位数据分成了16小段,9600的波特率:1_000_000_000/9600/16/20=325
reg[8:0] bps_DR;
always@(*)
case(Baud_Set)
0:bps_DR = 1_000_000_000/9600/16/20 -1;
1:bps_DR = 1_000_000_000/19200/16/20 -1;
2:bps_DR = 1_000_000_000/38400/16/20 -1;
3:bps_DR = 1_000_000_000/57600/16/20 -1;
4:bps_DR = 1_000_000_000/115200/16/20 -1;
default:bps_DR = 1_000_000_000/9600/16/20 -1;
endcase
/
//产生bps_CLK
wire bps_clk_16x;
assign bps_clk_16x = (div_cnt == bps_DR / 2); //16段的每一小段也要在中心处采样
reg RX_EN;
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
RX_EN <= 0;
else if(nedge_uart_rx)
RX_EN <= 1;
else if(Rx_Done || (sta_bit >= 4)) //sta_bit >= 4是错误的起始位,是一个干扰
RX_EN <= 0;
reg [8:0] div_cnt; //分频计数器
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
div_cnt <= 0;
else if(RX_EN)begin //nedge_uart_rx仅一个脉冲,不足以使得div_cnt一直加,所以这里用前面定义的RX_EN。
if(div_cnt == bps_DR)
div_cnt <= 0;
else
div_cnt <= div_cnt + 1'b1;
end
else
div_cnt <= 0;
//采样数据
reg[7:0] bps_cnt; //16*10=160个bps_clk,10是10位(包括起始和终止)
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
bps_cnt <= 0;
else if(RX_EN)begin
if(bps_clk_16x)begin
if(bps_cnt == 160)
bps_cnt <= 0;
else
bps_cnt <= bps_cnt + 1'b1;
end
else
bps_cnt <= bps_cnt;
end
else
bps_cnt <= 0;
//接收部分
reg [2:0] r_data[7:0]; //累加每一位处获得的值,8位;(二维数组) reg width name number
reg [2:0] sta_bit; //单独定义起始位
reg [2:0] sto_bit; //停止位
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)begin
sta_bit <= 0;
sto_bit <= 0;
r_data[0] <= 0; //只能一个个的赋值
r_data[1] <= 0;
r_data[2] <= 0;
r_data[3] <= 0;
r_data[4] <= 0;
r_data[5] <= 0;
r_data[6] <= 0;
r_data[7] <= 0;
end
else if(bps_clk_16x)begin
case(bps_cnt)
0:begin //等于零的时刻 把这些清零,防止出错
sta_bit <= 0;
sto_bit <= 0;
r_data[0] <= 0;
r_data[1] <= 0;
r_data[2] <= 0;
r_data[3] <= 0;
r_data[4] <= 0;
r_data[5] <= 0;
r_data[6] <= 0;
r_data[7] <= 0;
end
5,6,7,8,9,10,11:sta_bit <= sta_bit + uart_rx; //7次采样,16次中,主要是中间的几次有效和稳定
21,22,23,24,25,26,27:r_data[0] <= r_data[0] + uart_rx;
37,38,39,40,41,42,43:r_data[1] <= r_data[1] + uart_rx;
53,54,55,56,57,58,59:r_data[2] <= r_data[2] + uart_rx;
69,70,71,72,73,74,75:r_data[3] <= r_data[3] + uart_rx;
85,86,87,88,89,90,91:r_data[4] <= r_data[4] + uart_rx;
101,102,103,104,105,106,107:r_data[5] <= r_data[5] + uart_rx;
117,118,119,120,121,122,123:r_data[6] <= r_data[6] + uart_rx;
133,134,135,136,137,138,139:r_data[7] <= r_data[7] + uart_rx;
149,150,151,152,153,154,155:sto_bit <= sto_bit + uart_rx;
default:; //什么都不做
endcase
end
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
Data <= 0;
else if(bps_clk_16x && (bps_cnt == 159))begin //bps_cnt为159的时候就可以输出结果了
Data[0] <= (r_data[0] >= 4)?1'b1:1'b0;
Data[1] <= (r_data[1] >= 4)?1'b1:1'b0;
Data[2] <= (r_data[2] >= 4)?1'b1:1'b0;
Data[3] <= (r_data[3] >= 4)?1'b1:1'b0;
Data[4] <= (r_data[4] >= 4)?1'b1:1'b0;
Data[5] <= (r_data[5] >= 4)?1'b1:1'b0;
Data[6] <= (r_data[6] >= 4)?1'b1:1'b0;
Data[7] <= (r_data[7] >= 4)?1'b1:1'b0;
end
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
Rx_Done <= 0;
else if((div_cnt == bps_DR/2) && (bps_cnt == 160))
Rx_Done <= 1;
else
Rx_Done <= 0;
endmodule
uart_cmd
module uart_cmd(
Clk,
Reset_n,
rx_data,
rx_done,
ctrl,
time_set
);
input Clk;
input Reset_n;
input [7:0] rx_data;
input rx_done;
output reg[7:0] ctrl;
output reg[31:0] time_set;
reg [7:0] data_str[7:0];
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)begin
data_str[0] <= 0;
data_str[1] <= 0;
data_str[2] <= 0;
data_str[3] <= 0;
data_str[4] <= 0;
data_str[5] <= 0;
data_str[6] <= 0;
data_str[7] <= 0;
end
else if(rx_done)begin
data_str[7] <= rx_data;
data_str[6] <= data_str[7];
data_str[5] <= data_str[6];
data_str[4] <= data_str[5];
data_str[3] <= data_str[4];
data_str[2] <= data_str[3];
data_str[1] <= data_str[2];
data_str[0] <= data_str[1];
end
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)begin
ctrl <= 0;
time_set <= 0;
end
else if(rx_done)begin
if((data_str[0] == 8'h55) && (data_str[1] == 8'hA5) && (data_str[7] == 8'hF0))begin
time_set[7:0] <= data_str[2];
time_set[15:8] <= data_str[3];
time_set[23:16] <= data_str[4];
time_set[31:24] <= data_str[5];
ctrl <= data_str[6];
end
end
endmodule
测试文件uart_rx_ctrl_led_tb
`timescale 1ns / 1ps
module uart_rx_ctrl_led_tb(
);
reg Clk;
reg Reset_n;
wire Led;
reg uart_rx;
uart_rx_ctrl_led uart_rx_ctrl_led(
.Clk(Clk),
.Reset_n(Reset_n),
.Led(Led),
.uart_rx(uart_rx)
);
initial Clk = 1;
always#10 Clk = ~Clk;
initial begin
Reset_n = 0;
uart_rx = 1;
#201;
Reset_n = 1;
#200;
uart_tx_byte(8'h55); //调用task
#90000;
uart_tx_byte(8'ha5);
#90000;
uart_tx_byte(8'h12);
#90000;
uart_tx_byte(8'h34);
#90000;
uart_tx_byte(8'h56);
#90000;
uart_tx_byte(8'h78);
#90000 ;
uart_tx_byte(8'h9a);
#90000;
uart_tx_byte(8'hf0);
#90000;
$stop;
end
task uart_tx_byte; //创建任务
input [7:0]tx_data;
begin
uart_rx = 1;
#20;
uart_rx = 0;
#8680; //一位的发送时间
uart_rx = tx_data[0];
#8680;
uart_rx = tx_data[1];
#8680;
uart_rx = tx_data[2];
#8680;
uart_rx = tx_data[3];
#8680;
uart_rx = tx_data[4];
#8680;
uart_rx = tx_data[5];
#8680;
uart_rx = tx_data[6];
#8680;
uart_rx = tx_data[7];
#8680;
uart_rx = 1;
#8680;
end
endtask
endmodule
仿真结果:
发现ctrl和time_set的值都是0,没有发生变化。
而程序中,是将这些数据存在了data_str中,观察它的数据正确与否。
好像没什么问题,但ctrl和time_set的值却没有得到更新
然后重新看给ctrl和time_set赋值的部分代码
rx_done是在黄线处被捕获到,但此时的data_str还没有变为下一个值,还是前一个值。
像这种如果一个数据同时用来控制数据和使用数据结果时,会存在这样的问题。解决的思路:使rx_done晚一拍
使用如下代码,也就是寄存器,使其慢一拍
再者,如果单单是移位寄存器,复位操作没有任何意义。(即红框内的可以删除)
继续运行仿真,发现前几个值变成红色,这是因为:没有复位值,最开始是什么值不知道,移到八个数据之后,都是已知的了,就不是红色的了。
修改tb测试文件,看看能否依然更新结果(反面测试)
运行仿真,发现因为后一组数据不正确,而没有刷新上一组的值
整体逻辑图
板级验证
分配管脚
若亮灭时间间隔设置为0.5s,则time_set对应值为25000000,8’h017D7840;ctrl的值为10101010(8‘hAA),循环闪烁。
所以发送内容应该为:55 A5 40 78 7D 01 AA F0
然后发现,Led灯亮都不亮。
重新仿真看看,并把counter_led的波形添加进仿真结果中。
发现刚一上电,Time的值是0
看一下代码
counter=0-1,也就是全是fffffff…非常大才会清零,否则自加。也就是让counter一直在自加,加到很大
而在板级验证时,当time更新值到了25000000时,counter早就自加到了大于25000000的地方,那个条件判断依然不会满足,counter依然会不断自加
之所以等待几十秒后,灯才会闪烁是因为:counter不断自加,把32位的那个数据跑满了,才满足清零条件,这个时候才能进入那个判断,让灯闪烁起来。
一般用后者的方式清零
修改代码
然后再板级验证
最后
以上就是从容豌豆为你收集整理的FPGA学习笔记(四)——串口通信之接收数据(调试过程)的全部内容,希望文章能够帮你解决FPGA学习笔记(四)——串口通信之接收数据(调试过程)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复