中科因仑“3+1”工程特种兵精英论坛

标题: 基于Verilog的 UART 最终升级 [打印本页]

作者: 谭力源    时间: 2016-5-17 19:09
标题: 基于Verilog的 UART 最终升级
自从08年开始学FPGA以来的一年,我都是从事VHDL的,当时写了uart第一版本,现在看来戳的不行了,而且有问题恶心,哇哇吐。。。

因为时代的潮流09年我也开始了Verilog,后来有学了SOCP,NIOS II,一直走。。。

10年暑假,因为准备比赛,再次写了Verilog 版本的uart,双向fifo协议收发,功能上可以,没多大问题。。。

11年寒假,因为家里没有宽带,饥渴难耐,我TMD受不了自己那么空了。。。觉得自己一直来是自由发挥的,代码规范都不咋地,于是拿出《华为verilog规范》,又拿出以前的代码,怎么看都不顺眼,于是,创作了verilog 版的uart 第三版本!!!

我用fifo测试收发文件,上万数据没出现过错误,而且比较稳定,比起网上那些随便搞搞的东西,左看右看都感觉自己的略胜一筹。。。

当然我知道完美是没有极限的,所以我拿出三个文件,希望大家能够提出宝贵意见,看看还有什么可以优化的!!!!!!!!!!!!!

首先我分了三个module:
(1)clk_generator.v  负责接收个发送的时钟,修改bps就在此处,没有用常规的方法分频,因为觉得从完美的角度考虑,这有损是新片的最佳发挥,当然也会不稳定,再次我采纳了DDS中0~2^N 步进寻址的思维,设计了是能时钟
(2)uart_receiver.v  负责uart的数据接收,相对于数据的发送稍微简单一点,因为此处为了达到数据的最稳定,不能一读取到有数据就采样,我设计在中间时刻采样,前后保持,用了16*bps的clk来工作
(3)uart_transfer.v  扶着uart数据的发送,clk = bps,这是比较简单的一个模块,用脚趾头想想都能实现

下面我贴出这几个代码,如果大家有时间希望能给我挑挑毛病,尤其是时钟模块,虽然我觉得直接偶数分频方法出来的不准确(为了数据的准确),但我的也不是很爽:

/**************************************************************
*        File Name                :                clk_generator.v               
*        Author                        :                Crazy Bingo
*        Version                        :                Quartus II 9.1
*        CreateDate                :                2011/01/31
*        Description                :                To generator different bps
**************************************************************/
/***********************************
        fc        =        50*10^6
        fo        =        fc*N/(2^32)
        N        =        fo*(2^32)/fc
                =        fo*(2^32)/(50*10^6)
************************************/
module clk_generator
(
        input        clk,
        input        rst_n,
        output        clk_bps,
        output        clk_smp
);

//------------------------------------------
/************clk_smp        = 16*clk_bps************
Freq_Word1        <=        32'd25770;                Freq_Word1        <=        32'd412317;                //300        bps
Freq_Word1        <=        32'd51540;                Freq_Word2        <=        32'd824634;                //600        bps
Freq_Word1        <=        32'd103079;                Freq_Word2        <=        32'd1649267;        //1200        bps
Freq_Word1        <=        32'd206158;                Freq_Word2        <=        32'd3298535;        //2400        bps
Freq_Word1        <=        32'd412317;                Freq_Word2        <=        32'd6597070;        //4800        bps
Freq_Word1        <=        32'd824634;                Freq_Word2        <=        32'd13194140;        //9600        bps
Freq_Word1        <=        32'd1649267;        Freq_Word2        <=        32'd26388279;        //19200        bps
Freq_Word1        <=        32'd3298535;        Freq_Word2        <=        32'd52776558;        //38400        bps
Freq_Word1        <=        32'd3693672;        Freq_Word2        <=        32'd59098750;        //43000        bps
Freq_Word1        <=        32'd4810363;        Freq_Word2        <=        32'd76965814;        //56000        bps
Freq_Word1        <=        32'd4947802;        Freq_Word2        <=        32'd79164837;        //57600        bps
Freq_Word1        <=        32'd9895605;        Freq_Word2        <=        32'd158329674;        //115200bps
Freq_Word1        <=        32'd10995116;        Freq_Word2        <=        32'd175921860;        //128000bps
Freq_Word1        <=        32'd21990233;        Freq_Word2        <=        32'd351843721;        //256000bps
*****************************************************/
//only want to generate beautiful clk for bsp and sample
reg        [31:0]        bps_cnt1;
reg        [31:0]        bps_cnt2;
always@(posedge clk or negedge rst_n)
begin
        if(!rst_n)
                begin
                bps_cnt1 <= 0;
                bps_cnt2 <= 0;
                end
        else
                begin
                bps_cnt1 <= bps_cnt1+32'd10995116;        //Bps=128000bps
                bps_cnt2 <= bps_cnt2+32'd175921860;        //Bps=128000bps*16
                end
