Приемо-передатчик последовательного порта



// последовательный приемопередатчик

// 38400бит/сек
// 8бит, без четности

module serial(
input wire reset,
input wire clk5, //5mHz
input wire rx, //serial receive (RX) line

input wire [7:0]sbyte, //byte to be sent
input wire send, //start sending on this impulse

output reg [7:0]rx_byte, //byte received
output reg rx_start_bit, //mark RX start bit
output reg rx_stop_bit, //mark RX stop bit

output reg rbyte_ready, //signal when rewceived byte is ready
output reg tx, //serial transmitter (TX) line
output reg busy, //transmitter is busy
output reg tx_start_bit, //mark TX start bit
output reg tx_stop_bit, //mark TX stop bit
output reg test
);

//скорость приема и передачи определяется этой константой
//она рассчитана из исх. тактовой частоты 5Mhz и желаемой скорости 38400
//как 5000000/38400 = 130
parameter RCONST = 128;

reg [10:0]cnt;

//счетчик длительности принимаемого бита
always @(posedge clk5 or posedge reset)
begin
if(reset)
cnt <= 0;
else
begin
if(cnt == RCONST || num_bits==0)
cnt <= 0;
else
cnt <= cnt + 1'b1;
end
end

reg [3:0]num_bits; //счетчик принятых бит
reg [8:0]shift_reg; //сдвиговый регистр приемника

//приемник
always @(posedge clk5 or posedge reset)
begin
if(reset)
begin
num_bits <= 0;
shift_reg <= 0;
end
else
begin
//прием начинается когда RX падает в ноль
if(num_bits==0 && rx==1'b0)
num_bits <= 10;
else
if(cnt == RCONST)
num_bits <= num_bits - 1'b1;

//фиксация принятого бита где-то посередине
if(cnt == RCONST/2)
shift_reg <= {rx,shift_reg[8:1]};
end
end

always
@*
begin
rbyte_ready = (cnt == RCONST)&(num_bits==1);
rx_start_bit = (num_bits==10);
rx_stop_bit = (num_bits==1);
test = rx_start_bit | rx_stop_bit;
end

//запоминаем принятый байт по окончании приема
always @(posedge clk5 or posedge reset)
if(reset)
begin
rx_byte <= 0;
end
else
begin
if(rbyte_ready)
rx_byte <= shift_reg[7:0];
end

//передатчик
reg [8:0]send_reg;
reg [3:0]send_num;
reg [9:0]send_cnt;

wire send_time; assign send_time = (send_cnt == RCONST);

always @(posedge clk5 or posedge reset)
begin
if(reset)
begin
send_reg <= 0;
send_num <= 0;
send_cnt <= 0;
end
else
begin
//передача начинается по сигналу send
if(send)
send_cnt <= 0;
else
if(send_time)
send_cnt <= 0;
else
send_cnt <= send_cnt + 1'b1;

if(send)
begin
//загружаем передаваемый байт в сдвиговый регистр по сигналу send
send_reg <= {sbyte,1'b0};
send_num <= 10;
end
else
if(send_time && send_num!=0)
begin
//выдвигаем передаваемый байт
send_reg <= {1'b1,send_reg[8:1]};
send_num <= send_num - 1'b1;
end
end
end

always @*
begin
busy = (send_num!=0);
tx_start_bit = (send_num==10);
tx_stop_bit = (send_num==1);
end

always @(posedge clk5)
tx = send_reg[0] | (send_num==0);

endmodule