当前位置:首页 > > AdriftCoreFPGA芯研社


在实际工程应用中,常常需要通过串口一次性传输多字节的数据。例如,以太网模块可能会发送经过8字节对齐的一个时钟周期的64位数据,然后通过串口传递给下游的传感器设备。类似地,DDR模块可能以32字节对齐的形式发送一个时钟周期的256位数据,通过串口传输给上位机。如果每次都需要通过修改Verilog代码的方式将数据拆分成8位的块再发送给串口,将会显得非常繁琐。本文通过引入只有三种状态的状态机,只需更改参数就可实现对任意字节位宽的串口数据发送。这种方法使得在不同情境下轻松适应不同的数据格式,极大地提高了系统的灵活性和可维护性。




设计思路


首先,需要设计一个字节计数器,用于追踪数据发送到了哪一个字节。


接着,设计三个状态:空闲状态、准备发送状态和发送数据状态。


在准备发送状态下,主要完成两项任务:一是每到达一个字节就启用串口模块,二是加载数据以准备发送。


对于N字节数据的处理方式:每使用完一个字节后,执行右移八位的操作,然后将N位数据的低八位发送给串口模块(其中N为整数)。这确保了数据的有效传输,并适应了不同字节长度的情况。

data先从高位字节发送:

data <= data_tmp[DATAN_W-1:DATAN_W-8];
data_tmp <= {data_tmp[DATAN_W-9:0],data_tmp[DATAN_W-1:DATAN_W-8]};


data先从低位字节发送:

data     <= data_tmp[7:0];
data_tmp <= {data_tmp[7:0],data_tmp[DATAN_W-1:8]};


多字节发送模块


该模块实现了多数据传输的基础功能,当然,可以扩展一个busy信号,当状态机未工作完成的时候,反压上游模块,告诉上游模块不能再发。busy信号可以可以通过状态机的IDLE状态来生成。

