216 lines
6.6 KiB
Systemverilog
216 lines
6.6 KiB
Systemverilog
`ifndef _VIP_UART_SV_
|
|
`define _VIP_UART_SV_
|
|
|
|
module vip_uart
|
|
(
|
|
input logic rx,
|
|
output logic tx
|
|
);
|
|
|
|
parameter CAPTURE = "none"; // "none": no capture, "hex": hex capture, "raw": raw capture(ascii)
|
|
parameter DW = 8;
|
|
parameter BAUD_RATE = 115200;
|
|
parameter CHK_MODE = "none";
|
|
parameter STOP_BITS = 1.0;
|
|
parameter BAUD_ERROR = 0.0;
|
|
parameter SAMPLES = 32;
|
|
parameter SAMPLE_TH = $floor(SAMPLES*0.9);
|
|
parameter IDLE_BYTES_MS = 0;
|
|
|
|
parameter RX_BIT_WIDTH_NS = 1.0/(BAUD_RATE) * 1e9;//receiption do not take baud rate skew into account
|
|
parameter TX_BIT_WIDTH_NS = 1.0/(BAUD_RATE * (1 - BAUD_ERROR)) * 1e9;//transmit takes baud rate skew into account
|
|
|
|
parameter TX_STOP_WIDTH_NS = STOP_BITS * TX_BIT_WIDTH_NS;
|
|
parameter RX_SAMPLE_WIDTH_NS = RX_BIT_WIDTH_NS/SAMPLES;
|
|
|
|
parameter integer STOP_BIT_SAMPLES = $ceil(STOP_BITS*SAMPLES);
|
|
|
|
logic rx_clk;
|
|
logic [SAMPLES-1:0] rx_sample_bits;
|
|
logic [STOP_BIT_SAMPLES-1:0] rx_sample_stop_bits;
|
|
logic [DW-1:0]rx_data;//valid while rx_valid =1
|
|
logic rx_valid;//asserted when a new word receiption has just completed,deasserted when new receiption starts,
|
|
logic parity_error;//asserted when a parity error has been detected,deasserted when new receiption starts
|
|
logic stop_error;//asserted when a stop error has been detected,deasserted when new receiption starts
|
|
|
|
initial begin
|
|
tx = 1;
|
|
rx_valid = 0;
|
|
parity_error = 0;
|
|
stop_error = 0;
|
|
end
|
|
|
|
initial begin
|
|
integer log_file;
|
|
string full_path;
|
|
string instance_name;
|
|
int last_dot_pos;
|
|
|
|
// Get the full hierarchical path into a string
|
|
full_path = $sformatf("%m");
|
|
|
|
instance_name = full_path;
|
|
|
|
if (CAPTURE != "none") begin
|
|
log_file = $fopen({instance_name,"_capture.log"},"w");
|
|
if(log_file == 0) begin
|
|
$error("Failed to open log file for writing.");
|
|
end
|
|
end
|
|
|
|
if (CAPTURE == "hex") begin
|
|
$fwrite(log_file,"# %s hex capture log:\n",instance_name);
|
|
forever begin
|
|
repeat(16) begin
|
|
@(posedge rx_valid);
|
|
$fwrite(log_file,"%02x ",rx_data);
|
|
$fwrite(log_file,"\n");
|
|
end
|
|
end
|
|
end else if (CAPTURE == "raw") begin
|
|
$fwrite(log_file,"// %s raw capture log:\n",instance_name);
|
|
forever @(posedge rx_valid) begin
|
|
$fwrite(log_file,"%c",rx_data);
|
|
end
|
|
end
|
|
end
|
|
|
|
task automatic send(logic [DW-1:0] data);
|
|
tx = 0;
|
|
#(TX_BIT_WIDTH_NS*1ns);
|
|
for(int i = 0;i < DW;i++) begin
|
|
tx = data[i];
|
|
#(TX_BIT_WIDTH_NS*1ns);
|
|
end
|
|
|
|
case (CHK_MODE)
|
|
"odd":begin
|
|
tx = (^data) ^ 1;
|
|
#(TX_BIT_WIDTH_NS*1ns);
|
|
end
|
|
"even":begin
|
|
tx = (^data) ^ 0;
|
|
#(TX_BIT_WIDTH_NS*1ns);
|
|
end
|
|
endcase
|
|
|
|
tx = 1;
|
|
#(TX_STOP_WIDTH_NS*1ns);
|
|
#(IDLE_BYTES_MS*1ms);
|
|
endtask
|
|
|
|
initial begin
|
|
rx_clk = 0;
|
|
forever begin
|
|
rx_clk =#(RX_BIT_WIDTH_NS/SAMPLES/2*1ns) ~rx_clk;//generate 32x clk for receive
|
|
end
|
|
end
|
|
|
|
|
|
function automatic logic vote(logic [SAMPLES-1:0] data,int th);
|
|
int count_ones = 0;
|
|
logic [SAMPLES-1:0] mask;
|
|
|
|
int i = 0;
|
|
do begin
|
|
i++;
|
|
end while(data[i] == 0 && i < SAMPLES);//jump over contineous zeros
|
|
|
|
do begin
|
|
i++;
|
|
end while(data[i] == 1 && i < SAMPLES);//jump over contineous ones
|
|
|
|
mask = ~(2**i-1);
|
|
if(i < SAMPLES && (data & mask != 0)) begin//the remaining should be all zeros
|
|
$warning("samples=%b,glitch occured",data);
|
|
end
|
|
|
|
foreach(data[i]) begin
|
|
count_ones = data[i] ? (count_ones + 1) : count_ones;//only logic high is weighted
|
|
end
|
|
|
|
if(count_ones >= th) begin//find the majority by threshold
|
|
return 1;
|
|
end else begin
|
|
return 0;
|
|
end;
|
|
endfunction
|
|
|
|
initial begin
|
|
forever begin
|
|
receive();
|
|
end
|
|
end
|
|
|
|
task automatic receive();
|
|
logic stop_bit;
|
|
logic calc_parity;
|
|
logic parity_bit;
|
|
logic rx_bit;
|
|
@(negedge rx);
|
|
rx_valid = 0;
|
|
parity_error = 0;
|
|
stop_error = 0;
|
|
|
|
sample_one_bit(rx_bit);
|
|
if(rx_bit == 0) begin
|
|
for(int i = 0;i<DW;i++)begin
|
|
sample_one_bit(rx_data[i]);
|
|
end
|
|
|
|
case (CHK_MODE)
|
|
"odd","even":begin
|
|
sample_one_bit(parity_bit);
|
|
end
|
|
endcase
|
|
|
|
do_sample(STOP_BIT_SAMPLES, rx_sample_stop_bits);
|
|
stop_bit = vote(rx_sample_stop_bits,STOP_BIT_SAMPLES*0.9);
|
|
|
|
calc_parity = (^rx_data) ^ parity_bit;
|
|
case (CHK_MODE)
|
|
"odd":begin
|
|
if(calc_parity != 1) begin
|
|
$warning("%s parity error,data=%08b,parity=%01b",CHK_MODE,rx_data,parity_bit);
|
|
parity_error = 1;
|
|
end
|
|
end
|
|
"even":begin
|
|
if(calc_parity != 0) begin
|
|
$warning("%s parity error,data=%08b,parity=%01b",CHK_MODE,rx_data,parity_bit);
|
|
parity_error = 1;
|
|
end
|
|
end
|
|
endcase
|
|
|
|
if(stop_bit != 1) begin
|
|
$warning("stop error,stop=%f,samples = %b",STOP_BITS,rx_sample_bits);
|
|
stop_error = 1;
|
|
end
|
|
|
|
rx_valid = 1;
|
|
end else begin
|
|
$display("searching start bit...,samples = %b",rx_sample_bits);
|
|
end
|
|
|
|
endtask
|
|
|
|
task automatic sample_one_bit(output logic rx_bit);
|
|
do_sample(SAMPLES, rx_sample_bits);
|
|
rx_bit = vote(rx_sample_bits, SAMPLE_TH);
|
|
endtask
|
|
|
|
task automatic do_sample(input int xpoints, output logic [(STOP_BIT_SAMPLES > SAMPLES ? STOP_BIT_SAMPLES : SAMPLES)-1:0] rx_vec);
|
|
rx_vec = 0;
|
|
#((RX_SAMPLE_WIDTH_NS/2.0)*1ns);
|
|
rx_vec[0] = rx;
|
|
for(int i = 1;i<xpoints;i++) begin
|
|
#(RX_SAMPLE_WIDTH_NS*1ns);
|
|
rx_vec[i] = rx;
|
|
end
|
|
#((RX_SAMPLE_WIDTH_NS/2.0)*1ns);
|
|
endtask
|
|
endmodule
|
|
|
|
`endif
|