mirror of https://github.com/zxdos/zxuno.git
261 lines
8.5 KiB
Verilog
261 lines
8.5 KiB
Verilog
`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
|