Verilog realizes serial communication (UART)
This code refers to the relevant tutorials of wildfire to realize the sending and receiving loop. At the same time, the LED light can be controlled on and off through the serial port data. When the computer sends data, it is necessary to select the HEX sending mode and send hexadecimal data for control.
In UART protocol, it is high level when idle. In the common setting of one bit stop bit and no check bit, the start bit is low level, followed by 8-bit data bit, and finally a high-level stop bit.
Receiving module
The main design of the receiving module is to take the detection of the falling edge of the start bit as the start signal of the receiving system. Through the combination of the start signal and the bit count signal, we can get an enable signal throughout the overall operation process, and the control of the system can be realized through the judgment of the enable signal.
Another key problem is the time of level acquisition, which should be collected in the middle of a bit, which can effectively avoid the problem of unstable acquisition level.
The code of the receiving module is as follows:
module uart_receive ( input clk, input rst_n, input uart_rx, output reg receive_done, output reg [7:0] uart_data ); parameter SYSTERM_CLK = 50_000_000; //System clock frequency parameter UART_BPS = 115200; //Serial baud rate localparam BPS_COUNT_MAX = SYSTERM_CLK/UART_BPS; //To get the specified baud rate //BPS needs to be counted on the system clock_ Count times reg [7:0] reg_data;//Accept data cache reg [3:0] bit_count;//Used to count the number of bits received when receiving data reg [12:0] bps_count;//Used to calculate the time of one byte according to the clock reg start_bit;//After the falling edge of the start bit is detected, the high level of a clock is triggered reg reg1 ; reg reg2 ; reg reg3 ; reg bit_flag ;//A high level flag is generated in the middle of a level reg work_en ;//At the high level of this flag, the acceptance work starts and the work ends at the low level reg rx_flag ;//A high level is generated after the data buffer is full //Insert two-level registers for data synchronization to eliminate metastability always @(posedge clk or negedge rst_n) begin if (!rst_n) begin reg1 <= 1'b1;//Because the idle state of UART is high level, the level should be set to high level during reset end else begin reg1 <= uart_rx; end end //Insert two-level registers for data synchronization to eliminate metastability always @(posedge clk or negedge rst_n) begin if (!rst_n) begin reg2 <= 1'b1;//Because the idle state of UART is high level, the level should be set to high level during reset end else begin reg2 <=reg1; end end //Insert two-level registers for data synchronization to eliminate metastability always @(posedge clk or negedge rst_n) begin if (!rst_n) begin reg3 <= 1'b1;//Because the idle state of UART is high level, the level should be set to high level during reset end else begin reg3 <=reg2; end end //start_bit is used to detect the falling edge of the start bit always @(posedge clk or negedge rst_n) begin if (!rst_n) begin start_bit <= 1'b0; end else if ((reg3)&&(!reg2)) begin start_bit <= 1'b1; end else start_bit <= 1'b0; end //work_en, at the high level of this flag, the acceptance work starts and the work ends at the low level always @(posedge clk or negedge rst_n) begin if (!rst_n) begin work_en <= 1'b0; end else if (start_bit) begin work_en <= 1'b1; end else if ((bit_count == 4'd8) && (bit_flag == 1'b1)) begin work_en <= 1'b0; end else work_en <= work_en; end //bps_count is used to count the baud rate always @(posedge clk or negedge rst_n) begin if (!rst_n) begin bps_count <= 13'b0; end else if ((bps_count == BPS_COUNT_MAX -1) || (work_en == 0)) begin bps_count <= 13'b0;//Accounting is only performed when the work is enabled end else if (work_en == 1) begin bps_count <= bps_count + 1'b1; end else bps_count <= bps_count; end //bit_flag, which outputs the high level of a clock at the midpoint of a byte always @(posedge clk or negedge rst_n) begin if (!rst_n) begin bit_flag <= 1'b0; end else if (bps_count == BPS_COUNT_MAX/2-1) begin bit_flag <= 1'b1; end else bit_flag <= 1'b0; end //bit_count, which is used to count the bit currently received always @(posedge clk or negedge rst_n) begin if (!rst_n) begin bit_count <= 4'b0; end else if ((bit_count == 4'd8) && (bit_flag == 1'b1)) begin bit_count <= 4'b0; end else if (bit_flag == 1'b1) begin bit_count <= bit_count + 1'b1; end else bit_count <= bit_count; end //reg_data represents the cache of received data always @(posedge clk or negedge rst_n) begin if (!rst_n) begin reg_data <= 8'b0; end else if ((bit_flag == 1) && (work_en == 1) && (bit_count <= 4'd8) && (bit_count >= 4'd1)) begin reg_data <= {reg3,reg_data[7:1]}; end else reg_data <= reg_data; end //rx_flag outputs the high level of a clock after the receiving buffer is full always @(posedge clk or negedge rst_n) begin if (!rst_n) begin rx_flag <= 1'b0; end else if ((bit_count == 4'd8) && (bit_flag == 1'b1)) begin rx_flag <= 1'b1; end else rx_flag <= 1'b0; end //uart_data exports data from the data buffer always @(posedge clk or negedge rst_n) begin if (!rst_n) begin uart_data <= 8'b0; end else if (rx_flag == 1'b1) begin uart_data <= reg_data; end else uart_data <= uart_data; end //receive_done export received signal always @(posedge clk or negedge rst_n) begin if (!rst_n) begin receive_done <= 1'b0; end else if (rx_flag == 1'b1) begin receive_done <= 1'b1; end else receive_done <= 1'b0; end endmodule //uart_receive
Sending module
The implementation process of UART transmitting module is slightly simpler than the receiving process, because the transmitting module does not need to consider the position of acquisition level, but only needs to send the data in sequence by calculating the time of each bit after receiving the transmission enable.
The code is as follows:
module uart_send ( input wire clk, input wire rst_n, input wire [7:0] uart_in_data, input wire uart_in_flag, output reg uart_tx ); parameter SYSTERM_CLK = 50_000_000; //System clock frequency parameter UART_BPS = 115200; //Serial baud rate localparam BPS_COUNT_MAX = SYSTERM_CLK/UART_BPS; //To get the specified baud rate //BPS needs to be counted on the system clock_ Count times reg [12:0] bps_count;//Used to calculate the time of one byte according to the clock reg [3:0] bit_count;//Used to count the number of bits received when receiving data reg bit_flag ;//A high level flag is generated in the middle of a level reg work_en ;//At the high level of this flag, the acceptance work starts and the work ends at the low level //bps_ Baud rate counter always @(posedge clk or negedge rst_n) begin if (!rst_n) begin bps_count <= 13'b0; end else if ((bps_count ==BPS_COUNT_MAX - 1'b1) || (work_en == 1'b0)) begin bps_count <= 13'b0; end else if (work_en == 1'b1) begin bps_count <= bps_count + 1'b1; end else bps_count <= bps_count; end //work_en send work enable always @(posedge clk or negedge rst_n) begin if (!rst_n) begin work_en <= 1'b0; end else if ((bit_count == 4'd9) && (bit_flag == 1'b1)) begin work_en <= 1'b0; end else if (uart_in_flag == 1'b1) begin work_en <= 1'b1; end else work_en <= work_en; end //bit_flag signal of each bit always @(posedge clk or negedge rst_n) begin if (!rst_n) begin bit_flag <= 1'b0; end else if (bps_count == 13'b1) begin bit_flag <= 1'b1; end else bit_flag <= 1'b0; end //bit_count byte count always @(posedge clk or negedge rst_n) begin if (!rst_n) begin bit_count <= 4'b0; end else if ((bit_flag == 1'b1) && (work_en == 1'b1)) begin bit_count <= bit_count + 1'b1; end else if ((bit_count == 4'd9) && (bit_flag == 1'b1)) begin bit_count <= 4'b0; end else bit_count <= bit_count; end //uart_tx always @(posedge clk or negedge rst_n) begin if (!rst_n) begin uart_tx <= 1'b1;//Serial port transmission, high level when idle end else if (bit_flag == 1'b1) begin case (bit_count) 0: uart_tx <= 1'b0 ; 1: uart_tx <= uart_in_data[0]; 2: uart_tx <= uart_in_data[1]; 3: uart_tx <= uart_in_data[2]; 4: uart_tx <= uart_in_data[3]; 5: uart_tx <= uart_in_data[4]; 6: uart_tx <= uart_in_data[5]; 7: uart_tx <= uart_in_data[6]; 8: uart_tx <= uart_in_data[7]; 9: uart_tx <= 1'b1 ; default uart_tx <= 1'b1 ; endcase end end endmodule //uart_send
Top level module
The top layer only needs to instantiate the sending and receiving modules, and add led pins at the same time, which can realize the control of four LED lights in the process of sending and receiving loopback.
The code is as follows:
module uart_top ( input wire clk, input wire rst_n, input wire uart_rx, output wire uart_tx, output wire [3:0] led ); parameter SYSTERM_CLK = 26'd50_000_000; //System clock frequency parameter UART_BPS = 17'd115200; //Serial baud rate wire flag; wire [7:0] data; assign led = data[3:0]; uart_receive #( .SYSTERM_CLK (SYSTERM_CLK ), .UART_BPS (UART_BPS ) ) u_uart_receive( .clk (clk ), .rst_n (rst_n ), .uart_rx (uart_rx ), .receive_done (flag ), .uart_data (data ) ); uart_send #( .SYSTERM_CLK (SYSTERM_CLK ), .UART_BPS (UART_BPS ) ) u_uart_send( .clk (clk ), .rst_n (rst_n ), .uart_in_data (data ), .uart_in_flag (flag ), .uart_tx (uart_tx ) ); endmodule //uart_top