Files
APS/Labs/13. Peripheral units/peripheral modules/uart_rx.sv

289 lines
8.1 KiB
Systemverilog

// Copyright 2017 ETH Zurich and University of Bologna.
// Copyright and related rights are licensed under the Solderpad Hardware
// License, Version 0.51 (the “License”); you may not use this file except in
// compliance with the License. You may obtain a copy of the License at
// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law
// or agreed to in writing, software, hardware and materials distributed under
// this License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
// This file has been taken from https://github.com/pulp-platform/apb_uart_sv
// and modified by Andrei Solodovnikov in order to be used in
// Architectures of Processor Systems (APS) lab work project
// Changelog:
// some of the input signals has been hardcoded to constant values
// cfg_div_i input has been replaced by baudrate_i input signal.
// The signal cfg_div_i is now controled by baudrate_i input, and this control
// logic is work from assumption that clk_i is 10 MHz.
module uart_rx (
input logic clk_i,
input logic rst_i,
input logic rx_i,
output logic busy_o,
input logic [16:0] baudrate_i,
input logic parity_en_i,
input logic [1:0] stopbit_i,
output logic [7:0] rx_data_o,
output logic rx_valid_o
//, input logic cfg_en_i,
// input logic [1:0] cfg_bits_i,
// output logic err_o,
// input logic err_clr_i,
// input logic rx_ready_i
);
logic rx_ready_i;
logic cfg_en_i;
logic [1:0] cfg_bits_i;
logic rstn_i;
logic [15:0] cfg_div_i;
always_comb begin
case(baudrate_i)
17'd9600 : cfg_div_i = 15'd1041;
17'd19200 : cfg_div_i = 15'd520;
17'd38400 : cfg_div_i = 15'd259;
17'd57600 : cfg_div_i = 15'd173;
17'd115200: cfg_div_i = 15'd86;
default : cfg_div_i = 15'd1041;
endcase
end
assign rstn_i = !rst_i;
assign rx_ready_i = 1'b1;
assign cfg_en_i = 1'b1;
assign cfg_bits_i = 2'd3;
enum logic [2:0] {IDLE,START_BIT,DATA,SAVE_DATA,PARITY,STOP_BIT} CS, NS;
logic [7:0] reg_data;
logic [7:0] reg_data_next;
logic [2:0] reg_rx_sync;
logic [2:0] reg_bit_count;
logic [2:0] reg_bit_count_next;
logic [2:0] s_target_bits;
logic parity_bit;
logic parity_bit_next;
logic sampleData;
logic [15:0] baud_cnt;
logic baudgen_en;
logic bit_done;
logic start_bit;
logic set_error;
logic s_rx_fall;
assign busy_o = (CS != IDLE);
always_comb
begin
case(cfg_bits_i)
2'b00:
s_target_bits = 3'h4;
2'b01:
s_target_bits = 3'h5;
2'b10:
s_target_bits = 3'h6;
2'b11:
s_target_bits = 3'h7;
endcase
end
always_comb
begin
NS = CS;
sampleData = 1'b0;
reg_bit_count_next = reg_bit_count;
reg_data_next = reg_data;
rx_valid_o = 1'b0;
baudgen_en = 1'b0;
start_bit = 1'b0;
parity_bit_next = parity_bit;
set_error = 1'b0;
case(CS)
IDLE:
begin
if (s_rx_fall)
begin
NS = START_BIT;
baudgen_en = 1'b1;
start_bit = 1'b1;
end
end
START_BIT:
begin
parity_bit_next = 1'b0;
baudgen_en = 1'b1;
start_bit = 1'b1;
if (bit_done)
NS = DATA;
end
DATA:
begin
baudgen_en = 1'b1;
parity_bit_next = parity_bit ^ reg_rx_sync[2];
case(cfg_bits_i)
2'b00:
reg_data_next = {3'b000,reg_rx_sync[2],reg_data[4:1]};
2'b01:
reg_data_next = {2'b00,reg_rx_sync[2],reg_data[5:1]};
2'b10:
reg_data_next = {1'b0,reg_rx_sync[2],reg_data[6:1]};
2'b11:
reg_data_next = {reg_rx_sync[2],reg_data[7:1]};
endcase
if (bit_done)
begin
sampleData = 1'b1;
if (reg_bit_count == s_target_bits)
begin
reg_bit_count_next = 'h0;
NS = SAVE_DATA;
end
else
begin
reg_bit_count_next = reg_bit_count + 1;
end
end
end
SAVE_DATA:
begin
baudgen_en = 1'b1;
rx_valid_o = 1'b1;
if(rx_ready_i)
if (parity_en_i)
NS = PARITY;
else
NS = STOP_BIT;
end
PARITY:
begin
baudgen_en = 1'b1;
if (bit_done)
begin
if(parity_bit != reg_rx_sync[2])
set_error = 1'b1;
NS = STOP_BIT;
end
end
STOP_BIT:
begin
baudgen_en = 1'b1;
if (bit_done)
begin
NS = IDLE;
end
end
default:
NS = IDLE;
endcase
end
always_ff @(posedge clk_i or negedge rstn_i)
begin
if (rstn_i == 1'b0)
begin
CS <= IDLE;
reg_data <= 8'hFF;
reg_bit_count <= 'h0;
parity_bit <= 1'b0;
end
else
begin
if(bit_done)
parity_bit <= parity_bit_next;
if(sampleData)
reg_data <= reg_data_next;
reg_bit_count <= reg_bit_count_next;
if(cfg_en_i)
CS <= NS;
else
CS <= IDLE;
end
end
assign s_rx_fall = ~reg_rx_sync[1] & reg_rx_sync[2];
always_ff @(posedge clk_i or negedge rstn_i)
begin
if (rstn_i == 1'b0)
reg_rx_sync <= 3'b111;
else
begin
if (cfg_en_i)
reg_rx_sync <= {reg_rx_sync[1:0],rx_i};
else
reg_rx_sync <= 3'b111;
end
end
always_ff @(posedge clk_i or negedge rstn_i)
begin
if (rstn_i == 1'b0)
begin
baud_cnt <= 'h0;
bit_done <= 1'b0;
end
else
begin
if(baudgen_en)
begin
if(!start_bit && (baud_cnt == cfg_div_i))
begin
baud_cnt <= 'h0;
bit_done <= 1'b1;
end
else if(start_bit && (baud_cnt == {1'b0,cfg_div_i[15:1]}))
begin
baud_cnt <= 'h0;
bit_done <= 1'b1;
end
else
begin
baud_cnt <= baud_cnt + 1;
bit_done <= 1'b0;
end
end
else
begin
baud_cnt <= 'h0;
bit_done <= 1'b0;
end
end
end
// always_ff @(posedge clk_i or negedge rstn_i)
// begin
// if (rstn_i == 1'b0)
// begin
// err_o <= 1'b0;
// end
// else
// begin
// if(err_clr_i)
// begin
// err_o <= 1'b0;
// end
// else
// begin
// if(set_error)
// err_o <= 1'b1;
// end
// end
// end
assign rx_data_o = reg_data;
endmodule