end

//------------------------------------------
//clk_bps sync bps generater
reg        clk_bps_r0,clk_bps_r1,clk_bps_r2;
always@(posedge clk or negedge rst_n)
begin
        if(!rst_n)
                begin
                clk_bps_r0 <= 0;
                clk_bps_r1 <= 0;
                clk_bps_r2 <= 0;
                end
        else
                begin
                if(bps_cnt1 < 32'h7FFF_FFFF)
                        clk_bps_r0 <= 0;
                else
                        clk_bps_r0 <= 1;
                clk_bps_r1 <= clk_bps_r0;
                clk_bps_r2 <= clk_bps_r1;
                end
end
assign        clk_bps = ~clk_bps_r2 & clk_bps_r1;

//------------------------------------------
//clk_smp sync receive bps generator
reg        clk_smp_r0,clk_smp_r1,clk_smp_r2;
always@(posedge clk or negedge rst_n)
begin
        if(!rst_n)
                begin
                clk_smp_r0 <= 0;
                clk_smp_r1 <= 0;
                clk_smp_r2 <= 0;
                end
        else
                begin
                if(bps_cnt2 < 32'h7FFF_FFFF)
                        clk_smp_r0 <= 0;
                else
                        clk_smp_r0 <= 1;
                clk_smp_r1 <= clk_smp_r0;
                clk_smp_r2 <= clk_smp_r1;
                end
end
assign        clk_smp = ~clk_smp_r2 & clk_smp_r1;

endmodule



/**************************************************************
*        File Name                :                uart_receiver.v               
*        Author                        :                Crazy Bingo
*        Version                        :                Quartus II 9.1
*        CreateDate                :                2011/01/31
*        Description                :                rxd data Receiver
**************************************************************/
module uart_receiver
(
        input                                clk,
        input                                clk_smp,        //clk_smp=16*clk_bps
        input                                rst_n,
        
        input                                rxd,
        output                                rxd_flag,        //the flag of receive over
        output        reg        [7:0]        rxd_data        
);

//---------------------------------
//sync the data: rxd_sync
reg        rxd_sync_r0,rxd_sync_r1;                                
always@(posedge clk or negedge rst_n)
begin
        if(!rst_n)
                begin
                rxd_sync_r0 <= 1;
                rxd_sync_r1 <= 1;
                end
        else if(clk_smp == 1)
                begin
                rxd_sync_r0 <= rxd;
                rxd_sync_r1 <= rxd_sync_r0;
                end
end
wire        rxd_sync = rxd_sync_r1;

//---------------------------------
//sample data from pc to cpu
parameter        R_IDLE                =        1'b0;                //>=7 clk_smp : receive flag
parameter        R_SAMPLE        =        1'b1;                //sample data programmer
reg                        rxd_state;
reg        [3:0]        smp_cnt;                                        //sample cycle counter
reg        [2:0]        rxd_cnt;                                        //the lenth of data
always@(posedge clk or negedge rst_n)
begin
        if(!rst_n)
                begin
                smp_cnt <= 0;
                rxd_cnt <= 0;
                rxd_data <= 0;
                rxd_state <= R_IDLE;
                end
        else if(clk_smp == 1)
                begin
                case(rxd_state)
                R_IDLE:
                        begin
                        rxd_cnt <= 0;
                        if(rxd_sync == 1'b0)
                                begin
                                smp_cnt <= smp_cnt + 1'b1;
                                if(smp_cnt == 4'd7)                //8 clk_smp enable
                                        rxd_state <= R_SAMPLE;
                                end
                        else
                                smp_cnt <= 0;
                        end
                R_SAMPLE:        
                        begin
                        smp_cnt <= smp_cnt +1'b1;
                        if(smp_cnt == 4'd7)
                                begin
                                rxd_cnt <= rxd_cnt +1'b1;
                                if(rxd_cnt == 4'd7)
                                        rxd_state <= R_IDLE;
                                case(rxd_cnt)
                                3'd0:        rxd_data[0] <= rxd_sync;
                                3'd1:        rxd_data[1] <= rxd_sync;
                                3'd2:        rxd_data[2] <= rxd_sync;
                                3'd3:        rxd_data[3] <= rxd_sync;
                                3'd4:        rxd_data[4] <= rxd_sync;
                                3'd5:        rxd_data[5] <= rxd_sync;
                                3'd6:        rxd_data[6] <= rxd_sync;
                                3'd7:        rxd_data[7] <= rxd_sync;
                                endcase
                                end
                        end
                endcase
                end
end
wire        rxd_flag_r = (rxd_cnt == 4'd7) ? 1'b1 : 1'b0;

//---------------------------------
//the signal flag of rxd receive over
reg        rxd_flag_r0,rxd_flag_r1;
always@(posedge clk or negedge rst_n)
begin
        if(!rst_n)
                begin
                rxd_flag_r0 <= 0;
                rxd_flag_r1 <= 0;
                end
        else
                begin
                rxd_flag_r0 <= rxd_flag_r;
                rxd_flag_r1 <= rxd_flag_r0;
                end
end
assign        rxd_flag = ~rxd_flag_r1 & rxd_flag_r0;

endmodule


/**************************************************************
*        File Name                :                uart_transfer.v               
*        Author                        :                Crazy Bingo
*        Version                        :                Quartus II 9.1 SP2
*        CreateDate                :                2011/01/31
*        Description                :                txd data Transfer
**************************************************************/
module uart_transfer
(
        input                        clk,
        input                        clk_bps,
        input                        rst_n,
        input                        txd_en,                //txd data mark
        input        [7:0]        txd_data,
        
        output                        txd_flag,        //txd data over mark
        output        reg                txd
);

//-------------------------------------
//transfer data from cpu to pc
parameter        T_IDLE        =        1'b0;        //test the flag to transfer data
parameter        T_SEND        =        1'b1;        //uart transfer data
reg                        txd_state;
reg        [3:0]        txd_cnt;                        //txd data counter
reg                        txd_flag_r;
always@(posedge clk        or negedge rst_n)
begin
        if(!rst_n)
                begin
                txd_state <= T_IDLE;
                txd_flag_r <= 0;
                txd        <= 1'b1;        
                end
        else
                begin
                case(txd_state)
                T_IDLE:               
                        begin
                        txd <= 1;
                        txd_flag_r <= 0;
                        if(txd_en == 1)
                                txd_state <= T_SEND;
                        else
                                txd_state <= T_IDLE;
                        end        
                T_SEND:        
                        begin
                        if(clk_bps == 1)
                                begin
                                if(txd_cnt < 4'd9)        
                                        txd_cnt <= txd_cnt + 1'b1;
                                else
                                        begin
                                        txd_cnt <= 0;
                                        txd_state <= T_IDLE;
                                        txd_flag_r <= 1;
                                        end
                                case(txd_cnt)
                                4'd0:        txd <= 0;
                                4'd1:        txd <= txd_data[0];
                                4'd2:        txd <= txd_data[1];
                                4'd3:        txd <= txd_data[2];
                                4'd4:        txd <= txd_data[3];
                                4'd5:        txd <= txd_data[4];
                                4'd6:        txd <= txd_data[5];
                                4'd7:        txd <= txd_data[6];
                                4'd8:        txd <= txd_data[7];
                                4'd9:        txd <= 1;
                                endcase
                                end        
                        end               
                endcase
                end
end

//-------------------------------------
//Capture the falling of data transfer over
reg        txd_flag_r0,txd_flag_r1;
always@(posedge clk or negedge rst_n)
begin
        if(!rst_n)
                begin
                txd_flag_r0 <= 0;
                txd_flag_r1 <= 0;
                end
        else
                begin
                txd_flag_r0 <= txd_flag_r;
                txd_flag_r1 <= txd_flag_r0;
                end
end
assign        txd_flag = txd_flag_r1 & ~txd_flag_r0;

endmodule


点击此处下载 ourdev_618188VEGAZ0.rar(文件大小:3K) (原文件名:uart_io_design.rar)
文件在此处上传,谁要用我没意见,反正XXX

但给我挑毛病,因为我看大部分人写的uart都不是很爽,所以 ,谢谢了





欢迎光临 中科因仑“3+1”工程特种兵精英论坛 (http://bbs.enlern.com/) Powered by Discuz! X3.4