blob: 1507c774f96bf7c47f35f845967f3ad7231a56e0 [file] [log] [blame]
/*********************************************************
This block try to auto detect the baud-16x value for incoming 9600 data.
Here the System clock is unknown, As in caravel user_clock can be any thing between
4Mhz to 60Mhz.
local counter width: 20bit should be good enough the check idle OR struck at low state
Assumption is 9600 Baud => 0.104ms per bit => 1.04ms per chapter (Assumpting 10 bit per character including start+ parity bit)
With System clock 100Mhz, 1.04ms => 1,000,000 clock count, which can be easily check by 20 bit count => 2^20 = 1048576
1.Input data in double sync with local clock
2. State: IDLE: Wait for High to low edge,
On Fall edge, STATE = POS_EDGE
2. State: POS_EDGE1:
Wait for Pos edge, If Pos edge detected, STATE= NEG_EDGE1
increase local clk_cnt, if counter each 0xF_FFFF without transition, move to State: IDLE
3. State: NEG_EDGE1:
Wait for Neg edge, If Neg edge detected, capture the clk_cnt => ref1_cnt; State: POS_EDGE2
if counter reached 0xF_FFFF without transition, then STATE: IDLE
4. State: POS_EDGE2:
Wait for Pos edge, If Pos edge detected, STATE= NEG_EDGE2
increase local clk_cnt, if counter each 0xF_FFFF without transition, move to State: IDLE
5. State: NEG_EDGE2:
Wait for Neg edge, If Neg edge detected, capture the clk_cnt => ref2_cnt; State: COMPUTE
if counter reached 0xF_FFFF without transition, then STATE: IDLE
6. State: COMPUTE
if difference between ref1_cnt and ref2_cnt less than 16, then average out and divide by 16, Sub by 2,
and update Baud Value and STOP_EDGE
7. STATE:STOP_EDGE Wait pos edge, if timeout then go to IDLE, else enable tx/rx
***************************************************************************/
module uart_auto_det (
input logic mclk ,
input logic reset_n ,
input logic cfg_auto_det ,
input logic rxd ,
output logic [11:0] auto_baud_16x ,
output logic auto_tx_enb ,
output logic auto_rx_enb
);
parameter IDLE = 3'b000;
parameter POS_EDGE1 = 3'b001;
parameter NEG_EDGE1 = 3'b010;
parameter POS_EDGE2 = 3'b011;
parameter NEG_EDGE2 = 3'b100;
parameter COMPUTE = 3'b101;
parameter STOP_EDGE = 3'b110;
parameter AUTO_DONE = 3'b111;
logic [19:0] clk_cnt,ref1_cnt,ref2_cnt,ref_diff,baud_16x;
logic [2:0] state;
logic [2:0] rxd_sync;
logic timeout;
logic rxd_pedge;
logic rxd_nedge;
assign timeout = (clk_cnt == 20'hF_FFFF);
assign rxd_pedge = (rxd_sync[2] == 1'b0) & (rxd_sync[1] == 1'b1);
assign rxd_nedge = (rxd_sync[2] == 1'b1) & (rxd_sync[1] == 1'b0);
assign ref_diff = (ref1_cnt > ref2_cnt) ? (ref1_cnt - ref2_cnt) : (ref2_cnt - ref1_cnt);
assign baud_16x = (((ref1_cnt + ref2_cnt) >> 1) >> 4);
always @(negedge reset_n or posedge mclk)
begin
if(reset_n == 1'b0) begin
state <= IDLE;
clk_cnt <= 'b0;
ref1_cnt <= 'b0;
ref1_cnt <= 'b0;
auto_baud_16x <= 'b0;
auto_tx_enb <= 'b0;
auto_rx_enb <= 'b0;
rxd_sync <= 'b0;
end else begin
rxd_sync <= {rxd_sync[1:0],rxd};
case(state)
IDLE : begin
if(cfg_auto_det && rxd_nedge) begin
clk_cnt <= 'h0;
state <= POS_EDGE1;
end
end
POS_EDGE1 : begin
if(rxd_pedge) begin
clk_cnt <= 'h0;
state <= NEG_EDGE1;
end else if(timeout) begin
state <= IDLE;
end else begin
clk_cnt <= clk_cnt + 1;
end
end
NEG_EDGE1 : begin
if(rxd_nedge) begin
ref1_cnt <= clk_cnt;
clk_cnt <= 'h0;
state <= POS_EDGE2;
end else if(timeout) begin
state <= IDLE;
end else begin
clk_cnt <= clk_cnt + 1;
end
end
POS_EDGE2 : begin
if(rxd_pedge) begin
clk_cnt <= 'h0;
state <= NEG_EDGE2;
end else if(timeout) begin
state <= IDLE;
end else begin
clk_cnt <= clk_cnt + 1;
end
end
NEG_EDGE2 : begin
if(rxd_nedge) begin
ref2_cnt <= clk_cnt;
clk_cnt <= 'h0;
state <= COMPUTE;
end else if(timeout) begin
state <= IDLE;
end else begin
clk_cnt <= clk_cnt + 1;
end
end
COMPUTE: begin
if(ref_diff < 16) begin
// Average it, Generate Div-16 + Additional Sub by 2 is due to clk_ctl module div n implementation
if(baud_16x > 1)
auto_baud_16x <= baud_16x -1;
else
auto_baud_16x <= 0;
state <= STOP_EDGE;
end else begin
state <= IDLE;
end
end
STOP_EDGE : begin
if(rxd_pedge) begin
state <= AUTO_DONE;
end else if(timeout) begin
state <= IDLE;
end else begin
clk_cnt <= clk_cnt + 1;
end
end
AUTO_DONE: begin
auto_tx_enb <= 1'b1;
auto_rx_enb <= 1'b1;
end
endcase
end
end
endmodule