FPGA串口学习
程序员文章站
2022-06-23 17:28:51
...
FPGA学习笔记
串口与单口ram操作学习
实现功能如下,电脑给开发板发送串口数据,每接收4个数据后串口将4个数据发送给电脑。
代码部分
代码有串口发送程序,串口接收程序,时钟生成程序和ram控制程序。前三个程序直接用黑金教程里的程序修改的。
时钟程序
.
`timescale 1ns/1ps
//波特率115200
module clkdiv(clk50 , rst_n ,clkout ) ;
input clk50 ;
input rst_n ;
output clkout ;
reg clkout ;
reg [15:0]cnt ;
always @(posedge clk50 or negedge rst_n)
begin
if(!rst_n)
begin
clkout <= 1'b0 ;
cnt <= 0 ;
end
else if(cnt == 16'd13)
begin
clkout <= 1'b1 ;
cnt <= cnt + 16'd1 ;
end
else if(cnt == 16'd27)
begin
clkout <= 1'b0 ;
cnt <= 16'd0 ;
end
else
begin
cnt <= cnt + 16'd1 ;
end
end
endmodule
串口发送程序
`timescale 1ns/1ps
//module name : uart_send
//sen 1 bit every 16 clock , 1 begin position ,8 data position , 1 check position , 1 stop position
module uart_send(clk , rst_n , datain , wrsig , idle , tx , send_cnt , sendout) ;
input clk ; //clock
input rst_n ; //reset
input [7:0]datain ;//data to send
input wrsig ; //command of send
output idle ; //status of line,1 is busy
output tx ; //send data signal
output sendout ; // negetive when wr allow
output send_cnt ;
reg idle , tx ;
reg send , sendout ;
reg wrsigbuf , wrsigrise ;
reg presult ;
reg [7:0]cnt ;
parameter paritmode = 1'b0 ;
reg [1:0]send_cnt ;
//check rise edge of wrsig
always @(posedge clk)
begin
wrsigbuf <= wrsig ;
wrsigrise <= (~wrsigbuf) & wrsig ;
end
//start the send function
always @(posedge clk )
begin
if(wrsigrise && (~idle) )
begin
send = 1'b1 ;
end
else if(send_cnt==3 && cnt==168)
begin
send = 1'b0 ;
end
end
//function of sending
always @(posedge clk or negedge rst_n)
begin
if(~rst_n)
begin
tx <= 1'b0 ;
idle <= 1'b0 ;
cnt <= 8'd0 ;
presult <= 1'b0 ;
send_cnt <= 0 ;
sendout <= 0 ;
end
else if(send == 1'b1)
begin
case(cnt)
8'd0 : begin
tx <= 1'b0 ;
idle <= 1'b1 ;
cnt <= cnt + 8'd1 ;
sendout <= 0 ;
end
8'd16 : begin
tx <= datain[0] ;
presult <= datain[0] ^ paritmode ; //奇偶校验步骤
idle <= 1'b1 ;
cnt <= cnt + 8'd1 ;
sendout <= 0 ;
end
8'd32 : begin
tx <= datain[1] ;
presult <= datain[1] ^ presult ;
idle <= 1'b1 ;
cnt <= cnt + 8'd1 ;
sendout <= 0 ;
end
8'd48 : begin
tx <= datain[2] ;
presult <= datain[2] ^ presult ;
idle <= 1'b1 ;
cnt <= cnt + 8'd1 ;
sendout <= 0 ;
end
8'd64 : begin
tx <= datain[3] ;
presult <= datain[3] ^ presult ;
idle <= 1'b1 ;
cnt <= cnt + 8'd1 ;
sendout <= 0 ;
end
8'd80 : begin
tx <= datain[4] ;
presult <= datain[4] ^ presult ;
idle <= 1'b1 ;
cnt <= cnt + 8'd1 ;
sendout <= 0 ;
end
8'd96 : begin
tx <= datain[5] ;
presult <= datain[5] ^ presult ;
idle <= 1'b1 ;
cnt <= cnt + 8'd1 ;
end
8'd112 : begin
tx <= datain[6] ;
presult <= datain[6] ^ presult ;
idle <= 1'b1 ;
cnt <= cnt + 8'd1 ;
sendout <= 0 ;
end
8'd128 : begin
tx <= datain[7] ;
presult <= datain[7] ^ presult ;
idle <= 1'b1 ;
cnt <= cnt + 8'd1 ;
sendout <= 0 ;
end
8'd144 : begin
tx <= presult ;
presult <= datain[0] ^ paritmode ;
idle <= 1'b1 ;
cnt <= cnt + 8'd1 ;
sendout <= 0 ;
end
8'd160 : begin
tx <= 1'b1 ; //一帧数据发送结束
idle <= 1'b1 ;
cnt <= cnt + 8'd1 ;
sendout <= 0 ;
end
8'd168 : begin //变量恢复
tx <= 1'b1 ;
idle <= 1'b0 ;
cnt <= 0 ;
sendout <= 1 ;
send_cnt <= send_cnt + 1 ;
end
default : begin
cnt <= cnt + 8'd1 ;
sendout <= 0 ;
end
endcase
end
else
begin
tx <= 1'b1 ;
cnt <= 8'd0 ;
idle <= 1'b0 ;
sendout <= 0 ;
end
end
endmodule
串口接收程序
`timescale 1ns/1ps
//16个时钟接收一个数据,检测下降沿,还有帧检验和奇偶检验
module uart_rxd(clk , rst_n , dataout , rx , rsding , dataerror , fameerror) ;
input clk ; //时钟
input rst_n ; //复位信号
input rx ; //接收的数据
output dataout ;
output rsding ; //
output dataerror ;
output fameerror ;
reg [7:0]dataout ;
reg rsding , dataerror ;
reg fameerror ;
reg [7:0]cnt ;
reg rxbuf , rxfall , receive ;
parameter paritymode = 1'b0 ;
reg presult , idle ;
reg [1:0]rec_cnt ;
always @(posedge clk)
begin
rxbuf <= rx ;
rxfall <= rxbuf & (~rx) ;
end
always @(posedge clk)
begin
if(rxfall && (~idle))
begin
receive <= 1'b1 ;
end
else begin
if(cnt == 8'd152 )
begin
receive <= 1'b0 ;
end
end
end
always @(posedge clk or negedge rst_n)
begin
if(~rst_n) begin
cnt <= 8'd0 ;
dataerror <= 1'b0 ;
fameerror <= 1'b0 ;
idle <= 1'b0 ;
presult <= 1'b0 ;
rsding <= 1'b0 ;
rec_cnt <= 0 ;
end
else if(receive == 1'b1) begin
case(cnt)
8'd0 : begin
idle <= 1'b1 ;
rsding <= 1'b0 ;
cnt <= cnt + 8'd1 ;
end
8'd24 : begin //起始位16个时钟,第一位数据在8个时钟时接收,16+8
idle <= 1'b1 ;
dataout[0] <= rx ;
cnt <= cnt + 8'd1 ;
presult <= dataout[0] ^ paritymode ;
rsding <= 1'b0 ;
end
8'd40 : begin
idle <= 1'b1 ;
dataout[1] <= rx ;
cnt <= cnt + 8'd1 ;
presult <= dataout[1] ^ presult ;
rsding <= 1'b0 ;
end
8'd56 : begin
idle <= 1'b1 ;
dataout[2] <= rx ;
cnt <= cnt + 8'd1 ;
presult <= dataout[2] ^ presult ;
rsding <= 1'b0 ;
end
8'd72 : begin
idle <= 1'b1 ;
dataout[3] <= rx ;
cnt <= cnt + 8'd1 ;
presult <= dataout[3] ^ presult ;
rsding <= 1'b0 ;
end
8'd88 : begin
idle <= 1'b1 ;
dataout[4] <= rx ;
cnt <= cnt + 8'd1 ;
presult <= dataout[4] ^ presult ;
rsding <= 1'b0 ;
end
8'd104 : begin
idle <= 1'b1 ;
dataout[5] <= rx ;
cnt <= cnt + 8'd1 ;
presult <= dataout[5] ^ presult ;
rsding <= 1'b0 ;
end
8'd120 : begin
idle <= 1'b1 ;
dataout[6] <= rx ;
cnt <= cnt + 8'd1 ;
presult <= dataout[6] ^ presult ;
rsding <= 1'b0 ;
end
8'd136 : begin
idle <= 1'b1 ;
dataout[7] <= rx ;
cnt <= cnt + 8'd1 ;
presult <= dataout[7] ^ presult ;
rsding <= 0 ;
rec_cnt <= rec_cnt + 1 ;
end
8'd152 : begin
idle <= 1'b1 ;
if(1'b1 == rx)
fameerror <= 1'b0 ;
else
fameerror <= 1'b1 ;
rsding <= 1 ;
cnt <= 0 ;
end
default : begin
cnt <= cnt + 8'd1 ;
rsding <= 0 ;
end
endcase
end
else begin
cnt <= 8'd0 ;
rsding <= 1'b0 ;
idle <= 1'b0 ;
end
end
endmodule
RAM控制程序
//==================================================================================================
// Filename : uart_control.v
// Created On : 2019-04-27 15:00:48
// Last Modified : 2019-04-28 20:58:31
// Revision : 1.0
//
// Description : control the rden and wren port of the ram , to realize the function
// that each time receive four data in and then send four data out
// together .
//==================================================================================================
module uart_control(
input clk ,
input rst ,
input rdsig ,
input send ,
output reg rden ,
output reg wren ,
output reg [1:0] addr ,
output reg sendstart
) ;
reg [1:0] state ;
reg [1:0] cnt ;
parameter INITIAL = 2'd0 ;
parameter RECEIVE = 2'd1 ;
parameter SEND = 2'd2 ;
always @(posedge clk or negedge rst) begin
if (~rst) begin
// reset
rden <= 0 ;
wren <= 0 ;
state <= 2'd0 ;
addr <= 0 ;
cnt <= 0 ;
sendstart <= 0 ;
end
else begin
case(state)
INITIAL : begin
rden <= 0 ;
if(rdsig) begin
state <= 2'd1 ; //goto receive state when data receive finish
cnt <= 0 ;
end
else begin
state <= 2'd0 ; //waiting while no data in
end
end
RECEIVE : begin
case(cnt)
0: begin
wren <= 1 ;
cnt <= cnt + 1 ;
end
1: begin
cnt <= 0 ;
if(addr == 0) begin
addr <= 1 ;
state <= 0 ;
wren <= 0 ;
end
else if(addr == 1) begin
addr <= 2 ;
state <= 0 ;
wren <= 0 ;
end
else if(addr == 2) begin
addr <= 3 ;
state <= 0 ;
wren <= 0 ;
end
else begin
addr <= 0 ;
state <= 2 ;
wren <= 0 ;
sendstart <= 1 ;
end
end
endcase
end
SEND : begin
case(cnt)
0 : begin
rden <= 1 ;
sendstart <= 0 ;
cnt <= cnt + 1 ;
end
1 : begin
if (send) begin
if (addr == 3) begin //already send four data out
addr <= 0 ;
state <= 2'd0 ; //back to initial state
cnt <= 0 ;
end
else if(addr == 0) begin
addr <= addr + 1 ;
wren <=0 ;
state <= 2'd2 ;
end
else begin
addr <= addr + 1 ;
state <= 2'd2 ; //keep send state when still have data remaining
end
end
else begin
state <= 2'd2 ;
end
end
endcase
end
default : begin
end
endcase
end
end
endmodule
各模块连接如图所示,ram设为存储位数8位,大小为4。
结果分析
从结果中可以看出,每次发1个、2个和4个数据时结果正常,但是一次发其他数量数据时会有数据丢失。利用signaltap进行分析,发现是因为使用的是单口ram,因此读写不能同时进行。在发送头四个数据时,剩余的数据仍然在被接收但又没有存储下来,因此导致了丢失。(仅用单口ram能否完成此功能暂不确定,本人倾向于认为不可实现,如有知道的大神望告知)