Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // Verilog
- // - UART transmitter module.
- //
- module uart_tx(
- input wire clk , // Top level system clock input.
- input wire resetn , // Asynchronous active low reset.
- output wire uart_txd , // UART transmit pin.
- output wire uart_tx_busy, // Module busy sending previous item.
- input wire uart_tx_en , // Send the data on uart_tx_data
- input wire [PAYLOAD_BITS-1:0] uart_tx_data // The data to be sent
- );
- // ---------------------------------------------------------------------------
- // External parameters.
- //
- //
- // Input bit rate of the UART line.
- parameter BIT_RATE = 9600; // bits / sec
- localparam BIT_P = 1_000_000_000 * 1/BIT_RATE; // nanoseconds
- //
- // Clock frequency in hertz.
- parameter CLK_HZ = 50_000_000;
- localparam CLK_P = 1_000_000_000 * 1/CLK_HZ; // nanoseconds
- //
- // Number of data bits recieved per UART packet.
- parameter PAYLOAD_BITS = 8;
- //
- // Number of stop bits indicating the end of a packet.
- parameter STOP_BITS = 1;
- // ---------------------------------------------------------------------------
- // Internal parameters.
- //
- //
- // Number of clock cycles per uart bit.
- localparam CYCLES_PER_BIT = BIT_P / CLK_P;
- //
- // Size of the registers which store sample counts and bit durations.
- localparam COUNT_REG_LEN = 1+$clog2(CYCLES_PER_BIT);
- // ---------------------------------------------------------------------------
- // Internal registers.
- //
- //
- // Internally latched value of the uart_txd line. Helps break long timing
- // paths from the logic to the output pins.
- reg txd_reg;
- //
- // Storage for the serial data to be sent.
- reg [PAYLOAD_BITS-1:0] data_to_send;
- //
- // Counter for the number of cycles over a packet bit.
- reg [COUNT_REG_LEN-1:0] cycle_counter;
- //
- // Counter for the number of sent bits of the packet.
- reg [3:0] bit_counter;
- //
- // Current and next states of the internal FSM.
- reg [2:0] fsm_state;
- reg [2:0] n_fsm_state;
- localparam FSM_IDLE = 0;
- localparam FSM_START= 1;
- localparam FSM_SEND = 2;
- localparam FSM_STOP = 3;
- // ---------------------------------------------------------------------------
- // FSM next state selection.
- //
- assign uart_tx_busy = fsm_state != FSM_IDLE;
- assign uart_txd = txd_reg;
- wire next_bit = cycle_counter == CYCLES_PER_BIT;
- wire payload_done = bit_counter == PAYLOAD_BITS ;
- wire stop_done = bit_counter == STOP_BITS && fsm_state == FSM_STOP;
- //
- // Handle picking the next state.
- always @(*) begin : p_n_fsm_state
- case(fsm_state)
- FSM_IDLE : n_fsm_state = uart_tx_en ? FSM_START: FSM_IDLE ;
- FSM_START: n_fsm_state = next_bit ? FSM_SEND : FSM_START;
- FSM_SEND : n_fsm_state = payload_done ? FSM_STOP : FSM_SEND ;
- FSM_STOP : n_fsm_state = stop_done ? FSM_IDLE : FSM_STOP ;
- default : n_fsm_state = FSM_IDLE;
- endcase
- end
- // ---------------------------------------------------------------------------
- // Internal register setting and re-setting.
- //
- //
- // Handle updates to the sent data register.
- integer i = 0;
- always @(posedge clk) begin : p_data_to_send
- if(!resetn) begin
- data_to_send <= {PAYLOAD_BITS{1'b0}};
- end else if(fsm_state == FSM_IDLE && uart_tx_en) begin
- data_to_send <= uart_tx_data;
- end else if(fsm_state == FSM_SEND && next_bit ) begin
- for ( i = PAYLOAD_BITS-2; i >= 0; i = i - 1) begin
- data_to_send[i] <= data_to_send[i+1];
- end
- end
- end
- //
- // Increments the bit counter each time a new bit frame is sent.
- always @(posedge clk) begin : p_bit_counter
- if(!resetn) begin
- bit_counter <= 4'b0;
- end else if(fsm_state != FSM_SEND && fsm_state != FSM_STOP) begin
- bit_counter <= {COUNT_REG_LEN{1'b0}};
- end else if(fsm_state == FSM_SEND && n_fsm_state == FSM_STOP) begin
- bit_counter <= {COUNT_REG_LEN{1'b0}};
- end else if(fsm_state == FSM_STOP&& next_bit) begin
- bit_counter <= bit_counter + 1'b1;
- end else if(fsm_state == FSM_SEND && next_bit) begin
- bit_counter <= bit_counter + 1'b1;
- end
- end
- //
- // Increments the cycle counter when sending.
- always @(posedge clk) begin : p_cycle_counter
- if(!resetn) begin
- cycle_counter <= {COUNT_REG_LEN{1'b0}};
- end else if(next_bit) begin
- cycle_counter <= {COUNT_REG_LEN{1'b0}};
- end else if(fsm_state == FSM_START ||
- fsm_state == FSM_SEND ||
- fsm_state == FSM_STOP ) begin
- cycle_counter <= cycle_counter + 1'b1;
- end
- end
- //
- // Progresses the next FSM state.
- always @(posedge clk) begin : p_fsm_state
- if(!resetn) begin
- fsm_state <= FSM_IDLE;
- end else begin
- fsm_state <= n_fsm_state;
- end
- end
- //
- // Responsible for updating the internal value of the txd_reg.
- always @(posedge clk) begin : p_txd_reg
- if(!resetn) begin
- txd_reg <= 1'b1;
- end else if(fsm_state == FSM_IDLE) begin
- txd_reg <= 1'b1;
- end else if(fsm_state == FSM_START) begin
- txd_reg <= 1'b0;
- end else if(fsm_state == FSM_SEND) begin
- txd_reg <= data_to_send[0];
- end else if(fsm_state == FSM_STOP) begin
- txd_reg <= 1'b1;
- end
- end
- endmodule
- //
- // Module: tb
- //
- `timescale 1ns/1ns
- `define WAVES_FILE "./work/waves-tx.vcd"
- module tb;
- reg clk ; // Top level system clock input.
- reg resetn ;
- wire uart_txd ; // UART transmit pin.
- wire uart_tx_busy; // Module busy sending previous item.
- reg uart_tx_en ;
- reg [7:0] uart_tx_data; // The recieved data.
- //
- // Bit rate of the UART line we are testing.
- localparam BIT_RATE = 9600;
- localparam BIT_P = (1000000000/BIT_RATE);
- //
- // Period and frequency of the system clock.
- localparam CLK_HZ = 50000000;
- localparam CLK_P = 1000000000/ CLK_HZ;
- localparam CLK_P_2 = 500000000/ CLK_HZ;
- //
- // Make the clock tick.
- always #CLK_P_2 clk=~clk;
- //
- // Sends a single byte down the UART line.
- task send_byte;
- input [7:0] to_send;
- begin
- $display("Send data %b at time %d", to_send,$time);
- uart_tx_data= to_send;
- uart_tx_en = 1'b1;
- end
- endtask
- //
- // Run the test sequence.
- reg [7:0] to_send;
- initial begin
- resetn = 1'b0;
- clk = 1'b0;
- #40 resetn = 1'b1;
- $dumpfile(`WAVES_FILE);
- $dumpvars(0,tb);
- repeat(20) begin
- to_send = $random;
- send_byte(to_send);
- #1000;
- wait(!uart_tx_busy);
- end
- $display("BIT RATE : %db/s", BIT_RATE );
- $display("BIT PERIOD: %dns" , BIT_P );
- $display("CLK PERIOD: %dns" , CLK_P );
- $display("CYCLES/BIT: %d" , i_uart_tx.CYCLES_PER_BIT);
- $display("Finish simulation at time %d", $time);
- $finish();
- end
- //
- // Instance of the DUT
- uart_tx #(
- .BIT_RATE(BIT_RATE),
- .CLK_HZ (CLK_HZ )
- ) i_uart_tx(
- .clk (clk ),
- .resetn (resetn ),
- .uart_txd (uart_txd ),
- .uart_tx_en (uart_tx_en ),
- .uart_tx_busy (uart_tx_busy ),
- .uart_tx_data (uart_tx_data )
- );
- endmodule
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement