├── .gitignore ├── LICENSE ├── README.md ├── rsc ├── baud_gen_func_model.png ├── rx_func_model.png ├── tx_func_model.png ├── uart.png ├── uart_func_model.png └── uart_structure.png ├── testbench ├── BaudRateGenerator_tb.v ├── Uart8Receiver_tb.v ├── Uart8Transmitter_tb.v └── Uart8_tb.v └── uart ├── BaudRateGenerator.v ├── Uart8.v ├── Uart8Receiver.v ├── Uart8Transmitter.v └── UartStates.vh /.gitignore: -------------------------------------------------------------------------------- 1 | *tmp/ 2 | *.tmp 3 | 4 | incremental_db/ 5 | output_files/ 6 | greybox_tmp/ 7 | simulation/ 8 | db/ 9 | 10 | *.bak 11 | *.bdf 12 | *.bsf 13 | *.qip 14 | *.qsf 15 | *.qpf 16 | *.vwf 17 | *.qdf 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Dmitriy 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | verilog-uart 2 | ============ 3 | Simple 8-bit UART realization on [Verilog HDL](https://en.wikipedia.org/wiki/Verilog). 4 | 5 | Able to operate 8 bits of serial data, one start bit, one stop bit. 6 | 7 | Usage 8 | ----- 9 | ![uart](rsc/uart.png) 10 | 11 | ### Parameters: 12 | * `CLOCK_RATE` - board internal clock rate 13 | * `BAUD_RATE` - target baud rate 14 | 15 | ### IO: 16 | 17 | #### control: 18 | * `clk` - **[input]** board internal clock 19 | 20 | #### rx interface: 21 | * `rx` - **[input]** receiver input 22 | * `rxEn` - **[input]** enable/disable receiver 23 | * `out[7..0]` - **[output]** received data 24 | * `rxDone` - **[output]** end of transaction (1 posedge clk) 25 | * `rxBusy` - **[output]** transaction is in progress 26 | * `rxErr` - **[output]** transaction error: invalid start/stop bit (1 posedge clk) 27 | 28 | #### tx interface: 29 | * `txEn` - **[input]** enable/disable transmitter 30 | * `txStart` - **[input]** start of transaction (1 posedge clk) 31 | * `in[7..0]` - **[input]** data to transmit (stored inside while transaction is in progress) 32 | * `tx` - **[output]** transmitter output 33 | * `txDone` - **[output]** end of transaction (1 posedge clk) 34 | * `txBusy` - **[output]** transaction is in progress 35 | 36 | Demo 37 | ---- 38 | ![structure](rsc/uart_structure.png) 39 | 40 | Uart functional modeling on: 41 | * `CLOCK_RATE=32` 42 | * `BAUD_RATE=1` 43 | * `T(clk) = 0.5us` 44 | * `T(rxEn) = 800us` 45 | * `T(rx) = 128us` 46 | * `T(txEn) = 700us` 47 | * `T(txStart) = 200us` 48 | * `T(in) = 30us` (counter inc by 1) 49 | 50 | ![uart functional modeling](rsc/uart_func_model.png) 51 | 52 | Receiver functional modeling on: 53 | * `T(clk) = 1us` 54 | * `en=1` 55 | * `T(rx) = 144us` 56 | 57 | ![receiver functional modeling](rsc/rx_func_model.png) 58 | 59 | Transmitter functional modeling on: 60 | * `T(clk) = 1us` 61 | * `en=1` 62 | * `T(start) = 200us` 63 | * `T(in) = 30us` (counter inc by 1) 64 | 65 | ![transmitter functional modeling](rsc/tx_func_model.png) 66 | 67 | Baud rate generator functional modeling on: 68 | * `CLOCK_RATE=32` 69 | * `BAUD_RATE=1` 70 | * `T(clk) = 0.5us` 71 | 72 | ![baud rate generator functional modeling](rsc/baud_gen_func_model.png) 73 | 74 | 75 | TODO 76 | ---- 77 | * testbench 78 | * parameter to control data width 79 | -------------------------------------------------------------------------------- /rsc/baud_gen_func_model.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hell03end/verilog-uart/cad12f92f848973e8f63c95fe7a9af0601b8773b/rsc/baud_gen_func_model.png -------------------------------------------------------------------------------- /rsc/rx_func_model.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hell03end/verilog-uart/cad12f92f848973e8f63c95fe7a9af0601b8773b/rsc/rx_func_model.png -------------------------------------------------------------------------------- /rsc/tx_func_model.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hell03end/verilog-uart/cad12f92f848973e8f63c95fe7a9af0601b8773b/rsc/tx_func_model.png -------------------------------------------------------------------------------- /rsc/uart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hell03end/verilog-uart/cad12f92f848973e8f63c95fe7a9af0601b8773b/rsc/uart.png -------------------------------------------------------------------------------- /rsc/uart_func_model.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hell03end/verilog-uart/cad12f92f848973e8f63c95fe7a9af0601b8773b/rsc/uart_func_model.png -------------------------------------------------------------------------------- /rsc/uart_structure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hell03end/verilog-uart/cad12f92f848973e8f63c95fe7a9af0601b8773b/rsc/uart_structure.png -------------------------------------------------------------------------------- /testbench/BaudRateGenerator_tb.v: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hell03end/verilog-uart/cad12f92f848973e8f63c95fe7a9af0601b8773b/testbench/BaudRateGenerator_tb.v -------------------------------------------------------------------------------- /testbench/Uart8Receiver_tb.v: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hell03end/verilog-uart/cad12f92f848973e8f63c95fe7a9af0601b8773b/testbench/Uart8Receiver_tb.v -------------------------------------------------------------------------------- /testbench/Uart8Transmitter_tb.v: -------------------------------------------------------------------------------- 1 | `timescale 1 ns / 100 ps 2 | 3 | `ifndef SIMULATION_CYCLES 4 | `define SIMULATION_CYCLES 120 5 | `endif 6 | 7 | `include "UartStates.vh" 8 | 9 | 10 | module Uart8Transmitter_tb; 11 | parameter Tt = 20; // clock timout 12 | 13 | reg clk; 14 | reg en; 15 | reg start; 16 | reg [7:0] in; 17 | reg out; 18 | reg done; 19 | reg busy; 20 | 21 | Uart8Transmitter utx ( 22 | .clk ( clk ), 23 | .en ( en ), 24 | .start ( start ), 25 | .in ( in ), 26 | .out ( out ), 27 | .done ( done ), 28 | .busy ( busy ) 29 | ); 30 | 31 | // simulation init 32 | initial begin 33 | clk = 0; 34 | forever clk = #(Tt/2) ~clk; 35 | end 36 | 37 | initial begin 38 | rst_n = 0; 39 | repeat (4) @(posedge clk); 40 | rst_n = 1; 41 | end 42 | 43 | //register file reset 44 | integer i; 45 | initial begin 46 | for (i = 0; i < 32; i = i + 1) begin 47 | sm_top.sm_cpu.rf.rf[i] = 0; 48 | end 49 | end 50 | 51 | task disasmInstr ( 52 | input [31:0] instr 53 | ); 54 | reg [ 5:0] cmdOper; 55 | reg [ 5:0] cmdFunk; 56 | reg [ 4:0] cmdRs; 57 | reg [ 4:0] cmdRt; 58 | reg [ 4:0] cmdRd; 59 | reg [ 4:0] cmdSa; 60 | reg [15:0] cmdImm; 61 | reg signed [15:0] cmdImmS; 62 | 63 | begin 64 | cmdOper = instr[31:26]; 65 | cmdFunk = instr[ 5:0 ]; 66 | cmdRs = instr[25:21]; 67 | cmdRt = instr[20:16]; 68 | cmdRd = instr[15:11]; 69 | cmdSa = instr[10:6 ]; 70 | cmdImm = instr[15:0 ]; 71 | cmdImmS = instr[15:0 ]; 72 | 73 | $write(" "); 74 | 75 | casez( {cmdOper,cmdFunk} ) 76 | default : if (instr == 32'b0) begin 77 | $write ("nop"); 78 | end else begin 79 | $write ("new/unknown"); 80 | end 81 | 82 | { `C_SPEC, `F_ADDU } : $write ("addu $%1d, $%1d, $%1d", cmdRd, cmdRs, cmdRt); 83 | { `C_SPEC, `F_OR } : $write ("or $%1d, $%1d, $%1d", cmdRd, cmdRs, cmdRt); 84 | { `C_SPEC, `F_SRL } : $write ("srl $%1d, $%1d, $%1d", cmdRd, cmdRs, cmdRt); 85 | { `C_SPEC, `F_SLTU } : $write ("sltu $%1d, $%1d, $%1d", cmdRd, cmdRs, cmdRt); 86 | { `C_SPEC, `F_SUBU } : $write ("subu $%1d, $%1d, $%1d", cmdRd, cmdRs, cmdRt); 87 | 88 | { `C_ADDIU, `F_ANY } : $write ("addiu $%1d, $%1d, %1d", cmdRt, cmdRs, cmdImm); 89 | { `C_LUI, `F_ANY } : $write ("lui $%1d, %1d", cmdRt, cmdImm); 90 | 91 | { `C_BEQ, `F_ANY } : $write ("beq $%1d, $%1d, %1d", cmdRs, cmdRt, cmdImmS + 1); 92 | { `C_BNE, `F_ANY } : $write ("bne $%1d, $%1d, %1d", cmdRs, cmdRt, cmdImmS + 1); 93 | 94 | { `C_SPEC, `F_XOR } : $write ("xor $%1d, $%1d, %1d", cmdRd, cmdRs, cmdRt); 95 | { `C_XORI, `F_ANY } : $write ("xori $%1d, $%1d, %1d", cmdRd, cmdRs, cmdImm); 96 | { `C_SPEC, `F_JR } : $write ("jr $%1d", cmdRs); 97 | { `C_SPEC, `F_SLL } : $write ("sll $%1d, $%1d, %1d", cmdRd, cmdRs, cmdImm); 98 | 99 | { `C_LIB, `F_ANY } : $write ("lib $%1d, $%1d", cmdRd, cmdImm); 100 | endcase 101 | end 102 | endtask 103 | 104 | //simulation debug output 105 | integer cycle; initial cycle = 0; 106 | 107 | initial regAddr = 0; // get PC 108 | 109 | always @(posedge clk) begin 110 | $write ("%5d pc = %2d pcaddr = %h instr = %h v0 = %1d", 111 | cycle, regData, (regData << 2), sm_top.sm_cpu.instr, sm_top.sm_cpu.rf.rf[2]); 112 | 113 | disasmInstr(sm_top.sm_cpu.instr); 114 | 115 | $write("\n"); 116 | 117 | cycle = cycle + 1; 118 | 119 | if (cycle > `SIMULATION_CYCLES) begin 120 | $display ("Timeout"); 121 | $stop; 122 | end 123 | end 124 | 125 | endmodule 126 | -------------------------------------------------------------------------------- /testbench/Uart8_tb.v: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hell03end/verilog-uart/cad12f92f848973e8f63c95fe7a9af0601b8773b/testbench/Uart8_tb.v -------------------------------------------------------------------------------- /uart/BaudRateGenerator.v: -------------------------------------------------------------------------------- 1 | /* 2 | * Baud rate generator to divide {CLOCK_RATE} (internal board clock) into 3 | * a rx/tx {BAUD_RATE} pair with rx oversamples by 16x. 4 | */ 5 | module BaudRateGenerator #( 6 | parameter CLOCK_RATE = 100000000, // board internal clock (def == 100MHz) 7 | parameter BAUD_RATE = 9600 8 | )( 9 | input wire clk, // board clock 10 | output reg rxClk, // baud rate for rx 11 | output reg txClk // baud rate for tx 12 | ); 13 | parameter MAX_RATE_RX = CLOCK_RATE / (2 * BAUD_RATE * 16); // 16x oversample 14 | parameter MAX_RATE_TX = CLOCK_RATE / (2 * BAUD_RATE); 15 | parameter RX_CNT_WIDTH = $clog2(MAX_RATE_RX); 16 | parameter TX_CNT_WIDTH = $clog2(MAX_RATE_TX); 17 | 18 | reg [RX_CNT_WIDTH - 1:0] rxCounter = 0; 19 | reg [TX_CNT_WIDTH - 1:0] txCounter = 0; 20 | 21 | initial begin 22 | rxClk = 1'b0; 23 | txClk = 1'b0; 24 | end 25 | 26 | always @(posedge clk) begin 27 | // rx clock 28 | if (rxCounter == MAX_RATE_RX[RX_CNT_WIDTH-1:0]) begin 29 | rxCounter <= 0; 30 | rxClk <= ~rxClk; 31 | end else begin 32 | rxCounter <= rxCounter + 1'b1; 33 | end 34 | // tx clock 35 | if (txCounter == MAX_RATE_TX[TX_CNT_WIDTH-1:0]) begin 36 | txCounter <= 0; 37 | txClk <= ~txClk; 38 | end else begin 39 | txCounter <= txCounter + 1'b1; 40 | end 41 | end 42 | 43 | endmodule 44 | -------------------------------------------------------------------------------- /uart/Uart8.v: -------------------------------------------------------------------------------- 1 | /* 2 | * Simple 8-bit UART realization. 3 | * Combine receiver, transmitter and baud rate generator. 4 | * Able to operate 8 bits of serial data, one start bit, one stop bit. 5 | */ 6 | module Uart8 #( 7 | parameter CLOCK_RATE = 100000000, // board internal clock 8 | parameter BAUD_RATE = 9600 9 | )( 10 | input wire clk, 11 | 12 | // rx interface 13 | input wire rx, 14 | input wire rxEn, 15 | output wire [7:0] out, 16 | output wire rxDone, 17 | output wire rxBusy, 18 | output wire rxErr, 19 | 20 | // tx interface 21 | output wire tx, 22 | input wire txEn, 23 | input wire txStart, 24 | input wire [7:0] in, 25 | output wire txDone, 26 | output wire txBusy 27 | ); 28 | wire rxClk; 29 | wire txClk; 30 | 31 | BaudRateGenerator #( 32 | .CLOCK_RATE(CLOCK_RATE), 33 | .BAUD_RATE(BAUD_RATE) 34 | ) generatorInst ( 35 | .clk(clk), 36 | .rxClk(rxClk), 37 | .txClk(txClk) 38 | ); 39 | 40 | Uart8Receiver rxInst ( 41 | .clk(rxClk), 42 | .en(rxEn), 43 | .in(rx), 44 | .out(out), 45 | .done(rxDone), 46 | .busy(rxBusy), 47 | .err(rxErr) 48 | ); 49 | 50 | Uart8Transmitter txInst ( 51 | .clk(txClk), 52 | .en(txEn), 53 | .start(txStart), 54 | .in(in), 55 | .out(tx), 56 | .done(txDone), 57 | .busy(txBusy) 58 | ); 59 | 60 | endmodule 61 | -------------------------------------------------------------------------------- /uart/Uart8Receiver.v: -------------------------------------------------------------------------------- 1 | `include "UartStates.vh" 2 | 3 | /* 4 | * 8-bit UART Receiver. 5 | * Able to receive 8 bits of serial data, one start bit, one stop bit. 6 | * When receive is complete {done} is driven high for one clock cycle. 7 | * Output data should be taken away by a few clocks or can be lost. 8 | * When receive is in progress {busy} is driven high. 9 | * Clock should be decreased to baud rate. 10 | */ 11 | module Uart8Receiver ( 12 | input wire clk, // baud rate 13 | input wire en, 14 | input wire in, // rx 15 | output reg [7:0] out, // received data 16 | output reg done, // end on transaction 17 | output reg busy, // transaction is in process 18 | output reg err // error while receiving data 19 | ); 20 | // states of state machine 21 | reg [1:0] RESET = 2'b00; 22 | reg [1:0] IDLE = 2'b01; 23 | reg [1:0] DATA_BITS = 2'b10; 24 | reg [1:0] STOP_BIT = 2'b11; 25 | 26 | reg [2:0] state; 27 | reg [2:0] bitIdx = 3'b0; // for 8-bit data 28 | reg [1:0] inputSw = 2'b0; // shift reg for input signal state 29 | reg [3:0] clockCount = 4'b0; // count clocks for 16x oversample 30 | reg [7:0] receivedData = 8'b0; // temporary storage for input data 31 | 32 | initial begin 33 | out <= 8'b0; 34 | err <= 1'b0; 35 | done <= 1'b0; 36 | busy <= 1'b0; 37 | end 38 | 39 | always @(posedge clk) begin 40 | inputSw = { inputSw[0], in }; 41 | 42 | if (!en) begin 43 | state = RESET; 44 | end 45 | 46 | case (state) 47 | RESET: begin 48 | out <= 8'b0; 49 | err <= 1'b0; 50 | done <= 1'b0; 51 | busy <= 1'b0; 52 | bitIdx <= 3'b0; 53 | clockCount <= 4'b0; 54 | receivedData <= 8'b0; 55 | if (en) begin 56 | state <= IDLE; 57 | end 58 | end 59 | 60 | IDLE: begin 61 | done <= 1'b0; 62 | if (&clockCount) begin 63 | state <= DATA_BITS; 64 | out <= 8'b0; 65 | bitIdx <= 3'b0; 66 | clockCount <= 4'b0; 67 | receivedData <= 8'b0; 68 | busy <= 1'b1; 69 | err <= 1'b0; 70 | end else if (!(&inputSw) || |clockCount) begin 71 | // Check bit to make sure it's still low 72 | if (&inputSw) begin 73 | err <= 1'b1; 74 | state <= RESET; 75 | end 76 | clockCount <= clockCount + 4'b1; 77 | end 78 | end 79 | 80 | // Wait 8 full cycles to receive serial data 81 | DATA_BITS: begin 82 | if (&clockCount) begin // save one bit of received data 83 | clockCount <= 4'b0; 84 | // TODO: check the most popular value 85 | receivedData[bitIdx] <= inputSw[0]; 86 | if (&bitIdx) begin 87 | bitIdx <= 3'b0; 88 | state <= STOP_BIT; 89 | end else begin 90 | bitIdx <= bitIdx + 3'b1; 91 | end 92 | end else begin 93 | clockCount <= clockCount + 4'b1; 94 | end 95 | end 96 | 97 | /* 98 | * Baud clock may not be running at exactly the same rate as the 99 | * transmitter. Next start bit is allowed on at least half of stop bit. 100 | */ 101 | STOP_BIT: begin 102 | if (&clockCount || (clockCount >= 4'h8 && !(|inputSw))) begin 103 | state <= IDLE; 104 | done <= 1'b1; 105 | busy <= 1'b0; 106 | out <= receivedData; 107 | clockCount <= 4'b0; 108 | end else begin 109 | clockCount <= clockCount + 1; 110 | // Check bit to make sure it's still high 111 | if (!(|inputSw)) begin 112 | err <= 1'b1; 113 | state <= RESET; 114 | end 115 | end 116 | end 117 | 118 | default: state <= IDLE; 119 | endcase 120 | end 121 | 122 | endmodule 123 | -------------------------------------------------------------------------------- /uart/Uart8Transmitter.v: -------------------------------------------------------------------------------- 1 | `include "UartStates.vh" 2 | 3 | /* 4 | * 8-bit UART Transmitter. 5 | * Able to transmit 8 bits of serial data, one start bit, one stop bit. 6 | * When transmit is complete {done} is driven high for one clock cycle. 7 | * When transmit is in progress {busy} is driven high. 8 | * Clock should be decreased to baud rate. 9 | */ 10 | module Uart8Transmitter ( 11 | input wire clk, // baud rate 12 | input wire en, 13 | input wire start, // start of transaction 14 | input wire [7:0] in, // data to transmit 15 | output reg out, // tx 16 | output reg done, // end on transaction 17 | output reg busy // transaction is in process 18 | ); 19 | reg [2:0] state = `RESET; 20 | reg [7:0] data = 8'b0; // to store a copy of input data 21 | reg [2:0] bitIdx = 3'b0; // for 8-bit data 22 | reg [2:0] idx; 23 | 24 | assign idx = bitIdx; 25 | 26 | always @(posedge clk) begin 27 | case (state) 28 | default : begin 29 | state <= `IDLE; 30 | end 31 | `IDLE : begin 32 | out <= 1'b1; // drive line high for idle 33 | done <= 1'b0; 34 | busy <= 1'b0; 35 | bitIdx <= 3'b0; 36 | data <= 8'b0; 37 | if (start & en) begin 38 | data <= in; // save a copy of input data 39 | state <= `START_BIT; 40 | end 41 | end 42 | `START_BIT : begin 43 | out <= 1'b0; // send start bit (low) 44 | busy <= 1'b1; 45 | state <= `DATA_BITS; 46 | end 47 | `DATA_BITS : begin // Wait 8 clock cycles for data bits to be sent 48 | out <= data[idx]; 49 | if (&bitIdx) begin 50 | bitIdx <= 3'b0; 51 | state <= `STOP_BIT; 52 | end else begin 53 | bitIdx <= bitIdx + 1'b1; 54 | end 55 | end 56 | `STOP_BIT : begin // Send out Stop bit (high) 57 | done <= 1'b1; 58 | data <= 8'b0; 59 | state <= `IDLE; 60 | end 61 | endcase 62 | end 63 | 64 | endmodule 65 | -------------------------------------------------------------------------------- /uart/UartStates.vh: -------------------------------------------------------------------------------- 1 | // states of state machine 2 | `define RESET 3'b001 3 | `define IDLE 3'b010 4 | `define START_BIT 3'b011 // transmitter only 5 | `define DATA_BITS 3'b100 6 | `define STOP_BIT 3'b101 7 | --------------------------------------------------------------------------------