算是对前几天学的做了一个复习,利用串口收发模块和ram ip核进行了一个简易收发显示。资源已经上传有需要自取。
实验中遇到问题:1:创建的simple ram 双端口只有一个写请求信号,可以控制是否写入,但是会一直读出数据,ram ip配置界面没找到读请求控制信号,所以就算写入一些数据,最终会一直重复读取存储的数据。
2:wea信号不用一直触发,写一个数据时触发一次就好了
3:使用vivado+vscdoe时,如果还有文件在vscode打开,仿真和综合时会提示
必须关掉vscode才能继续。
4:小梅哥的crtl模块对tx_done进行了三个延时,因为ram读出数据加了两个寄存器,有3个周期延时,才能正确控制uart_tx模块的send_en信号,但是我没看他写的uart_tx模块,自己写的时候是要求一直触发send_en才能进行发送,所以不需要考虑tx_done延时,只需要发送状态为发送数据时即有按键输入时就触发,这比他的简单多了。
仿真时模拟串口依次发送:5a 11 22 33 44 55 66 5a
总共8个字节,所以addra一直加到08,dina为接收到的data_rx数据进行存储,但是想要读出数据时,addrb一直增加到所有的数据都读完再循环,但是除了写入的数据都是00,addra和addrb都是7位宽,所以应该以128个数据为一次循环,如图doutb:
上板验证:波特率设置为115200,从串口调试助手依次发送15 16 17 18 19,最终成功循环接收数据,验证确实为128个数据为一组循环。
主模块代码:
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2024/04/25 10:21:48
// Design Name:
// Module Name: uart_ram
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module uart_ram_t_r(
clk,
rst,
uart_tx,
uart_rx,
key_in,
led1,
led2
);
input clk;
input rst;
input uart_rx;
output uart_tx;
input key_in;
output led1;//Tx_done 显示
output led2;//Rx_done显示
//tx模块参数
wire [7:0]Data_tx;
wire Send_en;
wire Tx_done;
//rx模块参数
wire [7:0]Data_rx;
wire Rx_done;
//uart_ram ip 参数
wire [6:0]addra;
//wire [7:0]data;
wire wea;
wire [6:0]addrb;
//key_filter参数
//wire key_out;
//消除rst亚稳态
reg rst_reg1;
reg rst_reg2;
assign led1=Tx_done;
assign led2=Rx_done;
always @(posedge clk or negedge rst) begin
if(~rst)
rst_reg1<=1'b0;
else
rst_reg1<=1'b1;
end
always @(posedge clk or negedge rst) begin
if(~rst)
rst_reg2<=1'b0;
else
rst_reg2<=rst_reg1;
end
uart_tx uart_tx1(
.clk(clk),
.rst_n(rst_reg2),
.Data(Data_tx),
.Send_en(Send_en),
.Baud_set(3'b100),
.uart_tx(uart_tx),
.Tx_done(Tx_done)
);
uart_rx uart_rx1(
.uart_rx(uart_rx),
.clk(clk),
.rst_n(rst_reg2),
.Data_rx(Data_rx),
.Rx_done(Rx_done)
);
uart_ram uart_ram1(
.clka(clk),
.wea(wea),
.addra(addra),
.dina(Data_rx),
.clkb(clk),
.addrb(addrb),
.doutb(Data_tx)
);
ctrl ctrl(
.clk(clk),
.rst_reg2(rst_reg2),
.key(key_in),
.Rx_done(Rx_done),
.Tx_done(Tx_done),
.Send_en(Send_en),
.wea(wea),
.addra(addra),
.addrb(addrb)
);
endmodule
uart_tx模块:
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2024/03/08 20:35:22
// Design Name:
// Module Name: uart
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module uart_tx(
clk,
rst_n,
Data,
Send_en,
Baud_set,
uart_tx,
Tx_done
);
input clk;
input rst_n;
input [7:0]Data;
input Send_en;
input [2:0]Baud_set;
output reg uart_tx;
output reg Tx_done;
reg [19:0] count;
reg [19:0] BPS; //set Baud
reg [3:0]bit_count;//比特计数器
reg [7:0]Data_reg;//数据寄存器
parameter START_BIT=1'b0;
parameter STOP_BIT=1'b1;
//串口是异步收发器,保证发送的数据在时钟到来前稳定,对输入数据寄存
always@(posedge clk or negedge rst_n)
begin
if(~rst_n)
Data_reg<=8'd0;
else if(Send_en==1'b1);
Data_reg<=Data;
end
always@(posedge clk )
begin
case(Baud_set)
3'b000: BPS<=20'd5207 ;// Baud 9600
3'b001: BPS<=20'd2603 ;// Baud 19200
3'b010: BPS<=20'd1301 ;// Baud 38400
3'b011: BPS<=20'd867 ;// Baud 57600
3'b100: BPS<=20'd433 ;// Baud 115200
// 3'b101: BPS<=20'd434 ;// Baud 115200
default:BPS<=20'd433;
endcase
end
always@(posedge clk or negedge rst_n)
begin
if(~rst_n)
count<=20'd0;
else if(count==BPS)//波特率计数器
count<=20'd0;
else
count<=count+1'b1;
end
always @(posedge clk or negedge rst_n)
begin
if(~rst_n)
bit_count<=4'd0;
else if(bit_count==4'd11)//8位数据传输完成,并且保证停止位至少保持一个周期
bit_count<=4'd0;
else if(count==BPS && Send_en==1'b1)
bit_count<=bit_count+1'b1;
end
always @(posedge clk )
begin
case (bit_count)
4'd1: uart_tx<=1'b0;
4'd2: uart_tx<=Data[0];
4'd3: uart_tx<=Data[1];
4'd4: uart_tx<=Data[2];
4'd5: uart_tx<=Data[3];
4'd6: uart_tx<=Data[4];
4'd7: uart_tx<=Data[5];
4'd8: uart_tx<=Data[6];
4'd9: uart_tx<=Data[7];
4'd10: uart_tx<=1'b1;
//4'd11: uart_tx<=1'b1;
default :uart_tx=1'b1;
endcase
end
always @(posedge clk or negedge rst_n)
begin
if(~rst_n)
Tx_done<=1'b0;
else if(bit_count==4'd11)
Tx_done<=1'b1;
else
Tx_done<=1'b0;
end
endmodule
uart_rx模块:
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2024/03/12 16:07:50
// Design Name:
// Module Name: uart_rx
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module uart_rx(
uart_rx,
clk,
rst_n,
Data_rx,
Rx_done
);
input uart_rx;
input clk;
input rst_n;
output reg [7:0]Data_rx;
output reg Rx_done;
localparam COUNT_115200=20'd433; //对115200波特率计数
localparam COUNT_14=5'd30; //把一个比特位分割成14份,每一份间隔31个COUNT_115200
reg [19:0] count_115200;
reg [4:0]count_14;
//对uart_rx两级打拍寄存用于信号同步
reg uart_rx_sync1;
reg uart_rx_sync2;
//对同步后的信号uart_rx_sync2两级打拍寄存用于边缘判断
reg uart_rx_reg1;
reg uart_rx_reg2;
//定义上升沿和下降沿标志信号
wire posedge_uart_rx;
wire negedge_uart_rx;
//计数采样位置 当count_14每次加到26时,count_sample加一,算上起始位和停止位总共分为160份
reg [7:0]count_sample;
//存储6次采样结果
//reg [3:0]sample_bit;
//计数采样次数里面1的个数,如果1大于4就取1 反之取0
reg [3:0]number_start;
reg [3:0]number_stop;
reg [3:0]number_bit[7:0];
//数据接收使能 在检测到下降沿时开始有效为1,Rx_done有效即接收完成时无效为0,其余时间为无效0
reg Rx_en;
//对接收到的数据bit数进行计数
reg [3:0]bit_count;
//先对接收信号存储
//reg [7:0]Data;
//定义采样时钟信号 在每个count_sample的只有一个时钟有效 从而每个采样点只采样一次
reg sample_clk;
always@(posedge clk or negedge rst_n)
begin
if(~rst_n)
count_14<=5'd0;
else if(Rx_en==1'b1)
begin
if(count_14==COUNT_14)
count_14<=5'd0;
else
count_14<=count_14+1'b1;
end
end
//约束采样时钟
always@(posedge clk or negedge rst_n)
begin
if(~rst_n)
sample_clk<=1'b0;
else if(count_14==5'd10)
sample_clk<=1'b1;
else
sample_clk<=1'b0;
end
//将每个比特分成16份 从1计数到17 只采样6 7 8 9 10 11
always@(posedge clk or negedge rst_n)
begin
if(~rst_n)
count_sample<=8'd0;
else if((count_sample==8'd139) && (count_14==COUNT_14))
count_sample<=8'd0;
else if((count_14==COUNT_14) && (Rx_en==1'b1))
count_sample<=count_sample+1'b1;
end
/*
//进行采样
always@(posedge clk or negedge rst_n)
begin
if(~rst_n)
begin
sample_bit<=4'd0;
number_1<=4'd0;
end
else
else if((count_sample==5'd6) && (count_14==count_14))
begin
sample_bit[0]<=uart_rx_sync2;
if(uart_rx_sync2==1'b1)
number_1<=number_1+1'b1; //对采样值里面1进行计数
end
else if((count_sample==5'd7) && (count_14==count_14))
begin
sample_bit[1]<=uart_rx_sync2;
if(uart_rx_sync2==1'b1)
number_1<=number_1+1'b1;
end
else if((count_sample==5'd8) && (count_14==count_14))
begin
sample_bit[2]<=uart_rx_sync2;
if(uart_rx_sync2==1'b1)
number_1<=number_1+1'b1;
end
else if((count_sample==5'd9) && (count_14==count_14))
begin
sample_bit[3]<=uart_rx_sync2;
if(uart_rx_sync2==1'b1)
number_1<=number_1+1'b1;
end
else if((count_sample==5'd10) && (count_14==count_14))
begin
sample_bit[4]<=uart_rx_sync2;
if(uart_rx_sync2==1'b1)
number_1<=number_1+1'b1;
end
else if((count_sample==5'd11) && (count_14==count_14))
begin
sample_bit[5]<=uart_rx_sync2;
if(uart_rx_sync2==1'b1)
number_1<=number_1+1'b1;
end
end
always @(posedge clk)
begin
if(count_sample==5'd11)
if(number_1>=3)
end
*/
//对信号进行寄存两拍
always@(posedge clk or negedge rst_n)
begin
if(~rst_n)
begin
uart_rx_sync1<=1'b0;
uart_rx_sync2<=1'b0;
end
else
begin
uart_rx_sync1<=uart_rx;
uart_rx_sync2<=uart_rx_sync1;
end
end
//进行波特率计数
always@(posedge clk or negedge rst_n)
begin
if(~rst_n)
count_115200<=20'd0;
else if(Rx_en==1'b1)
begin
if(count_115200==COUNT_115200)
count_115200<=20'd0;
else
count_115200<=count_115200+1'b1;
end
end
//对稳定后的信号进行上升沿和下降沿判断
always@(posedge clk or negedge rst_n)
begin
if(~rst_n)
begin
uart_rx_reg1<=1'b0;
uart_rx_reg2<=1'b0;
end
else
begin
uart_rx_reg1<=uart_rx_sync2;
uart_rx_reg2<=uart_rx_reg1;
end
end
assign posedge_uart_rx=uart_rx_reg1 & !uart_rx_reg2;
assign negedge_uart_rx=!uart_rx_reg1 & uart_rx_reg2;
//对Rx_done进行限制
always@(posedge clk or negedge rst_n)
begin
if(~rst_n)
Rx_done<=1'b0;
else if(bit_count==4'd10)
Rx_done<=1'b1;
else
Rx_done<=1'b0;
end
//对Rx_en限制
always@(posedge clk or negedge rst_n)
begin
if(~rst_n)
Rx_en<=1'b0;
else if(negedge_uart_rx==1'b1)
Rx_en<=1'b1;
else if(Rx_done==1'b1)
Rx_en<=1'b0;
end
//对接收到的比特数进行计数
always@(posedge clk or negedge rst_n)
begin
if(~rst_n)
bit_count<=4'd0;
else if(bit_count==4'd10)
bit_count<=4'b0;
else if((count_115200==COUNT_115200) && (Rx_en==1'b1))
bit_count<=bit_count+1'b1;
end
/*
always@(*)
begin
case(bit_count)
4'd1: Data[0]=uart_rx_sync2;
4'd2: Data[1]=uart_rx_sync2;
4'd3: Data[2]=uart_rx_sync2;
4'd4: Data[3]=uart_rx_sync2;
4'd5: Data[4]=uart_rx_sync2;
4'd6: Data[5]=uart_rx_sync2;
4'd7: Data[6]=uart_rx_sync2;
4'd8: Data[7]=uart_rx_sync2;
endcase
end
*/
always @(posedge sample_clk or negedge rst_n)
begin
if(~rst_n)
begin
number_start<=4'd0;
number_stop<=4'd0;
number_bit[0]<=4'd0;
number_bit[1]<=4'd0;
number_bit[2]<=4'd0;
number_bit[3]<=4'd0;
number_bit[4]<=4'd0;
number_bit[5]<=4'd0;
number_bit[6]<=4'd0;
number_bit[7]<=4'd0;
end
else if(Rx_en==1'b1)
begin
case (count_sample)
8'd0:
begin
number_start<=4'd0;
number_stop<=4'd0;
number_bit[0]<=4'd0;
number_bit[1]<=4'd0;
number_bit[2]<=4'd0;
number_bit[3]<=4'd0;
number_bit[4]<=4'd0;
number_bit[5]<=4'd0;
number_bit[6]<=4'd0;
number_bit[7]<=4'd0;
end
8'd4,8'd5,8'd6,8'd7,8'd8,8'd9:number_start<=number_start+uart_rx_sync2;
8'd18,8'd19,8'd20,8'd21,8'd22,8'd23:number_bit[0]<=number_bit[0]+uart_rx_sync2;
8'd32,8'd33,8'd34,8'd35,8'd36,8'd37:number_bit[1]<=number_bit[1]+uart_rx_sync2;
8'd46,8'd47,8'd48,8'd49,8'd50,8'd51:number_bit[2]<=number_bit[2]+uart_rx_sync2;
8'd60,8'd61,8'd62,8'd63,8'd64,8'd65:number_bit[3]<=number_bit[3]+uart_rx_sync2;
8'd74,8'd75,8'd76,8'd77,8'd78,8'd79:number_bit[4]<=number_bit[4]+uart_rx_sync2;
8'd88,8'd89,8'd90,8'd91,8'd92,8'd93:number_bit[5]<=number_bit[5]+uart_rx_sync2;
8'd102,8'd103,8'd104,8'd105,8'd106,8'd107:number_bit[6]<=number_bit[6]+uart_rx_sync2;
8'd116,8'd117,8'd118,8'd119,8'd120,8'd121:number_bit[7]<=number_bit[7]+uart_rx_sync2;
8'd130,8'd131,8'd132,8'd133,8'd134,8'd135:number_stop<=number_stop+uart_rx_sync2;
default :
begin
number_start<=number_start;
number_stop<=number_stop;
number_bit[0]<=number_bit[0];
number_bit[1]<=number_bit[1];
number_bit[2]<=number_bit[2];
number_bit[3]<=number_bit[3];
number_bit[4]<=number_bit[4];
number_bit[5]<=number_bit[5];
number_bit[6]<=number_bit[6];
number_bit[7]<=number_bit[7];
end
endcase
end
end
always@(posedge clk or negedge rst_n)
begin
if(~rst_n)
Data_rx<=8'd0;
else if(count_sample==8'd139)
begin
if((number_start<=8'd2) &&(number_stop>=8'd4))
begin
Data_rx[0]<=(number_bit[0]>=8'd4) ? 1'b1 : 1'b0;
Data_rx[1]<=(number_bit[1]>=8'd4) ? 1'b1 : 1'b0;
Data_rx[2]<=(number_bit[2]>=8'd4) ? 1'b1 : 1'b0;
Data_rx[3]<=(number_bit[3]>=8'd4) ? 1'b1 : 1'b0;
Data_rx[4]<=(number_bit[4]>=8'd4) ? 1'b1 : 1'b0;
Data_rx[5]<=(number_bit[5]>=8'd4) ? 1'b1 : 1'b0;
Data_rx[6]<=(number_bit[6]>=8'd4) ? 1'b1 : 1'b0;
Data_rx[7]<=(number_bit[7]>=8'd4) ? 1'b1 : 1'b0;
end
end
end
endmodule
/*
//这个也能用
module uart_rx(
Clk,
Reset_n,
uart_rx,
Rx_Done,
Rx_Data
);
input Clk;
input Reset_n;
input uart_rx;
output reg Rx_Done;
output reg[7:0]Rx_Data;
parameter CLOCK_FREQ = 50_000_000;
parameter BAUD = 115200;
parameter MCNT_BAUD = CLOCK_FREQ / BAUD - 1;
reg [7:0]r_Rx_Data;
reg [29:0]baud_div_cnt;
reg en_baud_cnt;
reg [3:0]bit_cnt;
wire w_Rx_Done;
wire nedge_uart_rx;
reg r_uart_rx;
reg dff0_uart_rx,dff1_uart_rx;
//波特率计数器逻辑
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
baud_div_cnt <= 0;
else if(en_baud_cnt)begin
if(baud_div_cnt == MCNT_BAUD)
baud_div_cnt <= 0;
else
baud_div_cnt <= baud_div_cnt + 1'd1;
end
else
baud_div_cnt <= 0;
//UART 信号边沿检测逻辑
always@(posedge Clk)
dff0_uart_rx <= uart_rx;
always@(posedge Clk)
dff1_uart_rx <= dff0_uart_rx;
always@(posedge Clk)
r_uart_rx <= dff1_uart_rx;
assign nedge_uart_rx = (dff1_uart_rx == 0) && (r_uart_rx == 1);
//波特率计数器使能逻辑
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
en_baud_cnt <= 0;
else if(nedge_uart_rx)
en_baud_cnt <= 1;
else if((baud_div_cnt == MCNT_BAUD/2) && (bit_cnt == 0) && (dff1_uart_rx == 1))
en_baud_cnt <= 0;
else if((baud_div_cnt == MCNT_BAUD/2) && (bit_cnt == 9))
en_baud_cnt <= 0;
//位计数器逻辑
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
bit_cnt <= 0;
else if((bit_cnt == 9) && (baud_div_cnt == MCNT_BAUD/2))
bit_cnt <= 0;
else if(baud_div_cnt == MCNT_BAUD)
bit_cnt <= bit_cnt + 1'd1;
//位接收逻辑
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
r_Rx_Data <= 8'd0;
else if(baud_div_cnt == MCNT_BAUD/2)begin
case(bit_cnt)
1:r_Rx_Data[0] <= dff1_uart_rx;
2:r_Rx_Data[1] <= dff1_uart_rx;
3:r_Rx_Data[2] <= dff1_uart_rx;
4:r_Rx_Data[3] <= dff1_uart_rx;
5:r_Rx_Data[4] <= dff1_uart_rx;
6:r_Rx_Data[5] <= dff1_uart_rx;
7:r_Rx_Data[6] <= dff1_uart_rx;
8:r_Rx_Data[7] <= dff1_uart_rx;
default: r_Rx_Data <= r_Rx_Data;
endcase
end
//接收完成标志信号
assign w_Rx_Done = (baud_div_cnt == MCNT_BAUD/2) && (bit_cnt == 9);
always@(posedge Clk)
Rx_Done <= w_Rx_Done;
always@(posedge Clk)
if(w_Rx_Done)
Rx_Data <= r_Rx_Data;
endmodule
*/
ctrl模块代码:
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2024/04/25 15:04:58
// Design Name:
// Module Name: ctrl
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module ctrl(
clk,
rst_reg2,
key,
Rx_done,
Tx_done,
Send_en,
wea,
addra,
addrb
);
input clk;
input rst_reg2;
input key;
input Rx_done;
input Tx_done;
output reg Send_en;
output wea;
output reg [6:0]addra;
output reg [6:0]addrb;
//定义一个发送状态,用key来控制
reg send_state;
//按下按键,send_state=1 为读操作,当按下按键后连续读,再次按下按键停止读
always @(posedge clk or negedge rst_reg2) begin
if(~rst_reg2)
send_state<=1'b0;
else if(~key)
send_state<=~send_state;
end
assign wea=Rx_done;
//每次接收到一个字节数据时,ram地址加1
always @(posedge clk or negedge rst_reg2) begin
if(~rst_reg2)
addra<=7'd0;
else if(Rx_done)
addra<=addra+1'b1;
end
//每次发送完一个字节数据时,ram地址减1
always @(posedge clk or negedge rst_reg2) begin
if(~rst_reg2)
addrb<=1'b0;
else if((Tx_done==1'b1) && (send_state==1'b1))
addrb<=addrb+1'b1;
end
always @(posedge clk or negedge rst_reg2) begin
if(~rst_reg2)
Send_en<=1'b0;
else if(send_state==1'b1)
Send_en<=1'b1;
else
Send_en<=1'b0;
end
endmodule
串口学习暂时就先告一段落了,去年用高云的开发板整百兆以太网传输没搞出来,iic都没成功调用ov5640,第一阶段目标就是学完三大低俗协议,再手敲一个千兆以太网网络传输视频显示,不知道这样刚学完算不算入门,任重而道远啊,网上一堆说这都烂大街了,老师做的是射频通信,但是貌似fpga岗位最多的是高速接口?通信这玩意我能搞明白吗。。。但是最终肯定是往zynq靠,慢慢来吧,读研自我救赎之路宛如长征。