`timescale 1ns / 1ns module uart_tx_multibyte(
 clk      , //时钟 rst_n    , //复位 data_n   , //要发送的多字节数据 trans_go , //发送使能 uart_tx //串口发送数据 ); parameter IDLE     = 3'b001; //空闲状态 parameter S1       = 3'b010; //准备发送数据状态,使能,装载数据 parameter S2       = 3'b100; //发送数据状态 //CNTBY_N字节数,CNTBY_W字节数计数器的位宽(更改这两个参数,即可实现串口任意字节发送) parameter CNTBY_N  = 5; //发送数据字节个数 parameter DATAN_W  = 8*CNTBY_N; //发送数据个数 parameter CNTBY_W  = 3; //发送数据字节计数器位宽,5=3'b101 parameter DATA_W   = 8; //一次串行发送的数据个数 parameter STATE_W  = 3; //状态机位宽 input clk; input rst_n; input [DATAN_W-1:0]  data_n; input trans_go; output uart_tx; wire uart_tx; reg [CNTBY_W-1:0]  cnt_bytes; wire add_cnt_bytes; wire end_cnt_bytes; reg cnt_bytes_flag; wire tx_done; reg [DATA_W-1:0]   data; reg send_en; reg [STATE_W-1:0]  state_c; reg [STATE_W-1:0]  state_n; wire IDLE2S1_start; wire S12S2_start; wire S22S1_start ; wire S22IDLE_start ; reg [DATAN_W-1:0]  data_tmp;


my_uart_tx my_uart_tx( .clk (clk), //时钟 .rst_n (rst_n), //复位 .data (data), //发送数据 .send_en (send_en), //发送使能 .baud_set (4'd4), //波特率设置,默认9600bps .uart_tx (uart_tx), //串口发送 .tx_done (tx_done) //发送完成标志位 ); always @(posedge clk or negedge rst_n)begin if(!rst_n)
 cnt_bytes <= 0; else if(add_cnt_bytes)begin if(end_cnt_bytes)
 cnt_bytes <= 0; else cnt_bytes <= cnt_bytes + 1'b1; end end assign add_cnt_bytes = cnt_bytes_flag; assign end_cnt_bytes = add_cnt_bytes && cnt_bytes == CNTBY_N - 1; always @(*)begin if(tx_done)
 cnt_bytes_flag = 1; else cnt_bytes_flag = 0; end //状态机 //状态转移描述 always @(posedge clk or negedge rst_n)begin if(!rst_n)
 state_c <= IDLE; else state_c <= state_n; end //状态转移条件判断描述 always @(*)begin case(state_c)
 IDLE:begin if(IDLE2S1_start)
 state_n = S1; else state_n = state_c; end S1:begin if(S12S2_start)
 state_n = S2; else state_n = state_c; end S2:begin if(S22S1_start)
 state_n = S1; else if(S22IDLE_start)
 state_n = IDLE; else state_n = state_c; end default:
 state_n = IDLE; endcase end //状态转移条件描述 assign IDLE2S1_start =  state_c==IDLE  && trans_go; assign S12S2_start   =  state_c==S1    && send_en; assign S22S1_start   =  state_c==S2    && tx_done && !end_cnt_bytes; assign S22IDLE_start =  state_c==S2    && end_cnt_bytes; //状态输出 always @(posedge clk or negedge rst_n)begin if(!rst_n)
 send_en <= 0; else if(state_c == S1)
 send_en <= 1; else send_en <= 0; end always @(posedge clk or negedge rst_n)begin if(!rst_n)begin data <= 0;
 data_tmp <= 0; end else if(state_c == IDLE)begin data <= 0;
 data_tmp <= data_n; end else if(state_c == S1 && !send_en)begin data <= data_tmp[DATAN_W-1:DATAN_W-8];
 data_tmp <= {data_tmp[DATAN_W-9:0],data_tmp[DATAN_W-1:DATAN_W-8]}; end end endmodule 



UART发送模块



module my_uart_tx(
 clk      , //时钟 rst_n    , //复位 data     , //发送数据 send_en  , //发送使能 baud_set , //波特率设置 uart_tx  , //串口发送 tx_done //发送完成标志位 ); parameter DATA_W   = 8; parameter SET_W    = 3; parameter BYTE_D   = 10; parameter BAUT_W   = 17; parameter BYTE_W   = 4; input clk; input rst_n; input [DATA_W-1:0]   data; input send_en; input [SET_W-1:0]    baud_set; output uart_tx; output tx_done; reg uart_tx; reg tx_done; reg [BAUT_W-1:0]   cnt_baud; reg [BAUT_W-1:0]   baud; wire add_cnt_baud; wire end_cnt_baud; reg [BYTE_W-1:0]   cnt_byte; wire add_cnt_byte; wire end_cnt_byte; reg [BYTE_D-1:0]   uart_data; reg cnt_baud_flag; always @(posedge clk or negedge rst_n)begin if(!rst_n)
 cnt_baud <= 0; else if(add_cnt_baud)begin if(end_cnt_baud)
 cnt_baud <= 0; else cnt_baud <= cnt_baud + 1'b1; end end assign add_cnt_baud = cnt_baud_flag; assign end_cnt_baud = add_cnt_baud && cnt_baud == baud - 1; always @(posedge clk or negedge rst_n)begin if(!rst_n)
 cnt_byte <= 0; else if(add_cnt_byte)begin if(end_cnt_byte)
 cnt_byte <= 0; else cnt_byte <= cnt_byte + 1'b1; end end assign add_cnt_byte = end_cnt_baud; assign end_cnt_byte = add_cnt_byte && cnt_byte == BYTE_D - 1; always @(posedge clk or negedge rst_n)begin if(!rst_n)
 uart_tx <= 1; else if(cnt_baud == 0 && add_cnt_baud)
 uart_tx <= uart_data[cnt_byte]; end always @(posedge clk or negedge rst_n)begin if(!rst_n)
 uart_data <= 10'b0; else if(!cnt_baud_flag && send_en)
 uart_data <= {1'b1,data,1'b0}; end always @(posedge clk or negedge rst_n)begin if(!rst_n)
 cnt_baud_flag <= 0; else if(send_en)
 cnt_baud_flag <= 1; else if(end_cnt_byte)
 cnt_baud_flag <= 0; end always @(*)begin case(baud_set) 3'd0:baud = 17'd83333;//600bps 3'd1:baud = 17'd41666;//1200bps  3'd2:baud = 17'd20833;//2400bps  3'd3:baud = 17'd10416;//4800bps 3'd4:baud = 17'd5208 ;//9600bps  3'd5:baud = 17'd2604 ;//19200bps  3'd6:baud = 17'd1302 ;//38400bps  3'd7:baud = 17'd868 ;//57600bps default:
 baud = 0; endcase end always @(posedge clk or negedge rst_n)begin if(!rst_n)
 tx_done <= 0; else if(end_cnt_byte)
 tx_done <= 1; else tx_done <= 0; end endmodule


本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除( 邮箱:macysun@21ic.com )。
关闭