Created
June 15, 2025 21:30
-
-
Save ellisgl/a024220617a74cc03f2a2c1c9366444d to your computer and use it in GitHub Desktop.
UART / Serial String Reversal in Verilog (Tang Nano 9k)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
IO_LOC "clk" 52; | |
IO_PORT "clk" IO_TYPE=LVCMOS33 PULL_MODE=UP; | |
IO_LOC "uart_tx" 17; | |
IO_PORT "uart_tx" IO_TYPE=LVCMOS33; | |
IO_LOC "uart_rx" 18; | |
IO_PORT "uart_rx" IO_TYPE=LVCMOS33; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
`default_nettype none | |
module top | |
#( | |
// 27,000,000 (27Mhz) / 115200 Baud rate | |
parameter DELAY_FRAMES = 234 | |
) | |
( | |
input clk, | |
input uart_rx, | |
output uart_tx, | |
output reg [5:0] led | |
); | |
// UART RX signals | |
wire [7:0] rx_data; | |
wire rx_data_valid; | |
// UART TX signals | |
reg [7:0] tx_data; | |
reg tx_data_valid; | |
wire tx_busy; | |
// Instantiate UART RX module | |
uart_rx #( | |
.DELAY_FRAMES(DELAY_FRAMES) | |
) uart_rx_inst ( | |
.clk(clk), | |
.uart_rx(uart_rx), | |
.data_out(rx_data), | |
.data_valid(rx_data_valid) | |
); | |
// Instantiate UART TX module | |
uart_tx #( | |
.DELAY_FRAMES(DELAY_FRAMES) | |
) uart_tx_inst ( | |
.clk(clk), | |
.data_in(tx_data), | |
.data_valid(tx_data_valid), | |
.uart_tx(uart_tx), | |
.tx_busy(tx_busy) | |
); | |
// Buffer for received characters | |
localparam BUF_SIZE = 128; | |
reg [7:0] buffer [0:BUF_SIZE-1]; | |
reg [7:0] buf_len = 0; | |
// State machine states | |
localparam STATE_RECEIVING = 2'b00; | |
localparam STATE_REVERSING = 2'b01; | |
localparam STATE_TRANSMITTING = 2'b10; | |
reg [1:0] state = STATE_RECEIVING; | |
reg [7:0] tx_index = 0; | |
reg [3:0] delay_counter = 0; | |
reg tx_started = 0; | |
// Main state machine | |
always @(posedge clk) begin | |
tx_data_valid <= 0; // Default | |
case (state) | |
STATE_RECEIVING: begin | |
if (rx_data_valid) begin | |
if (rx_data == 8'h0A || rx_data == 8'h0D) begin | |
// Newline received - start reversing | |
if (buf_len > 0) begin | |
state <= STATE_REVERSING; | |
end | |
// If buffer is empty, stay in receiving state | |
end else if (buf_len < BUF_SIZE) begin | |
// Store the character | |
buffer[buf_len] <= rx_data; | |
buf_len <= buf_len + 1; | |
// If buffer is full, start reversing | |
if (buf_len + 1 >= BUF_SIZE) begin | |
state <= STATE_REVERSING; | |
end | |
end | |
// If buffer is full and we get a non-newline, just ignore it | |
end | |
end | |
STATE_REVERSING: begin | |
// Wait for TX to be completely idle and add a small delay | |
if (!tx_busy) begin | |
if (delay_counter < 8) begin | |
delay_counter <= delay_counter + 1; | |
end else begin | |
state <= STATE_TRANSMITTING; | |
tx_index <= 0; | |
delay_counter <= 0; | |
tx_started <= 0; | |
end | |
end else begin | |
delay_counter <= 0; // Reset counter while TX is busy | |
end | |
end | |
STATE_TRANSMITTING: begin | |
if (tx_index < buf_len) begin | |
if (!tx_started && !tx_busy) begin | |
// Start transmitting next byte | |
tx_data <= buffer[buf_len - 1 - tx_index]; | |
tx_data_valid <= 1; | |
tx_started <= 1; | |
end else if (tx_started && !tx_busy) begin | |
// Previous byte finished, move to next | |
tx_index <= tx_index + 1; | |
tx_started <= 0; | |
end | |
end else begin | |
// Done transmitting, reset for next message | |
state <= STATE_RECEIVING; | |
buf_len <= 0; | |
tx_index <= 0; | |
tx_started <= 0; | |
end | |
end | |
default: begin | |
state <= STATE_RECEIVING; | |
end | |
endcase | |
end | |
// Update LED with received data | |
always @(posedge clk) begin | |
if (rx_data_valid) begin | |
led <= ~rx_data[5:0]; | |
end | |
end | |
endmodule |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
`default_nettype none | |
module uart_rx | |
#( | |
parameter DELAY_FRAMES = 234 | |
) | |
( | |
input wire clk, | |
input wire uart_rx, | |
output reg [7:0] data_out, | |
output reg data_valid | |
); | |
localparam HALF_DELAY_WAIT = (DELAY_FRAMES / 2); | |
reg [3:0] rxState = 0; | |
reg [12:0] rxCounter = 0; | |
reg [7:0] dataIn = 0; | |
reg [2:0] rxBitNumber = 0; | |
localparam RX_STATE_IDLE = 0; | |
localparam RX_STATE_START_BIT = 1; | |
localparam RX_STATE_READ_WAIT = 2; | |
localparam RX_STATE_READ = 3; | |
localparam RX_STATE_STOP_BIT = 5; | |
always @(posedge clk) begin | |
data_valid <= 0; // Default to no valid data | |
case (rxState) | |
RX_STATE_IDLE: begin | |
if (uart_rx == 0) begin | |
rxState <= RX_STATE_START_BIT; | |
rxCounter <= 1; | |
rxBitNumber <= 0; | |
end | |
end | |
RX_STATE_START_BIT: begin | |
if (rxCounter == HALF_DELAY_WAIT) begin | |
rxState <= RX_STATE_READ_WAIT; | |
rxCounter <= 1; | |
end else | |
rxCounter <= rxCounter + 1; | |
end | |
RX_STATE_READ_WAIT: begin | |
rxCounter <= rxCounter + 1; | |
if ((rxCounter + 1) == DELAY_FRAMES) begin | |
rxState <= RX_STATE_READ; | |
end | |
end | |
RX_STATE_READ: begin | |
rxCounter <= 1; | |
dataIn <= {uart_rx, dataIn[7:1]}; | |
rxBitNumber <= rxBitNumber + 1; | |
if (rxBitNumber == 3'b111) | |
rxState <= RX_STATE_STOP_BIT; | |
else | |
rxState <= RX_STATE_READ_WAIT; | |
end | |
RX_STATE_STOP_BIT: begin | |
rxCounter <= rxCounter + 1; | |
if ((rxCounter + 1) == DELAY_FRAMES) begin | |
rxState <= RX_STATE_IDLE; | |
rxCounter <= 0; | |
data_out <= dataIn; | |
data_valid <= 1; | |
end | |
end | |
endcase | |
end | |
endmodule |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
`default_nettype none | |
module uart_tx | |
#( | |
parameter DELAY_FRAMES = 234 | |
) | |
( | |
input wire clk, | |
input wire [7:0] data_in, | |
input wire data_valid, | |
output reg uart_tx, | |
output wire tx_busy | |
); | |
reg [3:0] txState = 0; | |
reg [24:0] txCounter = 0; | |
reg [7:0] dataOut = 0; | |
reg [2:0] txBitNumber = 0; | |
localparam TX_STATE_IDLE = 0; | |
localparam TX_STATE_START_BIT = 1; | |
localparam TX_STATE_WRITE = 2; | |
localparam TX_STATE_STOP_BIT = 3; | |
assign tx_busy = (txState != TX_STATE_IDLE); | |
always @(posedge clk) begin | |
case (txState) | |
TX_STATE_IDLE: begin | |
uart_tx <= 1; | |
if (data_valid) begin | |
dataOut <= data_in; | |
txState <= TX_STATE_START_BIT; | |
txCounter <= 0; | |
txBitNumber <= 0; | |
end | |
end | |
TX_STATE_START_BIT: begin | |
uart_tx <= 0; | |
if ((txCounter + 1) == DELAY_FRAMES) begin | |
txState <= TX_STATE_WRITE; | |
txBitNumber <= 0; | |
txCounter <= 0; | |
end else | |
txCounter <= txCounter + 1; | |
end | |
TX_STATE_WRITE: begin | |
uart_tx <= dataOut[txBitNumber]; | |
if ((txCounter + 1) == DELAY_FRAMES) begin | |
if (txBitNumber == 3'b111) begin | |
txState <= TX_STATE_STOP_BIT; | |
end else begin | |
txState <= TX_STATE_WRITE; | |
txBitNumber <= txBitNumber + 1; | |
end | |
txCounter <= 0; | |
end else | |
txCounter <= txCounter + 1; | |
end | |
TX_STATE_STOP_BIT: begin | |
uart_tx <= 1; | |
if ((txCounter + 1) == DELAY_FRAMES) begin | |
txState <= TX_STATE_IDLE; | |
txCounter <= 0; | |
end else | |
txCounter <= txCounter + 1; | |
end | |
default: txState <= TX_STATE_IDLE; | |
endcase | |
end | |
endmodule |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment