Commit 8e2289a7 authored by Koen Martens's avatar Koen Martens
Browse files

uart: first implementation, output only

parent 01a06812
testsrc = $(wildcard *_tb.v)
SUBDIRS := gpio
SUBDIRS := gpio uart
include ../common.mk
module uart #(
parameter DATA_WIDTH = 8,
parameter ADDR_WIDTH = 3,
parameter CLK_RATE = 16000000
)(
input wire sin,
output reg sout,
// wishbone
input wire clk_i,
input wire rst_i,
input wire cyc_i,
input wire stb_i,
input wire we_i,
output reg ack_o,
input wire [ADDR_WIDTH-1:0] adr_i,
input wire [DATA_WIDTH-1:0] dat_i,
output reg [DATA_WIDTH-1:0] dat_o
);
// internal registers
reg [7:0] RBR; /* Receiver Buffer Register (RO) */
reg [7:0] THR; /* Transmitter Holding Register (WO) */
reg [7:0] DLL; /* Divisor Latch (LS) */
reg [7:0] DLM; /* Divisor Latch (MS) */
reg [7:0] LCR; /* Line Control Register */
wire LCR_DLAB = LCR[7]; /* Divisor Latch Access Bit */
reg [7:0] LSR; /* Line Status Register */
localparam LSR_BIT_THRE = 5;
localparam LSR_BIT_TEMT = 6;
//
reg [15:0] clk_counter;
reg [7:0] TSR; /* Transmitter Shift Register */
reg [3:0] transmit_bit_count;
always @(posedge clk_i) begin
if(rst_i || (clk_counter == 0)) begin
clk_counter <= { DLM, DLL };
end else begin
clk_counter <= clk_counter - 1;
end
end
always @(posedge clk_i) begin
if(rst_i) begin
RBR <= 'b0; THR <= 'b0; DLL <= 'b0; DLM <= 'b0; TSR <= 1'b0;
THR <= 'b0; LCR <= 'b0; LSR <= 'b0 | (1<<LSR_BIT_THRE) | (1<<LSR_BIT_TEMT);
sout <= 1'b0; transmit_bit_count <= 1'b0;
transmit_state <= TRANSMIT_IDLE;
dat_o = {DATA_WIDTH{1'b0}}; ack_o = 1'b0;
end else begin
if (cyc_i & stb_i & ~ack_o) begin
if (we_i) begin
case(adr_i)
3'b000: // RBR / DLL
if(LCR_DLAB) begin
DLL = dat_i;
end
3'b001: // THR / DLM
if(LCR_DLAB) begin
DLM = dat_i;
end else begin
THR = dat_i;
LSR[LSR_BIT_THRE] = 1'b0;
end
3'b011: // LCR
LCR = dat_i;
endcase
end
case(adr_i)
3'b000: dat_o = LCR_DLAB?DLL:RBR;
3'b011: dat_o = LCR;
default: dat_o = 'b0;
endcase
ack_o = 1'b1;
end else begin
ack_o = 1'b0;
end
end
end
// transmit
reg [2:0] transmit_state;
localparam TRANSMIT_IDLE = 3'b001;
localparam TRANSMIT_SHIFT = 3'b010;
localparam TRANSMIT_STOP = 3'b100;
always @(posedge clk_i) begin
if(clk_counter == 0) begin
case(transmit_state)
TRANSMIT_IDLE :
begin
sout = 1'b0;
if(LSR[LSR_BIT_THRE] == 1'b0) begin
sout = 1'b1; // start bit
TSR = THR;
LSR[LSR_BIT_THRE] = 1'b1;
transmit_bit_count = 7;
transmit_state = TRANSMIT_SHIFT;
end else begin
transmit_state = TRANSMIT_IDLE;
end
end
TRANSMIT_SHIFT :
begin
if(transmit_bit_count==0) begin
transmit_state = TRANSMIT_STOP;
end
sout = ~TSR[0];
TSR = TSR >> 1;
transmit_state = (transmit_bit_count==0)?TRANSMIT_STOP:TRANSMIT_SHIFT;
transmit_bit_count = transmit_bit_count - 1;
end
TRANSMIT_STOP :
begin
sout = 1'b0;
transmit_state = TRANSMIT_IDLE;
end
default :
begin
sout = 1'b0;
transmit_state = TRANSMIT_IDLE;
end
endcase
end
end
endmodule
`include "common/testbench.v"
`include "uart.v"
module uart_tb();
reg clk = 0;
always #0.5 clk = ~clk;
parameter TB_DATA_WIDTH = 8;
parameter TB_ADDR_WIDTH = 3;
reg tb_rst_i;
reg tb_cyc_i;
reg tb_stb_i;
reg tb_we_i;
wire tb_ack_o;
reg [TB_ADDR_WIDTH-1:0] tb_adr_i;
reg [TB_DATA_WIDTH-1:0] tb_dat_i;
wire [TB_DATA_WIDTH-1:0] tb_dat_o;
reg tb_sin;
wire tb_sout;
wire tb_delayed_rst_i;
wire tb_delayed_cyc_i;
wire tb_delayed_stb_i;
wire tb_delayed_we_i;
wire tb_delay_ack_o;
wire [TB_ADDR_WIDTH-1:0] tb_delayed_adr_i;
wire [TB_DATA_WIDTH-1:0] tb_delayed_dat_i;
wire [TB_DATA_WIDTH-1:0] tb_delay_dat_o;
wire tb_delayed_sin;
wire tb_delay_sout;
assign #`TB_IN_DELAY tb_delayed_rst_i = tb_rst_i;
assign #`TB_IN_DELAY tb_delayed_cyc_i = tb_cyc_i;
assign #`TB_IN_DELAY tb_delayed_stb_i = tb_stb_i;
assign #`TB_IN_DELAY tb_delayed_we_i = tb_we_i;
assign #`TB_IN_DELAY tb_delayed_adr_i = tb_adr_i;
assign #`TB_IN_DELAY tb_delayed_dat_i = tb_dat_i;
assign #`TB_IN_DELAY tb_delayed_sin = tb_sin;
assign #`TB_IN_DELAY tb_ack_o = tb_delay_ack_o;
assign #`TB_IN_DELAY tb_dat_o = tb_delay_dat_o;
assign #`TB_IN_DELAY tb_sout = tb_delay_sout;
uart #(
.DATA_WIDTH(TB_DATA_WIDTH)
) UUT (
.sout(tb_delay_sout),
.sin(tb_delayed_sin),
.clk_i(clk),
.rst_i(tb_delayed_rst_i),
.cyc_i(tb_delayed_cyc_i),
.stb_i(tb_delayed_stb_i),
.we_i(tb_delayed_we_i),
.ack_o(tb_delay_ack_o),
.adr_i(tb_delayed_adr_i),
.dat_i(tb_delayed_dat_i),
.dat_o(tb_delay_dat_o)
);
`include "common/testbench_tasks/tb_assert.v"
`include "common/testbench_tasks/wb_read.v"
`include "common/testbench_tasks/wb_write.v"
initial begin
$dumpfile(`VCD_OUTPUT);
$dumpvars(0, uart_tb);
tb_rst_i = 1'b1; tb_cyc_i = 1'b0; tb_stb_i = 1'b0; tb_we_i = 1'b0; tb_adr_i = 'b0; tb_dat_i = 'b0;
tb_sin = 'b0;
@(posedge clk);
@(negedge clk);
tb_assert(tb_ack_o === 1'b0, "reset ack_o");
@(posedge clk);
tb_rst_i = 1'b0;
tb_dat_i = 'bx;
@(negedge clk);
tb_assert(tb_ack_o === 1'b0, "post-reset ack_o");
// set DLAB (bit7) in LCR (reg 3)
wb_write(3, 8'h80);
// set DL = 0x0683 (=1667, 16MHz/1667 -> 9598 baud ~ 9600)
wb_write(0, 8'h03);
wb_write(1, 8'h00);
@(posedge clk);
@(posedge clk);
// reset DLAB (bit7) in LCR (reg 3)
wb_write(3, 8'h00);
// write 'K' to THR (reg 1)
wb_write(1, 8'h4b);
repeat(200) begin
wb_read(5, 8'h00);
end
// read input register
//wb_read(0, 8'h00);
//tb_assert(tb_uart_out === 8'h00, "uart out is 0");
$display("passed");
$finish;
end
endmodule
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment