Add new memory-mapped UART

This implementation uses a simpler state machine than the previous
version.

The receiver still needs to be implemented.
This commit is contained in:
Graham Edgecombe 2017-12-06 22:29:18 +00:00
parent bb5f2c8d8c
commit 6d638404f1
2 changed files with 72 additions and 122 deletions

22
top.sv
View file

@ -77,16 +77,18 @@ module top (
/* memory bus data */ /* memory bus data */
logic [31:0] mem_address; logic [31:0] mem_address;
logic [31:0] mem_read_value = ram_read_value | leds_read_value; logic [31:0] mem_read_value = ram_read_value | leds_read_value | uart_read_value;
logic [31:0] mem_write_value; logic [31:0] mem_write_value;
always_comb begin always_comb begin
ram_sel = 0; ram_sel = 0;
leds_sel = 0; leds_sel = 0;
uart_sel = 0;
casez (mem_address) casez (mem_address)
32'b00000000_00000000_????????_????????: ram_sel = 1; 32'b00000000_00000000_????????_????????: ram_sel = 1;
32'b00000000_00000001_00000000_000000??: leds_sel = 1; 32'b00000000_00000001_00000000_000000??: leds_sel = 1;
32'b00000000_00000010_00000000_0000????: uart_sel = 1;
endcase endcase
end end
@ -116,9 +118,8 @@ module top (
leds <= mem_write_value[7:0]; leds <= mem_write_value[7:0];
end end
logic uart_rx_received; logic uart_sel;
logic uart_tx_ready; logic [31:0] uart_read_value;
logic [7:0] uart_rx_byte;
uart uart ( uart uart (
.clk(pll_clk), .clk(pll_clk),
@ -129,16 +130,15 @@ module top (
.tx_out(uart_tx), .tx_out(uart_tx),
/* control in */ /* control in */
.tx_transmit_in(uart_rx_received), .sel_in(uart_sel),
.read_in(mem_read),
.write_mask_in(mem_write_mask),
/* data in */ /* data in */
.tx_byte_in(uart_rx_byte), .address_in(mem_address),
.write_value_in(mem_write_value),
/* control out */
.rx_received_out(uart_rx_received),
.tx_ready_out(uart_tx_ready),
/* data out */ /* data out */
.rx_byte_out(uart_rx_byte) .read_value_out(uart_read_value)
); );
endmodule endmodule

172
uart.sv
View file

@ -1,15 +1,11 @@
`ifndef UART `ifndef UART
`define UART `define UART
localparam UART_STATE_IDLE = 2'b00; localparam UART_REG_CLK_DIV = 2'b00;
localparam UART_STATE_START = 2'b01; localparam UART_REG_STATUS = 2'b01;
localparam UART_STATE_DATA = 2'b10; localparam UART_REG_DATA = 2'b10;
localparam UART_STATE_STOP = 2'b11;
module uart #( module uart (
parameter CLK_FREQ = 36000000,
parameter BAUD_RATE = 9600
) (
input clk, input clk,
input reset, input reset,
@ -18,128 +14,82 @@ module uart #(
output tx_out, output tx_out,
/* control in */ /* control in */
input tx_transmit_in, input sel_in,
input read_in,
input [3:0] write_mask_in,
/* data in */ /* data in */
input [7:0] tx_byte_in, input [31:0] address_in,
input [31:0] write_value_in,
/* control out */
output rx_received_out,
output tx_ready_out,
/* data out */ /* data out */
output [7:0] rx_byte_out output [31:0] read_value_out
); );
localparam CLK_DIV = CLK_FREQ / BAUD_RATE; logic [15:0] clk_div;
logic [11:0] rx_clk_div; logic [15:0] tx_clks;
logic [11:0] tx_clk_div; logic [3:0] tx_bits;
logic [9:0] tx_buf;
logic [1:0] rx_state; initial
logic [1:0] tx_state; tx_buf[0] = 1;
logic [2:0] rx_bit; assign tx_out = tx_buf[0];
logic [2:0] tx_bit;
logic [7:0] rx_pending_byte; always_comb begin
logic [7:0] tx_byte; if (sel_in) begin
case (address_in[3:2])
initial begin UART_REG_CLK_DIV: begin
rx_state <= UART_STATE_IDLE; read_value_out = {16'b0, clk_div};
tx_state <= UART_STATE_IDLE; end
rx_received_out <= 0; UART_REG_STATUS: begin
tx_out <= 1; read_value_out = {31'b0, |tx_bits};
tx_ready_out <= 1; end
UART_REG_DATA: begin
read_value_out = 0;
end
default: begin
read_value_out = 32'bx;
end
endcase
end else begin
read_value_out = 0;
end
end end
always @(posedge clk) begin always_ff @(posedge clk) begin
rx_received_out <= 0; if (sel_in) begin
case (address_in[3:2])
UART_REG_CLK_DIV: begin
if (write_mask_in[1])
clk_div[15:8] <= write_value_in[15:8];
if (rx_clk_div) if (write_mask_in[0])
rx_clk_div <= rx_clk_div - 1; clk_div[7:0] <= write_value_in[7:0];
if (tx_clk_div)
tx_clk_div <= tx_clk_div - 1;
case (rx_state)
UART_STATE_IDLE: begin
if (!rx_in) begin
rx_state <= UART_STATE_START;
rx_clk_div <= CLK_DIV / 2;
end end
end UART_REG_DATA: begin
UART_STATE_START: begin if (write_mask_in[0] && !tx_bits) begin
if (!rx_clk_div) begin tx_clks <= clk_div;
if (!rx_in) begin tx_bits <= 10;
rx_state <= UART_STATE_DATA; tx_buf <= {1'b1, write_value_in[7:0], 1'b0};
rx_clk_div <= CLK_DIV;
rx_bit <= 7;
end else begin
rx_state <= UART_STATE_IDLE;
end end
end end
end endcase
UART_STATE_DATA: begin end
if (!rx_clk_div) begin
rx_state <= rx_bit ? UART_STATE_DATA : UART_STATE_STOP;
rx_clk_div <= CLK_DIV;
rx_bit <= rx_bit - 1;
rx_pending_byte <= {rx_in, rx_pending_byte[7:1]};
end
end
UART_STATE_STOP: begin
if (!rx_clk_div) begin
rx_state <= UART_STATE_IDLE;
if (rx_in) begin
rx_received_out <= 1;
rx_byte_out <= rx_pending_byte;
end
end
end
endcase
case (tx_state) if (tx_bits) begin
UART_STATE_IDLE: begin if (tx_clks) begin
if (tx_transmit_in) begin tx_clks <= tx_clks - 1;
tx_state <= UART_STATE_START; end else begin
tx_clk_div <= CLK_DIV; tx_clks <= clk_div;
tx_byte <= tx_byte_in; tx_bits <= tx_bits - 1;
tx_out <= 0; tx_buf <= {1'b1, tx_buf[9:1]};
tx_ready_out <= 0;
end
end end
UART_STATE_START: begin end
if (!tx_clk_div) begin
tx_state <= UART_STATE_DATA;
tx_clk_div <= CLK_DIV;
tx_bit <= 7;
tx_byte <= {1'b0, tx_byte[7:1]};
tx_out <= tx_byte[0];
end
end
UART_STATE_DATA: begin
if (!tx_clk_div) begin
tx_state <= tx_bit ? UART_STATE_DATA : UART_STATE_STOP;
tx_clk_div <= CLK_DIV;
tx_bit <= tx_bit - 1;
tx_byte <= {1'b0, tx_byte[7:1]};
tx_out <= tx_bit ? tx_byte[0] : 1;
end
end
UART_STATE_STOP: begin
if (!tx_clk_div) begin
tx_state <= UART_STATE_IDLE;
tx_ready_out <= 1;
end
end
endcase
if (reset) begin if (reset) begin
rx_state <= UART_STATE_IDLE; tx_bits <= 0;
tx_state <= UART_STATE_IDLE; tx_buf[0] <= 1;
rx_received_out <= 0;
tx_out <= 1;
tx_ready_out <= 1;
end end
end end
endmodule endmodule