Commit 03b4f683 authored by Koen Martens's avatar Koen Martens
Browse files

uart: refactor top-level control to use transmit module

parent 41d41a01
`include "transmit.v"
module uart #(
parameter DATA_WIDTH = 8,
parameter ADDR_WIDTH = 3,
parameter CLK_RATE = 16000000
parameter ADDR_WIDTH = 3
)(
input wire sin,
output reg sout,
......@@ -27,69 +28,70 @@ module uart #(
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] clk16_counter;
reg reset_clk16_counter;
reg [3:0] clk_counter;
reg [7:0] TSR; /* Transmitter Shift Register */
reg [3:0] transmit_bit_count;
always @(posedge clk_i) begin
if(rst_i || reset_clk16_counter || (clk16_counter == 0)) begin
clk16_counter <= ({ DLM, DLL })-1;
reset_clk16_counter <= 1'b0;
clk_counter <= clk_counter + 1;
end else begin
clk16_counter <= clk16_counter - 1;
end
end
wire transmit_clk = (clk_counter == 0)?1'b1:1'b0;
/* Line Status Register */
wire [7:0] LSR = {LSR_FIFO_ERROR, LSR_TEMT, LSR_THRE, LSR_BI, LSR_FE, LSR_PE, LSR_OE, LSR_DR};
reg LSR_DR;
reg LSR_OE;
reg LSR_PE;
reg LSR_FE;
reg LSR_BI;
reg LSR_THRE;
wire LSR_TEMT = LSR_THRE & tsr_empty;
reg LSR_FIFO_ERROR;
// transmitter
wire tsr_latched;
wire tsr_empty;
transmit #(
.DATA_WIDTH(DATA_WIDTH),
.DIVISOR_WIDTH(16)
) transmitter (
.clk_i(clk_i),
.rst_i(rst_i),
.data_i(THR),
.clk_divisor_i({DLM, DLL}),
.sout(sout),
.data_latched_o(tsr_latched),
.empty_o(tsr_empty),
.data_ready_i(~LSR_THRE)
);
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);
clk16_counter = 0; clk_counter = 0; reset_clk16_counter = 1'b1;
sout <= 1'b1; transmit_bit_count <= 1'b0;
transmit_state <= TRANSMIT_IDLE;
RBR <= 'b0; THR <= 'b0; DLL <= 'b0; DLM <= 'b0;
THR <= 'b0; LCR <= 'b0;
LSR_OE = 1'b0; LSR_PE = 1'b0; LSR_FE = 1'b0; LSR_BI = 1'b0;
LSR_THRE = 1'b1; LSR_FIFO_ERROR = 1'b0; LSR_DR = 1'b0;
dat_o = {DATA_WIDTH{1'b0}}; ack_o = 1'b0;
end else begin
if(tsr_latched) LSR_THRE = 1'b1;
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;
reset_clk16_counter = 1'b1;
end
3'b001: // THR / DLM
if(LCR_DLAB) begin
DLM = dat_i;
reset_clk16_counter = 1'b1;
end else begin
THR = dat_i;
LSR[LSR_BIT_THRE] = 1'b0;
LSR_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;
3'b101:
begin
dat_o = LSR;
LSR = LSR & ~(8'b00011110);
LSR_OE = 1'b0; LSR_PE = 1'b0; LSR_FE = 1'b0; LSR_BI = 1'b0;
end
default: dat_o = 'b0;
endcase
......@@ -101,49 +103,4 @@ module uart #(
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 transmit_clk) begin
case(transmit_state)
TRANSMIT_IDLE :
begin
sout = 1'b1;
if(LSR[LSR_BIT_THRE] == 1'b0) begin
sout = 1'b0; // 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'b1;
transmit_state = TRANSMIT_IDLE;
end
default :
begin
sout = 1'b1;
transmit_state = TRANSMIT_IDLE;
end
endcase
end
endmodule
......@@ -68,6 +68,35 @@ module uart_tb();
`include "common/testbench_tasks/wb_check_read.v"
`include "common/testbench_tasks/wb_write.v"
task verify_sout(
input [TB_DATA_WIDTH-1:0] expected,
input string message = "",
input integer start_skip = 0,
input integer stop_skip = 0
);
integer bit_index;
string formatted;
begin
repeat((TB_DIVIDER * 16)-start_skip) begin
@(posedge clk);
tb_assert(tb_sout === 1'b0, {message, ", start bit = space (0)"});
end
bit_index = 0;
repeat(8) begin
repeat(TB_DIVIDER * 16) begin
@(posedge clk);
$sformat(formatted, {message, ", bit %0d (%b)"}, bit_index, expected[bit_index]);
tb_assert(tb_sout === expected[bit_index], formatted);
end
bit_index += 1;
end
repeat((TB_DIVIDER * 16)-stop_skip) begin
@(posedge clk);
tb_assert(tb_sout === 1'b1, {message, ", stop bit = mark (1)"});
end
end
endtask
reg [TB_DATA_WIDTH-1:0] read_data;
integer retry_count;
......@@ -106,38 +135,44 @@ module uart_tb();
wb_write(1, 8'h4b);
wb_read(5, read_data);
tb_assert(!(read_data & (1<<UUT.LSR_BIT_THRE)), "THRE not asserted");
tb_assert(read_data[5]===1'b1, "THRE asserted");
tb_assert(read_data[6]===1'b0, "TEMT deasserted");
// wait for THRE
retry_count = 0;
while(!(read_data & (1<<UUT.LSR_BIT_THRE))) begin
while(!(read_data & (1<<5))) begin
wb_read(5, read_data);
retry_count +=1;
tb_assert(retry_count<100, "wait for THRE timeout");
end
tb_assert(tb_sout == 1'b0, "start bit = space (0)");
repeat(TB_DIVIDER * 16) @(posedge clk);
tb_assert(tb_sout == 1'b1, "bit0 = mark (1)");
repeat(TB_DIVIDER * 16) @(posedge clk);
tb_assert(tb_sout == 1'b1, "bit0 = mark (1)");
repeat(TB_DIVIDER * 16) @(posedge clk);
tb_assert(tb_sout == 1'b0, "bit0 = space (0)");
repeat(TB_DIVIDER * 16) @(posedge clk);
tb_assert(tb_sout == 1'b1, "bit0 = mark (1)");
repeat(TB_DIVIDER * 16) @(posedge clk);
tb_assert(tb_sout == 1'b0, "bit0 = space (0)");
repeat(TB_DIVIDER * 16) @(posedge clk);
tb_assert(tb_sout == 1'b0, "bit0 = space (0)");
repeat(TB_DIVIDER * 16) @(posedge clk);
tb_assert(tb_sout == 1'b1, "bit0 = mark (1)");
repeat(TB_DIVIDER * 16) @(posedge clk);
tb_assert(tb_sout == 1'b0, "bit0 = space (0)");
repeat(TB_DIVIDER * 16) @(posedge clk);
tb_assert(tb_sout == 1'b1, "stop bit = mark (1)");
repeat(TB_DIVIDER * 16) @(posedge clk);
repeat(TB_DIVIDER * 16) @(posedge clk);
repeat(TB_DIVIDER * 16) @(posedge clk);
// write 8'b10110001 to THR (reg 1)
wb_write(1, 8'b10110001);
wb_read(5, read_data);
tb_assert(read_data[5]===1'b0, "THRE de-asserted after write byte 2");
tb_assert(read_data[6]===1'b0, "TEMT deasserted after write byte 2");
verify_sout(8'h4b, "byte 1", 12, 16);
wb_read(5, read_data);
tb_assert(read_data[5]===1'b0, "THRE deasserted at end of byte 1");
tb_assert(read_data[6]===1'b0, "TEMT deasserted at end of byte 1");
repeat(12) @(posedge clk);
wb_read(5, read_data);
tb_assert(read_data[5]===1'b1, "THRE asserted at start of byte 2");
tb_assert(read_data[6]===1'b0, "TEMT deasserted at start of byte 2");
verify_sout(8'b10110001, "byte 2", 4, 0);
repeat(TB_DIVIDER * 16) begin
@(posedge clk);
wb_read(5, read_data);
tb_assert(read_data[5]===1'b1, "THRE asserted after byte 2");
tb_assert(read_data[6]===1'b1, "TEMT asserted after byte 2");
tb_assert(tb_sout===1'b1, "sout 1 when idling");
end
$display("passed");
$finish;
......
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