zxuno-git/cores/Spectrum/common/uart.v

261 lines
8.5 KiB
Verilog
Raw Permalink Blame History

`timescale 1ns / 1ps
`default_nettype none
// This file is part of the ZXUNO Spectrum core.
// Creation date is 19:56:26 2015-10-17 by Miguel Angel Rodriguez Jodar
// (c)2014-2020 ZXUNO association.
// ZXUNO official repository: http://svn.zxuno.com/svn/zxuno
// Username: guest Password: zxuno
// Github repository for this core: https://github.com/mcleod-ideafix/zxuno_spectrum_core
//
// ZXUNO Spectrum core is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ZXUNO Spectrum core is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with the ZXUNO Spectrum core. If not, see <https://www.gnu.org/licenses/>.
//
// Any distributed copy of this file must keep this notice intact.
module uart (
// CPU interface
input wire clk, // 28 MHz
input wire [7:0] txdata,
input wire txbegin,
output wire txbusy,
output wire [7:0] rxdata,
output wire rxrecv,
input wire data_read,
// RS232 interface
input wire rx,
output wire tx,
output wire rts
);
parameter CLK = 28000000;
uart_tx #(.CLK(CLK)) transmitter (
.clk(clk),
.txdata(txdata),
.txbegin(txbegin),
.txbusy(txbusy),
.tx(tx)
);
uart_rx #(.CLK(CLK)) receiver (
.clk(clk),
.rxdata(rxdata),
.rxrecv(rxrecv),
.data_read(data_read),
.rx(rx),
.rts(rts)
);
endmodule
module uart_tx (
// CPU interface
input wire clk, // 28 MHz
input wire [7:0] txdata,
input wire txbegin,
output wire txbusy,
// RS232 interface
output reg tx
);
initial tx = 1'b1;
parameter CLK = 28000000;
parameter BPS = 115200;
parameter PERIOD = CLK / BPS;
parameter
IDLE = 2'd0,
START = 2'd1,
BIT = 2'd2,
STOP = 2'd3;
reg [7:0] txdata_reg;
reg [1:0] state = IDLE;
reg [15:0] bpscounter;
reg [2:0] bitcnt;
reg txbusy_ff = 1'b0;
assign txbusy = txbusy_ff;
always @(posedge clk) begin
if (txbegin == 1'b1 && txbusy_ff == 1'b0 && state == IDLE) begin
txdata_reg <= txdata;
txbusy_ff <= 1'b1;
state <= START;
bpscounter <= PERIOD;
end
if (txbegin == 1'b0 && txbusy_ff == 1'b1) begin
case (state)
START:
begin
tx <= 1'b0;
bpscounter <= bpscounter - 16'd1;
if (bpscounter == 16'h0000) begin
bpscounter <= PERIOD;
bitcnt <= 3'd7;
state <= BIT;
end
end
BIT:
begin
tx <= txdata_reg[0];
bpscounter <= bpscounter - 16'd1;
if (bpscounter == 16'h0000) begin
txdata_reg <= {1'b0, txdata_reg[7:1]};
bpscounter <= PERIOD;
bitcnt <= bitcnt - 3'd1;
if (bitcnt == 3'd0) begin
state <= STOP;
end
end
end
STOP:
begin
tx <= 1'b1;
bpscounter <= bpscounter - 16'd1;
if (bpscounter == 16'h0000) begin
bpscounter <= PERIOD;
txbusy_ff <= 1'b0;
state <= IDLE;
end
end
default:
begin
state <= IDLE;
txbusy_ff <= 1'b0;
end
endcase
end
end
endmodule
module uart_rx (
// CPU interface
input wire clk, // 28 MHz
output reg [7:0] rxdata,
output reg rxrecv,
input wire data_read,
// RS232 interface
input wire rx,
output reg rts
);
initial rxrecv = 1'b0;
initial rts = 1'b0;
parameter CLK = 28000000;
parameter BPS = 115200;
parameter PERIOD = CLK / BPS;
parameter HALFPERIOD = PERIOD / 2;
parameter
IDLE = 3'd0,
START = 3'd1,
BIT = 3'd2,
STOP = 3'd3,
WAIT = 3'd4;
// Sincronizacin de se<73>ales externas
reg [1:0] rx_ff = 2'b00;
always @(posedge clk) begin
rx_ff <= {rx_ff[0], rx};
end
wire rx_is_1 = (rx_ff == 2'b11);
wire rx_is_0 = (rx_ff == 2'b00);
wire rx_negedge = (rx_ff == 2'b10);
reg [15:0] bpscounter;
reg [2:0] state = IDLE;
reg [2:0] bitcnt;
reg [7:0] rxshiftreg;
always @(posedge clk) begin
case (state)
IDLE:
begin
rts <= 1'b0; // permitimos la recepci<63>n
rxrecv <= 1'b0; // si estamos aqui, es porque no hay bytes pendientes de leer
if (rx_negedge) begin
bpscounter <= PERIOD - 2; // porque ya hemos perdido 2 ciclos detectando el flanco negativo
state <= START;
end
end
START:
begin
bpscounter <= bpscounter - 16'd1;
if (bpscounter == HALFPERIOD) begin // sampleamos el bit a mitad de ciclo
if (!rx_is_0) begin // si no era una se<73>al de START de verdad
state <= IDLE;
end
end
else if (bpscounter == 16'h0000) begin
bpscounter <= PERIOD;
rxshiftreg <= 8'h00; // aqui iremos guardando los bits recibidos
bitcnt <= 3'd7;
state <= BIT;
end
end
BIT:
begin
bpscounter <= bpscounter - 16'd1;
if (bpscounter == HALFPERIOD) begin // sampleamos el bit a mitad de ciclo
if (rx_is_1) begin
rxshiftreg <= {1'b1, rxshiftreg[7:1]}; // los bits entran por la izquierda, del LSb al MSb
end
else if (rx_is_0) begin
rxshiftreg <= {1'b0, rxshiftreg[7:1]};
end
else begin
state <= IDLE;
end
end
else if (bpscounter == 16'h0000) begin
bitcnt <= bitcnt - 3'd1;
bpscounter <= PERIOD;
// if (bitcnt == 3'd3)
// rts <= 1'b1;
if (bitcnt == 3'd0)
state <= STOP;
end
end
//rts en stop: se come 1 de cada dos chars
//rts a mitad de stop o antes: en vez de ok recibo "-" pero hace eco bien
STOP:
begin
bpscounter <= bpscounter - 16'd1;
if (bpscounter == HALFPERIOD) begin
if (!rx_is_1) begin // si no era una se<73>al de STOP de verdad
state <= IDLE;
end
else begin
rxrecv <= 1'b1;
rts <= 1'b1;
rxdata <= rxshiftreg;
state <= WAIT;
end
end
end
WAIT:
begin
if (data_read == 1'b1) begin
state <= IDLE;
end
end
default: state <= IDLE;
endcase
end
endmodule