Commit 4577c1f2 authored by Koen Martens's avatar Koen Martens
Browse files

uart: implement receive module

parent 572dcf32
module receive #(
parameter DATA_WIDTH = 8,
parameter DIVISOR_WIDTH = 16
)(
input wire clk_i,
input wire rst_i,
output reg [DATA_WIDTH-1:0] data_o,
input wire [DIVISOR_WIDTH-1:0] clk_divisor_i,
input wire sin,
input wire data_latched_i,
output reg data_ready_o,
output reg framing_error_o
);
reg [DIVISOR_WIDTH-1:0] clk_counter;
wire [DIVISOR_WIDTH-1:0] clk_counter_next = clk_counter + 1;
reg [3:0] sample_counter;
wire [3:0] sample_counter_next = sample_counter + 1;
reg [3:0] state;
localparam STATE_IDLE = 'b0001;
localparam STATE_START = 'b0010;
localparam STATE_DATA = 'b0100;
localparam STATE_STOP = 'b1000;
reg [DATA_WIDTH-1:0] data_bit;
always @(posedge clk_i) begin
if(rst_i) begin
clk_counter <= 0;
sample_counter <= 0;
state <= STATE_IDLE;
data_ready_o <= 1'b0;
framing_error_o <= 1'b0;
data_o <= 0;
data_bit <= 0;
end else begin
if(data_ready_o && data_latched_i) data_ready_o <= 1'b0;
if(framing_error_o) framing_error_o <= 1'b0;
case(state)
STATE_IDLE:
begin
data_bit <= 0;
if(~sin) begin
clk_counter <= 1;
sample_counter <= 1;
state <= STATE_START;
end
end
STATE_START:
begin
data_o <= 0;
data_bit <= 1;
if(clk_counter == clk_divisor_i) begin
clk_counter <= 1;
sample_counter <= sample_counter_next;
if(sample_counter==7) begin
state <= sin?STATE_IDLE:STATE_DATA;
end
end else begin
clk_counter <= clk_counter_next;
end
end
STATE_DATA:
begin
if(clk_counter == clk_divisor_i) begin
clk_counter <= 1;
sample_counter <= sample_counter_next;
if(sample_counter==7) begin
data_o <= { sin, data_o[DATA_WIDTH-1:1] };
data_bit <= data_bit << 1;
state <= (data_bit == 1<<(DATA_WIDTH-1))?STATE_STOP:STATE_DATA;
end
end else begin
clk_counter <= clk_counter_next;
end
end
STATE_STOP:
begin
if(clk_counter == clk_divisor_i) begin
clk_counter <= 1;
sample_counter <= sample_counter_next;
if(sample_counter==7) begin
if(sin) begin
data_ready_o <= 1'b1;
state <= STATE_IDLE;
end else begin
framing_error_o = 1'b1;
state <= STATE_DATA;
end
end
end else begin
clk_counter <= clk_counter_next;
end
end
default:
begin
state = STATE_IDLE;
end
endcase
end
end
endmodule
`include "common/testbench.v"
`include "receive.v"
module receive_tb();
reg clk = 0;
always #0.5 clk = ~clk;
parameter TB_DATA_WIDTH = 8;
parameter TB_ADDR_WIDTH = 3;
parameter TB_DIVIDER = 8;
reg tb_rst_i;
reg [15:0] tb_clk_divisor_i;
reg tb_sin;
wire tb_data_ready_o;
wire tb_framing_error_o;
wire [TB_DATA_WIDTH-1:0] tb_data_o;
reg tb_data_latched_i;
wire tb_delayed_rst_i;
wire [15:0] tb_delayed_clk_divisor_i;
wire tb_delayed_sin;
wire tb_delay_data_ready_o;
wire tb_delay_framing_error_o;
wire [TB_DATA_WIDTH-1:0] tb_delay_data_o;
wire tb_delayed_data_latched_i;
assign #`TB_IN_DELAY tb_delayed_rst_i = tb_rst_i;
assign #`TB_IN_DELAY tb_delayed_clk_divisor_i = tb_clk_divisor_i;
assign #`TB_IN_DELAY tb_delayed_sin = tb_sin;
assign #`TB_IN_DELAY tb_delayed_data_latched_i = tb_data_latched_i;
assign #`TB_IN_DELAY tb_data_ready_o = tb_delay_data_ready_o;
assign #`TB_IN_DELAY tb_framing_error_o = tb_delay_framing_error_o;
assign #`TB_IN_DELAY tb_data_o = tb_delay_data_o;
receive #(
.DATA_WIDTH(TB_DATA_WIDTH)
) UUT (
.clk_i(clk),
.rst_i(tb_delayed_rst_i),
.clk_divisor_i(tb_delayed_clk_divisor_i),
.sin(tb_delayed_sin),
.data_ready_o(tb_delay_data_ready_o),
.framing_error_o(tb_delay_framing_error_o),
.data_o(tb_delay_data_o),
.data_latched_i(tb_delayed_data_latched_i)
);
`include "common/testbench_tasks/tb_assert.v"
task simulate_sin(
input [TB_DATA_WIDTH-1:0] data,
input string message = "",
input stop_bit = 1'b1
);
integer bit_index;
string formatted;
begin
tb_sin = 1'b0; // start bit
repeat(32) @(posedge clk);
bit_index = 0;
repeat(TB_DATA_WIDTH) begin
tb_sin = data[bit_index];
repeat(32) @(posedge clk);
bit_index += 1;
end
tb_sin = stop_bit; // stop bit
repeat(32) @(posedge clk);
end
endtask
integer counter;
initial begin
$dumpfile(`VCD_OUTPUT);
$dumpvars(0, receive_tb);
// reset
tb_rst_i = 1'b1; tb_clk_divisor_i = 2; tb_data_latched_i = 1'b0; tb_sin = 1'b1;
@(posedge clk);
tb_rst_i = 1'b0;
@(posedge clk);
tb_assert(tb_data_ready_o===1'b0, "data_ready_o not asserted after reset");
repeat(32) @(posedge clk);
simulate_sin(8'b01001011, "K");
tb_assert(tb_data_ready_o===1'b1, "data_ready_o asserted after 'K'");
tb_assert(tb_data_o==='h4b, "data_o is 0x4b ('K')");
tb_data_latched_i = 1'b1;
@(posedge clk);
@(negedge clk);
tb_data_latched_i = 1'b0;
tb_assert(tb_data_ready_o===1'b0, "data_ready_o asserted after latching 'K'");
repeat(32) @(posedge clk);
simulate_sin(8'b01001011, "framing error", 1'b0);
$display("passed");
$finish;
end
endmodule
`include "common/testbench.v"
`include "transmit.v"
module transmit_tb();
reg clk = 0;
always #0.5 clk = ~clk;
......
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